@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917

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 (124) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +45 -4
  3. package/closeAndGetPendingLocalState.md +51 -0
  4. package/dist/connectionManager.d.ts +2 -1
  5. package/dist/connectionManager.d.ts.map +1 -1
  6. package/dist/connectionManager.js +59 -17
  7. package/dist/connectionManager.js.map +1 -1
  8. package/dist/connectionStateHandler.d.ts +4 -4
  9. package/dist/connectionStateHandler.d.ts.map +1 -1
  10. package/dist/connectionStateHandler.js +7 -0
  11. package/dist/connectionStateHandler.js.map +1 -1
  12. package/dist/container.d.ts +44 -4
  13. package/dist/container.d.ts.map +1 -1
  14. package/dist/container.js +160 -105
  15. package/dist/container.js.map +1 -1
  16. package/dist/containerContext.d.ts +18 -8
  17. package/dist/containerContext.d.ts.map +1 -1
  18. package/dist/containerContext.js +47 -4
  19. package/dist/containerContext.js.map +1 -1
  20. package/dist/containerStorageAdapter.d.ts +41 -2
  21. package/dist/containerStorageAdapter.d.ts.map +1 -1
  22. package/dist/containerStorageAdapter.js +87 -11
  23. package/dist/containerStorageAdapter.js.map +1 -1
  24. package/dist/contracts.d.ts +2 -2
  25. package/dist/contracts.d.ts.map +1 -1
  26. package/dist/contracts.js.map +1 -1
  27. package/dist/deltaManager.d.ts +4 -5
  28. package/dist/deltaManager.d.ts.map +1 -1
  29. package/dist/deltaManager.js +7 -10
  30. package/dist/deltaManager.js.map +1 -1
  31. package/dist/deltaManagerProxy.d.ts +10 -22
  32. package/dist/deltaManagerProxy.d.ts.map +1 -1
  33. package/dist/deltaManagerProxy.js +14 -50
  34. package/dist/deltaManagerProxy.js.map +1 -1
  35. package/dist/index.d.ts +3 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/loader.d.ts +10 -1
  40. package/dist/loader.d.ts.map +1 -1
  41. package/dist/loader.js +25 -16
  42. package/dist/loader.js.map +1 -1
  43. package/dist/packageVersion.d.ts +1 -1
  44. package/dist/packageVersion.js +1 -1
  45. package/dist/packageVersion.js.map +1 -1
  46. package/dist/protocol.d.ts +1 -0
  47. package/dist/protocol.d.ts.map +1 -1
  48. package/dist/protocol.js +4 -2
  49. package/dist/protocol.js.map +1 -1
  50. package/dist/protocolTreeDocumentStorageService.d.ts +6 -2
  51. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  52. package/dist/protocolTreeDocumentStorageService.js +7 -4
  53. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  54. package/dist/utils.d.ts.map +1 -1
  55. package/dist/utils.js +2 -1
  56. package/dist/utils.js.map +1 -1
  57. package/lib/connectionManager.d.ts +2 -1
  58. package/lib/connectionManager.d.ts.map +1 -1
  59. package/lib/connectionManager.js +60 -18
  60. package/lib/connectionManager.js.map +1 -1
  61. package/lib/connectionStateHandler.d.ts +4 -4
  62. package/lib/connectionStateHandler.d.ts.map +1 -1
  63. package/lib/connectionStateHandler.js +7 -0
  64. package/lib/connectionStateHandler.js.map +1 -1
  65. package/lib/container.d.ts +44 -4
  66. package/lib/container.d.ts.map +1 -1
  67. package/lib/container.js +164 -109
  68. package/lib/container.js.map +1 -1
  69. package/lib/containerContext.d.ts +18 -8
  70. package/lib/containerContext.d.ts.map +1 -1
  71. package/lib/containerContext.js +48 -5
  72. package/lib/containerContext.js.map +1 -1
  73. package/lib/containerStorageAdapter.d.ts +41 -2
  74. package/lib/containerStorageAdapter.d.ts.map +1 -1
  75. package/lib/containerStorageAdapter.js +85 -11
  76. package/lib/containerStorageAdapter.js.map +1 -1
  77. package/lib/contracts.d.ts +2 -2
  78. package/lib/contracts.d.ts.map +1 -1
  79. package/lib/contracts.js.map +1 -1
  80. package/lib/deltaManager.d.ts +4 -5
  81. package/lib/deltaManager.d.ts.map +1 -1
  82. package/lib/deltaManager.js +7 -10
  83. package/lib/deltaManager.js.map +1 -1
  84. package/lib/deltaManagerProxy.d.ts +10 -22
  85. package/lib/deltaManagerProxy.d.ts.map +1 -1
  86. package/lib/deltaManagerProxy.js +14 -50
  87. package/lib/deltaManagerProxy.js.map +1 -1
  88. package/lib/index.d.ts +3 -2
  89. package/lib/index.d.ts.map +1 -1
  90. package/lib/index.js +2 -2
  91. package/lib/index.js.map +1 -1
  92. package/lib/loader.d.ts +10 -1
  93. package/lib/loader.d.ts.map +1 -1
  94. package/lib/loader.js +24 -16
  95. package/lib/loader.js.map +1 -1
  96. package/lib/packageVersion.d.ts +1 -1
  97. package/lib/packageVersion.js +1 -1
  98. package/lib/packageVersion.js.map +1 -1
  99. package/lib/protocol.d.ts +1 -0
  100. package/lib/protocol.d.ts.map +1 -1
  101. package/lib/protocol.js +3 -1
  102. package/lib/protocol.js.map +1 -1
  103. package/lib/protocolTreeDocumentStorageService.d.ts +6 -2
  104. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  105. package/lib/protocolTreeDocumentStorageService.js +7 -4
  106. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  107. package/lib/utils.d.ts.map +1 -1
  108. package/lib/utils.js +2 -1
  109. package/lib/utils.js.map +1 -1
  110. package/package.json +64 -56
  111. package/src/connectionManager.ts +65 -24
  112. package/src/connectionStateHandler.ts +17 -5
  113. package/src/container.ts +239 -137
  114. package/src/containerContext.ts +74 -11
  115. package/src/containerStorageAdapter.ts +113 -9
  116. package/src/contracts.ts +2 -2
  117. package/src/deltaManager.ts +12 -14
  118. package/src/deltaManagerProxy.ts +18 -73
  119. package/src/index.ts +3 -3
  120. package/src/loader.ts +31 -26
  121. package/src/packageVersion.ts +1 -1
  122. package/src/protocol.ts +4 -1
  123. package/src/protocolTreeDocumentStorageService.ts +6 -3
  124. package/src/utils.ts +7 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # @fluidframework/container-loader
2
+
3
+ ## 2.0.0-internal.4.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Container-loader deprecations ([#14891](https://github.com/microsoft/FluidFramework/pull-requests/14891)) [961e96f3c9](https://github.com/microsoft/FluidFramework/commits/961e96f3c92d1dcf9575e56c703fe1779af5442d)
8
+
9
+ The following types in the @fluidframework/container-loader package are not used by, or necessary to use our public api, so will be removed from export in the next major release:
10
+
11
+ - IContainerLoadOptions
12
+ - IContainerConfig
13
+ - IPendingContainerState
14
+ - ISerializableBlobContents
package/README.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @fluidframework/container-loader
2
2
 
3
+ <!-- AUTO-GENERATED-CONTENT:START (README_DEPENDENCY_GUIDELINES_SECTION:includeHeading=TRUE) -->
4
+
5
+ <!-- prettier-ignore-start -->
6
+ <!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
7
+
8
+ ## Using Fluid Framework libraries
9
+
10
+ When taking a dependency on a Fluid Framework library, we recommend using a `^` (caret) version range, such as `^1.3.4`.
11
+ While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
12
+ library consumers should always prefer `^`.
13
+
14
+ Note that when depending on a library version of the form 2.0.0-internal.x.y.z, called the Fluid internal version
15
+ scheme, you must use a `>= <` dependency range. Standard `^` and `~` ranges will not work as expected. See the
16
+ [@fluid-tools/version-tools](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/version-tools/README.md)
17
+ package for more information including tools to convert between version schemes.
18
+
19
+ <!-- prettier-ignore-end -->
20
+
21
+ <!-- AUTO-GENERATED-CONTENT:END -->
22
+
3
23
  **Topics covered below:**
4
24
 
5
25
  - [@fluidframework/container-loader](#fluidframeworkcontainer-loader)
@@ -10,6 +30,8 @@
10
30
  - [Loading](#loading)
11
31
  - [Connectivity](#connectivity)
12
32
  - [Closure](#closure)
33
+ - [`Container.close()`](#containerclose)
34
+ - [`Container.dispose()`](#containerdispose)
13
35
  - [Audience](#audience)
14
36
  - [ClientID and client identification](#clientid-and-client-identification)
15
37
  - [Error handling](#error-handling)
@@ -141,13 +163,14 @@ Errors are of [ICriticalContainerError](../../../common/lib/container-definition
141
163
  readonly errorType: string;
142
164
  ```
143
165
 
144
- There are 3 sources of errors:
166
+ There are 4 sources of errors:
145
167
 
146
168
  1. [ContainerErrorType](../../../common/lib/container-definitions/src/error.ts) - errors & warnings raised at loader level
147
- 2. [OdspErrorType](../../drivers/odsp-driver/src/odspError.ts) and [R11sErrorType](../../drivers/routerlicious-driver/src/documentDeltaConnection.ts) - errors raised by ODSP and R11S drivers.
148
- 3. Runtime errors, like `"summarizingError"`, `"dataCorruptionError"`. This class of errors is not pre-determined and depends on type of container loaded.
169
+ 2. [DriverErrorType](../../common/driver-definitions/src/driverError.ts) - errors that are likely to be raised from the driver level
170
+ 3. [OdspErrorType](../../drivers/odsp-driver/src/odspError.ts) and [RouterliciousErrorType](../../drivers/routerlicious-driver/src/documentDeltaConnection.ts) - errors raised by ODSP and R11S drivers.
171
+ 4. Runtime errors, like `"summarizingError"`, `"dataCorruptionError"`. This class of errors is not pre-determined and depends on type of container loaded.
149
172
 
150
- `ICriticalContainerError.errorType` is a string, which represents a union of 3 error types described above. Hosting application may package different drivers and open different types of containers, and only hosting application may have enough information to enumerate all possible error codes in such scenarios.
173
+ `ICriticalContainerError.errorType` is a string, which represents a union of 4 error types described above. Hosting application may package different drivers and open different types of containers, and only hosting application may have enough information to enumerate all possible error codes in such scenarios.
151
174
 
152
175
  Hosts must listen to `"closed"` event. If error object is present there, container was closed due to error and this information needs to be communicated to user in some way. If there is no error object, it was closed due to host application calling Container.close() (without specifying error).
153
176
  When container is closed, it is no longer connected to ordering service. It is also in read-only state, communicating to data stores not to allow user to make changes to container.
@@ -216,3 +239,21 @@ This information can be used by a host to build appropriate UX that allows user
216
239
  Note that when an active connection is in place, it's just a matter of time before changes will be flushed to storage unless there is some source of continuous local changes being generated that prevents container from ever being fully saved. But if there is no active connection, because the user is offline, for example, then a document may stay in a dirty state for very long time.
217
240
 
218
241
  `Container.isDirty` can be used to get current state of container.
242
+
243
+ <!-- AUTO-GENERATED-CONTENT:START (README_TRADEMARK_SECTION:includeHeading=TRUE) -->
244
+
245
+ <!-- prettier-ignore-start -->
246
+ <!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
247
+
248
+ ## Trademark
249
+
250
+ This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services.
251
+
252
+ Use of these trademarks or logos must follow Microsoft's [Trademark & Brand
253
+ Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
254
+
255
+ Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
256
+
257
+ <!-- prettier-ignore-end -->
258
+
259
+ <!-- AUTO-GENERATED-CONTENT:END -->
@@ -0,0 +1,51 @@
1
+ # closeAndGetPendingLocalState
2
+
3
+ Pending local state is a mechanism that Fluid provides for mitigating data loss, particularly in offline scenarios.
4
+ When changes are made in a Container, it puts the Container into a "dirty" state until the changes have been confirmed by the server.
5
+ If the Container is closed in this state, the changes may be lost if they did not reach the server (for example, if the user is offline).
6
+
7
+ `Container.closeAndGetPendingLocalState()` solves this problem by returning pending changes when closing a Container so they may be resupplied to a new container instance and resubmitted.
8
+
9
+ Additionally, the blob returned by closeAndGetPendingLocalState() contains enough data to load a container offline.
10
+ This method will still return a such a blob even if the container is not dirty when called.
11
+
12
+ ## Using `Container.closeAndGetPendingLocalState()`
13
+
14
+ When closeAndGetPendingLocalState() is called it will return a serialized blob containing the necessary data to load and resubmit the changes in a new container.
15
+
16
+ The stashed changes blob can be used by passing it to `Loader.resolve()` when loading a new container.
17
+ The container will then automatically submit the stashed changes when the Container reaches the "connected" state, if the changes were not originally successful.
18
+
19
+ **It's important that these blobs are not reused, since it can result in the same changes being submitted multiple times, possibly resulting in document corruption.**
20
+ Instead, closeAndGetPendingLocalState() should be called again on the new container, which will return a new blob containing all its pending changes, including any still-pending stashed changes it was loaded with.
21
+
22
+ ```ts
23
+ const myContainer = await myLoader.resolve(myRequest);
24
+ const pendingChanges = myContainer.closeAndGetPendingLocalState();
25
+ const myNewContainer = await myLoader.resolve(myRequest, pendingChanges);
26
+ ```
27
+
28
+ Note that closeAndGetPendingLocalState() cannot be called on a Container that is already closed or disposed.
29
+ The purpose of closeAndGetPendingLocalState() is to avoid data loss when a dirty container must be closed, not recovering from internal Container errors.
30
+
31
+ ## How it works
32
+
33
+ The blob contains ops and attachment blob uploads that were still pending whe the container was closed.
34
+ It also contains the container's last client ID, a snapshot older than the reference sequence number of the oldest pending op, and all sequenced ops the container has processed since the snapshot.
35
+
36
+ When the blob is supplied to `Loader.resolve()`, it will return a new container.
37
+ This container will load from the snapshot in the blob, and "replay" the saved ops in the blob by processing them one by one.
38
+ applyStashedOp() will be called for each stashed op after the op whose sequence number matches the stashed op's reference sequence number is processed (i.e., when the document is in the same state as when the op was originally made).
39
+
40
+ When the container connects to the delta stream and starts processing new ops, they are matched to stashed ops by client ID, so the container will not submit stashed ops that were originally successfully submitted.
41
+ When it processes its own join op (i.e., reaches "connected" state), the container will have seen any previously successful stashed ops, and it is safe to resubmit any remaining stashed ops.
42
+
43
+ ### applyStashedOp()
44
+
45
+ When an op is created, it is given a "reference sequence number," which is the sequence number of the last op processed by the container.
46
+ For various reasons, the server will only sequence ops if they are within the "collab window," limited by the "minimum sequence number" of the document.
47
+ For this reason, if an op is not successfully submitted while its reference sequence number is within this window, runtime will call SharedObject.reSubmit() to ask the DDS to merge it with later remote changes and resubmit the change.
48
+ Once the op is sequenced by the server, runtime will pass it to SharedObject.process().
49
+
50
+ The job of applyStashedOp() is to return a new DDS in a new container to the same state, so that the DDS is able to handle calls to reSubmit() or process() with the op, even though the DDS itself did not submit it.
51
+ Once stashed ops are passed to applyStashedOp(), they are handled by runtime the same as any other pending ops.
@@ -14,6 +14,7 @@ import { ReconnectMode, IConnectionManager, IConnectionManagerFactoryArgs } from
14
14
  */
15
15
  export declare class ConnectionManager implements IConnectionManager {
16
16
  private readonly serviceProvider;
17
+ readonly containerDirty: () => boolean;
17
18
  private client;
18
19
  private readonly logger;
19
20
  private readonly props;
@@ -83,7 +84,7 @@ export declare class ConnectionManager implements IConnectionManager {
83
84
  private get readonly();
84
85
  get readOnlyInfo(): ReadOnlyInfo;
85
86
  private static detailsFromConnection;
86
- constructor(serviceProvider: () => IDocumentService | undefined, client: IClient, reconnectAllowed: boolean, logger: ITelemetryLogger, props: IConnectionManagerFactoryArgs);
87
+ constructor(serviceProvider: () => IDocumentService | undefined, containerDirty: () => boolean, client: IClient, reconnectAllowed: boolean, logger: ITelemetryLogger, props: IConnectionManagerFactoryArgs);
87
88
  dispose(error?: ICriticalContainerError, switchToReadonly?: boolean): void;
88
89
  /**
89
90
  * Enables or disables automatic reconnecting.
@@ -1 +1 @@
1
- {"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ exports.ConnectionManager = void 0;
11
11
  const abort_controller_1 = __importDefault(require("abort-controller"));
12
12
  const common_utils_1 = require("@fluidframework/common-utils");
13
13
  const container_utils_1 = require("@fluidframework/container-utils");
14
+ const driver_definitions_1 = require("@fluidframework/driver-definitions");
14
15
  const driver_utils_1 = require("@fluidframework/driver-utils");
15
16
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
16
17
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
@@ -83,14 +84,28 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
83
84
  this._disposed = true;
84
85
  }
85
86
  }
87
+ const waitForOnline = async () => {
88
+ var _a;
89
+ // Only wait if we have a strong signal that we're offline - otherwise assume we're online.
90
+ if (((_a = globalThis.navigator) === null || _a === void 0 ? void 0 : _a.onLine) === false && globalThis.addEventListener !== undefined) {
91
+ return new Promise((resolve) => {
92
+ const resolveAndRemoveListener = () => {
93
+ resolve();
94
+ globalThis.removeEventListener("online", resolveAndRemoveListener);
95
+ };
96
+ globalThis.addEventListener("online", resolveAndRemoveListener);
97
+ });
98
+ }
99
+ };
86
100
  /**
87
101
  * Implementation of IConnectionManager, used by Container class
88
102
  * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
89
103
  * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
90
104
  */
91
105
  class ConnectionManager {
92
- constructor(serviceProvider, client, reconnectAllowed, logger, props) {
106
+ constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
93
107
  this.serviceProvider = serviceProvider;
108
+ this.containerDirty = containerDirty;
94
109
  this.client = client;
95
110
  this.logger = logger;
96
111
  this.props = props;
@@ -207,7 +222,19 @@ class ConnectionManager {
207
222
  }
208
223
  shouldJoinWrite() {
209
224
  // We don't have to wait for ack for topmost NoOps. So subtract those.
210
- return (this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore);
225
+ const outstandingOps = this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore;
226
+ // Previous behavior was to force write mode here only when there are outstanding ops (besides
227
+ // no-ops). The dirty signal from runtime should provide the same behavior, but also support
228
+ // stashed ops that weren't submitted to container layer yet. For safety, we want to retain the
229
+ // same behavior whenever dirty is false.
230
+ const isDirty = this.containerDirty();
231
+ if (outstandingOps !== isDirty) {
232
+ this.logger.sendTelemetryEvent({
233
+ eventName: "DesiredConnectionModeMismatch",
234
+ details: JSON.stringify({ outstandingOps, isDirty }),
235
+ });
236
+ }
237
+ return outstandingOps || isDirty;
211
238
  }
212
239
  /**
213
240
  * Tells if container is in read-only mode.
@@ -219,10 +246,7 @@ class ConnectionManager {
219
246
  * and do not know if user has write access to a file.
220
247
  */
221
248
  get readonly() {
222
- if (this._forceReadonly) {
223
- return true;
224
- }
225
- return this._readonlyPermissions;
249
+ return this.readOnlyInfo.readonly;
226
250
  }
227
251
  get readOnlyInfo() {
228
252
  const storageOnly = this.connection !== undefined && this.connection instanceof NoDeltaStream;
@@ -240,7 +264,6 @@ class ConnectionManager {
240
264
  return {
241
265
  claims: connection.claims,
242
266
  clientId: connection.clientId,
243
- existing: connection.existing,
244
267
  checkpointSequenceNumber: connection.checkpointSequenceNumber,
245
268
  get initialClients() {
246
269
  return connection.initialClients;
@@ -259,9 +282,7 @@ class ConnectionManager {
259
282
  // Ensure that things like triggerConnect() will short circuit
260
283
  this._reconnectMode = contracts_1.ReconnectMode.Never;
261
284
  this._outbound.clear();
262
- const disconnectReason = error !== undefined
263
- ? `Closing DeltaManager (${error.message})`
264
- : "Closing DeltaManager";
285
+ const disconnectReason = "Closing DeltaManager";
265
286
  // This raises "disconnect" event if we have active connection.
266
287
  this.disconnectFromDeltaStream(disconnectReason);
267
288
  if (switchToReadonly) {
@@ -346,7 +367,7 @@ class ConnectionManager {
346
367
  });
347
368
  }
348
369
  async connectCore(connectionMode) {
349
- var _a, _b;
370
+ var _a, _b, _c;
350
371
  (0, common_utils_1.assert)(!this._disposed, 0x26a /* "not closed" */);
351
372
  if (this.connection !== undefined) {
352
373
  return; // Connection attempt already completed successfully
@@ -415,7 +436,7 @@ class ConnectionManager {
415
436
  catch (origError) {
416
437
  if (typeof origError === "object" &&
417
438
  origError !== null &&
418
- (origError === null || origError === void 0 ? void 0 : origError.errorType) === driver_utils_1.DeltaStreamConnectionForbiddenError.errorType) {
439
+ (origError === null || origError === void 0 ? void 0 : origError.errorType) === driver_definitions_1.DriverErrorType.deltaStreamConnectionForbidden) {
419
440
  connection = new NoDeltaStream();
420
441
  requestedMode = "read";
421
442
  break;
@@ -435,11 +456,25 @@ class ConnectionManager {
435
456
  }, origError);
436
457
  lastError = origError;
437
458
  const retryDelayFromError = (0, driver_utils_1.getRetryDelayFromError)(origError);
438
- delayMs = retryDelayFromError !== null && retryDelayFromError !== void 0 ? retryDelayFromError : Math.min(delayMs * 2, MaxReconnectDelayInMs);
439
459
  if (retryDelayFromError !== undefined) {
460
+ // If the error told us to wait, then we wait.
440
461
  this.props.reconnectionDelayHandler(retryDelayFromError, origError);
462
+ await new Promise((resolve) => {
463
+ setTimeout(resolve, retryDelayFromError);
464
+ });
465
+ }
466
+ else if (((_c = globalThis.navigator) === null || _c === void 0 ? void 0 : _c.onLine) !== false) {
467
+ // If the error didn't tell us to wait, let's still wait a little bit before retrying.
468
+ // We skip this delay if we're confident we're offline, because we probably just need to wait to come back online.
469
+ await new Promise((resolve) => {
470
+ setTimeout(resolve, delayMs);
471
+ delayMs = Math.min(delayMs * 2, MaxReconnectDelayInMs);
472
+ });
441
473
  }
442
- await (0, driver_utils_1.waitForConnectedState)(delayMs);
474
+ // If we believe we're offline, we assume there's no point in trying until we at least think we're online.
475
+ // NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
476
+ // should probably live in the driver.
477
+ await waitForOnline();
443
478
  }
444
479
  }
445
480
  // If we retried more than once, log an event about how long it took (this will not log to error table)
@@ -469,7 +504,7 @@ class ConnectionManager {
469
504
  * @param args - The connection arguments
470
505
  */
471
506
  triggerConnect(connectionMode) {
472
- // reconnect() has async await of waitForConnectedState(), and that causes potential race conditions
507
+ // reconnect() includes async awaits, and that causes potential race conditions
473
508
  // where we might already have a connection. If it were to happen, it's possible that we will connect
474
509
  // with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
475
510
  // fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it
@@ -508,8 +543,8 @@ class ConnectionManager {
508
543
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
509
544
  this._outbound.pause();
510
545
  this._outbound.clear();
511
- this.props.disconnectHandler(reason);
512
546
  connection.dispose();
547
+ this.props.disconnectHandler(reason);
513
548
  this._connectionVerboseProps = {};
514
549
  return true;
515
550
  }
@@ -671,11 +706,18 @@ class ConnectionManager {
671
706
  if (this._disposed || this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
672
707
  return;
673
708
  }
709
+ // If the error tells us to wait before retrying, then do so.
674
710
  const delayMs = (0, driver_utils_1.getRetryDelayFromError)(error);
675
711
  if (error !== undefined && delayMs !== undefined) {
676
712
  this.props.reconnectionDelayHandler(delayMs, error);
677
- await (0, driver_utils_1.waitForConnectedState)(delayMs);
713
+ await new Promise((resolve) => {
714
+ setTimeout(resolve, delayMs);
715
+ });
678
716
  }
717
+ // If we believe we're offline, we assume there's no point in trying again until we at least think we're online.
718
+ // NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
719
+ // should probably live in the driver.
720
+ await waitForOnline();
679
721
  this.triggerConnect(requestedMode);
680
722
  }
681
723
  prepareMessageToSend(message) {