@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
@@ -2,97 +2,64 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { default as AbortController } from "abort-controller";
6
5
  import { v4 as uuid } from "uuid";
7
- import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
8
- import { normalizeError, logIfFalse, safeRaiseEvent, } from "@fluidframework/telemetry-utils";
9
- import { DriverErrorType, } from "@fluidframework/driver-definitions";
6
+ import { TypedEventEmitter } from "@fluid-internal/client-utils";
7
+ import { assert } from "@fluidframework/core-utils";
8
+ import { DataProcessingError, extractSafePropertiesFromMessage, normalizeError, safeRaiseEvent, isFluidError, DataCorruptionError, UsageError, } from "@fluidframework/telemetry-utils";
9
+ import { DriverErrorTypes, } from "@fluidframework/driver-definitions";
10
10
  import { MessageType, } from "@fluidframework/protocol-definitions";
11
- import { NonRetryableError, isClientMessage, } from "@fluidframework/driver-utils";
12
- import { ThrottlingWarning, DataCorruptionError, extractSafePropertiesFromMessage, DataProcessingError, } from "@fluidframework/container-utils";
13
- import { DeltaQueue } from "./deltaQueue";
11
+ import { NonRetryableError, isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
12
+ import { DeltaQueue } from "./deltaQueue.mjs";
13
+ import { ThrottlingWarning } from "./error.mjs";
14
+ /**
15
+ * Determines if message was sent by client, not service
16
+ */
17
+ function isClientMessage(message) {
18
+ if (isRuntimeMessage(message)) {
19
+ return true;
20
+ }
21
+ switch (message.type) {
22
+ case MessageType.Propose:
23
+ case MessageType.Reject:
24
+ case MessageType.NoOp:
25
+ case MessageType2.Accept:
26
+ case MessageType.Summarize:
27
+ return true;
28
+ default:
29
+ return false;
30
+ }
31
+ }
32
+ /**
33
+ * Like assert, but logs only if the condition is false, rather than throwing
34
+ * @param condition - The condition to attest too
35
+ * @param logger - The logger to log with
36
+ * @param event - The string or event to log
37
+ * @returns The outcome of the condition
38
+ */
39
+ function logIfFalse(condition, logger, event) {
40
+ if (condition) {
41
+ return true;
42
+ }
43
+ const newEvent = typeof event === "string"
44
+ ? { eventName: event, category: "error" }
45
+ : { category: "error", ...event };
46
+ logger.send(newEvent);
47
+ return false;
48
+ }
14
49
  /**
15
50
  * Manages the flow of both inbound and outbound messages. This class ensures that shared objects receive delta
16
51
  * messages in order regardless of possible network conditions or timings causing out of order delivery.
17
52
  */
18
53
  export class DeltaManager extends TypedEventEmitter {
19
- constructor(serviceProvider, logger, _active, createConnectionManager) {
20
- super();
21
- this.serviceProvider = serviceProvider;
22
- this.logger = logger;
23
- this._active = _active;
24
- this.pending = [];
25
- // The minimum sequence number and last sequence number received from the server
26
- this.minSequenceNumber = 0;
27
- // There are three numbers we track
28
- // * lastQueuedSequenceNumber is the last queued sequence number. If there are gaps in seq numbers, then this number
29
- // is not updated until we cover that gap, so it increases each time by 1.
30
- // * lastObservedSeqNumber is an estimation of last known sequence number for container in storage. It's initially
31
- // populated at web socket connection time (if storage provides that info) and is updated once ops shows up.
32
- // It's never less than lastQueuedSequenceNumber
33
- // * lastProcessedSequenceNumber - last processed sequence number
34
- this.lastQueuedSequenceNumber = 0;
35
- this.lastObservedSeqNumber = 0;
36
- this.lastProcessedSequenceNumber = 0;
37
- this.baseTerm = 0;
38
- /**
39
- * Track down the ops size.
40
- */
41
- this.opsSize = 0;
42
- // The sequence number we initially loaded from
43
- this.initSequenceNumber = 0;
44
- this.closed = false;
45
- this.throttlingIdSet = new Set();
46
- this.timeTillThrottling = 0;
47
- this.closeAbortController = new AbortController();
48
- this.deltaStorageDelayId = uuid();
49
- this.deltaStreamDelayId = uuid();
50
- this.messageBuffer = [];
51
- const props = {
52
- incomingOpHandler: (messages, reason) => {
53
- try {
54
- this.enqueueMessages(messages, reason);
55
- }
56
- catch (error) {
57
- this.logger.sendErrorEvent({ eventName: "EnqueueMessages_Exception" }, error);
58
- this.close(normalizeError(error));
59
- }
60
- },
61
- signalHandler: (message) => this._inboundSignal.push(message),
62
- reconnectionDelayHandler: (delayMs, error) => this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
63
- closeHandler: (error) => this.close(error),
64
- disconnectHandler: (reason) => this.disconnectHandler(reason),
65
- connectHandler: (connection) => this.connectHandler(connection),
66
- pongHandler: (latency) => this.emit("pong", latency),
67
- readonlyChangeHandler: (readonly) => safeRaiseEvent(this, this.logger, "readonly", readonly),
68
- };
69
- this.connectionManager = createConnectionManager(props);
70
- this._inbound = new DeltaQueue((op) => {
71
- this.processInboundMessage(op);
72
- });
73
- this._inbound.on("error", (error) => {
74
- this.close(DataProcessingError.wrapIfUnrecognized(error, "deltaManagerInboundErrorHandler", this.lastMessage));
75
- });
76
- // Inbound signal queue
77
- this._inboundSignal = new DeltaQueue((message) => {
78
- if (this.handler === undefined) {
79
- throw new Error("Attempted to process an inbound signal without a handler attached");
80
- }
81
- this.handler.processSignal({
82
- clientId: message.clientId,
83
- content: JSON.parse(message.content),
84
- });
85
- });
86
- this._inboundSignal.on("error", (error) => {
87
- this.close(normalizeError(error));
88
- });
89
- // Initially, all queues are created paused.
90
- // - outbound is flipped back and forth in setupNewSuccessfulConnection / disconnectFromDeltaStream
91
- // - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
54
+ get active() {
55
+ return this._active();
56
+ }
57
+ get disposed() {
58
+ return this._closed;
59
+ }
60
+ get IDeltaSender() {
61
+ return this;
92
62
  }
93
- get active() { return this._active(); }
94
- get disposed() { return this.closed; }
95
- get IDeltaSender() { return this; }
96
63
  get inbound() {
97
64
  return this._inbound;
98
65
  }
@@ -111,9 +78,6 @@ export class DeltaManager extends TypedEventEmitter {
111
78
  get lastKnownSeqNumber() {
112
79
  return this.lastObservedSeqNumber;
113
80
  }
114
- get referenceTerm() {
115
- return this.baseTerm;
116
- }
117
81
  get minimumSequenceNumber() {
118
82
  return this.minSequenceNumber;
119
83
  }
@@ -127,18 +91,33 @@ export class DeltaManager extends TypedEventEmitter {
127
91
  return this._checkpointSequenceNumber !== undefined;
128
92
  }
129
93
  // Forwarding connection manager properties / IDeltaManager implementation
130
- get maxMessageSize() { return this.connectionManager.maxMessageSize; }
131
- get version() { return this.connectionManager.version; }
132
- get serviceConfiguration() { return this.connectionManager.serviceConfiguration; }
133
- get outbound() { return this.connectionManager.outbound; }
134
- get readOnlyInfo() { return this.connectionManager.readOnlyInfo; }
135
- get clientDetails() { return this.connectionManager.clientDetails; }
136
- submit(type, contents, batch = false, metadata) {
94
+ get maxMessageSize() {
95
+ return this.connectionManager.maxMessageSize;
96
+ }
97
+ get version() {
98
+ return this.connectionManager.version;
99
+ }
100
+ get serviceConfiguration() {
101
+ return this.connectionManager.serviceConfiguration;
102
+ }
103
+ get outbound() {
104
+ return this.connectionManager.outbound;
105
+ }
106
+ get readOnlyInfo() {
107
+ return this.connectionManager.readOnlyInfo;
108
+ }
109
+ get clientDetails() {
110
+ return this.connectionManager.clientDetails;
111
+ }
112
+ submit(type, contents, batch = false, metadata, compression, referenceSequenceNumber) {
113
+ // Back-compat ADO:3455
114
+ const backCompatRefSeqNum = referenceSequenceNumber ?? this.lastProcessedSequenceNumber;
137
115
  const messagePartial = {
138
- contents: JSON.stringify(contents),
116
+ contents,
139
117
  metadata,
140
- referenceSequenceNumber: this.lastProcessedSequenceNumber,
118
+ referenceSequenceNumber: backCompatRefSeqNum,
141
119
  type,
120
+ compression,
142
121
  };
143
122
  if (!batch) {
144
123
  this.flush();
@@ -147,26 +126,48 @@ export class DeltaManager extends TypedEventEmitter {
147
126
  if (message === undefined) {
148
127
  return -1;
149
128
  }
150
- this.opsSize += message.contents.length;
129
+ assert(isClientMessage(message), 0x419 /* client sends non-client message */);
130
+ if (contents !== undefined) {
131
+ this.opsSize += contents.length;
132
+ }
151
133
  this.messageBuffer.push(message);
134
+ if (message.type === MessageType.NoOp) {
135
+ this.noOpCount++;
136
+ }
152
137
  this.emit("submitOp", message);
153
138
  if (!batch) {
154
139
  this.flush();
155
140
  }
156
141
  return message.clientSequenceNumber;
157
142
  }
158
- submitSignal(content) { return this.connectionManager.submitSignal(content); }
143
+ submitSignal(content, targetClientId) {
144
+ return this.connectionManager.submitSignal(content, targetClientId);
145
+ }
159
146
  flush() {
160
- if (this.messageBuffer.length === 0) {
147
+ const batch = this.messageBuffer;
148
+ if (batch.length === 0) {
161
149
  return;
162
150
  }
163
- // The prepareFlush event allows listeners to append metadata to the batch prior to submission.
164
- this.emit("prepareSend", this.messageBuffer);
165
- this.connectionManager.sendMessages(this.messageBuffer);
166
151
  this.messageBuffer = [];
152
+ // The prepareFlush event allows listeners to append metadata to the batch prior to submission.
153
+ this.emit("prepareSend", batch);
154
+ if (batch.length === 1) {
155
+ assert(batch[0].metadata?.batch === undefined, 0x3c9 /* no batch markup on single message */);
156
+ }
157
+ else {
158
+ assert(batch[0].metadata?.batch === true, 0x3ca /* no start batch markup */);
159
+ assert(batch[batch.length - 1].metadata?.batch === false, 0x3cb /* no end batch markup */);
160
+ }
161
+ this.connectionManager.sendMessages(batch);
162
+ assert(this.messageBuffer.length === 0, 0x3cc /* reentrancy */);
167
163
  }
168
164
  get connectionProps() {
169
- return Object.assign({ sequenceNumber: this.lastSequenceNumber, opsSize: this.opsSize > 0 ? this.opsSize : undefined }, this.connectionManager.connectionProps);
165
+ return {
166
+ sequenceNumber: this.lastSequenceNumber,
167
+ opsSize: this.opsSize > 0 ? this.opsSize : undefined,
168
+ deltaManagerState: this._disposed ? "disposed" : this._closed ? "closed" : "open",
169
+ ...this.connectionManager.connectionProps,
170
+ };
170
171
  }
171
172
  /**
172
173
  * Log error event with a bunch of internal to DeltaManager information about state of op processing
@@ -175,15 +176,121 @@ export class DeltaManager extends TypedEventEmitter {
175
176
  * @param event - Event to log.
176
177
  */
177
178
  logConnectionIssue(event) {
178
- var _a;
179
179
  assert(this.connectionManager.connected, 0x238 /* "called only in connected state" */);
180
180
  const pendingSorted = this.pending.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
181
- this.logger.sendErrorEvent(Object.assign(Object.assign(Object.assign(Object.assign({}, event), {
181
+ this.logger.sendTelemetryEvent({
182
+ ...event,
182
183
  // This directly tells us if fetching ops is in flight, and thus likely the reason of
183
184
  // stalled op processing
184
- fetchReason: this.fetchReason,
185
+ fetchReason: this.fetchReason,
185
186
  // A bunch of useful sequence numbers to understand if we are holding some ops from processing
186
- lastQueuedSequenceNumber: this.lastQueuedSequenceNumber, lastProcessedSequenceNumber: this.lastProcessedSequenceNumber, lastObserved: this.lastObservedSeqNumber }), this.connectionManager.connectionVerboseProps), { pendingOps: this.pending.length, pendingFirst: (_a = pendingSorted[0]) === null || _a === void 0 ? void 0 : _a.sequenceNumber, haveHandler: this.handler !== undefined, inboundLength: this.inbound.length, inboundPaused: this.inbound.paused }));
187
+ lastQueuedSequenceNumber: this.lastQueuedSequenceNumber,
188
+ lastProcessedSequenceNumber: this.lastProcessedSequenceNumber,
189
+ lastObserved: this.lastObservedSeqNumber,
190
+ // connection info
191
+ ...this.connectionManager.connectionVerboseProps,
192
+ pendingOps: this.pending.length,
193
+ pendingFirst: pendingSorted[0]?.sequenceNumber,
194
+ haveHandler: this.handler !== undefined,
195
+ inboundLength: this.inbound.length,
196
+ inboundPaused: this.inbound.paused,
197
+ });
198
+ }
199
+ constructor(serviceProvider, logger, _active, createConnectionManager) {
200
+ super();
201
+ this.serviceProvider = serviceProvider;
202
+ this.logger = logger;
203
+ this._active = _active;
204
+ this.pending = [];
205
+ // A boolean used to assert that ops are not being sent while processing another op.
206
+ this.currentlyProcessingOps = false;
207
+ // The minimum sequence number and last sequence number received from the server
208
+ this.minSequenceNumber = 0;
209
+ // There are three numbers we track
210
+ // * lastQueuedSequenceNumber is the last queued sequence number. If there are gaps in seq numbers, then this number
211
+ // is not updated until we cover that gap, so it increases each time by 1.
212
+ // * lastObservedSeqNumber is an estimation of last known sequence number for container in storage. It's initially
213
+ // populated at web socket connection time (if storage provides that info) and is updated once ops shows up.
214
+ // It's never less than lastQueuedSequenceNumber
215
+ // * lastProcessedSequenceNumber - last processed sequence number
216
+ this.lastQueuedSequenceNumber = 0;
217
+ this.lastObservedSeqNumber = 0;
218
+ this.lastProcessedSequenceNumber = 0;
219
+ /** count number of noops sent by the client which may not be acked */
220
+ this.noOpCount = 0;
221
+ /** Track clientSequenceNumber of the last op */
222
+ this.lastClientSequenceNumber = 0;
223
+ /**
224
+ * Track down the ops size.
225
+ */
226
+ this.opsSize = 0;
227
+ // The sequence number we initially loaded from
228
+ // In case of reading from a snapshot or pending state, its value will be equal to
229
+ // the last message that got serialized.
230
+ this.initSequenceNumber = 0;
231
+ this._closed = false;
232
+ this._disposed = false;
233
+ this.throttlingIdSet = new Set();
234
+ this.timeTillThrottling = 0;
235
+ this.closeAbortController = new AbortController();
236
+ this.deltaStorageDelayId = uuid();
237
+ this.deltaStreamDelayId = uuid();
238
+ this.messageBuffer = [];
239
+ const props = {
240
+ incomingOpHandler: (messages, reason) => {
241
+ try {
242
+ this.enqueueMessages(messages, reason);
243
+ }
244
+ catch (error) {
245
+ this.logger.sendErrorEvent({ eventName: "EnqueueMessages_Exception" }, error);
246
+ this.close(normalizeError(error));
247
+ }
248
+ },
249
+ signalHandler: (signals) => {
250
+ for (const signal of signals) {
251
+ this._inboundSignal.push(signal);
252
+ }
253
+ },
254
+ reconnectionDelayHandler: (delayMs, error) => this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
255
+ closeHandler: (error) => this.close(error),
256
+ disconnectHandler: (reason) => this.disconnectHandler(reason),
257
+ connectHandler: (connection) => this.connectHandler(connection),
258
+ pongHandler: (latency) => this.emit("pong", latency),
259
+ readonlyChangeHandler: (readonly, readonlyConnectionReason) => {
260
+ safeRaiseEvent(this, this.logger, "readonly", readonly, readonlyConnectionReason);
261
+ },
262
+ establishConnectionHandler: (reason) => this.establishingConnection(reason),
263
+ cancelConnectionHandler: (reason) => this.cancelEstablishingConnection(reason),
264
+ };
265
+ this.connectionManager = createConnectionManager(props);
266
+ this._inbound = new DeltaQueue((op) => {
267
+ this.processInboundMessage(op);
268
+ });
269
+ this._inbound.on("error", (error) => {
270
+ this.close(DataProcessingError.wrapIfUnrecognized(error, "deltaManagerInboundErrorHandler", this.lastMessage));
271
+ });
272
+ // Inbound signal queue
273
+ this._inboundSignal = new DeltaQueue((message) => {
274
+ if (this.handler === undefined) {
275
+ throw new Error("Attempted to process an inbound signal without a handler attached");
276
+ }
277
+ this.handler.processSignal({
278
+ clientId: message.clientId,
279
+ content: JSON.parse(message.content),
280
+ });
281
+ });
282
+ this._inboundSignal.on("error", (error) => {
283
+ this.close(normalizeError(error));
284
+ });
285
+ // Initially, all queues are created paused.
286
+ // - outbound is flipped back and forth in setupNewSuccessfulConnection / disconnectFromDeltaStream
287
+ // - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
288
+ }
289
+ cancelEstablishingConnection(reason) {
290
+ this.emit("cancelEstablishingConnection", reason);
291
+ }
292
+ establishingConnection(reason) {
293
+ this.emit("establishingConnection", reason);
187
294
  }
188
295
  connectHandler(connection) {
189
296
  this.refreshDelayInfo(this.deltaStreamDelayId);
@@ -202,8 +309,10 @@ export class DeltaManager extends TypedEventEmitter {
202
309
  // state. As requirements change, so should these checks.
203
310
  assert(this.messageBuffer.length === 0, 0x0e9 /* "messageBuffer is not empty on new connection" */);
204
311
  this.opsSize = 0;
205
- this.emit("connect", connection, checkpointSequenceNumber !== undefined ?
206
- this.lastObservedSeqNumber - this.lastSequenceNumber : undefined);
312
+ this.noOpCount = 0;
313
+ this.emit("connect", connection, checkpointSequenceNumber !== undefined
314
+ ? this.lastObservedSeqNumber - this.lastSequenceNumber
315
+ : undefined);
207
316
  // If we got some initial ops, then we know the gap and call above fetched ops to fill it.
208
317
  // Same is true for "write" mode even if we have no ops - we will get "join" own op very very soon.
209
318
  // However if we are connecting as view-only, then there is no good signal to realize if client is behind.
@@ -219,16 +328,12 @@ export class DeltaManager extends TypedEventEmitter {
219
328
  this.fetchMissingDeltas("AfterReadConnection");
220
329
  }
221
330
  }
222
- dispose() {
223
- throw new Error("Not implemented.");
224
- }
225
331
  /**
226
332
  * Sets the sequence number from which inbound messages should be returned
227
333
  */
228
- async attachOpHandler(minSequenceNumber, sequenceNumber, term, handler, prefetchType = "none") {
334
+ async attachOpHandler(minSequenceNumber, sequenceNumber, handler, prefetchType = "none") {
229
335
  this.initSequenceNumber = sequenceNumber;
230
336
  this.lastProcessedSequenceNumber = sequenceNumber;
231
- this.baseTerm = term;
232
337
  this.minSequenceNumber = minSequenceNumber;
233
338
  this.lastQueuedSequenceNumber = sequenceNumber;
234
339
  this.lastObservedSeqNumber = sequenceNumber;
@@ -243,7 +348,7 @@ export class DeltaManager extends TypedEventEmitter {
243
348
  // setupNewSuccessfulConnection. But it should do nothing, because there is no way to fetch ops before
244
349
  // we know snapshot sequence number that is set in attachOpHandler. So all such calls should be noop.
245
350
  assert(this.fetchReason === undefined, 0x268 /* "There can't be pending fetch that early in boot sequence!" */);
246
- if (this.closed) {
351
+ if (this._closed) {
247
352
  return;
248
353
  }
249
354
  this._inbound.resume();
@@ -264,8 +369,7 @@ export class DeltaManager extends TypedEventEmitter {
264
369
  assert(this.fetchReason !== undefined || this.pending.length === 0, 0x269 /* "pending ops are not dropped" */);
265
370
  }
266
371
  connect(args) {
267
- var _a;
268
- const fetchOpsFromStorage = (_a = args.fetchOpsFromStorage) !== null && _a !== void 0 ? _a : true;
372
+ const fetchOpsFromStorage = args.fetchOpsFromStorage ?? true;
269
373
  logIfFalse(this.handler !== undefined || !fetchOpsFromStorage, this.logger, "CantFetchWithoutBaseline"); // can't fetch if no baseline
270
374
  // Note: There is race condition here.
271
375
  // We want to issue request to storage as soon as possible, to
@@ -278,9 +382,9 @@ export class DeltaManager extends TypedEventEmitter {
278
382
  // on the wire, we might be always behind.
279
383
  // See comment at the end of "connect" handler
280
384
  if (fetchOpsFromStorage) {
281
- this.fetchMissingDeltas(args.reason);
385
+ this.fetchMissingDeltas(args.reason.text);
282
386
  }
283
- this.connectionManager.connect(args.mode);
387
+ this.connectionManager.connect(args.reason, args.mode);
284
388
  }
285
389
  async getDeltas(from, // inclusive
286
390
  to, // exclusive
@@ -298,8 +402,14 @@ export class DeltaManager extends TypedEventEmitter {
298
402
  // It is possible that due to asynchrony (including await above), required ops were already
299
403
  // received through delta stream. Validate that before moving forward.
300
404
  if (this.lastQueuedSequenceNumber >= lastExpectedOp) {
301
- this.logger.sendPerformanceEvent(Object.assign({ reason: fetchReason, eventName: "ExtraStorageCall", early: true, from,
302
- to }, this.connectionManager.connectionVerboseProps));
405
+ this.logger.sendPerformanceEvent({
406
+ reason: fetchReason,
407
+ eventName: "ExtraStorageCall",
408
+ early: true,
409
+ from,
410
+ to,
411
+ ...this.connectionManager.connectionVerboseProps,
412
+ });
303
413
  return;
304
414
  }
305
415
  // Be prepared for the case where webSocket would receive the ops that we are trying to fill through
@@ -324,14 +434,14 @@ export class DeltaManager extends TypedEventEmitter {
324
434
  // This is useless for known ranges (to is defined) as it means request is over either way.
325
435
  // And it will cancel unbound request too early, not allowing us to learn where the end of the file is.
326
436
  if (!opsFromFetch && cancelFetch(op)) {
327
- controller.abort();
437
+ controller.abort("DeltaManager getDeltas fetch cancelled");
328
438
  this._inbound.off("push", opListener);
329
439
  }
330
440
  };
331
441
  try {
332
442
  this._inbound.on("push", opListener);
333
443
  assert(this.closeAbortController.signal.onabort === null, 0x1e8 /* "reentrancy" */);
334
- this.closeAbortController.signal.onabort = () => controller.abort();
444
+ this.closeAbortController.signal.onabort = () => controller.abort(this.closeAbortController.signal.reason);
335
445
  const stream = this.deltaStorage.fetchMessages(from, // inclusive
336
446
  to, // exclusive
337
447
  controller.signal, cacheOnly, fetchReason);
@@ -351,6 +461,13 @@ export class DeltaManager extends TypedEventEmitter {
351
461
  }
352
462
  }
353
463
  finally {
464
+ if (controller.signal.aborted) {
465
+ this.logger.sendTelemetryEvent({
466
+ eventName: "DeltaManager_GetDeltasAborted",
467
+ fetchReason,
468
+ reason: controller.signal.reason,
469
+ });
470
+ }
354
471
  this.closeAbortController.signal.onabort = null;
355
472
  this._inbound.off("push", opListener);
356
473
  assert(!opsFromFetch, 0x289 /* "logic error" */);
@@ -358,14 +475,47 @@ export class DeltaManager extends TypedEventEmitter {
358
475
  }
359
476
  /**
360
477
  * Closes the connection and clears inbound & outbound queues.
478
+ *
479
+ * Differences from dispose:
480
+ * - close will trigger readonly notification
481
+ * - close emits "closed"
482
+ * - close cannot be called after dispose
361
483
  */
362
484
  close(error) {
363
- if (this.closed) {
485
+ if (this._closed) {
486
+ return;
487
+ }
488
+ this._closed = true;
489
+ this.connectionManager.dispose(error, true /* switchToReadonly */);
490
+ this.clearQueues();
491
+ this.emit("closed", error);
492
+ }
493
+ /**
494
+ * Disposes the connection and clears the inbound & outbound queues.
495
+ *
496
+ * Differences from close:
497
+ * - dispose will emit "disposed"
498
+ * - dispose will remove all listeners
499
+ * - dispose can be called after closure
500
+ */
501
+ dispose(error) {
502
+ if (this._disposed) {
364
503
  return;
365
504
  }
366
- this.closed = true;
367
- this.connectionManager.dispose(error);
368
- this.closeAbortController.abort();
505
+ if (error !== undefined && !isFluidError(error)) {
506
+ throw new UsageError("Error must be a Fluid error");
507
+ }
508
+ this._disposed = true;
509
+ this._closed = true; // We consider "disposed" as a further state than "closed"
510
+ this.connectionManager.dispose(error, false /* switchToReadonly */);
511
+ this.clearQueues();
512
+ // This needs to be the last thing we do (before removing listeners), as it causes
513
+ // Container to dispose context and break ability of data stores / runtime to "hear" from delta manager.
514
+ this.emit("disposed", error);
515
+ this.removeAllListeners();
516
+ }
517
+ clearQueues() {
518
+ this.closeAbortController.abort("DeltaManager is closed");
369
519
  this._inbound.clear();
370
520
  this._inboundSignal.clear();
371
521
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
@@ -374,11 +524,6 @@ export class DeltaManager extends TypedEventEmitter {
374
524
  this._inboundSignal.pause();
375
525
  // Drop pending messages - this will ensure catchUp() does not go into infinite loop
376
526
  this.pending = [];
377
- // This needs to be the last thing we do (before removing listeners), as it causes
378
- // Container to dispose context and break ability of data stores / runtime to "hear"
379
- // from delta manager, including notification (above) about readonly state.
380
- this.emit("closed", error);
381
- this.removeAllListeners();
382
527
  }
383
528
  refreshDelayInfo(id) {
384
529
  this.throttlingIdSet.delete(id);
@@ -399,7 +544,7 @@ export class DeltaManager extends TypedEventEmitter {
399
544
  emitDelayInfo(id, delayMs, error) {
400
545
  const timeNow = Date.now();
401
546
  this.throttlingIdSet.add(id);
402
- if (delayMs > 0 && (timeNow + delayMs > this.timeTillThrottling)) {
547
+ if (delayMs > 0 && timeNow + delayMs > this.timeTillThrottling) {
403
548
  this.timeTillThrottling = timeNow + delayMs;
404
549
  const throttlingWarning = ThrottlingWarning.wrap(error, delayMs / 1000 /* retryAfterSeconds */, this.logger);
405
550
  this.emit("throttled", throttlingWarning);
@@ -410,13 +555,12 @@ export class DeltaManager extends TypedEventEmitter {
410
555
  // for example, it's not clear if serverMetadata or timestamp property is a property of message or server state.
411
556
  // We only extract the most obvious fields that are sufficient (with high probability) to detect sequence number
412
557
  // reuse.
413
- // Also payload goes to telemetry, so no PII, including content!!
558
+ // Also payload goes to telemetry, so no content or anything else that shouldn't be logged for privacy reasons
414
559
  // Note: It's possible for a duplicate op to be broadcasted and have everything the same except the timestamp.
415
560
  comparableMessagePayload(m) {
416
561
  return `${m.clientId}-${m.type}-${m.minimumSequenceNumber}-${m.referenceSequenceNumber}-${m.timestamp}`;
417
562
  }
418
563
  enqueueMessages(messages, reason, allowGaps = false) {
419
- var _a, _b;
420
564
  if (this.handler === undefined) {
421
565
  // We did not setup handler yet.
422
566
  // This happens when we connect to web socket faster than we get attributes for container
@@ -463,31 +607,48 @@ export class DeltaManager extends TypedEventEmitter {
463
607
  }
464
608
  let eventName;
465
609
  // Report if we found some issues
466
- if (duplicate !== 0 || gap !== 0 && !allowGaps || initialGap > 0 && this.fetchReason === undefined) {
610
+ if (duplicate !== 0 ||
611
+ (gap !== 0 && !allowGaps) ||
612
+ (initialGap > 0 && this.fetchReason === undefined)) {
467
613
  eventName = "enqueueMessages";
468
614
  // Also report if we are fetching ops, and same range comes in, thus making this fetch obsolete.
469
615
  }
470
- else if (this.fetchReason !== undefined && this.fetchReason !== reason &&
471
- (from <= this.lastQueuedSequenceNumber + 1 && last > this.lastQueuedSequenceNumber)) {
616
+ else if (this.fetchReason !== undefined &&
617
+ this.fetchReason !== reason &&
618
+ from <= this.lastQueuedSequenceNumber + 1 &&
619
+ last > this.lastQueuedSequenceNumber) {
472
620
  eventName = "enqueueMessagesExtraFetch";
473
621
  }
474
622
  // Report if there is something to report
475
623
  // Do not report when pending fetch is in progress, as such reporting will not
476
624
  // correctly take into account pending ops.
477
625
  if (eventName !== undefined) {
478
- this.logger.sendPerformanceEvent(Object.assign({ eventName,
479
- reason, previousReason: this.prevEnqueueMessagesReason, from, to: last + 1, length: messages.length, fetchReason: this.fetchReason, duplicate: duplicate > 0 ? duplicate : undefined, initialGap: initialGap !== 0 ? initialGap : undefined, gap: gap > 0 ? gap : undefined, firstMissing, dmInitialSeqNumber: this.initialSequenceNumber }, this.connectionManager.connectionVerboseProps));
626
+ this.logger.sendPerformanceEvent({
627
+ eventName,
628
+ reason,
629
+ previousReason: this.prevEnqueueMessagesReason,
630
+ from,
631
+ to: last + 1,
632
+ length: messages.length,
633
+ fetchReason: this.fetchReason,
634
+ duplicate: duplicate > 0 ? duplicate : undefined,
635
+ initialGap: initialGap !== 0 ? initialGap : undefined,
636
+ gap: gap > 0 ? gap : undefined,
637
+ firstMissing,
638
+ dmInitialSeqNumber: this.initialSequenceNumber,
639
+ ...this.connectionManager.connectionVerboseProps,
640
+ });
480
641
  }
481
642
  }
482
643
  this.updateLatestKnownOpSeqNumber(messages[messages.length - 1].sequenceNumber);
483
- const n = (_a = this.previouslyProcessedMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber;
644
+ const n = this.previouslyProcessedMessage?.sequenceNumber;
484
645
  assert(n === undefined || n === this.lastQueuedSequenceNumber, 0x0ec /* "Unexpected value for previously processed message's sequence number" */);
485
646
  for (const message of messages) {
486
647
  // Check that the messages are arriving in the expected order
487
648
  if (message.sequenceNumber <= this.lastQueuedSequenceNumber) {
488
649
  // Validate that we do not have data loss, i.e. sequencing is reset and started again
489
650
  // with numbers that this client already observed before.
490
- if (((_b = this.previouslyProcessedMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber) === message.sequenceNumber) {
651
+ if (this.previouslyProcessedMessage?.sequenceNumber === message.sequenceNumber) {
491
652
  const message1 = this.comparableMessagePayload(this.previouslyProcessedMessage);
492
653
  const message2 = this.comparableMessagePayload(message);
493
654
  if (message1 !== message2) {
@@ -499,8 +660,8 @@ export class DeltaManager extends TypedEventEmitter {
499
660
  // hit. One example is that some clients could be submitting ops to two different service
500
661
  // instances such that the same sequence number is reused for two different ops.
501
662
  // pre-0.58 error message: twoMessagesWithSameSeqNumAndDifferentPayload
502
- "Found two messages with the same sequenceNumber but different payloads. Likely to be a "
503
- + "service issue", DriverErrorType.fileOverwrittenInStorage, {
663
+ "Found two messages with the same sequenceNumber but different payloads. Likely to be a " +
664
+ "service issue", DriverErrorTypes.fileOverwrittenInStorage, {
504
665
  clientId: this.connectionManager.clientId,
505
666
  sequenceNumber: message.sequenceNumber,
506
667
  message1,
@@ -528,40 +689,73 @@ export class DeltaManager extends TypedEventEmitter {
528
689
  }
529
690
  processInboundMessage(message) {
530
691
  const startTime = Date.now();
692
+ assert(!this.currentlyProcessingOps, 0x3af /* Already processing ops. */);
693
+ this.currentlyProcessingOps = true;
531
694
  this.lastProcessedMessage = message;
532
- // All non-system messages are coming from some client, and should have clientId
533
- // System messages may have no clientId (but some do, like propose, noop, summarize)
534
- assert(message.clientId !== undefined
535
- || !(isClientMessage(message)), 0x0ed /* "non-system message have to have clientId" */);
695
+ const isString = typeof message.clientId === "string";
696
+ assert(message.clientId === null || isString, 0x41a /* undefined or string */);
697
+ // All client messages are coming from some client, and should have clientId,
698
+ // and non-client message should not have clientId. But, there are two exceptions:
699
+ // 1. (Legacy) We can see message.type === "attach" or "chunkedOp" for legacy files before RTM
700
+ // 2. Non-immediate noops (contents: null) can be sent by service without clientId
701
+ if (!isString && isClientMessage(message) && message.type !== MessageType.NoOp) {
702
+ throw new DataCorruptionError("Mismatch in clientId", {
703
+ ...extractSafePropertiesFromMessage(message),
704
+ messageType: message.type,
705
+ });
706
+ }
536
707
  // TODO Remove after SPO picks up the latest build.
537
- if (typeof message.contents === "string"
538
- && message.contents !== ""
539
- && message.type !== MessageType.ClientLeave) {
708
+ if (typeof message.contents === "string" &&
709
+ message.contents !== "" &&
710
+ message.type !== MessageType.ClientLeave) {
540
711
  message.contents = JSON.parse(message.contents);
541
712
  }
713
+ // Validate client sequence number has no gap. Decrement the noOpCount by gap
714
+ // If the count ends up negative, that means we have a real gap and throw error
715
+ if (this.connectionManager.clientId !== undefined &&
716
+ this.connectionManager.clientId === message.clientId) {
717
+ if (message.type === MessageType.NoOp) {
718
+ this.noOpCount--;
719
+ }
720
+ const clientSeqNumGap = message.clientSequenceNumber - this.lastClientSequenceNumber - 1;
721
+ this.noOpCount -= clientSeqNumGap;
722
+ if (this.noOpCount < 0) {
723
+ throw new Error(`gap in client sequence number: ${clientSeqNumGap}`);
724
+ }
725
+ this.lastClientSequenceNumber = message.clientSequenceNumber;
726
+ }
542
727
  this.connectionManager.beforeProcessingIncomingOp(message);
543
728
  // Watch the minimum sequence number and be ready to update as needed
544
729
  if (this.minSequenceNumber > message.minimumSequenceNumber) {
545
730
  // pre-0.58 error message: msnMovesBackwards
546
- throw new DataCorruptionError("Found a lower minimumSequenceNumber (msn) than previously recorded", Object.assign(Object.assign({}, extractSafePropertiesFromMessage(message)), { clientId: this.connectionManager.clientId }));
731
+ throw new DataCorruptionError("Found a lower minimumSequenceNumber (msn) than previously recorded", {
732
+ ...extractSafePropertiesFromMessage(message),
733
+ clientId: this.connectionManager.clientId,
734
+ });
735
+ }
736
+ // Client ops: MSN has to be lower than sequence #, as client can continue to send ops with same
737
+ // reference sequence number as this op.
738
+ // System ops (when no clients are connected) are the only ops where equation is possible.
739
+ const diff = message.sequenceNumber - message.minimumSequenceNumber;
740
+ if (diff < 0 || (diff === 0 && message.clientId !== null)) {
741
+ throw new DataCorruptionError("MSN has to be lower than sequence #", extractSafePropertiesFromMessage(message));
547
742
  }
548
743
  this.minSequenceNumber = message.minimumSequenceNumber;
549
744
  if (message.sequenceNumber !== this.lastProcessedSequenceNumber + 1) {
550
745
  // pre-0.58 error message: nonSequentialSequenceNumber
551
- throw new DataCorruptionError("Found a non-Sequential sequenceNumber", Object.assign(Object.assign({}, extractSafePropertiesFromMessage(message)), { clientId: this.connectionManager.clientId }));
746
+ throw new DataCorruptionError("Found a non-Sequential sequenceNumber", {
747
+ ...extractSafePropertiesFromMessage(message),
748
+ clientId: this.connectionManager.clientId,
749
+ });
552
750
  }
553
751
  this.lastProcessedSequenceNumber = message.sequenceNumber;
554
752
  // a bunch of code assumes that this is true
555
753
  assert(this.lastProcessedSequenceNumber <= this.lastObservedSeqNumber, 0x267 /* "lastObservedSeqNumber should be updated first" */);
556
- // Back-compat for older server with no term
557
- if (message.term === undefined) {
558
- message.term = 1;
559
- }
560
- this.baseTerm = message.term;
561
754
  if (this.handler === undefined) {
562
755
  throw new Error("Attempted to process an inbound message without a handler attached");
563
756
  }
564
757
  this.handler.process(message);
758
+ this.currentlyProcessingOps = false;
565
759
  const endTime = Date.now();
566
760
  // Should be last, after changing this.lastProcessedSequenceNumber above, as many callers
567
761
  // test this.lastProcessedSequenceNumber instead of using op.sequenceNumber itself.
@@ -576,16 +770,18 @@ export class DeltaManager extends TypedEventEmitter {
576
770
  });
577
771
  }
578
772
  /**
579
- * Retrieves the missing deltas between the given sequence numbers
580
- */
773
+ * Retrieves the missing deltas between the given sequence numbers
774
+ */
581
775
  async fetchMissingDeltasCore(reason, cacheOnly, to) {
582
- var _a;
583
776
  // Exit out early if we're already fetching deltas
584
777
  if (this.fetchReason !== undefined) {
585
778
  return;
586
779
  }
587
- if (this.closed) {
588
- this.logger.sendTelemetryEvent({ eventName: "fetchMissingDeltasClosedConnection", reason });
780
+ if (this._closed) {
781
+ this.logger.sendTelemetryEvent({
782
+ eventName: "fetchMissingDeltasClosedConnection",
783
+ reason,
784
+ });
589
785
  return;
590
786
  }
591
787
  if (this.handler === undefined) {
@@ -595,7 +791,7 @@ export class DeltaManager extends TypedEventEmitter {
595
791
  }
596
792
  try {
597
793
  let from = this.lastQueuedSequenceNumber + 1;
598
- const n = (_a = this.previouslyProcessedMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber;
794
+ const n = this.previouslyProcessedMessage?.sequenceNumber;
599
795
  if (n !== undefined) {
600
796
  // If we already processed at least one op, then we have this.previouslyProcessedMessage populated
601
797
  // and can use it to validate that we are operating on same file, i.e. it was not overwritten.
@@ -627,7 +823,7 @@ export class DeltaManager extends TypedEventEmitter {
627
823
  * Sorts pending ops and attempts to apply them
628
824
  */
629
825
  processPendingOps(reason) {
630
- if (this.closed) {
826
+ if (this._closed) {
631
827
  return;
632
828
  }
633
829
  assert(this.handler !== undefined, 0x26c /* "handler should be installed" */);
@@ -660,4 +856,4 @@ export class DeltaManager extends TypedEventEmitter {
660
856
  }
661
857
  }
662
858
  }
663
- //# sourceMappingURL=deltaManager.js.map
859
+ //# sourceMappingURL=deltaManager.mjs.map