@fluidframework/container-loader 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.204906

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 (146) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/api-extractor.json +9 -1
  3. package/api-report/container-loader.api.md +153 -0
  4. package/dist/catchUpMonitor.d.ts +2 -2
  5. package/dist/catchUpMonitor.d.ts.map +1 -1
  6. package/dist/connectionManager.d.ts +2 -15
  7. package/dist/connectionManager.d.ts.map +1 -1
  8. package/dist/connectionManager.js +117 -100
  9. package/dist/connectionManager.js.map +1 -1
  10. package/dist/connectionState.js +1 -1
  11. package/dist/connectionState.js.map +1 -1
  12. package/dist/connectionStateHandler.js +9 -9
  13. package/dist/connectionStateHandler.js.map +1 -1
  14. package/dist/container-loader-alpha.d.ts +333 -0
  15. package/dist/container-loader-beta.d.ts +333 -0
  16. package/dist/container-loader-public.d.ts +333 -0
  17. package/dist/container-loader-untrimmed.d.ts +333 -0
  18. package/dist/container.d.ts +22 -2
  19. package/dist/container.d.ts.map +1 -1
  20. package/dist/container.js +235 -222
  21. package/dist/container.js.map +1 -1
  22. package/dist/containerContext.js +16 -16
  23. package/dist/containerContext.js.map +1 -1
  24. package/dist/containerStorageAdapter.d.ts +1 -1
  25. package/dist/containerStorageAdapter.d.ts.map +1 -1
  26. package/dist/containerStorageAdapter.js +9 -11
  27. package/dist/containerStorageAdapter.js.map +1 -1
  28. package/dist/contracts.d.ts +5 -4
  29. package/dist/contracts.d.ts.map +1 -1
  30. package/dist/contracts.js +1 -1
  31. package/dist/contracts.js.map +1 -1
  32. package/dist/debugLogger.d.ts.map +1 -1
  33. package/dist/debugLogger.js +5 -4
  34. package/dist/debugLogger.js.map +1 -1
  35. package/dist/deltaManager.d.ts.map +1 -1
  36. package/dist/deltaManager.js +92 -96
  37. package/dist/deltaManager.js.map +1 -1
  38. package/dist/deltaQueue.js +14 -14
  39. package/dist/deltaQueue.js.map +1 -1
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +6 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/loader.d.ts +6 -9
  45. package/dist/loader.d.ts.map +1 -1
  46. package/dist/loader.js +26 -91
  47. package/dist/loader.js.map +1 -1
  48. package/dist/location-redirection-utilities/index.d.ts +6 -0
  49. package/dist/location-redirection-utilities/index.d.ts.map +1 -0
  50. package/dist/location-redirection-utilities/index.js +11 -0
  51. package/dist/location-redirection-utilities/index.js.map +1 -0
  52. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +22 -0
  53. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
  54. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +51 -0
  55. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
  56. package/dist/packageVersion.d.ts +1 -1
  57. package/dist/packageVersion.js +1 -1
  58. package/dist/packageVersion.js.map +1 -1
  59. package/dist/protocol.d.ts +1 -2
  60. package/dist/protocol.d.ts.map +1 -1
  61. package/dist/protocol.js +3 -5
  62. package/dist/protocol.js.map +1 -1
  63. package/dist/retriableDocumentStorageService.d.ts +3 -2
  64. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  65. package/dist/retriableDocumentStorageService.js +18 -11
  66. package/dist/retriableDocumentStorageService.js.map +1 -1
  67. package/dist/tsdoc-metadata.json +1 -1
  68. package/dist/utils.d.ts +23 -1
  69. package/dist/utils.d.ts.map +1 -1
  70. package/dist/utils.js +11 -3
  71. package/dist/utils.js.map +1 -1
  72. package/lib/catchUpMonitor.d.ts +2 -2
  73. package/lib/catchUpMonitor.d.ts.map +1 -1
  74. package/lib/connectionManager.d.ts +2 -15
  75. package/lib/connectionManager.d.ts.map +1 -1
  76. package/lib/connectionManager.js +120 -101
  77. package/lib/connectionManager.js.map +1 -1
  78. package/lib/connectionStateHandler.js +9 -9
  79. package/lib/connectionStateHandler.js.map +1 -1
  80. package/lib/container.d.ts +22 -2
  81. package/lib/container.d.ts.map +1 -1
  82. package/lib/container.js +236 -223
  83. package/lib/container.js.map +1 -1
  84. package/lib/containerContext.js +16 -16
  85. package/lib/containerContext.js.map +1 -1
  86. package/lib/containerStorageAdapter.d.ts +1 -1
  87. package/lib/containerStorageAdapter.d.ts.map +1 -1
  88. package/lib/containerStorageAdapter.js +9 -11
  89. package/lib/containerStorageAdapter.js.map +1 -1
  90. package/lib/contracts.d.ts +5 -4
  91. package/lib/contracts.d.ts.map +1 -1
  92. package/lib/contracts.js.map +1 -1
  93. package/lib/debugLogger.d.ts.map +1 -1
  94. package/lib/debugLogger.js +5 -4
  95. package/lib/debugLogger.js.map +1 -1
  96. package/lib/deltaManager.d.ts.map +1 -1
  97. package/lib/deltaManager.js +92 -96
  98. package/lib/deltaManager.js.map +1 -1
  99. package/lib/deltaQueue.js +14 -14
  100. package/lib/deltaQueue.js.map +1 -1
  101. package/lib/index.d.ts +2 -0
  102. package/lib/index.d.ts.map +1 -1
  103. package/lib/index.js +2 -0
  104. package/lib/index.js.map +1 -1
  105. package/lib/loader.d.ts +6 -9
  106. package/lib/loader.d.ts.map +1 -1
  107. package/lib/loader.js +27 -92
  108. package/lib/loader.js.map +1 -1
  109. package/lib/location-redirection-utilities/index.d.ts +6 -0
  110. package/lib/location-redirection-utilities/index.d.ts.map +1 -0
  111. package/lib/location-redirection-utilities/index.js +6 -0
  112. package/lib/location-redirection-utilities/index.js.map +1 -0
  113. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +22 -0
  114. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
  115. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +46 -0
  116. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
  117. package/lib/packageVersion.d.ts +1 -1
  118. package/lib/packageVersion.js +1 -1
  119. package/lib/packageVersion.js.map +1 -1
  120. package/lib/protocol.d.ts +1 -2
  121. package/lib/protocol.d.ts.map +1 -1
  122. package/lib/protocol.js +1 -3
  123. package/lib/protocol.js.map +1 -1
  124. package/lib/retriableDocumentStorageService.d.ts +3 -2
  125. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  126. package/lib/retriableDocumentStorageService.js +18 -11
  127. package/lib/retriableDocumentStorageService.js.map +1 -1
  128. package/lib/utils.d.ts +23 -1
  129. package/lib/utils.d.ts.map +1 -1
  130. package/lib/utils.js +9 -1
  131. package/lib/utils.js.map +1 -1
  132. package/package.json +24 -26
  133. package/src/connectionManager.ts +69 -34
  134. package/src/container.ts +48 -30
  135. package/src/containerStorageAdapter.ts +3 -9
  136. package/src/contracts.ts +8 -4
  137. package/src/debugLogger.ts +5 -1
  138. package/src/deltaManager.ts +16 -31
  139. package/src/index.ts +5 -0
  140. package/src/loader.ts +31 -99
  141. package/src/location-redirection-utilities/index.ts +9 -0
  142. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +59 -0
  143. package/src/packageVersion.ts +1 -1
  144. package/src/protocol.ts +2 -6
  145. package/src/retriableDocumentStorageService.ts +29 -15
  146. package/src/utils.ts +23 -1
@@ -5,8 +5,10 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ConnectionManager = void 0;
8
+ const core_interfaces_1 = require("@fluidframework/core-interfaces");
8
9
  const core_utils_1 = require("@fluidframework/core-utils");
9
10
  const client_utils_1 = require("@fluid-internal/client-utils");
11
+ const driver_definitions_1 = require("@fluidframework/driver-definitions");
10
12
  const driver_utils_1 = require("@fluidframework/driver-utils");
11
13
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
12
14
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
@@ -36,9 +38,15 @@ const clientNoDeltaStream = {
36
38
  };
37
39
  const clientIdNoDeltaStream = "storage-only client";
38
40
  class NoDeltaStream extends client_utils_1.TypedEventEmitter {
39
- constructor(storageOnlyReason) {
41
+ /**
42
+ * Connection which is not connected to socket.
43
+ * @param storageOnlyReason - Reason on why the connection to delta stream is not allowed.
44
+ * @param readonlyConnectionReason - reason/error if any which lead to using NoDeltaStream.
45
+ */
46
+ constructor(storageOnlyReason, readonlyConnectionReason) {
40
47
  super();
41
48
  this.storageOnlyReason = storageOnlyReason;
49
+ this.readonlyConnectionReason = readonlyConnectionReason;
42
50
  this.clientId = clientIdNoDeltaStream;
43
51
  this.claims = {
44
52
  scopes: [protocol_definitions_1.ScopeType.DocRead],
@@ -101,67 +109,6 @@ const waitForOnline = async () => {
101
109
  * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
102
110
  */
103
111
  class ConnectionManager {
104
- constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
105
- this.serviceProvider = serviceProvider;
106
- this.containerDirty = containerDirty;
107
- this.client = client;
108
- this.logger = logger;
109
- this.props = props;
110
- /** tracks host requiring read-only mode. */
111
- this._forceReadonly = false;
112
- /** True if there is pending (async) reconnection from "read" to "write" */
113
- this.pendingReconnect = false;
114
- this.clientSequenceNumber = 0;
115
- this.clientSequenceNumberObserved = 0;
116
- /** Counts the number of non-runtime ops sent by the client which may not be acked. */
117
- this.localOpsToIgnore = 0;
118
- this.connectFirstConnection = true;
119
- this._connectionVerboseProps = {};
120
- this._connectionProps = {};
121
- this._disposed = false;
122
- this.opHandler = (documentId, messagesArg) => {
123
- const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
124
- this.props.incomingOpHandler(messages, "opHandler");
125
- };
126
- // Always connect in write mode after getting nacked.
127
- this.nackHandler = (documentId, messages) => {
128
- const message = messages[0];
129
- if (this._readonlyPermissions === true) {
130
- this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
131
- return;
132
- }
133
- const reconnectInfo = getNackReconnectInfo(message.content);
134
- // If the nack indicates we cannot retry, then close the container outright
135
- if (!reconnectInfo.canRetry) {
136
- this.props.closeHandler(reconnectInfo);
137
- return;
138
- }
139
- this.reconnectOnError("write", reconnectInfo);
140
- };
141
- // Connection mode is always read on disconnect/error unless the system mode was write.
142
- this.disconnectHandlerInternal = (disconnectReason) => {
143
- // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
144
- // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
145
- this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
146
- };
147
- this.errorHandler = (error) => {
148
- this.reconnectOnError(this.defaultReconnectionMode, error);
149
- };
150
- this.clientDetails = this.client.details;
151
- this.defaultReconnectionMode = this.client.mode;
152
- this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
153
- // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
154
- // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
155
- this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
156
- if (this.connection === undefined) {
157
- throw new Error("Attempted to submit an outbound message without connection");
158
- }
159
- this.connection.submit(messages);
160
- });
161
- this._outbound.on("error", (error) => {
162
- this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
163
- });
164
- }
165
112
  get connectionVerboseProps() {
166
113
  return this._connectionVerboseProps;
167
114
  }
@@ -275,6 +222,71 @@ class ConnectionManager {
275
222
  reason,
276
223
  };
277
224
  }
225
+ constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
226
+ this.serviceProvider = serviceProvider;
227
+ this.containerDirty = containerDirty;
228
+ this.client = client;
229
+ this.logger = logger;
230
+ this.props = props;
231
+ /** tracks host requiring read-only mode. */
232
+ this._forceReadonly = false;
233
+ /** True if there is pending (async) reconnection from "read" to "write" */
234
+ this.pendingReconnect = false;
235
+ this.clientSequenceNumber = 0;
236
+ this.clientSequenceNumberObserved = 0;
237
+ /** Counts the number of non-runtime ops sent by the client which may not be acked. */
238
+ this.localOpsToIgnore = 0;
239
+ this.connectFirstConnection = true;
240
+ this._connectionVerboseProps = {};
241
+ this._connectionProps = {};
242
+ this._disposed = false;
243
+ this.opHandler = (documentId, messagesArg) => {
244
+ const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
245
+ this.props.incomingOpHandler(messages, "opHandler");
246
+ };
247
+ this.signalHandler = (signalsArg) => {
248
+ const signals = Array.isArray(signalsArg) ? signalsArg : [signalsArg];
249
+ this.props.signalHandler(signals);
250
+ };
251
+ // Always connect in write mode after getting nacked.
252
+ this.nackHandler = (documentId, messages) => {
253
+ const message = messages[0];
254
+ if (this._readonlyPermissions === true) {
255
+ this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
256
+ return;
257
+ }
258
+ const reconnectInfo = getNackReconnectInfo(message.content);
259
+ // If the nack indicates we cannot retry, then close the container outright
260
+ if (!reconnectInfo.canRetry) {
261
+ this.props.closeHandler(reconnectInfo);
262
+ return;
263
+ }
264
+ this.reconnectOnError("write", reconnectInfo);
265
+ };
266
+ // Connection mode is always read on disconnect/error unless the system mode was write.
267
+ this.disconnectHandlerInternal = (disconnectReason) => {
268
+ // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
269
+ // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
270
+ this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
271
+ };
272
+ this.errorHandler = (error) => {
273
+ this.reconnectOnError(this.defaultReconnectionMode, error);
274
+ };
275
+ this.clientDetails = this.client.details;
276
+ this.defaultReconnectionMode = this.client.mode;
277
+ this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
278
+ // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
279
+ // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
280
+ this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
281
+ if (this.connection === undefined) {
282
+ throw new Error("Attempted to submit an outbound message without connection");
283
+ }
284
+ this.connection.submit(messages);
285
+ });
286
+ this._outbound.on("error", (error) => {
287
+ this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
288
+ });
289
+ }
278
290
  dispose(error, switchToReadonly = true) {
279
291
  if (this._disposed) {
280
292
  return;
@@ -294,7 +306,7 @@ class ConnectionManager {
294
306
  // Notify everyone we are in read-only state.
295
307
  // Useful for data stores in case we hit some critical error,
296
308
  // to switch to a mode where user edits are not accepted
297
- this.set_readonlyPermissions(true, oldReadonlyValue);
309
+ this.set_readonlyPermissions(true, oldReadonlyValue, disconnectReason);
298
310
  }
299
311
  }
300
312
  /**
@@ -310,21 +322,7 @@ class ConnectionManager {
310
322
  }
311
323
  }
312
324
  /**
313
- * Sends signal to runtime (and data stores) to be read-only.
314
- * Hosts may have read only views, indicating to data stores that no edits are allowed.
315
- * This is independent from this._readonlyPermissions (permissions) and this.connectionMode
316
- * (server can return "write" mode even when asked for "read")
317
- * Leveraging same "readonly" event as runtime & data stores should behave the same in such case
318
- * as in read-only permissions.
319
- * But this.active can be used by some DDSes to figure out if ops can be sent
320
- * (for example, read-only view still participates in code proposals / upgrades decisions)
321
- *
322
- * Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
323
- * the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
324
- * there are pending in the outbound queue, it will stop sending until force readonly is
325
- * cleared.
326
- *
327
- * @param readonly - set or clear force readonly.
325
+ * {@inheritDoc Container.forceReadonly}
328
326
  */
329
327
  forceReadonly(readonly) {
330
328
  if (readonly !== this._forceReadonly) {
@@ -358,10 +356,10 @@ class ConnectionManager {
358
356
  }
359
357
  }
360
358
  }
361
- set_readonlyPermissions(newReadonlyValue, oldReadonlyValue) {
359
+ set_readonlyPermissions(newReadonlyValue, oldReadonlyValue, readonlyConnectionReason) {
362
360
  this._readonlyPermissions = newReadonlyValue;
363
361
  if (oldReadonlyValue !== this.readonly) {
364
- this.props.readonlyChangeHandler(this.readonly);
362
+ this.props.readonlyChangeHandler(this.readonly, readonlyConnectionReason);
365
363
  }
366
364
  }
367
365
  connect(reason, connectionMode) {
@@ -439,10 +437,29 @@ class ConnectionManager {
439
437
  this.logger.sendTelemetryEvent({ eventName: "ReceivedClosedConnection" });
440
438
  connection = undefined;
441
439
  }
440
+ this.logger.sendTelemetryEvent({
441
+ eventName: "ConnectionReceived",
442
+ connected: connection !== undefined && connection.disposed === false,
443
+ }, undefined, core_interfaces_1.LogLevel.verbose);
442
444
  }
443
445
  catch (origError) {
444
446
  if ((0, utils_1.isDeltaStreamConnectionForbiddenError)(origError)) {
445
- connection = new NoDeltaStream(origError.storageOnlyReason);
447
+ connection = new NoDeltaStream(origError.storageOnlyReason, {
448
+ text: origError.message,
449
+ error: origError,
450
+ });
451
+ requestedMode = "read";
452
+ break;
453
+ }
454
+ else if ((0, telemetry_utils_1.isFluidError)(origError) &&
455
+ // eslint-disable-next-line import/no-deprecated
456
+ origError.errorType === driver_definitions_1.DriverErrorType.outOfStorageError) {
457
+ // If we get out of storage error from calling joinsession, then use the NoDeltaStream object so
458
+ // that user can at least load the container.
459
+ connection = new NoDeltaStream(undefined, {
460
+ text: origError.message,
461
+ error: origError,
462
+ });
446
463
  requestedMode = "read";
447
464
  break;
448
465
  }
@@ -499,8 +516,8 @@ class ConnectionManager {
499
516
  duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
500
517
  }, lastError);
501
518
  }
502
- // Check for abort signal after while loop as well
503
- if (abortSignal.aborted === true) {
519
+ // Check for abort signal after while loop as well or we've been disposed
520
+ if (abortSignal.aborted === true || this._disposed) {
504
521
  connection.dispose();
505
522
  this.logger.sendTelemetryEvent({
506
523
  eventName: "ConnectionAttemptCancelled",
@@ -550,7 +567,7 @@ class ConnectionManager {
550
567
  this.connection = undefined;
551
568
  // Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError
552
569
  connection.off("op", this.opHandler);
553
- connection.off("signal", this.props.signalHandler);
570
+ connection.off("signal", this.signalHandler);
554
571
  connection.off("nack", this.nackHandler);
555
572
  connection.off("disconnect", this.disconnectHandlerInternal);
556
573
  connection.off("error", this.errorHandler);
@@ -603,7 +620,7 @@ class ConnectionManager {
603
620
  // removed after those packages have released and become ubiquitous.
604
621
  (0, core_utils_1.assert)(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
605
622
  (0, core_utils_1.assert)(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
606
- this.set_readonlyPermissions(readonly, oldReadonlyValue);
623
+ this.set_readonlyPermissions(readonly, oldReadonlyValue, isNoDeltaStreamConnection(connection) ? connection.readonlyConnectionReason : undefined);
607
624
  if (this._disposed) {
608
625
  // Raise proper events, Log telemetry event and close connection.
609
626
  this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
@@ -611,7 +628,7 @@ class ConnectionManager {
611
628
  }
612
629
  this._outbound.resume();
613
630
  connection.on("op", this.opHandler);
614
- connection.on("signal", this.props.signalHandler);
631
+ connection.on("signal", this.signalHandler);
615
632
  connection.on("nack", this.nackHandler);
616
633
  connection.on("disconnect", this.disconnectHandlerInternal);
617
634
  connection.on("error", this.errorHandler);
@@ -662,26 +679,26 @@ class ConnectionManager {
662
679
  type: protocol_1.SignalType.Clear,
663
680
  }),
664
681
  };
665
- this.props.signalHandler(clearSignal);
666
- for (const priorClient of connection.initialClients ?? []) {
667
- const joinSignal = {
668
- clientId: null,
669
- content: JSON.stringify({
670
- type: protocol_1.SignalType.ClientJoin,
671
- content: priorClient, // ISignalClient
672
- }),
673
- };
674
- this.props.signalHandler(joinSignal);
682
+ // list of signals to process due to this new connection
683
+ let signalsToProcess = [clearSignal];
684
+ const clientJoinSignals = (connection.initialClients ?? []).map((priorClient) => ({
685
+ clientId: null,
686
+ content: JSON.stringify({
687
+ type: protocol_1.SignalType.ClientJoin,
688
+ content: priorClient, // ISignalClient
689
+ }),
690
+ }));
691
+ if (clientJoinSignals.length > 0) {
692
+ signalsToProcess = signalsToProcess.concat(clientJoinSignals);
675
693
  }
676
694
  // Unfortunately, there is no defined order between initialSignals (including join & leave signals)
677
695
  // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
678
696
  // for "self" and connection.initialClients does not contain "self", so we have to process them after
679
697
  // "clear" signal above.
680
- if (connection.initialSignals !== undefined) {
681
- for (const signal of connection.initialSignals) {
682
- this.props.signalHandler(signal);
683
- }
698
+ if (connection.initialSignals !== undefined && connection.initialSignals.length > 0) {
699
+ signalsToProcess = signalsToProcess.concat(connection.initialSignals);
684
700
  }
701
+ this.props.signalHandler(signalsToProcess);
685
702
  }
686
703
  /**
687
704
  * Disconnect the current connection and reconnect. Closes the container if it fails.