@fluidframework/driver-base 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.225277

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 (73) hide show
  1. package/.eslintrc.js +8 -7
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +117 -0
  4. package/README.md +37 -1
  5. package/api-extractor-esm.json +4 -0
  6. package/api-extractor-lint.json +4 -0
  7. package/api-extractor.json +2 -2
  8. package/api-report/driver-base.api.md +112 -0
  9. package/dist/{documentDeltaConnection.js → documentDeltaConnection.cjs} +228 -113
  10. package/dist/documentDeltaConnection.cjs.map +1 -0
  11. package/dist/documentDeltaConnection.d.ts +34 -18
  12. package/dist/documentDeltaConnection.d.ts.map +1 -1
  13. package/dist/driver-base-alpha.d.ts +26 -0
  14. package/dist/driver-base-beta.d.ts +30 -0
  15. package/dist/driver-base-public.d.ts +30 -0
  16. package/dist/driver-base-untrimmed.d.ts +213 -0
  17. package/dist/driverUtils.cjs +146 -0
  18. package/dist/driverUtils.cjs.map +1 -0
  19. package/dist/driverUtils.d.ts +36 -0
  20. package/dist/driverUtils.d.ts.map +1 -0
  21. package/dist/index.cjs +14 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
  26. package/dist/packageVersion.cjs.map +1 -0
  27. package/dist/packageVersion.d.ts +1 -1
  28. package/dist/packageVersion.d.ts.map +1 -1
  29. package/dist/tsdoc-metadata.json +11 -0
  30. package/lib/{documentDeltaConnection.d.ts → documentDeltaConnection.d.mts} +35 -19
  31. package/lib/documentDeltaConnection.d.mts.map +1 -0
  32. package/lib/{documentDeltaConnection.js → documentDeltaConnection.mjs} +218 -104
  33. package/lib/documentDeltaConnection.mjs.map +1 -0
  34. package/lib/driver-base-alpha.d.mts +26 -0
  35. package/lib/driver-base-beta.d.mts +30 -0
  36. package/lib/driver-base-public.d.mts +30 -0
  37. package/lib/driver-base-untrimmed.d.mts +213 -0
  38. package/lib/driverUtils.d.mts +36 -0
  39. package/lib/driverUtils.d.mts.map +1 -0
  40. package/lib/driverUtils.mjs +140 -0
  41. package/lib/driverUtils.mjs.map +1 -0
  42. package/lib/index.d.mts +7 -0
  43. package/lib/index.d.mts.map +1 -0
  44. package/lib/index.mjs +7 -0
  45. package/lib/index.mjs.map +1 -0
  46. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +2 -2
  47. package/lib/packageVersion.d.mts.map +1 -0
  48. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  49. package/lib/packageVersion.mjs.map +1 -0
  50. package/package.json +151 -43
  51. package/{lib/index.d.ts → prettier.config.cjs} +4 -2
  52. package/src/documentDeltaConnection.ts +748 -563
  53. package/src/driverUtils.ts +159 -0
  54. package/src/index.ts +2 -1
  55. package/src/packageVersion.ts +1 -1
  56. package/tsc-multi.test.json +4 -0
  57. package/tsconfig.json +11 -13
  58. package/dist/documentDeltaConnection.js.map +0 -1
  59. package/dist/index.js +0 -18
  60. package/dist/index.js.map +0 -1
  61. package/dist/packageVersion.js.map +0 -1
  62. package/lib/documentDeltaConnection.d.ts.map +0 -1
  63. package/lib/documentDeltaConnection.js.map +0 -1
  64. package/lib/index.d.ts.map +0 -1
  65. package/lib/index.js +0 -6
  66. package/lib/index.js.map +0 -1
  67. package/lib/packageVersion.d.ts.map +0 -1
  68. package/lib/packageVersion.js.map +0 -1
  69. package/lib/test/types/validateDriverBasePrevious.d.ts +0 -2
  70. package/lib/test/types/validateDriverBasePrevious.d.ts.map +0 -1
  71. package/lib/test/types/validateDriverBasePrevious.js +0 -4
  72. package/lib/test/types/validateDriverBasePrevious.js.map +0 -1
  73. package/tsconfig.esnext.json +0 -7
@@ -2,32 +2,54 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { assert, extractLogSafeErrorProperties } from "@fluidframework/common-utils";
6
- import { createGenericNetworkError } from "@fluidframework/driver-utils";
5
+ import { assert } from "@fluidframework/core-utils";
6
+ import { UsageError, createGenericNetworkError } from "@fluidframework/driver-utils";
7
7
  import { ScopeType, } from "@fluidframework/protocol-definitions";
8
- import { ChildLogger, getCircularReplacer, loggerToMonitoringContext, EventEmitterWithErrorHandling, } from "@fluidframework/telemetry-utils";
9
- // For now, this package is versioned and released in unison with the specific drivers
10
- import { pkgVersion as driverVersion } from "./packageVersion";
8
+ import { LogLevel } from "@fluidframework/core-interfaces";
9
+ import { extractLogSafeErrorProperties, getCircularReplacer, EventEmitterWithErrorHandling, normalizeError, createChildMonitoringContext, } from "@fluidframework/telemetry-utils";
10
+ import { pkgVersion as driverVersion } from "./packageVersion.mjs";
11
11
  /**
12
- * Represents a connection to a stream of delta updates
12
+ * Represents a connection to a stream of delta updates.
13
+ * @internal
13
14
  */
14
15
  export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
16
+ get hasDetails() {
17
+ return !!this._details;
18
+ }
19
+ get disposed() {
20
+ assert(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
21
+ return this._disposed;
22
+ }
23
+ /**
24
+ * @deprecated Implementors should manage their own logger or monitoring context
25
+ */
26
+ get logger() {
27
+ return this.mc.logger;
28
+ }
29
+ get details() {
30
+ if (!this._details) {
31
+ throw new Error("Internal error: calling method before _details is initialized!");
32
+ }
33
+ return this._details;
34
+ }
15
35
  /**
16
36
  * @param socket - websocket to be used
17
37
  * @param documentId - ID of the document
18
38
  * @param logger - for reporting telemetry events
19
39
  * @param enableLongPollingDowngrades - allow connection to be downgraded to long-polling on websocket failure
20
40
  */
21
- constructor(socket, documentId, logger, enableLongPollingDowngrades = false) {
41
+ constructor(socket, documentId, logger, enableLongPollingDowngrades = false, connectionId) {
22
42
  super((name, error) => {
43
+ this.addPropsToError(error);
23
44
  logger.sendErrorEvent({
24
45
  eventName: "DeltaConnection:EventException",
25
- name,
46
+ name: name,
26
47
  }, error);
27
48
  });
28
49
  this.socket = socket;
29
50
  this.documentId = documentId;
30
51
  this.enableLongPollingDowngrades = enableLongPollingDowngrades;
52
+ this.connectionId = connectionId;
31
53
  // Listen for ops sent before we receive a response to connect_document
32
54
  this.queuedMessages = [];
33
55
  this.queuedSignals = [];
@@ -49,10 +71,15 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
49
71
  this.queuedMessages.push(...msgs);
50
72
  };
51
73
  this.earlySignalHandler = (msg) => {
52
- this.queuedSignals.push(msg);
74
+ if (Array.isArray(msg)) {
75
+ this.queuedSignals.push(...msg);
76
+ }
77
+ else {
78
+ this.queuedSignals.push(msg);
79
+ }
53
80
  };
54
- this.mc = loggerToMonitoringContext(ChildLogger.create(logger, "DeltaConnection"));
55
- this.on("newListener", (event, listener) => {
81
+ this.mc = createChildMonitoringContext({ logger, namespace: "DeltaConnection" });
82
+ this.on("newListener", (event, _listener) => {
56
83
  assert(!this.disposed, 0x20a /* "register for event on disposed object" */);
57
84
  // Some events are already forwarded - see this.addTrackedListener() calls in initialize().
58
85
  if (DocumentDeltaConnection.eventsAlwaysForwarded.includes(event)) {
@@ -69,31 +96,29 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
69
96
  // though some logic (naming assert in initialMessages getter) might need to be adjusted (it becomes noop)
70
97
  assert((this.listeners(event).length !== 0) === this.trackedListeners.has(event), 0x20b /* "mismatch" */);
71
98
  if (!this.trackedListeners.has(event)) {
72
- this.addTrackedListener(event, (...args) => {
73
- this.emit(event, ...args);
74
- });
99
+ if (event === "pong") {
100
+ // Empty callback for tracking purposes in this class
101
+ this.trackedListeners.set("pong", () => { });
102
+ const sendPingLoop = () => {
103
+ const start = Date.now();
104
+ this.socket.volatile?.emit("ping", () => {
105
+ this.emit("pong", Date.now() - start);
106
+ // Schedule another ping event in 1 minute
107
+ this.trackLatencyTimeout = setTimeout(() => {
108
+ sendPingLoop();
109
+ }, 1000 * 60);
110
+ });
111
+ };
112
+ sendPingLoop();
113
+ }
114
+ else {
115
+ this.addTrackedListener(event, (...args) => {
116
+ this.emit(event, ...args);
117
+ });
118
+ }
75
119
  }
76
120
  });
77
121
  }
78
- get hasDetails() {
79
- return !!this._details;
80
- }
81
- get disposed() {
82
- assert(this._disposed || this.socket.connected, 0x244 /* "Socket is closed, but connection is not!" */);
83
- return this._disposed;
84
- }
85
- /**
86
- * @deprecated - Implementors should manage their own logger or monitoring context
87
- */
88
- get logger() {
89
- return this.mc.logger;
90
- }
91
- get details() {
92
- if (!this._details) {
93
- throw new Error("Internal error: calling method before _details is initialized!");
94
- }
95
- return this._details;
96
- }
97
122
  /**
98
123
  * Get the ID of the client who is sending the message
99
124
  *
@@ -146,7 +171,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
146
171
  get serviceConfiguration() {
147
172
  return this.details.serviceConfiguration;
148
173
  }
149
- checkNotClosed() {
174
+ checkNotDisposed() {
150
175
  assert(!this.disposed, 0x20c /* "connection disposed" */);
151
176
  }
152
177
  /**
@@ -155,7 +180,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
155
180
  * @returns messages sent during the connection
156
181
  */
157
182
  get initialMessages() {
158
- this.checkNotClosed();
183
+ this.checkNotDisposed();
159
184
  // If we call this when the earlyOpHandler is not attached, then the queuedMessages may not include the
160
185
  // latest ops. This could possibly indicate that initialMessages was called twice.
161
186
  assert(this.earlyOpHandlerAttached, 0x08e /* "Potentially missed initial messages" */);
@@ -177,7 +202,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
177
202
  * @returns signals sent during the connection
178
203
  */
179
204
  get initialSignals() {
180
- this.checkNotClosed();
205
+ this.checkNotDisposed();
181
206
  assert(this.listeners("signal").length !== 0, 0x090 /* "No signal handler is setup!" */);
182
207
  this.removeEarlySignalHandler();
183
208
  if (this.queuedSignals.length > 0) {
@@ -194,7 +219,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
194
219
  * @returns initial client list sent during the connection
195
220
  */
196
221
  get initialClients() {
197
- this.checkNotClosed();
222
+ this.checkNotDisposed();
198
223
  return this.details.initialClients;
199
224
  }
200
225
  emitMessages(type, messages) {
@@ -205,59 +230,89 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
205
230
  this.socket.emit(type, this.clientId, messages);
206
231
  }
207
232
  }
208
- submitCore(type, messages) {
209
- this.emitMessages(type, [messages]);
210
- }
211
233
  /**
212
234
  * Submits a new delta operation to the server
213
235
  *
214
236
  * @param message - delta operation to submit
215
237
  */
216
238
  submit(messages) {
217
- this.checkNotClosed();
218
- this.submitCore("submitOp", messages);
239
+ this.checkNotDisposed();
240
+ this.emitMessages("submitOp", [messages]);
219
241
  }
220
242
  /**
221
243
  * Submits a new signal to the server
222
244
  *
223
- * @param message - signal to submit
245
+ * @param content - Content of the signal.
246
+ * @param targetClientId - When specified, the signal is only sent to the provided client id.
247
+ */
248
+ submitSignal(content, targetClientId) {
249
+ this.checkNotDisposed();
250
+ if (targetClientId && this.details.supportedFeatures?.submit_signals_v2 !== true) {
251
+ throw new UsageError("Sending signals to specific client ids is not supported.");
252
+ }
253
+ this.emitMessages("submitSignal", [[content]]);
254
+ }
255
+ /**
256
+ * Disconnect from the websocket and close the websocket too.
224
257
  */
225
- submitSignal(message) {
226
- this.checkNotClosed();
227
- this.submitCore("submitSignal", [message]);
258
+ closeSocket(error) {
259
+ if (this._disposed) {
260
+ // This would be rare situation due to complexity around socket emitting events.
261
+ return;
262
+ }
263
+ this.closeSocketCore(error);
264
+ }
265
+ closeSocketCore(error) {
266
+ this.disconnect(error);
228
267
  }
229
268
  /**
230
- * Disconnect from the websocket, and permanently disable this DocumentDeltaConnection.
269
+ * Disconnect from the websocket, and permanently disable this DocumentDeltaConnection and close the socket.
270
+ * However the OdspDocumentDeltaConnection differ in dispose as in there we don't close the socket. There is no
271
+ * multiplexing here, so we need to close the socket here.
231
272
  */
232
273
  dispose() {
233
- this.disposeCore(false, // socketProtocolError
234
- createGenericNetworkError(
274
+ this.logger.sendTelemetryEvent({
275
+ eventName: "ClientClosingDeltaConnection",
276
+ driverVersion,
277
+ details: JSON.stringify({
278
+ ...this.getConnectionDetailsProps(),
279
+ }),
280
+ });
281
+ this.disconnect(createGenericNetworkError(
235
282
  // pre-0.58 error message: clientClosingConnection
236
283
  "Client closing delta connection", { canRetry: true }, { driverVersion }));
237
284
  }
238
- disposeCore(socketProtocolError, err) {
285
+ disconnect(err) {
239
286
  // Can't check this.disposed here, as we get here on socket closure,
240
287
  // so _disposed & socket.connected might be not in sync while processing
241
288
  // "dispose" event.
242
289
  if (this._disposed) {
243
290
  return;
244
291
  }
292
+ if (this.trackLatencyTimeout !== undefined) {
293
+ clearTimeout(this.trackLatencyTimeout);
294
+ this.trackLatencyTimeout = undefined;
295
+ }
245
296
  // We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by
246
297
  // DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is
247
298
  // important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that
248
299
  // OdspDocumentDeltaConnection may still send disconnect_document which is allowed; this is only intended
249
300
  // to prevent normal messages from being emitted.
250
301
  this._disposed = true;
302
+ // Remove all listeners listening on the socket. These are listeners on socket and not on this connection
303
+ // object. Anyway since we have disposed this connection object, nobody should listen to event on socket
304
+ // anymore.
251
305
  this.removeTrackedListeners();
252
- this.disconnect(socketProtocolError, err);
306
+ // Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.
307
+ this.disconnectCore();
308
+ // Let user of connection object know about disconnect.
309
+ this.emit("disconnect", err);
253
310
  }
254
311
  /**
255
312
  * Disconnect from the websocket.
256
- * @param socketProtocolError - true if error happened on socket / socket.io protocol level
257
- * (not on Fluid protocol level)
258
313
  * @param reason - reason for disconnect
259
314
  */
260
- disconnect(socketProtocolError, reason) {
315
+ disconnectCore() {
261
316
  this.socket.disconnect();
262
317
  }
263
318
  async initialize(connectMessage, timeout) {
@@ -267,23 +322,56 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
267
322
  // Socket.io's reconnect_attempt event is unreliable, so we track connect_error count instead.
268
323
  let internalSocketConnectionFailureCount = 0;
269
324
  const isInternalSocketReconnectionEnabled = () => this.socket.io.reconnection();
270
- const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled()
271
- ? this.socket.io.reconnectionAttempts()
272
- : 0;
325
+ const getMaxInternalSocketReconnectionAttempts = () => isInternalSocketReconnectionEnabled() ? this.socket.io.reconnectionAttempts() : 0;
273
326
  const getMaxAllowedInternalSocketConnectionFailures = () => getMaxInternalSocketReconnectionAttempts() + 1;
274
327
  this._details = await new Promise((resolve, reject) => {
275
- const fail = (socketProtocolError, err) => {
276
- this.disposeCore(socketProtocolError, err);
328
+ const failAndCloseSocket = (err) => {
329
+ try {
330
+ this.closeSocket(err);
331
+ }
332
+ catch (failError) {
333
+ const normalizedError = this.addPropsToError(failError);
334
+ this.logger.sendErrorEvent({ eventName: "CloseSocketError" }, normalizedError);
335
+ }
336
+ reject(err);
337
+ };
338
+ const failConnection = (err) => {
339
+ try {
340
+ this.disconnect(err);
341
+ }
342
+ catch (failError) {
343
+ const normalizedError = this.addPropsToError(failError);
344
+ this.logger.sendErrorEvent({ eventName: "FailConnectionError" }, normalizedError);
345
+ }
277
346
  reject(err);
278
347
  };
348
+ // Immediately set the connection timeout.
349
+ // Give extra 2 seconds for handshake on top of socket connection timeout.
350
+ this.socketConnectionTimeout = setTimeout(() => {
351
+ failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
352
+ }, timeout + 2000);
279
353
  // Listen for connection issues
280
354
  this.addConnectionListener("connect_error", (error) => {
281
- var _a;
282
355
  internalSocketConnectionFailureCount++;
283
356
  let isWebSocketTransportError = false;
284
357
  try {
285
- const description = error === null || error === void 0 ? void 0 : error.description;
286
- if (description && typeof description === "object") {
358
+ const description = error?.description;
359
+ const context = error?.context;
360
+ if (context && typeof context === "object") {
361
+ const statusText = context.statusText?.code;
362
+ // Self-Signed Certificate ErrorCode Found in error.context
363
+ if (statusText === "DEPTH_ZERO_SELF_SIGNED_CERT") {
364
+ failAndCloseSocket(this.createErrorObject("connect_error", error, false));
365
+ return;
366
+ }
367
+ }
368
+ else if (description && typeof description === "object") {
369
+ const errorCode = description.error?.code;
370
+ // Self-Signed Certificate ErrorCode Found in error.description
371
+ if (errorCode === "DEPTH_ZERO_SELF_SIGNED_CERT") {
372
+ failAndCloseSocket(this.createErrorObject("connect_error", error, false));
373
+ return;
374
+ }
287
375
  if (error.type === "TransportError") {
288
376
  isWebSocketTransportError = true;
289
377
  }
@@ -295,7 +383,7 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
295
383
  // Handle socket transport downgrading when not offline.
296
384
  if (isWebSocketTransportError &&
297
385
  this.enableLongPollingDowngrades &&
298
- ((_a = this.socket.io.opts.transports) === null || _a === void 0 ? void 0 : _a[0]) !== "polling") {
386
+ this.socket.io.opts.transports?.[0] !== "polling") {
299
387
  // Downgrade transports to polling upgrade mechanism.
300
388
  this.socket.io.opts.transports = ["polling", "websocket"];
301
389
  // Don't alter reconnection behavior if already enabled.
@@ -307,15 +395,16 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
307
395
  }
308
396
  // Allow built-in socket.io reconnection handling.
309
397
  if (isInternalSocketReconnectionEnabled() &&
310
- internalSocketConnectionFailureCount < getMaxAllowedInternalSocketConnectionFailures()) {
398
+ internalSocketConnectionFailureCount <
399
+ getMaxAllowedInternalSocketConnectionFailures()) {
311
400
  // Reconnection is enabled and maximum reconnect attempts have not been reached.
312
401
  return;
313
402
  }
314
- fail(true, this.createErrorObject("connect_error", error));
403
+ failAndCloseSocket(this.createErrorObject("connect_error", error));
315
404
  });
316
405
  // Listen for timeouts
317
406
  this.addConnectionListener("connect_timeout", () => {
318
- fail(true, this.createErrorObject("connect_timeout"));
407
+ failAndCloseSocket(this.createErrorObject("connect_timeout"));
319
408
  });
320
409
  this.addConnectionListener("connect_document_success", (response) => {
321
410
  // If we sent a nonce and the server supports nonces, check that the nonces match
@@ -331,16 +420,20 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
331
420
  // The only time we expect a mismatch in requested/actual is if we lack write permissions
332
421
  // In this case we will get "read", even if we requested "write"
333
422
  if (actualMode !== requestedMode) {
334
- fail(false, this.createErrorObject("connect_document_success", "Connected in a different mode than was requested", false));
423
+ failConnection(this.createErrorObject("connect_document_success", "Connected in a different mode than was requested", false));
335
424
  return;
336
425
  }
337
426
  }
338
427
  else {
339
428
  if (actualMode === "write") {
340
- fail(false, this.createErrorObject("connect_document_success", "Connected in write mode without write permissions", false));
429
+ failConnection(this.createErrorObject("connect_document_success", "Connected in write mode without write permissions", false));
341
430
  return;
342
431
  }
343
432
  }
433
+ this.logger.sendTelemetryEvent({
434
+ eventName: "ConnectDocumentSuccess",
435
+ pendingClientId: response.clientId,
436
+ }, undefined, LogLevel.verbose);
344
437
  this.checkpointSequenceNumber = response.checkpointSequenceNumber;
345
438
  this.removeConnectionListeners();
346
439
  resolve(response);
@@ -348,20 +441,20 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
348
441
  // Socket can be disconnected while waiting for Fluid protocol messages
349
442
  // (connect_document_error / connect_document_success), as well as before DeltaManager
350
443
  // had a chance to register its handlers.
351
- this.addTrackedListener("disconnect", (reason) => {
352
- const err = this.createErrorObject("disconnect", reason);
353
- this.emit("disconnect", err);
354
- fail(true, err);
444
+ this.addTrackedListener("disconnect", (reason, details) => {
445
+ failAndCloseSocket(this.createErrorObjectWithProps("disconnect", reason, {
446
+ socketErrorType: details?.context?.type,
447
+ // https://www.rfc-editor.org/rfc/rfc6455#section-7.4
448
+ socketCode: details?.context?.code,
449
+ }));
355
450
  });
356
- this.addTrackedListener("error", ((error) => {
357
- // First, raise an error event, to give clients a chance to observe error contents
451
+ this.addTrackedListener("error", (error) => {
358
452
  // This includes "Invalid namespace" error, which we consider critical (reconnecting will not help)
359
453
  const err = this.createErrorObject("error", error, error !== "Invalid namespace");
360
- this.emit("error", err);
361
454
  // Disconnect socket - required if happened before initial handshake
362
- fail(true, err);
363
- }));
364
- this.addConnectionListener("connect_document_error", ((error) => {
455
+ failAndCloseSocket(err);
456
+ });
457
+ this.addConnectionListener("connect_document_error", (error) => {
365
458
  // If we sent a nonce and the server supports nonces, check that the nonces match
366
459
  if (connectMessage.nonce !== undefined &&
367
460
  error.nonce !== undefined &&
@@ -370,16 +463,30 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
370
463
  }
371
464
  // This is not an socket.io error - it's Fluid protocol error.
372
465
  // In this case fail connection and indicate that we were unable to create connection
373
- fail(false, this.createErrorObject("connect_document_error", error));
374
- }));
466
+ failConnection(this.createErrorObject("connect_document_error", error));
467
+ });
375
468
  this.socket.emit("connect_document", connectMessage);
376
- // Give extra 2 seconds for handshake on top of socket connection timeout
377
- this.socketConnectionTimeout = setTimeout(() => {
378
- fail(false, this.createErrorObject("orderingServiceHandshakeTimeout"));
379
- }, timeout + 2000);
380
469
  });
381
470
  assert(!this.disposed, 0x246 /* "checking consistency of socket & _disposed flags" */);
382
471
  }
472
+ addPropsToError(errorToBeNormalized) {
473
+ const normalizedError = normalizeError(errorToBeNormalized, {
474
+ props: {
475
+ details: JSON.stringify({
476
+ ...this.getConnectionDetailsProps(),
477
+ }),
478
+ },
479
+ });
480
+ return normalizedError;
481
+ }
482
+ getConnectionDetailsProps() {
483
+ return {
484
+ disposed: this._disposed,
485
+ socketConnected: this.socket?.connected,
486
+ clientId: this._details?.clientId,
487
+ connectionId: this.connectionId,
488
+ };
489
+ }
383
490
  removeEarlyOpHandler() {
384
491
  this.socket.removeListener("op", this.earlyOpHandler);
385
492
  this.earlyOpHandlerAttached = false;
@@ -418,33 +525,40 @@ export class DocumentDeltaConnection extends EventEmitterWithErrorHandling {
418
525
  }
419
526
  this.connectionListeners.clear();
420
527
  }
528
+ getErrorMessage(error) {
529
+ if (error?.type !== "TransportError") {
530
+ return extractLogSafeErrorProperties(error, true).message;
531
+ }
532
+ // JSON.stringify drops Error.message
533
+ const messagePrefix = error?.message !== undefined ? `${error.message}: ` : "";
534
+ // Websocket errors reported by engine.io-client.
535
+ // They are Error objects with description containing WS error and description = "TransportError"
536
+ // Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
537
+ return `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
538
+ }
539
+ createErrorObjectWithProps(handler, error, props, canRetry = true) {
540
+ return createGenericNetworkError(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
541
+ ...props,
542
+ driverVersion,
543
+ details: JSON.stringify({
544
+ ...this.getConnectionDetailsProps(),
545
+ }),
546
+ });
547
+ }
421
548
  /**
422
549
  * Error raising for socket.io issues
423
550
  */
424
551
  createErrorObject(handler, error, canRetry = true) {
425
- // Note: we suspect the incoming error object is either:
426
- // - a string: log it in the message (if not a string, it may contain PII but will print as [object Object])
427
- // - an Error object thrown by socket.io engine. Be careful with not recording PII!
428
- let message;
429
- if ((error === null || error === void 0 ? void 0 : error.type) === "TransportError") {
430
- // JSON.stringify drops Error.message
431
- const messagePrefix = ((error === null || error === void 0 ? void 0 : error.message) !== undefined)
432
- ? `${error.message}: `
433
- : "";
434
- // Websocket errors reported by engine.io-client.
435
- // They are Error objects with description containing WS error and description = "TransportError"
436
- // Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
437
- message = `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
438
- }
439
- else {
440
- message = extractLogSafeErrorProperties(error).message;
441
- }
442
- const errorObj = createGenericNetworkError(`socket.io (${handler}): ${message}`, { canRetry }, { driverVersion });
443
- return errorObj;
552
+ return createGenericNetworkError(`socket.io (${handler}): ${this.getErrorMessage(error)}`, { canRetry }, {
553
+ driverVersion,
554
+ details: JSON.stringify({
555
+ ...this.getConnectionDetailsProps(),
556
+ }),
557
+ });
444
558
  }
445
559
  }
446
560
  DocumentDeltaConnection.eventsToForward = ["nack", "op", "signal", "pong"];
447
561
  // WARNING: These are critical events that we can't miss, so registration for them has to be in place at all times!
448
562
  // Including before handshake is over, and after that (but before DeltaManager had a chance to put its own handlers)
449
563
  DocumentDeltaConnection.eventsAlwaysForwarded = ["disconnect", "error"];
450
- //# sourceMappingURL=documentDeltaConnection.js.map
564
+ //# sourceMappingURL=documentDeltaConnection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentDeltaConnection.mjs","sourceRoot":"","sources":["../src/documentDeltaConnection.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils\";\nimport {\n\tIAnyDriverError,\n\tIDocumentDeltaConnection,\n\tIDocumentDeltaConnectionEvents,\n} from \"@fluidframework/driver-definitions\";\nimport { UsageError, createGenericNetworkError } from \"@fluidframework/driver-utils\";\nimport {\n\tConnectionMode,\n\tIClientConfiguration,\n\tIConnect,\n\tIConnected,\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n\tISignalClient,\n\tISignalMessage,\n\tITokenClaims,\n\tScopeType,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryProperties, LogLevel } from \"@fluidframework/core-interfaces\";\nimport {\n\tITelemetryLoggerExt,\n\textractLogSafeErrorProperties,\n\tgetCircularReplacer,\n\tMonitoringContext,\n\tEventEmitterWithErrorHandling,\n\tnormalizeError,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils\";\nimport type { Socket } from \"socket.io-client\";\n// For now, this package is versioned and released in unison with the specific drivers\nimport { pkgVersion as driverVersion } from \"./packageVersion\";\n\n/**\n * Represents a connection to a stream of delta updates.\n * @internal\n */\nexport class DocumentDeltaConnection\n\textends EventEmitterWithErrorHandling<IDocumentDeltaConnectionEvents>\n\timplements IDocumentDeltaConnection, IDisposable\n{\n\tstatic readonly eventsToForward = [\"nack\", \"op\", \"signal\", \"pong\"];\n\n\t// WARNING: These are critical events that we can't miss, so registration for them has to be in place at all times!\n\t// Including before handshake is over, and after that (but before DeltaManager had a chance to put its own handlers)\n\tstatic readonly eventsAlwaysForwarded = [\"disconnect\", \"error\"];\n\n\t/**\n\t * Last known sequence number to ordering service at the time of connection\n\t * It may lap actual last sequence number (quite a bit, if container is very active).\n\t * But it's best information for client to figure out how far it is behind, at least\n\t * for \"read\" connections. \"write\" connections may use own \"join\" op to similar information,\n\t * that is likely to be more up-to-date.\n\t */\n\tpublic checkpointSequenceNumber: number | undefined;\n\n\t// Listen for ops sent before we receive a response to connect_document\n\tprotected readonly queuedMessages: ISequencedDocumentMessage[] = [];\n\tprotected readonly queuedSignals: ISignalMessage[] = [];\n\n\t/**\n\t * A flag to indicate whether we have our handler attached. If it's attached, we're queueing incoming ops\n\t * to later be retrieved via initialMessages.\n\t */\n\tprivate earlyOpHandlerAttached: boolean = false;\n\n\tprivate socketConnectionTimeout: ReturnType<typeof setTimeout> | undefined;\n\n\tprivate _details: IConnected | undefined;\n\n\tprivate trackLatencyTimeout: ReturnType<typeof setTimeout> | undefined;\n\n\t// Listeners only needed while the connection is in progress\n\tprivate readonly connectionListeners: Map<string, (...args: any[]) => void> = new Map();\n\t// Listeners used throughout the lifetime of the DocumentDeltaConnection\n\tprivate readonly trackedListeners: Map<string, (...args: any[]) => void> = new Map();\n\n\tprotected get hasDetails(): boolean {\n\t\treturn !!this._details;\n\t}\n\n\tpublic get disposed() {\n\t\tassert(\n\t\t\tthis._disposed || this.socket.connected,\n\t\t\t0x244 /* \"Socket is closed, but connection is not!\" */,\n\t\t);\n\t\treturn this._disposed;\n\t}\n\n\t/**\n\t * Flag to indicate whether the DocumentDeltaConnection is expected to still be capable of sending messages.\n\t * After disconnection, we flip this to prevent any stale messages from being emitted.\n\t */\n\tprotected _disposed: boolean = false;\n\tprivate readonly mc: MonitoringContext;\n\n\t/**\n\t * @deprecated Implementors should manage their own logger or monitoring context\n\t */\n\tprotected get logger(): ITelemetryLoggerExt {\n\t\treturn this.mc.logger;\n\t}\n\n\tpublic get details(): IConnected {\n\t\tif (!this._details) {\n\t\t\tthrow new Error(\"Internal error: calling method before _details is initialized!\");\n\t\t}\n\t\treturn this._details;\n\t}\n\n\t/**\n\t * @param socket - websocket to be used\n\t * @param documentId - ID of the document\n\t * @param logger - for reporting telemetry events\n\t * @param enableLongPollingDowngrades - allow connection to be downgraded to long-polling on websocket failure\n\t */\n\tprotected constructor(\n\t\tprotected readonly socket: Socket,\n\t\tpublic documentId: string,\n\t\tlogger: ITelemetryLoggerExt,\n\t\tprivate readonly enableLongPollingDowngrades: boolean = false,\n\t\tprotected readonly connectionId?: string,\n\t) {\n\t\tsuper((name, error) => {\n\t\t\tthis.addPropsToError(error);\n\t\t\tlogger.sendErrorEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: \"DeltaConnection:EventException\",\n\t\t\t\t\tname: name as string,\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t});\n\n\t\tthis.mc = createChildMonitoringContext({ logger, namespace: \"DeltaConnection\" });\n\n\t\tthis.on(\"newListener\", (event, _listener) => {\n\t\t\tassert(!this.disposed, 0x20a /* \"register for event on disposed object\" */);\n\n\t\t\t// Some events are already forwarded - see this.addTrackedListener() calls in initialize().\n\t\t\tif (DocumentDeltaConnection.eventsAlwaysForwarded.includes(event)) {\n\t\t\t\tassert(this.trackedListeners.has(event), 0x245 /* \"tracked listener\" */);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!DocumentDeltaConnection.eventsToForward.includes(event)) {\n\t\t\t\tthrow new Error(`DocumentDeltaConnection: Registering for unknown event: ${event}`);\n\t\t\t}\n\n\t\t\t// Whenever listener is added, we should subscribe on same event on socket, so these two things\n\t\t\t// should be in sync. This currently assumes that nobody unregisters and registers back listeners,\n\t\t\t// and that there are no \"internal\" listeners installed (like \"error\" case we skip above)\n\t\t\t// Better flow might be to always unconditionally register all handlers on successful connection,\n\t\t\t// though some logic (naming assert in initialMessages getter) might need to be adjusted (it becomes noop)\n\t\t\tassert(\n\t\t\t\t(this.listeners(event).length !== 0) === this.trackedListeners.has(event),\n\t\t\t\t0x20b /* \"mismatch\" */,\n\t\t\t);\n\t\t\tif (!this.trackedListeners.has(event)) {\n\t\t\t\tif (event === \"pong\") {\n\t\t\t\t\t// Empty callback for tracking purposes in this class\n\t\t\t\t\tthis.trackedListeners.set(\"pong\", () => {});\n\n\t\t\t\t\tconst sendPingLoop = () => {\n\t\t\t\t\t\tconst start = Date.now();\n\n\t\t\t\t\t\tthis.socket.volatile?.emit(\"ping\", () => {\n\t\t\t\t\t\t\tthis.emit(\"pong\", Date.now() - start);\n\n\t\t\t\t\t\t\t// Schedule another ping event in 1 minute\n\t\t\t\t\t\t\tthis.trackLatencyTimeout = setTimeout(() => {\n\t\t\t\t\t\t\t\tsendPingLoop();\n\t\t\t\t\t\t\t}, 1000 * 60);\n\t\t\t\t\t\t});\n\t\t\t\t\t};\n\n\t\t\t\t\tsendPingLoop();\n\t\t\t\t} else {\n\t\t\t\t\tthis.addTrackedListener(event, (...args: any[]) => {\n\t\t\t\t\t\tthis.emit(event, ...args);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get the ID of the client who is sending the message\n\t *\n\t * @returns the client ID\n\t */\n\tpublic get clientId(): string {\n\t\treturn this.details.clientId;\n\t}\n\n\t/**\n\t * Get the mode of the client\n\t *\n\t * @returns the client mode\n\t */\n\tpublic get mode(): ConnectionMode {\n\t\treturn this.details.mode;\n\t}\n\n\t/**\n\t * Get the claims of the client who is sending the message\n\t *\n\t * @returns client claims\n\t */\n\tpublic get claims(): ITokenClaims {\n\t\treturn this.details.claims;\n\t}\n\n\t/**\n\t * Get whether or not this is an existing document\n\t *\n\t * @returns true if the document exists\n\t */\n\tpublic get existing(): boolean {\n\t\treturn this.details.existing;\n\t}\n\n\t/**\n\t * Get the maximum size of a message before chunking is required\n\t *\n\t * @returns the maximum size of a message before chunking is required\n\t */\n\tpublic get maxMessageSize(): number {\n\t\treturn this.details.serviceConfiguration.maxMessageSize;\n\t}\n\n\t/**\n\t * Semver of protocol being used with the service\n\t */\n\tpublic get version(): string {\n\t\treturn this.details.version;\n\t}\n\n\t/**\n\t * Configuration details provided by the service\n\t */\n\tpublic get serviceConfiguration(): IClientConfiguration {\n\t\treturn this.details.serviceConfiguration;\n\t}\n\n\tprivate checkNotDisposed() {\n\t\tassert(!this.disposed, 0x20c /* \"connection disposed\" */);\n\t}\n\n\t/**\n\t * Get messages sent during the connection\n\t *\n\t * @returns messages sent during the connection\n\t */\n\tpublic get initialMessages(): ISequencedDocumentMessage[] {\n\t\tthis.checkNotDisposed();\n\n\t\t// If we call this when the earlyOpHandler is not attached, then the queuedMessages may not include the\n\t\t// latest ops. This could possibly indicate that initialMessages was called twice.\n\t\tassert(this.earlyOpHandlerAttached, 0x08e /* \"Potentially missed initial messages\" */);\n\t\t// We will lose ops and perf will tank as we need to go to storage to become current!\n\t\tassert(this.listeners(\"op\").length !== 0, 0x08f /* \"No op handler is setup!\" */);\n\n\t\tthis.removeEarlyOpHandler();\n\n\t\tif (this.queuedMessages.length > 0) {\n\t\t\t// Some messages were queued.\n\t\t\t// add them to the list of initialMessages to be processed\n\t\t\tthis.details.initialMessages.push(...this.queuedMessages);\n\t\t\tthis.details.initialMessages.sort((a, b) => a.sequenceNumber - b.sequenceNumber);\n\t\t\tthis.queuedMessages.length = 0;\n\t\t}\n\t\treturn this.details.initialMessages;\n\t}\n\n\t/**\n\t * Get signals sent during the connection\n\t *\n\t * @returns signals sent during the connection\n\t */\n\tpublic get initialSignals(): ISignalMessage[] {\n\t\tthis.checkNotDisposed();\n\t\tassert(this.listeners(\"signal\").length !== 0, 0x090 /* \"No signal handler is setup!\" */);\n\n\t\tthis.removeEarlySignalHandler();\n\n\t\tif (this.queuedSignals.length > 0) {\n\t\t\t// Some signals were queued.\n\t\t\t// add them to the list of initialSignals to be processed\n\t\t\tthis.details.initialSignals.push(...this.queuedSignals);\n\t\t\tthis.queuedSignals.length = 0;\n\t\t}\n\t\treturn this.details.initialSignals;\n\t}\n\n\t/**\n\t * Get initial client list\n\t *\n\t * @returns initial client list sent during the connection\n\t */\n\tpublic get initialClients(): ISignalClient[] {\n\t\tthis.checkNotDisposed();\n\t\treturn this.details.initialClients;\n\t}\n\n\tprotected emitMessages(type: string, messages: IDocumentMessage[][]) {\n\t\t// Although the implementation here disconnects the socket and does not reuse it, other subclasses\n\t\t// (e.g. OdspDocumentDeltaConnection) may reuse the socket. In these cases, we need to avoid emitting\n\t\t// on the still-live socket.\n\t\tif (!this.disposed) {\n\t\t\tthis.socket.emit(type, this.clientId, messages);\n\t\t}\n\t}\n\n\t/**\n\t * Submits a new delta operation to the server\n\t *\n\t * @param message - delta operation to submit\n\t */\n\tpublic submit(messages: IDocumentMessage[]): void {\n\t\tthis.checkNotDisposed();\n\t\tthis.emitMessages(\"submitOp\", [messages]);\n\t}\n\n\t/**\n\t * Submits a new signal to the server\n\t *\n\t * @param content - Content of the signal.\n\t * @param targetClientId - When specified, the signal is only sent to the provided client id.\n\t */\n\tpublic submitSignal(content: IDocumentMessage, targetClientId?: string): void {\n\t\tthis.checkNotDisposed();\n\n\t\tif (targetClientId && this.details.supportedFeatures?.submit_signals_v2 !== true) {\n\t\t\tthrow new UsageError(\"Sending signals to specific client ids is not supported.\");\n\t\t}\n\n\t\tthis.emitMessages(\"submitSignal\", [[content]]);\n\t}\n\n\t/**\n\t * Disconnect from the websocket and close the websocket too.\n\t */\n\tprivate closeSocket(error: IAnyDriverError) {\n\t\tif (this._disposed) {\n\t\t\t// This would be rare situation due to complexity around socket emitting events.\n\t\t\treturn;\n\t\t}\n\t\tthis.closeSocketCore(error);\n\t}\n\n\tprotected closeSocketCore(error: IAnyDriverError) {\n\t\tthis.disconnect(error);\n\t}\n\n\t/**\n\t * Disconnect from the websocket, and permanently disable this DocumentDeltaConnection and close the socket.\n\t * However the OdspDocumentDeltaConnection differ in dispose as in there we don't close the socket. There is no\n\t * multiplexing here, so we need to close the socket here.\n\t */\n\tpublic dispose() {\n\t\tthis.logger.sendTelemetryEvent({\n\t\t\teventName: \"ClientClosingDeltaConnection\",\n\t\t\tdriverVersion,\n\t\t\tdetails: JSON.stringify({\n\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t}),\n\t\t});\n\t\tthis.disconnect(\n\t\t\tcreateGenericNetworkError(\n\t\t\t\t// pre-0.58 error message: clientClosingConnection\n\t\t\t\t\"Client closing delta connection\",\n\t\t\t\t{ canRetry: true },\n\t\t\t\t{ driverVersion },\n\t\t\t),\n\t\t);\n\t}\n\n\tprotected disconnect(err: IAnyDriverError) {\n\t\t// Can't check this.disposed here, as we get here on socket closure,\n\t\t// so _disposed & socket.connected might be not in sync while processing\n\t\t// \"dispose\" event.\n\t\tif (this._disposed) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.trackLatencyTimeout !== undefined) {\n\t\t\tclearTimeout(this.trackLatencyTimeout);\n\t\t\tthis.trackLatencyTimeout = undefined;\n\t\t}\n\n\t\t// We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by\n\t\t// DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is\n\t\t// important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that\n\t\t// OdspDocumentDeltaConnection may still send disconnect_document which is allowed; this is only intended\n\t\t// to prevent normal messages from being emitted.\n\t\tthis._disposed = true;\n\n\t\t// Remove all listeners listening on the socket. These are listeners on socket and not on this connection\n\t\t// object. Anyway since we have disposed this connection object, nobody should listen to event on socket\n\t\t// anymore.\n\t\tthis.removeTrackedListeners();\n\n\t\t// Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.\n\t\tthis.disconnectCore();\n\n\t\t// Let user of connection object know about disconnect.\n\t\tthis.emit(\"disconnect\", err);\n\t}\n\n\t/**\n\t * Disconnect from the websocket.\n\t * @param reason - reason for disconnect\n\t */\n\tprotected disconnectCore() {\n\t\tthis.socket.disconnect();\n\t}\n\n\tprotected async initialize(connectMessage: IConnect, timeout: number) {\n\t\tthis.socket.on(\"op\", this.earlyOpHandler);\n\t\tthis.socket.on(\"signal\", this.earlySignalHandler);\n\t\tthis.earlyOpHandlerAttached = true;\n\n\t\t// Socket.io's reconnect_attempt event is unreliable, so we track connect_error count instead.\n\t\tlet internalSocketConnectionFailureCount: number = 0;\n\t\tconst isInternalSocketReconnectionEnabled = (): boolean => this.socket.io.reconnection();\n\t\tconst getMaxInternalSocketReconnectionAttempts = (): number =>\n\t\t\tisInternalSocketReconnectionEnabled() ? this.socket.io.reconnectionAttempts() : 0;\n\t\tconst getMaxAllowedInternalSocketConnectionFailures = (): number =>\n\t\t\tgetMaxInternalSocketReconnectionAttempts() + 1;\n\n\t\tthis._details = await new Promise<IConnected>((resolve, reject) => {\n\t\t\tconst failAndCloseSocket = (err: IAnyDriverError) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.closeSocket(err);\n\t\t\t\t} catch (failError) {\n\t\t\t\t\tconst normalizedError = this.addPropsToError(failError);\n\t\t\t\t\tthis.logger.sendErrorEvent({ eventName: \"CloseSocketError\" }, normalizedError);\n\t\t\t\t}\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\tconst failConnection = (err: IAnyDriverError) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.disconnect(err);\n\t\t\t\t} catch (failError) {\n\t\t\t\t\tconst normalizedError = this.addPropsToError(failError);\n\t\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t\t{ eventName: \"FailConnectionError\" },\n\t\t\t\t\t\tnormalizedError,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treject(err);\n\t\t\t};\n\n\t\t\t// Immediately set the connection timeout.\n\t\t\t// Give extra 2 seconds for handshake on top of socket connection timeout.\n\t\t\tthis.socketConnectionTimeout = setTimeout(() => {\n\t\t\t\tfailConnection(this.createErrorObject(\"orderingServiceHandshakeTimeout\"));\n\t\t\t}, timeout + 2000);\n\n\t\t\t// Listen for connection issues\n\t\t\tthis.addConnectionListener(\"connect_error\", (error) => {\n\t\t\t\tinternalSocketConnectionFailureCount++;\n\t\t\t\tlet isWebSocketTransportError = false;\n\t\t\t\ttry {\n\t\t\t\t\tconst description = error?.description;\n\t\t\t\t\tconst context = error?.context;\n\n\t\t\t\t\tif (context && typeof context === \"object\") {\n\t\t\t\t\t\tconst statusText = context.statusText?.code;\n\n\t\t\t\t\t\t// Self-Signed Certificate ErrorCode Found in error.context\n\t\t\t\t\t\tif (statusText === \"DEPTH_ZERO_SELF_SIGNED_CERT\") {\n\t\t\t\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\t\t\t\tthis.createErrorObject(\"connect_error\", error, false),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (description && typeof description === \"object\") {\n\t\t\t\t\t\tconst errorCode = description.error?.code;\n\n\t\t\t\t\t\t// Self-Signed Certificate ErrorCode Found in error.description\n\t\t\t\t\t\tif (errorCode === \"DEPTH_ZERO_SELF_SIGNED_CERT\") {\n\t\t\t\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\t\t\t\tthis.createErrorObject(\"connect_error\", error, false),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (error.type === \"TransportError\") {\n\t\t\t\t\t\t\tisWebSocketTransportError = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// That's a WebSocket. Clear it as we can't log it.\n\t\t\t\t\t\tdescription.target = undefined;\n\t\t\t\t\t}\n\t\t\t\t} catch (_e) {}\n\n\t\t\t\t// Handle socket transport downgrading when not offline.\n\t\t\t\tif (\n\t\t\t\t\tisWebSocketTransportError &&\n\t\t\t\t\tthis.enableLongPollingDowngrades &&\n\t\t\t\t\tthis.socket.io.opts.transports?.[0] !== \"polling\"\n\t\t\t\t) {\n\t\t\t\t\t// Downgrade transports to polling upgrade mechanism.\n\t\t\t\t\tthis.socket.io.opts.transports = [\"polling\", \"websocket\"];\n\t\t\t\t\t// Don't alter reconnection behavior if already enabled.\n\t\t\t\t\tif (!isInternalSocketReconnectionEnabled()) {\n\t\t\t\t\t\t// Allow single reconnection attempt using polling upgrade mechanism.\n\t\t\t\t\t\tthis.socket.io.reconnection(true);\n\t\t\t\t\t\tthis.socket.io.reconnectionAttempts(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Allow built-in socket.io reconnection handling.\n\t\t\t\tif (\n\t\t\t\t\tisInternalSocketReconnectionEnabled() &&\n\t\t\t\t\tinternalSocketConnectionFailureCount <\n\t\t\t\t\t\tgetMaxAllowedInternalSocketConnectionFailures()\n\t\t\t\t) {\n\t\t\t\t\t// Reconnection is enabled and maximum reconnect attempts have not been reached.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfailAndCloseSocket(this.createErrorObject(\"connect_error\", error));\n\t\t\t});\n\n\t\t\t// Listen for timeouts\n\t\t\tthis.addConnectionListener(\"connect_timeout\", () => {\n\t\t\t\tfailAndCloseSocket(this.createErrorObject(\"connect_timeout\"));\n\t\t\t});\n\n\t\t\tthis.addConnectionListener(\"connect_document_success\", (response: IConnected) => {\n\t\t\t\t// If we sent a nonce and the server supports nonces, check that the nonces match\n\t\t\t\tif (\n\t\t\t\t\tconnectMessage.nonce !== undefined &&\n\t\t\t\t\tresponse.nonce !== undefined &&\n\t\t\t\t\tresponse.nonce !== connectMessage.nonce\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst requestedMode = connectMessage.mode;\n\t\t\t\tconst actualMode = response.mode;\n\t\t\t\tconst writingPermitted = response.claims.scopes.includes(ScopeType.DocWrite);\n\n\t\t\t\tif (writingPermitted) {\n\t\t\t\t\t// The only time we expect a mismatch in requested/actual is if we lack write permissions\n\t\t\t\t\t// In this case we will get \"read\", even if we requested \"write\"\n\t\t\t\t\tif (actualMode !== requestedMode) {\n\t\t\t\t\t\tfailConnection(\n\t\t\t\t\t\t\tthis.createErrorObject(\n\t\t\t\t\t\t\t\t\"connect_document_success\",\n\t\t\t\t\t\t\t\t\"Connected in a different mode than was requested\",\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (actualMode === \"write\") {\n\t\t\t\t\t\tfailConnection(\n\t\t\t\t\t\t\tthis.createErrorObject(\n\t\t\t\t\t\t\t\t\"connect_document_success\",\n\t\t\t\t\t\t\t\t\"Connected in write mode without write permissions\",\n\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.logger.sendTelemetryEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"ConnectDocumentSuccess\",\n\t\t\t\t\t\tpendingClientId: response.clientId,\n\t\t\t\t\t},\n\t\t\t\t\tundefined,\n\t\t\t\t\tLogLevel.verbose,\n\t\t\t\t);\n\n\t\t\t\tthis.checkpointSequenceNumber = response.checkpointSequenceNumber;\n\n\t\t\t\tthis.removeConnectionListeners();\n\t\t\t\tresolve(response);\n\t\t\t});\n\n\t\t\t// Socket can be disconnected while waiting for Fluid protocol messages\n\t\t\t// (connect_document_error / connect_document_success), as well as before DeltaManager\n\t\t\t// had a chance to register its handlers.\n\t\t\tthis.addTrackedListener(\"disconnect\", (reason, details) => {\n\t\t\t\tfailAndCloseSocket(\n\t\t\t\t\tthis.createErrorObjectWithProps(\"disconnect\", reason, {\n\t\t\t\t\t\tsocketErrorType: details?.context?.type,\n\t\t\t\t\t\t// https://www.rfc-editor.org/rfc/rfc6455#section-7.4\n\t\t\t\t\t\tsocketCode: details?.context?.code,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\tthis.addTrackedListener(\"error\", (error) => {\n\t\t\t\t// This includes \"Invalid namespace\" error, which we consider critical (reconnecting will not help)\n\t\t\t\tconst err = this.createErrorObject(\"error\", error, error !== \"Invalid namespace\");\n\t\t\t\t// Disconnect socket - required if happened before initial handshake\n\t\t\t\tfailAndCloseSocket(err);\n\t\t\t});\n\n\t\t\tthis.addConnectionListener(\"connect_document_error\", (error) => {\n\t\t\t\t// If we sent a nonce and the server supports nonces, check that the nonces match\n\t\t\t\tif (\n\t\t\t\t\tconnectMessage.nonce !== undefined &&\n\t\t\t\t\terror.nonce !== undefined &&\n\t\t\t\t\terror.nonce !== connectMessage.nonce\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// This is not an socket.io error - it's Fluid protocol error.\n\t\t\t\t// In this case fail connection and indicate that we were unable to create connection\n\t\t\t\tfailConnection(this.createErrorObject(\"connect_document_error\", error));\n\t\t\t});\n\n\t\t\tthis.socket.emit(\"connect_document\", connectMessage);\n\t\t});\n\n\t\tassert(!this.disposed, 0x246 /* \"checking consistency of socket & _disposed flags\" */);\n\t}\n\n\tprivate addPropsToError(errorToBeNormalized: unknown) {\n\t\tconst normalizedError = normalizeError(errorToBeNormalized, {\n\t\t\tprops: {\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t\treturn normalizedError;\n\t}\n\n\tprotected getConnectionDetailsProps() {\n\t\treturn {\n\t\t\tdisposed: this._disposed,\n\t\t\tsocketConnected: this.socket?.connected,\n\t\t\tclientId: this._details?.clientId,\n\t\t\tconnectionId: this.connectionId,\n\t\t};\n\t}\n\n\tprotected earlyOpHandler = (documentId: string, msgs: ISequencedDocumentMessage[]) => {\n\t\tthis.queuedMessages.push(...msgs);\n\t};\n\n\tprotected earlySignalHandler = (msg: ISignalMessage | ISignalMessage[]) => {\n\t\tif (Array.isArray(msg)) {\n\t\t\tthis.queuedSignals.push(...msg);\n\t\t} else {\n\t\t\tthis.queuedSignals.push(msg);\n\t\t}\n\t};\n\n\tprivate removeEarlyOpHandler() {\n\t\tthis.socket.removeListener(\"op\", this.earlyOpHandler);\n\t\tthis.earlyOpHandlerAttached = false;\n\t}\n\n\tprivate removeEarlySignalHandler() {\n\t\tthis.socket.removeListener(\"signal\", this.earlySignalHandler);\n\t}\n\n\tprivate addConnectionListener(event: string, listener: (...args: any[]) => void) {\n\t\tassert(\n\t\t\t!DocumentDeltaConnection.eventsAlwaysForwarded.includes(event),\n\t\t\t0x247 /* \"Use addTrackedListener instead\" */,\n\t\t);\n\t\tassert(\n\t\t\t!DocumentDeltaConnection.eventsToForward.includes(event),\n\t\t\t0x248 /* \"should not subscribe to forwarded events\" */,\n\t\t);\n\t\tthis.socket.on(event, listener);\n\t\tassert(!this.connectionListeners.has(event), 0x20d /* \"double connection listener\" */);\n\t\tthis.connectionListeners.set(event, listener);\n\t}\n\n\tprotected addTrackedListener(event: string, listener: (...args: any[]) => void) {\n\t\tthis.socket.on(event, listener);\n\t\tassert(!this.trackedListeners.has(event), 0x20e /* \"double tracked listener\" */);\n\t\tthis.trackedListeners.set(event, listener);\n\t}\n\n\tprivate removeTrackedListeners() {\n\t\tfor (const [event, listener] of this.trackedListeners.entries()) {\n\t\t\tthis.socket.off(event, listener);\n\t\t}\n\t\t// removeTrackedListeners removes all listeners, including connection listeners\n\t\tthis.removeConnectionListeners();\n\n\t\tthis.removeEarlyOpHandler();\n\t\tthis.removeEarlySignalHandler();\n\n\t\tthis.trackedListeners.clear();\n\t}\n\n\tprivate removeConnectionListeners() {\n\t\tif (this.socketConnectionTimeout !== undefined) {\n\t\t\tclearTimeout(this.socketConnectionTimeout);\n\t\t}\n\n\t\tfor (const [event, listener] of this.connectionListeners.entries()) {\n\t\t\tthis.socket.off(event, listener);\n\t\t}\n\t\tthis.connectionListeners.clear();\n\t}\n\n\tprivate getErrorMessage(error?: any): string {\n\t\tif (error?.type !== \"TransportError\") {\n\t\t\treturn extractLogSafeErrorProperties(error, true).message;\n\t\t}\n\t\t// JSON.stringify drops Error.message\n\t\tconst messagePrefix = error?.message !== undefined ? `${error.message}: ` : \"\";\n\n\t\t// Websocket errors reported by engine.io-client.\n\t\t// They are Error objects with description containing WS error and description = \"TransportError\"\n\t\t// Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,\n\t\treturn `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;\n\t}\n\n\tprivate createErrorObjectWithProps(\n\t\thandler: string,\n\t\terror?: any,\n\t\tprops?: ITelemetryProperties,\n\t\tcanRetry = true,\n\t): IAnyDriverError {\n\t\treturn createGenericNetworkError(\n\t\t\t`socket.io (${handler}): ${this.getErrorMessage(error)}`,\n\t\t\t{ canRetry },\n\t\t\t{\n\t\t\t\t...props,\n\t\t\t\tdriverVersion,\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t}\n\n\t/**\n\t * Error raising for socket.io issues\n\t */\n\tprotected createErrorObject(handler: string, error?: any, canRetry = true): IAnyDriverError {\n\t\treturn createGenericNetworkError(\n\t\t\t`socket.io (${handler}): ${this.getErrorMessage(error)}`,\n\t\t\t{ canRetry },\n\t\t\t{\n\t\t\t\tdriverVersion,\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\t...this.getConnectionDetailsProps(),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t}\n}\n"]}