@fluidframework/container-loader 2.0.0-dev.6.4.0.191515 → 2.0.0-dev.7.2.0.203917

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 +110 -98
  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 +113 -99
  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 +58 -31
  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
@@ -4,9 +4,12 @@
4
4
  */
5
5
  import { assert } from "@fluidframework/core-utils";
6
6
  import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import {
8
+ // eslint-disable-next-line import/no-deprecated
9
+ DriverErrorType, } from "@fluidframework/driver-definitions";
7
10
  import { canRetryOnError, createWriteError, createGenericNetworkError, getRetryDelayFromError, logNetworkFailure, isRuntimeMessage, calculateMaxWaitTime, } from "@fluidframework/driver-utils";
8
11
  import { MessageType, ScopeType, } from "@fluidframework/protocol-definitions";
9
- import { formatTick, GenericError, normalizeError, UsageError, } from "@fluidframework/telemetry-utils";
12
+ import { formatTick, GenericError, isFluidError, normalizeError, UsageError, } from "@fluidframework/telemetry-utils";
10
13
  import { ReconnectMode, } from "./contracts";
11
14
  import { DeltaQueue } from "./deltaQueue";
12
15
  import { SignalType } from "./protocol";
@@ -33,9 +36,15 @@ const clientNoDeltaStream = {
33
36
  };
34
37
  const clientIdNoDeltaStream = "storage-only client";
35
38
  class NoDeltaStream extends TypedEventEmitter {
36
- constructor(storageOnlyReason) {
39
+ /**
40
+ * Connection which is not connected to socket.
41
+ * @param storageOnlyReason - Reason on why the connection to delta stream is not allowed.
42
+ * @param readonlyConnectionReason - reason/error if any which lead to using NoDeltaStream.
43
+ */
44
+ constructor(storageOnlyReason, readonlyConnectionReason) {
37
45
  super();
38
46
  this.storageOnlyReason = storageOnlyReason;
47
+ this.readonlyConnectionReason = readonlyConnectionReason;
39
48
  this.clientId = clientIdNoDeltaStream;
40
49
  this.claims = {
41
50
  scopes: [ScopeType.DocRead],
@@ -98,67 +107,6 @@ const waitForOnline = async () => {
98
107
  * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
99
108
  */
100
109
  export class ConnectionManager {
101
- constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
102
- this.serviceProvider = serviceProvider;
103
- this.containerDirty = containerDirty;
104
- this.client = client;
105
- this.logger = logger;
106
- this.props = props;
107
- /** tracks host requiring read-only mode. */
108
- this._forceReadonly = false;
109
- /** True if there is pending (async) reconnection from "read" to "write" */
110
- this.pendingReconnect = false;
111
- this.clientSequenceNumber = 0;
112
- this.clientSequenceNumberObserved = 0;
113
- /** Counts the number of non-runtime ops sent by the client which may not be acked. */
114
- this.localOpsToIgnore = 0;
115
- this.connectFirstConnection = true;
116
- this._connectionVerboseProps = {};
117
- this._connectionProps = {};
118
- this._disposed = false;
119
- this.opHandler = (documentId, messagesArg) => {
120
- const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
121
- this.props.incomingOpHandler(messages, "opHandler");
122
- };
123
- // Always connect in write mode after getting nacked.
124
- this.nackHandler = (documentId, messages) => {
125
- const message = messages[0];
126
- if (this._readonlyPermissions === true) {
127
- this.props.closeHandler(createWriteError("writeOnReadOnlyDocument", { driverVersion: undefined }));
128
- return;
129
- }
130
- const reconnectInfo = getNackReconnectInfo(message.content);
131
- // If the nack indicates we cannot retry, then close the container outright
132
- if (!reconnectInfo.canRetry) {
133
- this.props.closeHandler(reconnectInfo);
134
- return;
135
- }
136
- this.reconnectOnError("write", reconnectInfo);
137
- };
138
- // Connection mode is always read on disconnect/error unless the system mode was write.
139
- this.disconnectHandlerInternal = (disconnectReason) => {
140
- // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
141
- // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
142
- this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
143
- };
144
- this.errorHandler = (error) => {
145
- this.reconnectOnError(this.defaultReconnectionMode, error);
146
- };
147
- this.clientDetails = this.client.details;
148
- this.defaultReconnectionMode = this.client.mode;
149
- this._reconnectMode = reconnectAllowed ? ReconnectMode.Enabled : ReconnectMode.Never;
150
- // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
151
- // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
152
- this._outbound = new DeltaQueue((messages) => {
153
- if (this.connection === undefined) {
154
- throw new Error("Attempted to submit an outbound message without connection");
155
- }
156
- this.connection.submit(messages);
157
- });
158
- this._outbound.on("error", (error) => {
159
- this.props.closeHandler(normalizeError(error));
160
- });
161
- }
162
110
  get connectionVerboseProps() {
163
111
  return this._connectionVerboseProps;
164
112
  }
@@ -272,6 +220,71 @@ export class ConnectionManager {
272
220
  reason,
273
221
  };
274
222
  }
223
+ constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
224
+ this.serviceProvider = serviceProvider;
225
+ this.containerDirty = containerDirty;
226
+ this.client = client;
227
+ this.logger = logger;
228
+ this.props = props;
229
+ /** tracks host requiring read-only mode. */
230
+ this._forceReadonly = false;
231
+ /** True if there is pending (async) reconnection from "read" to "write" */
232
+ this.pendingReconnect = false;
233
+ this.clientSequenceNumber = 0;
234
+ this.clientSequenceNumberObserved = 0;
235
+ /** Counts the number of non-runtime ops sent by the client which may not be acked. */
236
+ this.localOpsToIgnore = 0;
237
+ this.connectFirstConnection = true;
238
+ this._connectionVerboseProps = {};
239
+ this._connectionProps = {};
240
+ this._disposed = false;
241
+ this.opHandler = (documentId, messagesArg) => {
242
+ const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
243
+ this.props.incomingOpHandler(messages, "opHandler");
244
+ };
245
+ this.signalHandler = (signalsArg) => {
246
+ const signals = Array.isArray(signalsArg) ? signalsArg : [signalsArg];
247
+ this.props.signalHandler(signals);
248
+ };
249
+ // Always connect in write mode after getting nacked.
250
+ this.nackHandler = (documentId, messages) => {
251
+ const message = messages[0];
252
+ if (this._readonlyPermissions === true) {
253
+ this.props.closeHandler(createWriteError("writeOnReadOnlyDocument", { driverVersion: undefined }));
254
+ return;
255
+ }
256
+ const reconnectInfo = getNackReconnectInfo(message.content);
257
+ // If the nack indicates we cannot retry, then close the container outright
258
+ if (!reconnectInfo.canRetry) {
259
+ this.props.closeHandler(reconnectInfo);
260
+ return;
261
+ }
262
+ this.reconnectOnError("write", reconnectInfo);
263
+ };
264
+ // Connection mode is always read on disconnect/error unless the system mode was write.
265
+ this.disconnectHandlerInternal = (disconnectReason) => {
266
+ // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
267
+ // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
268
+ this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
269
+ };
270
+ this.errorHandler = (error) => {
271
+ this.reconnectOnError(this.defaultReconnectionMode, error);
272
+ };
273
+ this.clientDetails = this.client.details;
274
+ this.defaultReconnectionMode = this.client.mode;
275
+ this._reconnectMode = reconnectAllowed ? ReconnectMode.Enabled : ReconnectMode.Never;
276
+ // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
277
+ // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
278
+ this._outbound = new DeltaQueue((messages) => {
279
+ if (this.connection === undefined) {
280
+ throw new Error("Attempted to submit an outbound message without connection");
281
+ }
282
+ this.connection.submit(messages);
283
+ });
284
+ this._outbound.on("error", (error) => {
285
+ this.props.closeHandler(normalizeError(error));
286
+ });
287
+ }
275
288
  dispose(error, switchToReadonly = true) {
276
289
  if (this._disposed) {
277
290
  return;
@@ -291,7 +304,7 @@ export class ConnectionManager {
291
304
  // Notify everyone we are in read-only state.
292
305
  // Useful for data stores in case we hit some critical error,
293
306
  // to switch to a mode where user edits are not accepted
294
- this.set_readonlyPermissions(true, oldReadonlyValue);
307
+ this.set_readonlyPermissions(true, oldReadonlyValue, disconnectReason);
295
308
  }
296
309
  }
297
310
  /**
@@ -307,21 +320,7 @@ export class ConnectionManager {
307
320
  }
308
321
  }
309
322
  /**
310
- * Sends signal to runtime (and data stores) to be read-only.
311
- * Hosts may have read only views, indicating to data stores that no edits are allowed.
312
- * This is independent from this._readonlyPermissions (permissions) and this.connectionMode
313
- * (server can return "write" mode even when asked for "read")
314
- * Leveraging same "readonly" event as runtime & data stores should behave the same in such case
315
- * as in read-only permissions.
316
- * But this.active can be used by some DDSes to figure out if ops can be sent
317
- * (for example, read-only view still participates in code proposals / upgrades decisions)
318
- *
319
- * Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
320
- * the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
321
- * there are pending in the outbound queue, it will stop sending until force readonly is
322
- * cleared.
323
- *
324
- * @param readonly - set or clear force readonly.
323
+ * {@inheritDoc Container.forceReadonly}
325
324
  */
326
325
  forceReadonly(readonly) {
327
326
  if (readonly !== this._forceReadonly) {
@@ -355,10 +354,10 @@ export class ConnectionManager {
355
354
  }
356
355
  }
357
356
  }
358
- set_readonlyPermissions(newReadonlyValue, oldReadonlyValue) {
357
+ set_readonlyPermissions(newReadonlyValue, oldReadonlyValue, readonlyConnectionReason) {
359
358
  this._readonlyPermissions = newReadonlyValue;
360
359
  if (oldReadonlyValue !== this.readonly) {
361
- this.props.readonlyChangeHandler(this.readonly);
360
+ this.props.readonlyChangeHandler(this.readonly, readonlyConnectionReason);
362
361
  }
363
362
  }
364
363
  connect(reason, connectionMode) {
@@ -439,7 +438,22 @@ export class ConnectionManager {
439
438
  }
440
439
  catch (origError) {
441
440
  if (isDeltaStreamConnectionForbiddenError(origError)) {
442
- connection = new NoDeltaStream(origError.storageOnlyReason);
441
+ connection = new NoDeltaStream(origError.storageOnlyReason, {
442
+ text: origError.message,
443
+ error: origError,
444
+ });
445
+ requestedMode = "read";
446
+ break;
447
+ }
448
+ else if (isFluidError(origError) &&
449
+ // eslint-disable-next-line import/no-deprecated
450
+ origError.errorType === DriverErrorType.outOfStorageError) {
451
+ // If we get out of storage error from calling joinsession, then use the NoDeltaStream object so
452
+ // that user can at least load the container.
453
+ connection = new NoDeltaStream(undefined, {
454
+ text: origError.message,
455
+ error: origError,
456
+ });
443
457
  requestedMode = "read";
444
458
  break;
445
459
  }
@@ -547,7 +561,7 @@ export class ConnectionManager {
547
561
  this.connection = undefined;
548
562
  // Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError
549
563
  connection.off("op", this.opHandler);
550
- connection.off("signal", this.props.signalHandler);
564
+ connection.off("signal", this.signalHandler);
551
565
  connection.off("nack", this.nackHandler);
552
566
  connection.off("disconnect", this.disconnectHandlerInternal);
553
567
  connection.off("error", this.errorHandler);
@@ -600,7 +614,7 @@ export class ConnectionManager {
600
614
  // removed after those packages have released and become ubiquitous.
601
615
  assert(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
602
616
  assert(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
603
- this.set_readonlyPermissions(readonly, oldReadonlyValue);
617
+ this.set_readonlyPermissions(readonly, oldReadonlyValue, isNoDeltaStreamConnection(connection) ? connection.readonlyConnectionReason : undefined);
604
618
  if (this._disposed) {
605
619
  // Raise proper events, Log telemetry event and close connection.
606
620
  this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
@@ -608,7 +622,7 @@ export class ConnectionManager {
608
622
  }
609
623
  this._outbound.resume();
610
624
  connection.on("op", this.opHandler);
611
- connection.on("signal", this.props.signalHandler);
625
+ connection.on("signal", this.signalHandler);
612
626
  connection.on("nack", this.nackHandler);
613
627
  connection.on("disconnect", this.disconnectHandlerInternal);
614
628
  connection.on("error", this.errorHandler);
@@ -659,26 +673,26 @@ export class ConnectionManager {
659
673
  type: SignalType.Clear,
660
674
  }),
661
675
  };
662
- this.props.signalHandler(clearSignal);
663
- for (const priorClient of connection.initialClients ?? []) {
664
- const joinSignal = {
665
- clientId: null,
666
- content: JSON.stringify({
667
- type: SignalType.ClientJoin,
668
- content: priorClient, // ISignalClient
669
- }),
670
- };
671
- this.props.signalHandler(joinSignal);
676
+ // list of signals to process due to this new connection
677
+ let signalsToProcess = [clearSignal];
678
+ const clientJoinSignals = (connection.initialClients ?? []).map((priorClient) => ({
679
+ clientId: null,
680
+ content: JSON.stringify({
681
+ type: SignalType.ClientJoin,
682
+ content: priorClient, // ISignalClient
683
+ }),
684
+ }));
685
+ if (clientJoinSignals.length > 0) {
686
+ signalsToProcess = signalsToProcess.concat(clientJoinSignals);
672
687
  }
673
688
  // Unfortunately, there is no defined order between initialSignals (including join & leave signals)
674
689
  // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
675
690
  // for "self" and connection.initialClients does not contain "self", so we have to process them after
676
691
  // "clear" signal above.
677
- if (connection.initialSignals !== undefined) {
678
- for (const signal of connection.initialSignals) {
679
- this.props.signalHandler(signal);
680
- }
692
+ if (connection.initialSignals !== undefined && connection.initialSignals.length > 0) {
693
+ signalsToProcess = signalsToProcess.concat(connection.initialSignals);
681
694
  }
695
+ this.props.signalHandler(signalsToProcess);
682
696
  }
683
697
  /**
684
698
  * Disconnect the current connection and reconnect. Closes the container if it fails.