@fluidframework/container-loader 1.4.0-121020 → 2.0.0-dev-rc.1.0.0.224419

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 (333) hide show
  1. package/.eslintrc.js +18 -21
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +364 -0
  4. package/README.md +152 -56
  5. package/api-extractor-lint.json +4 -0
  6. package/api-extractor.json +2 -2
  7. package/api-report/container-loader.api.md +143 -0
  8. package/dist/{audience.js → audience.cjs} +15 -13
  9. package/dist/audience.cjs.map +1 -0
  10. package/dist/audience.d.ts +4 -6
  11. package/dist/audience.d.ts.map +1 -1
  12. package/dist/catchUpMonitor.cjs +43 -0
  13. package/dist/catchUpMonitor.cjs.map +1 -0
  14. package/dist/catchUpMonitor.d.ts +29 -0
  15. package/dist/catchUpMonitor.d.ts.map +1 -0
  16. package/dist/{connectionManager.js → connectionManager.cjs} +397 -240
  17. package/dist/connectionManager.cjs.map +1 -0
  18. package/dist/connectionManager.d.ts +23 -33
  19. package/dist/connectionManager.d.ts.map +1 -1
  20. package/dist/{connectionState.js → connectionState.cjs} +5 -7
  21. package/dist/connectionState.cjs.map +1 -0
  22. package/dist/connectionState.d.ts +3 -5
  23. package/dist/connectionState.d.ts.map +1 -1
  24. package/dist/connectionStateHandler.cjs +474 -0
  25. package/dist/connectionStateHandler.cjs.map +1 -0
  26. package/dist/connectionStateHandler.d.ts +127 -29
  27. package/dist/connectionStateHandler.d.ts.map +1 -1
  28. package/dist/container-loader-alpha.d.ts +274 -0
  29. package/dist/container-loader-beta.d.ts +75 -0
  30. package/dist/container-loader-public.d.ts +75 -0
  31. package/dist/container-loader-untrimmed.d.ts +331 -0
  32. package/dist/container.cjs +1585 -0
  33. package/dist/container.cjs.map +1 -0
  34. package/dist/container.d.ts +227 -83
  35. package/dist/container.d.ts.map +1 -1
  36. package/dist/containerContext.cjs +74 -0
  37. package/dist/containerContext.cjs.map +1 -0
  38. package/dist/containerContext.d.ts +33 -59
  39. package/dist/containerContext.d.ts.map +1 -1
  40. package/dist/containerStorageAdapter.cjs +234 -0
  41. package/dist/containerStorageAdapter.cjs.map +1 -0
  42. package/dist/containerStorageAdapter.d.ts +48 -23
  43. package/dist/containerStorageAdapter.d.ts.map +1 -1
  44. package/dist/{contracts.js → contracts.cjs} +5 -5
  45. package/dist/contracts.cjs.map +1 -0
  46. package/dist/contracts.d.ts +45 -17
  47. package/dist/contracts.d.ts.map +1 -1
  48. package/dist/debugLogger.cjs +101 -0
  49. package/dist/debugLogger.cjs.map +1 -0
  50. package/dist/debugLogger.d.ts +30 -0
  51. package/dist/debugLogger.d.ts.map +1 -0
  52. package/dist/{deltaManager.js → deltaManager.cjs} +379 -186
  53. package/dist/deltaManager.cjs.map +1 -0
  54. package/dist/deltaManager.d.ts +54 -18
  55. package/dist/deltaManager.d.ts.map +1 -1
  56. package/dist/{deltaQueue.js → deltaQueue.cjs} +29 -28
  57. package/dist/deltaQueue.cjs.map +1 -0
  58. package/dist/deltaQueue.d.ts +3 -4
  59. package/dist/deltaQueue.d.ts.map +1 -1
  60. package/dist/disposal.cjs +25 -0
  61. package/dist/disposal.cjs.map +1 -0
  62. package/dist/disposal.d.ts +13 -0
  63. package/dist/disposal.d.ts.map +1 -0
  64. package/dist/error.cjs +32 -0
  65. package/dist/error.cjs.map +1 -0
  66. package/dist/error.d.ts +23 -0
  67. package/dist/error.d.ts.map +1 -0
  68. package/dist/index.cjs +19 -0
  69. package/dist/index.cjs.map +1 -0
  70. package/dist/index.d.ts +5 -2
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/loader.cjs +148 -0
  73. package/dist/loader.cjs.map +1 -0
  74. package/dist/loader.d.ts +38 -19
  75. package/dist/loader.d.ts.map +1 -1
  76. package/dist/location-redirection-utilities/index.cjs +11 -0
  77. package/dist/location-redirection-utilities/index.cjs.map +1 -0
  78. package/dist/location-redirection-utilities/index.d.ts +6 -0
  79. package/dist/location-redirection-utilities/index.d.ts.map +1 -0
  80. package/dist/location-redirection-utilities/resolveWithLocationRedirection.cjs +53 -0
  81. package/dist/location-redirection-utilities/resolveWithLocationRedirection.cjs.map +1 -0
  82. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +24 -0
  83. package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
  84. package/dist/{collabWindowTracker.js → noopHeuristic.cjs} +37 -39
  85. package/dist/noopHeuristic.cjs.map +1 -0
  86. package/dist/noopHeuristic.d.ts +23 -0
  87. package/dist/noopHeuristic.d.ts.map +1 -0
  88. package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
  89. package/dist/packageVersion.cjs.map +1 -0
  90. package/dist/packageVersion.d.ts +1 -1
  91. package/dist/packageVersion.d.ts.map +1 -1
  92. package/dist/protocol.cjs +99 -0
  93. package/dist/protocol.cjs.map +1 -0
  94. package/dist/protocol.d.ts +38 -0
  95. package/dist/protocol.d.ts.map +1 -0
  96. package/dist/{protocolTreeDocumentStorageService.js → protocolTreeDocumentStorageService.cjs} +8 -5
  97. package/dist/protocolTreeDocumentStorageService.cjs.map +1 -0
  98. package/dist/protocolTreeDocumentStorageService.d.ts +8 -4
  99. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  100. package/dist/quorum.cjs +16 -0
  101. package/dist/quorum.cjs.map +1 -0
  102. package/dist/quorum.d.ts +1 -14
  103. package/dist/quorum.d.ts.map +1 -1
  104. package/dist/{retriableDocumentStorageService.js → retriableDocumentStorageService.cjs} +36 -21
  105. package/dist/retriableDocumentStorageService.cjs.map +1 -0
  106. package/dist/retriableDocumentStorageService.d.ts +7 -5
  107. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  108. package/dist/tsdoc-metadata.json +11 -0
  109. package/dist/{utils.js → utils.cjs} +52 -14
  110. package/dist/utils.cjs.map +1 -0
  111. package/dist/utils.d.ts +34 -1
  112. package/dist/utils.d.ts.map +1 -1
  113. package/lib/{audience.d.ts → audience.d.mts} +4 -10
  114. package/lib/audience.d.mts.map +1 -0
  115. package/lib/{audience.js → audience.mjs} +15 -17
  116. package/lib/audience.mjs.map +1 -0
  117. package/lib/catchUpMonitor.d.mts +29 -0
  118. package/lib/catchUpMonitor.d.mts.map +1 -0
  119. package/lib/catchUpMonitor.mjs +39 -0
  120. package/lib/catchUpMonitor.mjs.map +1 -0
  121. package/lib/{connectionManager.d.ts → connectionManager.d.mts} +23 -33
  122. package/lib/connectionManager.d.mts.map +1 -0
  123. package/lib/{connectionManager.js → connectionManager.mjs} +378 -218
  124. package/lib/connectionManager.mjs.map +1 -0
  125. package/lib/{connectionState.d.ts → connectionState.d.mts} +3 -5
  126. package/lib/connectionState.d.mts.map +1 -0
  127. package/lib/{connectionState.js → connectionState.mjs} +4 -6
  128. package/lib/connectionState.mjs.map +1 -0
  129. package/lib/connectionStateHandler.d.mts +179 -0
  130. package/lib/connectionStateHandler.d.mts.map +1 -0
  131. package/lib/connectionStateHandler.mjs +469 -0
  132. package/lib/connectionStateHandler.mjs.map +1 -0
  133. package/lib/container-loader-alpha.d.mts +274 -0
  134. package/lib/container-loader-beta.d.mts +75 -0
  135. package/lib/container-loader-public.d.mts +75 -0
  136. package/lib/container-loader-untrimmed.d.mts +331 -0
  137. package/lib/container.d.mts +382 -0
  138. package/lib/container.d.mts.map +1 -0
  139. package/lib/container.mjs +1579 -0
  140. package/lib/container.mjs.map +1 -0
  141. package/lib/containerContext.d.mts +58 -0
  142. package/lib/containerContext.d.mts.map +1 -0
  143. package/lib/containerContext.mjs +70 -0
  144. package/lib/containerContext.mjs.map +1 -0
  145. package/lib/containerStorageAdapter.d.mts +73 -0
  146. package/lib/containerStorageAdapter.d.mts.map +1 -0
  147. package/lib/containerStorageAdapter.mjs +228 -0
  148. package/lib/containerStorageAdapter.mjs.map +1 -0
  149. package/lib/{contracts.d.ts → contracts.d.mts} +45 -17
  150. package/lib/contracts.d.mts.map +1 -0
  151. package/lib/{contracts.js → contracts.mjs} +4 -4
  152. package/lib/contracts.mjs.map +1 -0
  153. package/lib/debugLogger.d.mts +30 -0
  154. package/lib/debugLogger.d.mts.map +1 -0
  155. package/lib/debugLogger.mjs +93 -0
  156. package/lib/debugLogger.mjs.map +1 -0
  157. package/lib/{deltaManager.d.ts → deltaManager.d.mts} +54 -18
  158. package/lib/deltaManager.d.mts.map +1 -0
  159. package/lib/{deltaManager.js → deltaManager.mjs} +361 -165
  160. package/lib/deltaManager.mjs.map +1 -0
  161. package/lib/{deltaQueue.d.ts → deltaQueue.d.mts} +3 -4
  162. package/lib/deltaQueue.d.mts.map +1 -0
  163. package/lib/{deltaQueue.js → deltaQueue.mjs} +25 -24
  164. package/lib/deltaQueue.mjs.map +1 -0
  165. package/lib/disposal.d.mts +13 -0
  166. package/lib/disposal.d.mts.map +1 -0
  167. package/lib/disposal.mjs +21 -0
  168. package/lib/disposal.mjs.map +1 -0
  169. package/lib/error.d.mts +23 -0
  170. package/lib/error.d.mts.map +1 -0
  171. package/lib/error.mjs +28 -0
  172. package/lib/error.mjs.map +1 -0
  173. package/lib/index.d.mts +11 -0
  174. package/lib/index.d.mts.map +1 -0
  175. package/lib/index.mjs +10 -0
  176. package/lib/index.mjs.map +1 -0
  177. package/lib/{loader.d.ts → loader.d.mts} +39 -20
  178. package/lib/loader.d.mts.map +1 -0
  179. package/lib/loader.mjs +143 -0
  180. package/lib/loader.mjs.map +1 -0
  181. package/lib/location-redirection-utilities/index.d.mts +6 -0
  182. package/lib/location-redirection-utilities/index.d.mts.map +1 -0
  183. package/lib/location-redirection-utilities/index.mjs +6 -0
  184. package/lib/location-redirection-utilities/index.mjs.map +1 -0
  185. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts +24 -0
  186. package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.mts.map +1 -0
  187. package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs +48 -0
  188. package/lib/location-redirection-utilities/resolveWithLocationRedirection.mjs.map +1 -0
  189. package/lib/noopHeuristic.d.mts +23 -0
  190. package/lib/noopHeuristic.d.mts.map +1 -0
  191. package/lib/{collabWindowTracker.js → noopHeuristic.mjs} +33 -35
  192. package/lib/noopHeuristic.mjs.map +1 -0
  193. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
  194. package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
  195. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  196. package/lib/packageVersion.mjs.map +1 -0
  197. package/lib/protocol.d.mts +38 -0
  198. package/lib/protocol.d.mts.map +1 -0
  199. package/lib/protocol.mjs +94 -0
  200. package/lib/protocol.mjs.map +1 -0
  201. package/lib/{protocolTreeDocumentStorageService.d.ts → protocolTreeDocumentStorageService.d.mts} +8 -4
  202. package/lib/protocolTreeDocumentStorageService.d.mts.map +1 -0
  203. package/lib/{protocolTreeDocumentStorageService.js → protocolTreeDocumentStorageService.mjs} +8 -5
  204. package/lib/protocolTreeDocumentStorageService.mjs.map +1 -0
  205. package/lib/quorum.d.mts +4 -0
  206. package/lib/quorum.d.mts.map +1 -0
  207. package/lib/quorum.mjs +12 -0
  208. package/lib/quorum.mjs.map +1 -0
  209. package/lib/{retriableDocumentStorageService.d.ts → retriableDocumentStorageService.d.mts} +7 -5
  210. package/lib/retriableDocumentStorageService.d.mts.map +1 -0
  211. package/lib/{retriableDocumentStorageService.js → retriableDocumentStorageService.mjs} +35 -20
  212. package/lib/retriableDocumentStorageService.mjs.map +1 -0
  213. package/lib/utils.d.mts +67 -0
  214. package/lib/utils.d.mts.map +1 -0
  215. package/lib/{utils.js → utils.mjs} +47 -11
  216. package/lib/utils.mjs.map +1 -0
  217. package/package.json +163 -70
  218. package/prettier.config.cjs +8 -0
  219. package/src/audience.ts +59 -49
  220. package/src/catchUpMonitor.ts +61 -0
  221. package/src/connectionManager.ts +1154 -910
  222. package/src/connectionState.ts +22 -25
  223. package/src/connectionStateHandler.ts +689 -319
  224. package/src/container.ts +2476 -1792
  225. package/src/containerContext.ts +98 -330
  226. package/src/containerStorageAdapter.ts +301 -105
  227. package/src/contracts.ts +184 -146
  228. package/src/debugLogger.ts +123 -0
  229. package/src/deltaManager.ts +1165 -900
  230. package/src/deltaQueue.ts +156 -152
  231. package/src/disposal.ts +25 -0
  232. package/src/error.ts +44 -0
  233. package/src/index.ts +14 -15
  234. package/src/loader.ts +356 -427
  235. package/src/location-redirection-utilities/index.ts +9 -0
  236. package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +61 -0
  237. package/src/noopHeuristic.ts +107 -0
  238. package/src/packageVersion.ts +1 -1
  239. package/src/protocol.ts +150 -0
  240. package/src/protocolTreeDocumentStorageService.ts +35 -35
  241. package/src/quorum.ts +11 -50
  242. package/src/retriableDocumentStorageService.ts +135 -95
  243. package/src/utils.ts +159 -86
  244. package/tsc-multi.test.json +4 -0
  245. package/tsconfig.json +10 -12
  246. package/dist/audience.js.map +0 -1
  247. package/dist/collabWindowTracker.d.ts +0 -19
  248. package/dist/collabWindowTracker.d.ts.map +0 -1
  249. package/dist/collabWindowTracker.js.map +0 -1
  250. package/dist/connectionManager.js.map +0 -1
  251. package/dist/connectionState.js.map +0 -1
  252. package/dist/connectionStateHandler.js +0 -280
  253. package/dist/connectionStateHandler.js.map +0 -1
  254. package/dist/container.js +0 -1284
  255. package/dist/container.js.map +0 -1
  256. package/dist/containerContext.js +0 -217
  257. package/dist/containerContext.js.map +0 -1
  258. package/dist/containerStorageAdapter.js +0 -104
  259. package/dist/containerStorageAdapter.js.map +0 -1
  260. package/dist/contracts.js.map +0 -1
  261. package/dist/deltaManager.js.map +0 -1
  262. package/dist/deltaManagerProxy.d.ts +0 -54
  263. package/dist/deltaManagerProxy.d.ts.map +0 -1
  264. package/dist/deltaManagerProxy.js +0 -115
  265. package/dist/deltaManagerProxy.js.map +0 -1
  266. package/dist/deltaQueue.js.map +0 -1
  267. package/dist/index.js +0 -16
  268. package/dist/index.js.map +0 -1
  269. package/dist/loader.js +0 -241
  270. package/dist/loader.js.map +0 -1
  271. package/dist/packageVersion.js.map +0 -1
  272. package/dist/protocolTreeDocumentStorageService.js.map +0 -1
  273. package/dist/quorum.js +0 -44
  274. package/dist/quorum.js.map +0 -1
  275. package/dist/retriableDocumentStorageService.js.map +0 -1
  276. package/dist/utils.js.map +0 -1
  277. package/lib/audience.d.ts.map +0 -1
  278. package/lib/audience.js.map +0 -1
  279. package/lib/collabWindowTracker.d.ts +0 -19
  280. package/lib/collabWindowTracker.d.ts.map +0 -1
  281. package/lib/collabWindowTracker.js.map +0 -1
  282. package/lib/connectionManager.d.ts.map +0 -1
  283. package/lib/connectionManager.js.map +0 -1
  284. package/lib/connectionState.d.ts.map +0 -1
  285. package/lib/connectionState.js.map +0 -1
  286. package/lib/connectionStateHandler.d.ts +0 -81
  287. package/lib/connectionStateHandler.d.ts.map +0 -1
  288. package/lib/connectionStateHandler.js +0 -276
  289. package/lib/connectionStateHandler.js.map +0 -1
  290. package/lib/container.d.ts +0 -238
  291. package/lib/container.d.ts.map +0 -1
  292. package/lib/container.js +0 -1276
  293. package/lib/container.js.map +0 -1
  294. package/lib/containerContext.d.ts +0 -84
  295. package/lib/containerContext.d.ts.map +0 -1
  296. package/lib/containerContext.js +0 -213
  297. package/lib/containerContext.js.map +0 -1
  298. package/lib/containerStorageAdapter.d.ts +0 -48
  299. package/lib/containerStorageAdapter.d.ts.map +0 -1
  300. package/lib/containerStorageAdapter.js +0 -99
  301. package/lib/containerStorageAdapter.js.map +0 -1
  302. package/lib/contracts.d.ts.map +0 -1
  303. package/lib/contracts.js.map +0 -1
  304. package/lib/deltaManager.d.ts.map +0 -1
  305. package/lib/deltaManager.js.map +0 -1
  306. package/lib/deltaManagerProxy.d.ts +0 -54
  307. package/lib/deltaManagerProxy.d.ts.map +0 -1
  308. package/lib/deltaManagerProxy.js +0 -110
  309. package/lib/deltaManagerProxy.js.map +0 -1
  310. package/lib/deltaQueue.d.ts.map +0 -1
  311. package/lib/deltaQueue.js.map +0 -1
  312. package/lib/index.d.ts +0 -8
  313. package/lib/index.d.ts.map +0 -1
  314. package/lib/index.js +0 -8
  315. package/lib/index.js.map +0 -1
  316. package/lib/loader.d.ts.map +0 -1
  317. package/lib/loader.js +0 -236
  318. package/lib/loader.js.map +0 -1
  319. package/lib/packageVersion.js.map +0 -1
  320. package/lib/protocolTreeDocumentStorageService.d.ts.map +0 -1
  321. package/lib/protocolTreeDocumentStorageService.js.map +0 -1
  322. package/lib/quorum.d.ts +0 -21
  323. package/lib/quorum.d.ts.map +0 -1
  324. package/lib/quorum.js +0 -38
  325. package/lib/quorum.js.map +0 -1
  326. package/lib/retriableDocumentStorageService.d.ts.map +0 -1
  327. package/lib/retriableDocumentStorageService.js.map +0 -1
  328. package/lib/utils.d.ts +0 -34
  329. package/lib/utils.d.ts.map +0 -1
  330. package/lib/utils.js.map +0 -1
  331. package/src/collabWindowTracker.ts +0 -102
  332. package/src/deltaManagerProxy.ts +0 -158
  333. package/tsconfig.esnext.json +0 -7
@@ -3,21 +3,21 @@
3
3
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
4
  * Licensed under the MIT License.
5
5
  */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
6
  Object.defineProperty(exports, "__esModule", { value: true });
10
7
  exports.ConnectionManager = void 0;
11
- const abort_controller_1 = __importDefault(require("abort-controller"));
12
- const common_utils_1 = require("@fluidframework/common-utils");
13
- const container_utils_1 = require("@fluidframework/container-utils");
8
+ const core_interfaces_1 = require("@fluidframework/core-interfaces");
9
+ const core_utils_1 = require("@fluidframework/core-utils");
10
+ const client_utils_1 = require("@fluid-internal/client-utils");
11
+ const driver_definitions_1 = require("@fluidframework/driver-definitions");
14
12
  const driver_utils_1 = require("@fluidframework/driver-utils");
15
13
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
16
14
  const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
17
- const contracts_1 = require("./contracts");
18
- const deltaQueue_1 = require("./deltaQueue");
19
- const MaxReconnectDelayInMs = 8000;
20
- const InitialReconnectDelayInMs = 1000;
15
+ const contracts_1 = require("./contracts.cjs");
16
+ const deltaQueue_1 = require("./deltaQueue.cjs");
17
+ const protocol_1 = require("./protocol.cjs");
18
+ const utils_1 = require("./utils.cjs");
19
+ // We double this value in first try in when we calculate time to wait for in "calculateMaxWaitTime" function.
20
+ const InitialReconnectDelayInMs = 500;
21
21
  const DefaultChunkSize = 16 * 1024;
22
22
  const fatalConnectErrorProp = { fatalConnectError: true };
23
23
  function getNackReconnectInfo(nackContent) {
@@ -30,10 +30,25 @@ function getNackReconnectInfo(nackContent) {
30
30
  * Implementation of IDocumentDeltaConnection that does not support submitting
31
31
  * or receiving ops. Used in storage-only mode.
32
32
  */
33
- class NoDeltaStream extends common_utils_1.TypedEventEmitter {
34
- constructor() {
35
- super(...arguments);
36
- this.clientId = "storage-only client";
33
+ const clientNoDeltaStream = {
34
+ mode: "read",
35
+ details: { capabilities: { interactive: true } },
36
+ permission: [],
37
+ user: { id: "storage-only client" },
38
+ scopes: [],
39
+ };
40
+ const clientIdNoDeltaStream = "storage-only client";
41
+ class NoDeltaStream extends client_utils_1.TypedEventEmitter {
42
+ /**
43
+ * Connection which is not connected to socket.
44
+ * @param storageOnlyReason - Reason on why the connection to delta stream is not allowed.
45
+ * @param readonlyConnectionReason - reason/error if any which lead to using NoDeltaStream.
46
+ */
47
+ constructor(storageOnlyReason, readonlyConnectionReason) {
48
+ super();
49
+ this.storageOnlyReason = storageOnlyReason;
50
+ this.readonlyConnectionReason = readonlyConnectionReason;
51
+ this.clientId = clientIdNoDeltaStream;
37
52
  this.claims = {
38
53
  scopes: [protocol_definitions_1.ScopeType.DocRead],
39
54
  };
@@ -43,11 +58,12 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
43
58
  this.version = "";
44
59
  this.initialMessages = [];
45
60
  this.initialSignals = [];
46
- this.initialClients = [];
61
+ this.initialClients = [
62
+ { client: clientNoDeltaStream, clientId: clientIdNoDeltaStream },
63
+ ];
47
64
  this.serviceConfiguration = {
48
65
  maxMessageSize: 0,
49
66
  blockSize: 0,
50
- summary: undefined,
51
67
  };
52
68
  this.checkpointSequenceNumber = undefined;
53
69
  this._disposed = false;
@@ -66,85 +82,49 @@ class NoDeltaStream extends common_utils_1.TypedEventEmitter {
66
82
  content: { message: "Cannot submit signal with storage-only connection", code: 403 },
67
83
  });
68
84
  }
69
- get disposed() { return this._disposed; }
70
- dispose() { this._disposed = true; }
85
+ get disposed() {
86
+ return this._disposed;
87
+ }
88
+ dispose() {
89
+ this._disposed = true;
90
+ }
71
91
  }
92
+ function isNoDeltaStreamConnection(connection) {
93
+ return connection instanceof NoDeltaStream;
94
+ }
95
+ const waitForOnline = async () => {
96
+ // Only wait if we have a strong signal that we're offline - otherwise assume we're online.
97
+ if (globalThis.navigator?.onLine === false && globalThis.addEventListener !== undefined) {
98
+ return new Promise((resolve) => {
99
+ const resolveAndRemoveListener = () => {
100
+ resolve();
101
+ globalThis.removeEventListener("online", resolveAndRemoveListener);
102
+ };
103
+ globalThis.addEventListener("online", resolveAndRemoveListener);
104
+ });
105
+ }
106
+ };
72
107
  /**
73
108
  * Implementation of IConnectionManager, used by Container class
74
- * Implements constant connectivity to relay service, by reconnecting in case of loast connection or error.
75
- * Exposes various controls to influecen this process, including manual reconnects, forced read-only mode, etc.
109
+ * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
110
+ * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
76
111
  */
77
112
  class ConnectionManager {
78
- constructor(serviceProvider, client, reconnectAllowed, logger, props) {
79
- this.serviceProvider = serviceProvider;
80
- this.client = client;
81
- this.logger = logger;
82
- this.props = props;
83
- /** tracks host requiring read-only mode. */
84
- this._forceReadonly = false;
85
- /** True if there is pending (async) reconnection from "read" to "write" */
86
- this.pendingReconnect = false;
87
- this.clientSequenceNumber = 0;
88
- this.clientSequenceNumberObserved = 0;
89
- /** Counts the number of noops sent by the client which may not be acked. */
90
- this.trailingNoopCount = 0;
91
- this.connectFirstConnection = true;
92
- this._connectionVerboseProps = {};
93
- this._connectionProps = {};
94
- this.closed = false;
95
- this.opHandler = (documentId, messagesArg) => {
96
- const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
97
- this.props.incomingOpHandler(messages, "opHandler");
98
- };
99
- // Always connect in write mode after getting nacked.
100
- this.nackHandler = (documentId, messages) => {
101
- const message = messages[0];
102
- if (this._readonlyPermissions === true) {
103
- this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
104
- return;
105
- }
106
- const reconnectInfo = getNackReconnectInfo(message.content);
107
- // If the nack indicates we cannot retry, then close the container outright
108
- if (!reconnectInfo.canRetry) {
109
- this.props.closeHandler(reconnectInfo);
110
- return;
111
- }
112
- this.reconnectOnError("write", reconnectInfo);
113
- };
114
- // Connection mode is always read on disconnect/error unless the system mode was write.
115
- this.disconnectHandlerInternal = (disconnectReason) => {
116
- // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
117
- // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
118
- this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
119
- };
120
- this.errorHandler = (error) => {
121
- this.reconnectOnError(this.defaultReconnectionMode, error);
122
- };
123
- this.clientDetails = this.client.details;
124
- this.defaultReconnectionMode = this.client.mode;
125
- this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
126
- // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
127
- // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
128
- this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
129
- if (this.connection === undefined) {
130
- throw new Error("Attempted to submit an outbound message without connection");
131
- }
132
- this.connection.submit(messages);
133
- });
134
- this._outbound.on("error", (error) => {
135
- this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
136
- });
113
+ get connectionVerboseProps() {
114
+ return this._connectionVerboseProps;
137
115
  }
138
- get connectionVerboseProps() { return this._connectionVerboseProps; }
139
116
  /**
140
117
  * The current connection mode, initially read.
141
118
  */
142
119
  get connectionMode() {
143
- var _a, _b;
144
- return (_b = (_a = this.connection) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : "read";
120
+ return this.connection?.mode ?? "read";
121
+ }
122
+ get connected() {
123
+ return this.connection !== undefined;
124
+ }
125
+ get clientId() {
126
+ return this.connection?.clientId;
145
127
  }
146
- get connected() { return this.connection !== undefined; }
147
- get clientId() { var _a; return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.clientId; }
148
128
  /**
149
129
  * Automatic reconnecting enabled or disabled.
150
130
  * If set to Never, then reconnecting will never be allowed.
@@ -153,8 +133,7 @@ class ConnectionManager {
153
133
  return this._reconnectMode;
154
134
  }
155
135
  get maxMessageSize() {
156
- var _a, _b, _c;
157
- return (_c = (_b = (_a = this.connection) === null || _a === void 0 ? void 0 : _a.serviceConfiguration) === null || _b === void 0 ? void 0 : _b.maxMessageSize) !== null && _c !== void 0 ? _c : DefaultChunkSize;
136
+ return this.connection?.serviceConfiguration?.maxMessageSize ?? DefaultChunkSize;
158
137
  }
159
138
  get version() {
160
139
  if (this.connection === undefined) {
@@ -163,12 +142,10 @@ class ConnectionManager {
163
142
  return this.connection.version;
164
143
  }
165
144
  get serviceConfiguration() {
166
- var _a;
167
- return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.serviceConfiguration;
145
+ return this.connection?.serviceConfiguration;
168
146
  }
169
147
  get scopes() {
170
- var _a;
171
- return (_a = this.connection) === null || _a === void 0 ? void 0 : _a.claims.scopes;
148
+ return this.connection?.claims.scopes;
172
149
  }
173
150
  get outbound() {
174
151
  return this._outbound;
@@ -176,20 +153,31 @@ class ConnectionManager {
176
153
  /**
177
154
  * Returns set of props that can be logged in telemetry that provide some insights / statistics
178
155
  * about current or last connection (if there is no connection at the moment)
179
- */
156
+ */
180
157
  get connectionProps() {
181
- if (this.connection !== undefined) {
182
- return this._connectionProps;
183
- }
184
- else {
185
- return Object.assign(Object.assign({}, this._connectionProps), {
158
+ return this.connection !== undefined
159
+ ? this._connectionProps
160
+ : {
161
+ ...this._connectionProps,
186
162
  // Report how many ops this client sent in last disconnected session
187
- sentOps: this.clientSequenceNumber });
188
- }
163
+ sentOps: this.clientSequenceNumber,
164
+ };
189
165
  }
190
166
  shouldJoinWrite() {
191
167
  // We don't have to wait for ack for topmost NoOps. So subtract those.
192
- return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.trailingNoopCount);
168
+ const outstandingOps = this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore;
169
+ // Previous behavior was to force write mode here only when there are outstanding ops (besides
170
+ // no-ops). The dirty signal from runtime should provide the same behavior, but also support
171
+ // stashed ops that weren't submitted to container layer yet. For safety, we want to retain the
172
+ // same behavior whenever dirty is false.
173
+ const isDirty = this.containerDirty();
174
+ if (outstandingOps !== isDirty) {
175
+ this.logger.sendTelemetryEvent({
176
+ eventName: "DesiredConnectionModeMismatch",
177
+ details: JSON.stringify({ outstandingOps, isDirty }),
178
+ });
179
+ }
180
+ return outstandingOps || isDirty;
193
181
  }
194
182
  /**
195
183
  * Tells if container is in read-only mode.
@@ -201,82 +189,141 @@ class ConnectionManager {
201
189
  * and do not know if user has write access to a file.
202
190
  */
203
191
  get readonly() {
204
- if (this._forceReadonly) {
205
- return true;
206
- }
207
- return this._readonlyPermissions;
192
+ return this.readOnlyInfo.readonly;
208
193
  }
209
194
  get readOnlyInfo() {
210
- const storageOnly = this.connection !== undefined && this.connection instanceof NoDeltaStream;
195
+ let storageOnly = false;
196
+ let storageOnlyReason;
197
+ if (isNoDeltaStreamConnection(this.connection)) {
198
+ storageOnly = true;
199
+ storageOnlyReason = this.connection.storageOnlyReason;
200
+ }
211
201
  if (storageOnly || this._forceReadonly || this._readonlyPermissions === true) {
212
202
  return {
213
203
  readonly: true,
214
204
  forced: this._forceReadonly,
215
205
  permissions: this._readonlyPermissions,
216
206
  storageOnly,
207
+ storageOnlyReason,
217
208
  };
218
209
  }
219
210
  return { readonly: this._readonlyPermissions };
220
211
  }
221
- static detailsFromConnection(connection) {
212
+ static detailsFromConnection(connection, reason) {
222
213
  return {
223
214
  claims: connection.claims,
224
215
  clientId: connection.clientId,
225
- existing: connection.existing,
226
216
  checkpointSequenceNumber: connection.checkpointSequenceNumber,
227
- get initialClients() { return connection.initialClients; },
217
+ get initialClients() {
218
+ return connection.initialClients;
219
+ },
228
220
  mode: connection.mode,
229
221
  serviceConfiguration: connection.serviceConfiguration,
230
222
  version: connection.version,
223
+ reason,
224
+ };
225
+ }
226
+ constructor(serviceProvider, containerDirty, client, reconnectAllowed, logger, props) {
227
+ this.serviceProvider = serviceProvider;
228
+ this.containerDirty = containerDirty;
229
+ this.client = client;
230
+ this.logger = logger;
231
+ this.props = props;
232
+ /** tracks host requiring read-only mode. */
233
+ this._forceReadonly = false;
234
+ /** True if there is pending (async) reconnection from "read" to "write" */
235
+ this.pendingReconnect = false;
236
+ this.clientSequenceNumber = 0;
237
+ this.clientSequenceNumberObserved = 0;
238
+ /** Counts the number of non-runtime ops sent by the client which may not be acked. */
239
+ this.localOpsToIgnore = 0;
240
+ this.connectFirstConnection = true;
241
+ this._connectionVerboseProps = {};
242
+ this._connectionProps = {};
243
+ this._disposed = false;
244
+ this.opHandler = (documentId, messagesArg) => {
245
+ const messages = Array.isArray(messagesArg) ? messagesArg : [messagesArg];
246
+ this.props.incomingOpHandler(messages, "opHandler");
247
+ };
248
+ this.signalHandler = (signalsArg) => {
249
+ const signals = Array.isArray(signalsArg) ? signalsArg : [signalsArg];
250
+ this.props.signalHandler(signals);
251
+ };
252
+ // Always connect in write mode after getting nacked.
253
+ this.nackHandler = (documentId, messages) => {
254
+ const message = messages[0];
255
+ if (this._readonlyPermissions === true) {
256
+ this.props.closeHandler((0, driver_utils_1.createWriteError)("writeOnReadOnlyDocument", { driverVersion: undefined }));
257
+ return;
258
+ }
259
+ const reconnectInfo = getNackReconnectInfo(message.content);
260
+ // If the nack indicates we cannot retry, then close the container outright
261
+ if (!reconnectInfo.canRetry) {
262
+ this.props.closeHandler(reconnectInfo);
263
+ return;
264
+ }
265
+ this.reconnectOnError("write", reconnectInfo);
231
266
  };
267
+ // Connection mode is always read on disconnect/error unless the system mode was write.
268
+ this.disconnectHandlerInternal = (disconnectReason) => {
269
+ // Note: we might get multiple disconnect calls on same socket, as early disconnect notification
270
+ // ("server_disconnect", ODSP-specific) is mapped to "disconnect"
271
+ this.reconnectOnError(this.defaultReconnectionMode, disconnectReason);
272
+ };
273
+ this.errorHandler = (error) => {
274
+ this.reconnectOnError(this.defaultReconnectionMode, error);
275
+ };
276
+ this.clientDetails = this.client.details;
277
+ this.defaultReconnectionMode = this.client.mode;
278
+ this._reconnectMode = reconnectAllowed ? contracts_1.ReconnectMode.Enabled : contracts_1.ReconnectMode.Never;
279
+ // Outbound message queue. The outbound queue is represented as a queue of an array of ops. Ops contained
280
+ // within an array *must* fit within the maxMessageSize and are guaranteed to be ordered sequentially.
281
+ this._outbound = new deltaQueue_1.DeltaQueue((messages) => {
282
+ if (this.connection === undefined) {
283
+ throw new Error("Attempted to submit an outbound message without connection");
284
+ }
285
+ this.connection.submit(messages);
286
+ });
287
+ this._outbound.on("error", (error) => {
288
+ this.props.closeHandler((0, telemetry_utils_1.normalizeError)(error));
289
+ });
232
290
  }
233
- dispose(error) {
234
- if (this.closed) {
291
+ dispose(error, switchToReadonly = true) {
292
+ if (this._disposed) {
235
293
  return;
236
294
  }
237
- this.closed = true;
238
- this.pendingConnection = undefined;
295
+ this._disposed = true;
239
296
  // Ensure that things like triggerConnect() will short circuit
240
297
  this._reconnectMode = contracts_1.ReconnectMode.Never;
241
298
  this._outbound.clear();
242
- const disconnectReason = error !== undefined
243
- ? `Closing DeltaManager (${error.message})`
244
- : "Closing DeltaManager";
299
+ const disconnectReason = {
300
+ text: "Closing DeltaManager",
301
+ error,
302
+ };
303
+ const oldReadonlyValue = this.readonly;
245
304
  // This raises "disconnect" event if we have active connection.
246
305
  this.disconnectFromDeltaStream(disconnectReason);
247
- // Notify everyone we are in read-only state.
248
- // Useful for data stores in case we hit some critical error,
249
- // to switch to a mode where user edits are not accepted
250
- this.set_readonlyPermissions(true);
306
+ if (switchToReadonly) {
307
+ // Notify everyone we are in read-only state.
308
+ // Useful for data stores in case we hit some critical error,
309
+ // to switch to a mode where user edits are not accepted
310
+ this.set_readonlyPermissions(true, oldReadonlyValue, disconnectReason);
311
+ }
251
312
  }
252
313
  /**
253
314
  * Enables or disables automatic reconnecting.
254
315
  * Will throw an error if reconnectMode set to Never.
255
- */
256
- setAutoReconnect(mode) {
257
- (0, common_utils_1.assert)(mode !== contracts_1.ReconnectMode.Never && this._reconnectMode !== contracts_1.ReconnectMode.Never, 0x278 /* "API is not supported for non-connecting or closed container" */);
316
+ */
317
+ setAutoReconnect(mode, reason) {
318
+ (0, core_utils_1.assert)(mode !== contracts_1.ReconnectMode.Never && this._reconnectMode !== contracts_1.ReconnectMode.Never, 0x278 /* "API is not supported for non-connecting or closed container" */);
258
319
  this._reconnectMode = mode;
259
320
  if (mode !== contracts_1.ReconnectMode.Enabled) {
260
321
  // immediately disconnect - do not rely on service eventually dropping connection.
261
- this.disconnectFromDeltaStream("setAutoReconnect");
322
+ this.disconnectFromDeltaStream(reason);
262
323
  }
263
324
  }
264
325
  /**
265
- * Sends signal to runtime (and data stores) to be read-only.
266
- * Hosts may have read only views, indicating to data stores that no edits are allowed.
267
- * This is independent from this._readonlyPermissions (permissions) and this.connectionMode
268
- * (server can return "write" mode even when asked for "read")
269
- * Leveraging same "readonly" event as runtime & data stores should behave the same in such case
270
- * as in read-only permissions.
271
- * But this.active can be used by some DDSes to figure out if ops can be sent
272
- * (for example, read-only view still participates in code proposals / upgrades decisions)
273
- *
274
- * Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
275
- * the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
276
- * there are pending in the outbound queue, it will stop sending until force readonly is
277
- * cleared.
278
- *
279
- * @param readonly - set or clear force readonly.
326
+ * {@inheritDoc Container.forceReadonly}
280
327
  */
281
328
  forceReadonly(readonly) {
282
329
  if (readonly !== this._forceReadonly) {
@@ -289,7 +336,7 @@ class ConnectionManager {
289
336
  this._forceReadonly = readonly;
290
337
  if (oldValue !== this.readonly) {
291
338
  if (this._reconnectMode === contracts_1.ReconnectMode.Never) {
292
- throw new container_utils_1.UsageError("API is not supported for non-connecting or closed container");
339
+ throw new telemetry_utils_1.UsageError("API is not supported for non-connecting or closed container");
293
340
  }
294
341
  let reconnect = false;
295
342
  if (this.readonly === true) {
@@ -301,42 +348,40 @@ class ConnectionManager {
301
348
  // host logic error.
302
349
  this.logger.sendErrorEvent({ eventName: "ForceReadonlyPendingChanged" });
303
350
  }
304
- reconnect = this.disconnectFromDeltaStream("Force readonly");
351
+ reconnect = this.disconnectFromDeltaStream({ text: "Force readonly" });
305
352
  }
306
353
  this.props.readonlyChangeHandler(this.readonly);
307
354
  if (reconnect) {
308
355
  // reconnect if we disconnected from before.
309
- this.triggerConnect("read");
356
+ this.triggerConnect({ text: "Force Readonly" }, "read");
310
357
  }
311
358
  }
312
359
  }
313
- set_readonlyPermissions(readonly) {
314
- const oldValue = this.readonly;
315
- this._readonlyPermissions = readonly;
316
- if (oldValue !== this.readonly) {
317
- this.props.readonlyChangeHandler(this.readonly);
360
+ set_readonlyPermissions(newReadonlyValue, oldReadonlyValue, readonlyConnectionReason) {
361
+ this._readonlyPermissions = newReadonlyValue;
362
+ if (oldReadonlyValue !== this.readonly) {
363
+ this.props.readonlyChangeHandler(this.readonly, readonlyConnectionReason);
318
364
  }
319
365
  }
320
- connect(connectionMode) {
321
- this.connectCore(connectionMode).catch((error) => {
322
- const normalizedError = (0, telemetry_utils_1.normalizeError)(error, { props: fatalConnectErrorProp });
366
+ connect(reason, connectionMode) {
367
+ this.connectCore(reason, connectionMode).catch((e) => {
368
+ const normalizedError = (0, telemetry_utils_1.normalizeError)(e, { props: fatalConnectErrorProp });
323
369
  this.props.closeHandler(normalizedError);
324
370
  });
325
371
  }
326
- async connectCore(connectionMode) {
327
- var _a, _b;
328
- (0, common_utils_1.assert)(!this.closed, 0x26a /* "not closed" */);
372
+ async connectCore(reason, connectionMode) {
373
+ (0, core_utils_1.assert)(!this._disposed, 0x26a /* "not closed" */);
329
374
  if (this.connection !== undefined) {
330
375
  return; // Connection attempt already completed successfully
331
376
  }
332
377
  let pendingConnectionMode;
333
378
  if (this.pendingConnection !== undefined) {
334
379
  pendingConnectionMode = this.pendingConnection.connectionMode;
335
- this.cancelConnection(); // Throw out in-progress connection attempt in favor of new attempt
336
- (0, common_utils_1.assert)(this.pendingConnection === undefined, 0x344 /* this.pendingConnection should be undefined */);
380
+ this.cancelConnection(reason); // Throw out in-progress connection attempt in favor of new attempt
381
+ (0, core_utils_1.assert)(this.pendingConnection === undefined, 0x344 /* this.pendingConnection should be undefined */);
337
382
  }
338
383
  // If there is no specified ConnectionMode, try the previous mode, if there is no previous mode use default
339
- let requestedMode = (_a = connectionMode !== null && connectionMode !== void 0 ? connectionMode : pendingConnectionMode) !== null && _a !== void 0 ? _a : this.defaultReconnectionMode;
384
+ let requestedMode = connectionMode ?? pendingConnectionMode ?? this.defaultReconnectionMode;
340
385
  // if we have any non-acked ops from last connection, reconnect as "write".
341
386
  // without that we would connect in view-only mode, which will result in immediate
342
387
  // firing of "connected" event from Container and switch of current clientId (as tracked
@@ -346,31 +391,37 @@ class ConnectionManager {
346
391
  requestedMode = "write";
347
392
  }
348
393
  const docService = this.serviceProvider();
349
- (0, common_utils_1.assert)(docService !== undefined, 0x2a7 /* "Container is not attached" */);
394
+ (0, core_utils_1.assert)(docService !== undefined, 0x2a7 /* "Container is not attached" */);
350
395
  let connection;
351
- if (((_b = docService.policies) === null || _b === void 0 ? void 0 : _b.storageOnly) === true) {
396
+ if (docService.policies?.storageOnly === true) {
352
397
  connection = new NoDeltaStream();
353
- this.setupNewSuccessfulConnection(connection, "read");
354
- (0, common_utils_1.assert)(this.pendingConnection === undefined, 0x2b3 /* "logic error" */);
398
+ this.setupNewSuccessfulConnection(connection, "read", reason);
399
+ (0, core_utils_1.assert)(this.pendingConnection === undefined, 0x2b3 /* "logic error" */);
355
400
  return;
356
401
  }
357
402
  let delayMs = InitialReconnectDelayInMs;
358
403
  let connectRepeatCount = 0;
359
- const connectStartTime = common_utils_1.performance.now();
404
+ const connectStartTime = client_utils_1.performance.now();
360
405
  let lastError;
361
- const abortController = new abort_controller_1.default();
406
+ const abortController = new AbortController();
362
407
  const abortSignal = abortController.signal;
363
- this.pendingConnection = { abort: () => { abortController.abort(); }, connectionMode: requestedMode };
408
+ this.pendingConnection = {
409
+ abort: () => {
410
+ abortController.abort();
411
+ },
412
+ connectionMode: requestedMode,
413
+ };
414
+ this.props.establishConnectionHandler(reason);
364
415
  // This loop will keep trying to connect until successful, with a delay between each iteration.
365
416
  while (connection === undefined) {
366
- if (this.closed) {
417
+ if (this._disposed) {
367
418
  throw new Error("Attempting to connect a closed DeltaManager");
368
419
  }
369
420
  if (abortSignal.aborted === true) {
370
421
  this.logger.sendTelemetryEvent({
371
422
  eventName: "ConnectionAttemptCancelled",
372
423
  attempts: connectRepeatCount,
373
- duration: telemetry_utils_1.TelemetryLogger.formatTick(common_utils_1.performance.now() - connectStartTime),
424
+ duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
374
425
  connectionEstablished: false,
375
426
  });
376
427
  return;
@@ -378,17 +429,42 @@ class ConnectionManager {
378
429
  connectRepeatCount++;
379
430
  try {
380
431
  this.client.mode = requestedMode;
381
- connection = await docService.connectToDeltaStream(Object.assign(Object.assign({}, this.client), { mode: requestedMode }));
432
+ connection = await docService.connectToDeltaStream({
433
+ ...this.client,
434
+ mode: requestedMode,
435
+ });
382
436
  if (connection.disposed) {
383
437
  // Nobody observed this connection, so drop it on the floor and retry.
384
438
  this.logger.sendTelemetryEvent({ eventName: "ReceivedClosedConnection" });
385
439
  connection = undefined;
386
440
  }
441
+ this.logger.sendTelemetryEvent({
442
+ eventName: "ConnectionReceived",
443
+ connected: connection !== undefined && connection.disposed === false,
444
+ }, undefined, core_interfaces_1.LogLevel.verbose);
387
445
  }
388
446
  catch (origError) {
389
- if (typeof origError === "object" && origError !== null &&
390
- (origError === null || origError === void 0 ? void 0 : origError.errorType) === driver_utils_1.DeltaStreamConnectionForbiddenError.errorType) {
391
- connection = new NoDeltaStream();
447
+ this.logger.sendTelemetryEvent({
448
+ eventName: "ConnectToDeltaStreamException",
449
+ connected: connection !== undefined && connection.disposed === false,
450
+ }, undefined, core_interfaces_1.LogLevel.verbose);
451
+ if ((0, utils_1.isDeltaStreamConnectionForbiddenError)(origError)) {
452
+ connection = new NoDeltaStream(origError.storageOnlyReason, {
453
+ text: origError.message,
454
+ error: origError,
455
+ });
456
+ requestedMode = "read";
457
+ break;
458
+ }
459
+ else if ((0, telemetry_utils_1.isFluidError)(origError) &&
460
+ // eslint-disable-next-line import/no-deprecated
461
+ origError.errorType === driver_definitions_1.DriverErrorType.outOfStorageError) {
462
+ // If we get out of storage error from calling joinsession, then use the NoDeltaStream object so
463
+ // that user can at least load the container.
464
+ connection = new NoDeltaStream(undefined, {
465
+ text: origError.message,
466
+ error: origError,
467
+ });
392
468
  requestedMode = "read";
393
469
  break;
394
470
  }
@@ -403,15 +479,36 @@ class ConnectionManager {
403
479
  attempts: connectRepeatCount,
404
480
  delay: delayMs,
405
481
  eventName: "DeltaConnectionFailureToConnect",
406
- duration: telemetry_utils_1.TelemetryLogger.formatTick(common_utils_1.performance.now() - connectStartTime),
482
+ duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
407
483
  }, origError);
408
484
  lastError = origError;
485
+ const waitStartTime = client_utils_1.performance.now();
409
486
  const retryDelayFromError = (0, driver_utils_1.getRetryDelayFromError)(origError);
410
- delayMs = retryDelayFromError !== null && retryDelayFromError !== void 0 ? retryDelayFromError : Math.min(delayMs * 2, MaxReconnectDelayInMs);
411
- if (retryDelayFromError !== undefined) {
412
- this.props.reconnectionDelayHandler(retryDelayFromError, origError);
487
+ // If the error told us to wait or browser signals us that we are offline, then calculate the time we
488
+ // want to wait for before retrying. then we wait for that time. If the error didn't tell us to wait,
489
+ // let's still wait a little bit before retrying. We can skip this delay if we're confident we're offline,
490
+ // because we probably just need to wait to come back online. But we never have strong signal of being
491
+ // offline, so we at least wait for sometime.
492
+ if (retryDelayFromError !== undefined || globalThis.navigator?.onLine !== false) {
493
+ delayMs = (0, driver_utils_1.calculateMaxWaitTime)(delayMs, origError);
413
494
  }
414
- await (0, driver_utils_1.waitForConnectedState)(delayMs);
495
+ // Raise event in case the delay was there.
496
+ this.props.reconnectionDelayHandler(delayMs, origError);
497
+ await new Promise((resolve) => {
498
+ setTimeout(resolve, delayMs);
499
+ });
500
+ // If we believe we're offline, we assume there's no point in trying until we at least think we're online.
501
+ // NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
502
+ // should probably live in the driver.
503
+ await waitForOnline();
504
+ this.logger.sendPerformanceEvent({
505
+ eventName: "WaitBetweenConnectionAttempts",
506
+ duration: client_utils_1.performance.now() - waitStartTime,
507
+ details: JSON.stringify({
508
+ retryDelayFromError,
509
+ delayMs,
510
+ }),
511
+ });
415
512
  }
416
513
  }
417
514
  // If we retried more than once, log an event about how long it took (this will not log to error table)
@@ -419,55 +516,61 @@ class ConnectionManager {
419
516
  (0, driver_utils_1.logNetworkFailure)(this.logger, {
420
517
  eventName: "MultipleDeltaConnectionFailures",
421
518
  attempts: connectRepeatCount,
422
- duration: telemetry_utils_1.TelemetryLogger.formatTick(common_utils_1.performance.now() - connectStartTime),
519
+ duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
423
520
  }, lastError);
424
521
  }
425
- // Check for abort signal after while loop as well
426
- if (abortSignal.aborted === true) {
522
+ // Check for abort signal after while loop as well or we've been disposed
523
+ if (abortSignal.aborted === true || this._disposed) {
427
524
  connection.dispose();
428
525
  this.logger.sendTelemetryEvent({
429
526
  eventName: "ConnectionAttemptCancelled",
430
527
  attempts: connectRepeatCount,
431
- duration: telemetry_utils_1.TelemetryLogger.formatTick(common_utils_1.performance.now() - connectStartTime),
528
+ duration: (0, telemetry_utils_1.formatTick)(client_utils_1.performance.now() - connectStartTime),
432
529
  connectionEstablished: true,
433
530
  });
434
531
  return;
435
532
  }
436
- this.setupNewSuccessfulConnection(connection, requestedMode);
533
+ this.setupNewSuccessfulConnection(connection, requestedMode, reason);
437
534
  }
438
535
  /**
439
- * Start the connection. Any error should result in container being close.
440
- * And report the error if it excape for any reason.
536
+ * Start the connection. Any error should result in container being closed.
537
+ * And report the error if it escapes for any reason.
441
538
  * @param args - The connection arguments
442
539
  */
443
- triggerConnect(connectionMode) {
444
- (0, common_utils_1.assert)(this.connection === undefined, 0x239 /* "called only in disconnected state" */);
540
+ triggerConnect(reason, connectionMode) {
541
+ // reconnect() includes async awaits, and that causes potential race conditions
542
+ // where we might already have a connection. If it were to happen, it's possible that we will connect
543
+ // with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
544
+ // fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it
545
+ // directly in connectCore - see this.shouldJoinWrite() test as an example.
546
+ // assert(this.connection === undefined, 0x239 /* "called only in disconnected state" */);
445
547
  if (this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
446
548
  return;
447
549
  }
448
- this.connect(connectionMode);
550
+ this.connect(reason, connectionMode);
449
551
  }
450
552
  /**
451
553
  * Disconnect the current connection.
452
554
  * @param reason - Text description of disconnect reason to emit with disconnect event
555
+ * @param error - Error causing the disconnect if any.
453
556
  * @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
454
557
  */
455
558
  disconnectFromDeltaStream(reason) {
456
559
  this.pendingReconnect = false;
457
560
  if (this.connection === undefined) {
458
561
  if (this.pendingConnection !== undefined) {
459
- this.cancelConnection();
562
+ this.cancelConnection(reason);
460
563
  return true;
461
564
  }
462
565
  return false;
463
566
  }
464
- (0, common_utils_1.assert)(this.pendingConnection === undefined, 0x27b /* "reentrancy may result in incorrect behavior" */);
567
+ (0, core_utils_1.assert)(this.pendingConnection === undefined, 0x27b /* "reentrancy may result in incorrect behavior" */);
465
568
  const connection = this.connection;
466
569
  // Avoid any re-entrancy - clear object reference
467
570
  this.connection = undefined;
468
571
  // Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError
469
572
  connection.off("op", this.opHandler);
470
- connection.off("signal", this.props.signalHandler);
573
+ connection.off("signal", this.signalHandler);
471
574
  connection.off("nack", this.nackHandler);
472
575
  connection.off("disconnect", this.disconnectHandlerInternal);
473
576
  connection.off("error", this.errorHandler);
@@ -475,48 +578,60 @@ class ConnectionManager {
475
578
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
476
579
  this._outbound.pause();
477
580
  this._outbound.clear();
478
- this.props.disconnectHandler(reason);
479
581
  connection.dispose();
582
+ this.props.disconnectHandler(reason);
480
583
  this._connectionVerboseProps = {};
481
584
  return true;
482
585
  }
483
586
  /**
484
587
  * Cancel in-progress connection attempt.
485
588
  */
486
- cancelConnection() {
487
- (0, common_utils_1.assert)(this.pendingConnection !== undefined, 0x345 /* this.pendingConnection is undefined when trying to cancel */);
589
+ cancelConnection(reason) {
590
+ (0, core_utils_1.assert)(this.pendingConnection !== undefined, 0x345 /* this.pendingConnection is undefined when trying to cancel */);
488
591
  this.pendingConnection.abort();
489
592
  this.pendingConnection = undefined;
490
593
  this.logger.sendTelemetryEvent({ eventName: "ConnectionCancelReceived" });
594
+ this.props.cancelConnectionHandler({
595
+ text: `Cancel Pending Connection due to ${reason.text}`,
596
+ error: reason.error,
597
+ });
491
598
  }
492
599
  /**
493
600
  * Once we've successfully gotten a connection, we need to set up state, attach event listeners, and process
494
601
  * initial messages.
495
602
  * @param connection - The newly established connection
496
603
  */
497
- setupNewSuccessfulConnection(connection, requestedMode) {
604
+ setupNewSuccessfulConnection(connection, requestedMode, reason) {
498
605
  // Old connection should have been cleaned up before establishing a new one
499
- (0, common_utils_1.assert)(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
500
- (0, common_utils_1.assert)(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
606
+ (0, core_utils_1.assert)(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
607
+ (0, core_utils_1.assert)(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
501
608
  this.pendingConnection = undefined;
609
+ const oldReadonlyValue = this.readonly;
502
610
  this.connection = connection;
503
611
  // Does information in scopes & mode matches?
504
612
  // If we asked for "write" and got "read", then file is read-only
505
613
  // But if we ask read, server can still give us write.
506
614
  const readonly = !connection.claims.scopes.includes(protocol_definitions_1.ScopeType.DocWrite);
615
+ if (connection.mode !== requestedMode) {
616
+ this.logger.sendTelemetryEvent({
617
+ eventName: "ConnectionModeMismatch",
618
+ requestedMode,
619
+ mode: connection.mode,
620
+ });
621
+ }
507
622
  // This connection mode validation logic is moving to the driver layer in 0.44. These two asserts can be
508
623
  // removed after those packages have released and become ubiquitous.
509
- (0, common_utils_1.assert)(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
510
- (0, common_utils_1.assert)(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
511
- this.set_readonlyPermissions(readonly);
512
- if (this.closed) {
624
+ (0, core_utils_1.assert)(requestedMode === "read" || readonly === (this.connectionMode === "read"), 0x0e7 /* "claims/connectionMode mismatch" */);
625
+ (0, core_utils_1.assert)(!readonly || this.connectionMode === "read", 0x0e8 /* "readonly perf with write connection" */);
626
+ this.set_readonlyPermissions(readonly, oldReadonlyValue, isNoDeltaStreamConnection(connection) ? connection.readonlyConnectionReason : undefined);
627
+ if (this._disposed) {
513
628
  // Raise proper events, Log telemetry event and close connection.
514
- this.disconnectFromDeltaStream("ConnectionManager already closed");
629
+ this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
515
630
  return;
516
631
  }
517
632
  this._outbound.resume();
518
633
  connection.on("op", this.opHandler);
519
- connection.on("signal", this.props.signalHandler);
634
+ connection.on("signal", this.signalHandler);
520
635
  connection.on("nack", this.nackHandler);
521
636
  connection.on("disconnect", this.disconnectHandlerInternal);
522
637
  connection.on("error", this.errorHandler);
@@ -542,7 +657,8 @@ class ConnectionManager {
542
657
  this._connectionProps.connectionMode = connection.mode;
543
658
  let last = -1;
544
659
  if (initialMessages.length !== 0) {
545
- this._connectionVerboseProps.connectionInitialOpsFrom = initialMessages[0].sequenceNumber;
660
+ this._connectionVerboseProps.connectionInitialOpsFrom =
661
+ initialMessages[0].sequenceNumber;
546
662
  last = initialMessages[initialMessages.length - 1].sequenceNumber;
547
663
  this._connectionVerboseProps.connectionInitialOpsTo = last + 1;
548
664
  // Update knowledge of how far we are behind, before raising "connect" event
@@ -553,15 +669,39 @@ class ConnectionManager {
553
669
  }
554
670
  }
555
671
  this.props.incomingOpHandler(initialMessages, this.connectFirstConnection ? "InitialOps" : "ReconnectOps");
556
- if (connection.initialSignals !== undefined) {
557
- for (const signal of connection.initialSignals) {
558
- this.props.signalHandler(signal);
559
- }
560
- }
561
- const details = ConnectionManager.detailsFromConnection(connection);
672
+ const details = ConnectionManager.detailsFromConnection(connection, reason);
562
673
  details.checkpointSequenceNumber = checkpointSequenceNumber;
563
674
  this.props.connectHandler(details);
564
675
  this.connectFirstConnection = false;
676
+ // Synthesize clear & join signals out of initialClients state.
677
+ // This allows us to have single way to process signals, and makes it simpler to initialize
678
+ // protocol in Container.
679
+ const clearSignal = {
680
+ clientId: null,
681
+ content: JSON.stringify({
682
+ type: protocol_1.SignalType.Clear,
683
+ }),
684
+ };
685
+ // list of signals to process due to this new connection
686
+ let signalsToProcess = [clearSignal];
687
+ const clientJoinSignals = (connection.initialClients ?? []).map((priorClient) => ({
688
+ clientId: null,
689
+ content: JSON.stringify({
690
+ type: protocol_1.SignalType.ClientJoin,
691
+ content: priorClient, // ISignalClient
692
+ }),
693
+ }));
694
+ if (clientJoinSignals.length > 0) {
695
+ signalsToProcess = signalsToProcess.concat(clientJoinSignals);
696
+ }
697
+ // Unfortunately, there is no defined order between initialSignals (including join & leave signals)
698
+ // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
699
+ // for "self" and connection.initialClients does not contain "self", so we have to process them after
700
+ // "clear" signal above.
701
+ if (connection.initialSignals !== undefined && connection.initialSignals.length > 0) {
702
+ signalsToProcess = signalsToProcess.concat(connection.initialSignals);
703
+ }
704
+ this.props.signalHandler(signalsToProcess);
565
705
  }
566
706
  /**
567
707
  * Disconnect the current connection and reconnect. Closes the container if it fails.
@@ -571,8 +711,7 @@ class ConnectionManager {
571
711
  * @returns A promise that resolves when the connection is reestablished or we stop trying
572
712
  */
573
713
  reconnectOnError(requestedMode, error) {
574
- this.reconnect(requestedMode, error.message, error)
575
- .catch(this.props.closeHandler);
714
+ this.reconnect(requestedMode, { text: error.message, error }).catch(this.props.closeHandler);
576
715
  }
577
716
  /**
578
717
  * Disconnect the current connection and reconnect.
@@ -581,20 +720,20 @@ class ConnectionManager {
581
720
  * @param error - Error reconnect information including whether or not to reconnect
582
721
  * @returns A promise that resolves when the connection is reestablished or we stop trying
583
722
  */
584
- async reconnect(requestedMode, disconnectMessage, error) {
723
+ async reconnect(requestedMode, reason) {
585
724
  // We quite often get protocol errors before / after observing nack/disconnect
586
725
  // we do not want to run through same sequence twice.
587
726
  // If we're already disconnected/disconnecting it's not appropriate to call this again.
588
- (0, common_utils_1.assert)(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
589
- this.disconnectFromDeltaStream(disconnectMessage);
727
+ (0, core_utils_1.assert)(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
728
+ this.disconnectFromDeltaStream(reason);
590
729
  // We will always trigger reconnect, even if canRetry is false.
591
730
  // Any truly fatal error state will result in container close upon attempted reconnect,
592
731
  // which is a preferable to closing abruptly when a live connection fails.
593
- if (error !== undefined && !error.canRetry) {
732
+ if (reason.error?.canRetry === false) {
594
733
  this.logger.sendTelemetryEvent({
595
734
  eventName: "reconnectingDespiteFatalError",
596
735
  reconnectMode: this.reconnectMode,
597
- }, error);
736
+ }, reason.error);
598
737
  }
599
738
  if (this.reconnectMode === contracts_1.ReconnectMode.Never) {
600
739
  // Do not raise container error if we are closing just because we lost connection.
@@ -603,25 +742,37 @@ class ConnectionManager {
603
742
  this.props.closeHandler();
604
743
  }
605
744
  // If closed then we can't reconnect
606
- if (this.closed || this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
745
+ if (this._disposed || this.reconnectMode !== contracts_1.ReconnectMode.Enabled) {
607
746
  return;
608
747
  }
609
- const delayMs = (0, driver_utils_1.getRetryDelayFromError)(error);
610
- if (error !== undefined && delayMs !== undefined) {
611
- this.props.reconnectionDelayHandler(delayMs, error);
612
- await (0, driver_utils_1.waitForConnectedState)(delayMs);
748
+ // If the error tells us to wait before retrying, then do so.
749
+ const delayMs = (0, driver_utils_1.getRetryDelayFromError)(reason.error);
750
+ if (reason.error !== undefined && delayMs !== undefined) {
751
+ this.props.reconnectionDelayHandler(delayMs, reason.error);
752
+ await new Promise((resolve) => {
753
+ setTimeout(resolve, delayMs);
754
+ });
613
755
  }
614
- this.triggerConnect(requestedMode);
756
+ // If we believe we're offline, we assume there's no point in trying again until we at least think we're online.
757
+ // NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
758
+ // should probably live in the driver.
759
+ await waitForOnline();
760
+ this.triggerConnect({
761
+ text: reason.error !== undefined
762
+ ? "Reconnecting due to Error"
763
+ : `Reconnecting due to: ${reason.text}`,
764
+ error: reason.error,
765
+ }, requestedMode);
615
766
  }
616
767
  prepareMessageToSend(message) {
617
- var _a, _b;
618
768
  if (this.readonly === true) {
619
- (0, common_utils_1.assert)(this.readOnlyInfo.readonly === true, 0x1f0 /* "Unexpected mismatch in readonly" */);
620
- const error = new container_utils_1.GenericError("deltaManagerReadonlySubmit", undefined /* error */, {
769
+ (0, core_utils_1.assert)(this.readOnlyInfo.readonly === true, 0x1f0 /* "Unexpected mismatch in readonly" */);
770
+ const error = new telemetry_utils_1.GenericError("deltaManagerReadonlySubmit", undefined /* error */, {
621
771
  readonly: this.readOnlyInfo.readonly,
622
772
  forcedReadonly: this.readOnlyInfo.forced,
623
773
  readonlyPermissions: this.readOnlyInfo.permissions,
624
774
  storageOnly: this.readOnlyInfo.storageOnly,
775
+ storageOnlyReason: this.readOnlyInfo.storageOnlyReason,
625
776
  });
626
777
  this.props.closeHandler(error);
627
778
  return undefined;
@@ -629,30 +780,33 @@ class ConnectionManager {
629
780
  // reset clientSequenceNumber if we are using new clientId.
630
781
  // we keep info about old connection as long as possible to be able to account for all non-acked ops
631
782
  // that we pick up on next connection.
632
- (0, common_utils_1.assert)(!!this.connection, 0x0e4 /* "Lost old connection!" */);
633
- if (this.lastSubmittedClientId !== ((_a = this.connection) === null || _a === void 0 ? void 0 : _a.clientId)) {
634
- this.lastSubmittedClientId = (_b = this.connection) === null || _b === void 0 ? void 0 : _b.clientId;
783
+ (0, core_utils_1.assert)(!!this.connection, 0x0e4 /* "Lost old connection!" */);
784
+ if (this.lastSubmittedClientId !== this.connection?.clientId) {
785
+ this.lastSubmittedClientId = this.connection?.clientId;
635
786
  this.clientSequenceNumber = 0;
636
787
  this.clientSequenceNumberObserved = 0;
637
788
  }
638
- if (message.type === protocol_definitions_1.MessageType.NoOp) {
639
- this.trailingNoopCount++;
789
+ if (!(0, driver_utils_1.isRuntimeMessage)(message)) {
790
+ this.localOpsToIgnore++;
640
791
  }
641
792
  else {
642
- this.trailingNoopCount = 0;
793
+ this.localOpsToIgnore = 0;
643
794
  }
644
- return Object.assign(Object.assign({}, message), { clientSequenceNumber: ++this.clientSequenceNumber });
795
+ return {
796
+ ...message,
797
+ clientSequenceNumber: ++this.clientSequenceNumber,
798
+ };
645
799
  }
646
- submitSignal(content) {
800
+ submitSignal(content, targetClientId) {
647
801
  if (this.connection !== undefined) {
648
- this.connection.submitSignal(content);
802
+ this.connection.submitSignal(content, targetClientId);
649
803
  }
650
804
  else {
651
805
  this.logger.sendErrorEvent({ eventName: "submitSignalDisconnected" });
652
806
  }
653
807
  }
654
808
  sendMessages(messages) {
655
- (0, common_utils_1.assert)(this.connected, 0x2b4 /* "not connected on sending ops!" */);
809
+ (0, core_utils_1.assert)(this.connected, 0x2b4 /* "not connected on sending ops!" */);
656
810
  // If connection is "read" or implicit "read" (got leave op for "write" connection),
657
811
  // then op can't make it through - we will get a nack if op is sent.
658
812
  // We can short-circuit this process.
@@ -662,26 +816,29 @@ class ConnectionManager {
662
816
  if (this.connectionMode === "read") {
663
817
  if (!this.pendingReconnect) {
664
818
  this.pendingReconnect = true;
665
- Promise.resolve().then(async () => {
666
- if (this.pendingReconnect) { // still valid?
819
+ Promise.resolve()
820
+ .then(async () => {
821
+ if (this.pendingReconnect) {
822
+ // still valid?
667
823
  await this.reconnect("write", // connectionMode
668
- "Switch to write");
824
+ { text: "Switch to write" });
669
825
  }
670
826
  })
671
827
  .catch(() => { });
672
828
  }
673
829
  return;
674
830
  }
675
- (0, common_utils_1.assert)(!this.pendingReconnect, 0x2b5 /* "logic error" */);
831
+ (0, core_utils_1.assert)(!this.pendingReconnect, 0x2b5 /* "logic error" */);
676
832
  this._outbound.push(messages);
677
833
  }
678
834
  beforeProcessingIncomingOp(message) {
679
835
  // if we have connection, and message is local, then we better treat is as local!
680
- (0, common_utils_1.assert)(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId, 0x0ee /* "Not accounting local messages correctly" */);
681
- if (this.lastSubmittedClientId !== undefined && this.lastSubmittedClientId === message.clientId) {
836
+ (0, core_utils_1.assert)(this.clientId !== message.clientId || this.lastSubmittedClientId === message.clientId, 0x0ee /* "Not accounting local messages correctly" */);
837
+ if (this.lastSubmittedClientId !== undefined &&
838
+ this.lastSubmittedClientId === message.clientId) {
682
839
  const clientSequenceNumber = message.clientSequenceNumber;
683
- (0, common_utils_1.assert)(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* "client seq# not growing" */);
684
- (0, common_utils_1.assert)(clientSequenceNumber <= this.clientSequenceNumber, 0x0f0 /* "Incoming local client seq# > generated by this client" */);
840
+ (0, core_utils_1.assert)(this.clientSequenceNumberObserved < clientSequenceNumber, 0x0ef /* "client seq# not growing" */);
841
+ (0, core_utils_1.assert)(clientSequenceNumber <= this.clientSequenceNumber, 0x0f0 /* "Incoming local client seq# > generated by this client" */);
685
842
  this.clientSequenceNumberObserved = clientSequenceNumber;
686
843
  }
687
844
  if (message.type === protocol_definitions_1.MessageType.ClientLeave) {
@@ -697,7 +854,7 @@ class ConnectionManager {
697
854
  // Clients need to be able to transition to "read" state after some time of inactivity!
698
855
  // Note - this may close container!
699
856
  this.reconnect("read", // connectionMode
700
- "Switch to read").catch((error) => {
857
+ { text: "Switch to read" }).catch((error) => {
701
858
  this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection" }, error);
702
859
  });
703
860
  }
@@ -705,4 +862,4 @@ class ConnectionManager {
705
862
  }
706
863
  }
707
864
  exports.ConnectionManager = ConnectionManager;
708
- //# sourceMappingURL=connectionManager.js.map
865
+ //# sourceMappingURL=connectionManager.cjs.map