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

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