@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
package/dist/container.js DELETED
@@ -1,1284 +0,0 @@
1
- "use strict";
2
- /*!
3
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
- * Licensed under the MIT License.
5
- */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.Container = exports.waitContainerToCatchUp = void 0;
11
- // eslint-disable-next-line import/no-internal-modules
12
- const merge_1 = __importDefault(require("lodash/merge"));
13
- const uuid_1 = require("uuid");
14
- const common_utils_1 = require("@fluidframework/common-utils");
15
- const container_definitions_1 = require("@fluidframework/container-definitions");
16
- const container_utils_1 = require("@fluidframework/container-utils");
17
- const driver_utils_1 = require("@fluidframework/driver-utils");
18
- const protocol_base_1 = require("@fluidframework/protocol-base");
19
- const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
20
- const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
21
- const audience_1 = require("./audience");
22
- const containerContext_1 = require("./containerContext");
23
- const contracts_1 = require("./contracts");
24
- const deltaManager_1 = require("./deltaManager");
25
- const deltaManagerProxy_1 = require("./deltaManagerProxy");
26
- const loader_1 = require("./loader");
27
- const packageVersion_1 = require("./packageVersion");
28
- const connectionStateHandler_1 = require("./connectionStateHandler");
29
- const retriableDocumentStorageService_1 = require("./retriableDocumentStorageService");
30
- const protocolTreeDocumentStorageService_1 = require("./protocolTreeDocumentStorageService");
31
- const containerStorageAdapter_1 = require("./containerStorageAdapter");
32
- const utils_1 = require("./utils");
33
- const quorum_1 = require("./quorum");
34
- const collabWindowTracker_1 = require("./collabWindowTracker");
35
- const connectionManager_1 = require("./connectionManager");
36
- const connectionState_1 = require("./connectionState");
37
- const detachedContainerRefSeqNumber = 0;
38
- const dirtyContainerEvent = "dirty";
39
- const savedContainerEvent = "saved";
40
- /**
41
- * Waits until container connects to delta storage and gets up-to-date
42
- * Useful when resolving URIs and hitting 404, due to container being loaded from (stale) snapshot and not being
43
- * up to date. Host may chose to wait in such case and retry resolving URI.
44
- * Warning: Will wait infinitely for connection to establish if there is no connection.
45
- * May result in deadlock if Container.disconnect() is called and never followed by a call to Container.connect().
46
- * @returns true: container is up to date, it processed all the ops that were know at the time of first connection
47
- * false: storage does not provide indication of how far the client is. Container processed
48
- * all the ops known to it, but it maybe still behind.
49
- * @throws an error beginning with `"Container closed"` if the container is closed before it catches up.
50
- */
51
- async function waitContainerToCatchUp(container) {
52
- // Make sure we stop waiting if container is closed.
53
- if (container.closed) {
54
- throw new container_utils_1.UsageError("waitContainerToCatchUp: Container closed");
55
- }
56
- return new Promise((resolve, reject) => {
57
- const deltaManager = container.deltaManager;
58
- const closedCallback = (err) => {
59
- container.off("closed", closedCallback);
60
- const baseMessage = "Container closed while waiting to catch up";
61
- reject(err !== undefined
62
- ? (0, telemetry_utils_1.wrapError)(err, (innerMessage) => new container_utils_1.GenericError(`${baseMessage}: ${innerMessage}`))
63
- : new container_utils_1.GenericError(baseMessage));
64
- };
65
- container.on("closed", closedCallback);
66
- const waitForOps = () => {
67
- (0, common_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp
68
- || container.connectionState === connectionState_1.ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
69
- const hasCheckpointSequenceNumber = deltaManager.hasCheckpointSequenceNumber;
70
- const connectionOpSeqNumber = deltaManager.lastKnownSeqNumber;
71
- (0, common_utils_1.assert)(deltaManager.lastSequenceNumber <= connectionOpSeqNumber, 0x266 /* "lastKnownSeqNumber should never be below last processed sequence number" */);
72
- if (deltaManager.lastSequenceNumber === connectionOpSeqNumber) {
73
- container.off("closed", closedCallback);
74
- resolve(hasCheckpointSequenceNumber);
75
- return;
76
- }
77
- const callbackOps = (message) => {
78
- if (connectionOpSeqNumber <= message.sequenceNumber) {
79
- container.off("closed", closedCallback);
80
- resolve(hasCheckpointSequenceNumber);
81
- deltaManager.off("op", callbackOps);
82
- }
83
- };
84
- deltaManager.on("op", callbackOps);
85
- };
86
- // We can leverage DeltaManager's "connect" event here and test for ConnectionState.Disconnected
87
- // But that works only if service provides us checkPointSequenceNumber
88
- // Our internal testing is based on R11S that does not, but almost all tests connect as "write" and
89
- // use this function to catch up, so leveraging our own join op as a fence/barrier
90
- if (container.connectionState === connectionState_1.ConnectionState.Connected) {
91
- waitForOps();
92
- return;
93
- }
94
- const callback = () => {
95
- container.off(telemetry_utils_1.connectedEventName, callback);
96
- waitForOps();
97
- };
98
- container.on(telemetry_utils_1.connectedEventName, callback);
99
- if (container.connectionState === connectionState_1.ConnectionState.Disconnected) {
100
- container.connect();
101
- }
102
- });
103
- }
104
- exports.waitContainerToCatchUp = waitContainerToCatchUp;
105
- const getCodeProposal =
106
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
107
- (quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
108
- /**
109
- * Helper function to report to telemetry cases where operation takes longer than expected (1s)
110
- * @param logger - logger to use
111
- * @param eventName - event name
112
- * @param action - functor to call and measure
113
- */
114
- async function ReportIfTooLong(logger, eventName, action) {
115
- const event = telemetry_utils_1.PerformanceEvent.start(logger, { eventName });
116
- const props = await action();
117
- if (event.duration > 1000) {
118
- event.end(props);
119
- }
120
- }
121
- const summarizerClientType = "summarizer";
122
- class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
123
- constructor(loader, config) {
124
- var _a, _b;
125
- super((name, error) => {
126
- this.mc.logger.sendErrorEvent({
127
- eventName: "ContainerEventHandlerException",
128
- name: typeof name === "string" ? name : undefined,
129
- }, error);
130
- });
131
- this.loader = loader;
132
- // Tells if container can reconnect on losing fist connection
133
- // If false, container gets closed on loss of connection.
134
- this._canReconnect = true;
135
- this._lifecycleState = "loading";
136
- this._attachState = container_definitions_1.AttachState.Detached;
137
- /** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
138
- this.inboundQueuePausedFromInit = true;
139
- this.firstConnection = true;
140
- this.connectionTransitionTimes = [];
141
- this.messageCountAfterDisconnection = 0;
142
- this.attachStarted = false;
143
- this._dirtyContainer = false;
144
- this.setAutoReconnectTime = common_utils_1.performance.now();
145
- this._audience = new audience_1.Audience();
146
- this.clientDetailsOverride = config.clientDetailsOverride;
147
- this._resolvedUrl = config.resolvedUrl;
148
- if (config.canReconnect !== undefined) {
149
- this._canReconnect = config.canReconnect;
150
- }
151
- // Create logger for data stores to use
152
- const type = this.client.details.type;
153
- const interactive = this.client.details.capabilities.interactive;
154
- const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
155
- // Need to use the property getter for docId because for detached flow we don't have the docId initially.
156
- // We assign the id later so property getter is used.
157
- this.subLogger = telemetry_utils_1.ChildLogger.create(loader.services.subLogger, undefined, {
158
- all: {
159
- clientType,
160
- containerId: (0, uuid_1.v4)(),
161
- docId: () => { var _a, _b; return (_b = (_a = this._resolvedUrl) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined; },
162
- containerAttachState: () => this._attachState,
163
- containerLifecycleState: () => this._lifecycleState,
164
- containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
165
- serializedContainer: config.serializedContainerState !== undefined,
166
- },
167
- // we need to be judicious with our logging here to avoid generating too much data
168
- // all data logged here should be broadly applicable, and not specific to a
169
- // specific error or class of errors
170
- error: {
171
- // load information to associate errors with the specific load point
172
- dmInitialSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.initialSequenceNumber; },
173
- dmLastProcessedSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastSequenceNumber; },
174
- dmLastKnownSeqNumber: () => { var _a; return (_a = this._deltaManager) === null || _a === void 0 ? void 0 : _a.lastKnownSeqNumber; },
175
- containerLoadedFromVersionId: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
176
- containerLoadedFromVersionDate: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
177
- // message information to associate errors with the specific execution state
178
- // dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
179
- dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
180
- dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
181
- dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
182
- connectionStateDuration: () => common_utils_1.performance.now() - this.connectionTransitionTimes[this.connectionState],
183
- },
184
- });
185
- // Prefix all events in this file with container-loader
186
- this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
187
- const summarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree")) !== null && _a !== void 0 ? _a : this.loader.services.options.summarizeProtocolTree;
188
- this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
189
- this.connectionStateHandler = new connectionStateHandler_1.ConnectionStateHandler({
190
- quorumClients: () => { var _a; return (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.quorum; },
191
- logConnectionStateChangeTelemetry: (value, oldState, reason) => this.logConnectionStateChangeTelemetry(value, oldState, reason),
192
- shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
193
- maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
194
- logConnectionIssue: (eventName, details) => {
195
- // We get here when socket does not receive any ops on "write" connection, including
196
- // its own join op. Attempt recovery option.
197
- this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: common_utils_1.performance.now() - this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
198
- },
199
- connectionStateChanged: () => {
200
- // Fire events only if container is fully loaded and not closed
201
- if (this._lifecycleState === "loaded") {
202
- this.propagateConnectionState();
203
- }
204
- },
205
- }, this.mc.logger, (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId);
206
- this.on(savedContainerEvent, () => {
207
- this.connectionStateHandler.containerSaved();
208
- });
209
- this._deltaManager = this.createDeltaManager();
210
- this._storage = new containerStorageAdapter_1.ContainerStorageAdapter(() => {
211
- if (this.attachState !== container_definitions_1.AttachState.Attached) {
212
- if (this.loader.services.detachedBlobStorage !== undefined) {
213
- return new containerStorageAdapter_1.BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.mc.logger);
214
- }
215
- this.mc.logger.sendErrorEvent({
216
- eventName: "NoRealStorageInDetachedContainer",
217
- });
218
- throw new Error("Real storage calls not allowed in Unattached container");
219
- }
220
- return this.storageService;
221
- });
222
- const isDomAvailable = typeof document === "object" &&
223
- document !== null &&
224
- typeof document.addEventListener === "function" &&
225
- document.addEventListener !== null;
226
- // keep track of last time page was visible for telemetry
227
- if (isDomAvailable) {
228
- this.lastVisible = document.hidden ? common_utils_1.performance.now() : undefined;
229
- this.visibilityEventHandler = () => {
230
- if (document.hidden) {
231
- this.lastVisible = common_utils_1.performance.now();
232
- }
233
- else {
234
- // settimeout so this will hopefully fire after disconnect event if being hidden caused it
235
- setTimeout(() => { this.lastVisible = undefined; }, 0);
236
- }
237
- };
238
- document.addEventListener("visibilitychange", this.visibilityEventHandler);
239
- }
240
- // We observed that most users of platform do not check Container.connected event on load, causing bugs.
241
- // As such, we are raising events when new listener pops up.
242
- // Note that we can raise both "disconnected" & "connect" events at the same time,
243
- // if we are in connecting stage.
244
- this.on("newListener", (event, listener) => {
245
- // Fire events on the end of JS turn, giving a chance for caller to be in consistent state.
246
- Promise.resolve().then(() => {
247
- switch (event) {
248
- case dirtyContainerEvent:
249
- if (this._dirtyContainer) {
250
- listener();
251
- }
252
- break;
253
- case savedContainerEvent:
254
- if (!this._dirtyContainer) {
255
- listener();
256
- }
257
- break;
258
- case telemetry_utils_1.connectedEventName:
259
- if (this.connected) {
260
- listener(this.clientId);
261
- }
262
- break;
263
- case telemetry_utils_1.disconnectedEventName:
264
- if (!this.connected) {
265
- listener();
266
- }
267
- break;
268
- default:
269
- }
270
- }).catch((error) => {
271
- this.mc.logger.sendErrorEvent({ eventName: "RaiseConnectedEventError" }, error);
272
- });
273
- });
274
- }
275
- /**
276
- * Load an existing container.
277
- */
278
- static async load(loader, loadOptions, pendingLocalState) {
279
- const container = new Container(loader, {
280
- clientDetailsOverride: loadOptions.clientDetailsOverride,
281
- resolvedUrl: loadOptions.resolvedUrl,
282
- canReconnect: loadOptions.canReconnect,
283
- serializedContainerState: pendingLocalState,
284
- });
285
- return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
286
- var _a, _b;
287
- const version = loadOptions.version;
288
- const defaultMode = { opsBeforeReturn: "cached" };
289
- // if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
290
- // to return container, so ignore this value and use undefined for opsBeforeReturn
291
- const mode = pendingLocalState
292
- ? Object.assign(Object.assign({}, ((_a = loadOptions.loadMode) !== null && _a !== void 0 ? _a : defaultMode)), { opsBeforeReturn: undefined }) : (_b = loadOptions.loadMode) !== null && _b !== void 0 ? _b : defaultMode;
293
- const onClosed = (err) => {
294
- // pre-0.58 error message: containerClosedWithoutErrorDuringLoad
295
- reject(err !== null && err !== void 0 ? err : new container_utils_1.GenericError("Container closed without error during load"));
296
- };
297
- container.on("closed", onClosed);
298
- container.load(version, mode, pendingLocalState)
299
- .finally(() => {
300
- container.removeListener("closed", onClosed);
301
- })
302
- .then((props) => {
303
- event.end(Object.assign(Object.assign({}, props), loadOptions.loadMode));
304
- resolve(container);
305
- }, (error) => {
306
- const err = (0, telemetry_utils_1.normalizeError)(error);
307
- // Depending where error happens, we can be attempting to connect to web socket
308
- // and continuously retrying (consider offline mode)
309
- // Host has no container to close, so it's prudent to do it here
310
- container.close(err);
311
- onClosed(err);
312
- });
313
- }), { start: true, end: true, cancel: "generic" });
314
- }
315
- /**
316
- * Create a new container in a detached state.
317
- */
318
- static async createDetached(loader, codeDetails) {
319
- const container = new Container(loader, {});
320
- return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
321
- await container.createDetached(codeDetails);
322
- return container;
323
- }, { start: true, end: true, cancel: "generic" });
324
- }
325
- /**
326
- * Create a new container in a detached state that is initialized with a
327
- * snapshot from a previous detached container.
328
- */
329
- static async rehydrateDetachedFromSnapshot(loader, snapshot) {
330
- const container = new Container(loader, {});
331
- return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
332
- const deserializedSummary = JSON.parse(snapshot);
333
- await container.rehydrateDetachedFromSnapshot(deserializedSummary);
334
- return container;
335
- }, { start: true, end: true, cancel: "generic" });
336
- }
337
- setLoaded() {
338
- // It's conceivable the container could be closed when this is called
339
- // Only transition states if currently loading
340
- if (this._lifecycleState === "loading") {
341
- // Propagate current connection state through the system.
342
- this.propagateConnectionState();
343
- this._lifecycleState = "loaded";
344
- }
345
- }
346
- get closed() {
347
- return (this._lifecycleState === "closing" || this._lifecycleState === "closed");
348
- }
349
- get storage() {
350
- return this._storage;
351
- }
352
- get storageService() {
353
- if (this._storageService === undefined) {
354
- throw new Error("Attempted to access storageService before it was defined");
355
- }
356
- return this._storageService;
357
- }
358
- get context() {
359
- if (this._context === undefined) {
360
- throw new container_utils_1.GenericError("Attempted to access context before it was defined");
361
- }
362
- return this._context;
363
- }
364
- get protocolHandler() {
365
- if (this._protocolHandler === undefined) {
366
- throw new Error("Attempted to access protocolHandler before it was defined");
367
- }
368
- return this._protocolHandler;
369
- }
370
- get connectionMode() { return this._deltaManager.connectionManager.connectionMode; }
371
- get IFluidRouter() { return this; }
372
- get resolvedUrl() {
373
- return this._resolvedUrl;
374
- }
375
- get loadedFromVersion() {
376
- return this._loadedFromVersion;
377
- }
378
- get readOnlyInfo() {
379
- return this._deltaManager.readOnlyInfo;
380
- }
381
- get closeSignal() {
382
- return this._deltaManager.closeAbortController.signal;
383
- }
384
- /**
385
- * Tracks host requiring read-only mode.
386
- */
387
- forceReadonly(readonly) {
388
- this._deltaManager.connectionManager.forceReadonly(readonly);
389
- }
390
- get deltaManager() {
391
- return this._deltaManager;
392
- }
393
- get connectionState() {
394
- return this.connectionStateHandler.connectionState;
395
- }
396
- get connected() {
397
- return this.connectionStateHandler.connected;
398
- }
399
- /**
400
- * Service configuration details. If running in offline mode will be undefined otherwise will contain service
401
- * configuration details returned as part of the initial connection.
402
- */
403
- get serviceConfiguration() {
404
- return this._deltaManager.serviceConfiguration;
405
- }
406
- /**
407
- * The server provided id of the client.
408
- * Set once this.connected is true, otherwise undefined
409
- */
410
- get clientId() {
411
- return this.connectionStateHandler.clientId;
412
- }
413
- /**
414
- * The server provided claims of the client.
415
- * Set once this.connected is true, otherwise undefined
416
- */
417
- get scopes() {
418
- return this._deltaManager.connectionManager.scopes;
419
- }
420
- get clientDetails() {
421
- return this._deltaManager.clientDetails;
422
- }
423
- /**
424
- * Get the code details that are currently specified for the container.
425
- * @returns The current code details if any are specified, undefined if none are specified.
426
- */
427
- getSpecifiedCodeDetails() {
428
- return this.getCodeDetailsFromQuorum();
429
- }
430
- /**
431
- * Get the code details that were used to load the container.
432
- * @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
433
- * loaded.
434
- */
435
- getLoadedCodeDetails() {
436
- var _a;
437
- return (_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails;
438
- }
439
- /**
440
- * Retrieves the audience associated with the document
441
- */
442
- get audience() {
443
- return this._audience;
444
- }
445
- /**
446
- * Returns true if container is dirty.
447
- * Which means data loss if container is closed at that same moment
448
- * Most likely that happens when there is no network connection to ordering service
449
- */
450
- get isDirty() {
451
- return this._dirtyContainer;
452
- }
453
- get serviceFactory() { return this.loader.services.documentServiceFactory; }
454
- get urlResolver() { return this.loader.services.urlResolver; }
455
- get scope() { return this.loader.services.scope; }
456
- get codeLoader() { return this.loader.services.codeLoader; }
457
- /**
458
- * Retrieves the quorum associated with the document
459
- */
460
- getQuorum() {
461
- return this.protocolHandler.quorum;
462
- }
463
- close(error) {
464
- // 1. Ensure that close sequence is exactly the same no matter if it's initiated by host or by DeltaManager
465
- // 2. We need to ensure that we deliver disconnect event to runtime properly. See connectionStateChanged
466
- // handler. We only deliver events if container fully loaded. Transitioning from "loading" ->
467
- // "closing" will lose that info (can also solve by tracking extra state).
468
- this._deltaManager.close(error);
469
- (0, common_utils_1.assert)(this.connectionState === connectionState_1.ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
470
- (0, common_utils_1.assert)(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
471
- }
472
- closeCore(error) {
473
- var _a, _b, _c, _d;
474
- (0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
475
- try {
476
- // Ensure that we raise all key events even if one of these throws
477
- try {
478
- // Raise event first, to ensure we capture _lifecycleState before transition.
479
- // This gives us a chance to know what errors happened on open vs. on fully loaded container.
480
- this.mc.logger.sendTelemetryEvent({
481
- eventName: "ContainerClose",
482
- category: error === undefined ? "generic" : "error",
483
- }, error);
484
- this._lifecycleState = "closing";
485
- (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
486
- this.connectionStateHandler.dispose();
487
- (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
488
- (_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
489
- // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
490
- // about file, like file being overwritten in storage, but client having stale local cache.
491
- // Driver need to ensure all caches are cleared on critical errors
492
- (_d = this.service) === null || _d === void 0 ? void 0 : _d.dispose(error);
493
- }
494
- catch (exception) {
495
- this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
496
- }
497
- this.emit("closed", error);
498
- this.removeAllListeners();
499
- if (this.visibilityEventHandler !== undefined) {
500
- document.removeEventListener("visibilitychange", this.visibilityEventHandler);
501
- }
502
- }
503
- finally {
504
- this._lifecycleState = "closed";
505
- }
506
- }
507
- closeAndGetPendingLocalState() {
508
- // runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
509
- // container at the same time we get pending state, otherwise this container could reconnect and resubmit with
510
- // a new clientId and a future container using stale pending state without the new clientId would resubmit them
511
- (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
512
- (0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
513
- (0, common_utils_1.assert)(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
514
- (0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined, 0x30b /* Must have a valid protocol handler instance */);
515
- const pendingState = {
516
- pendingRuntimeState: this.context.getPendingLocalState(),
517
- url: this.resolvedUrl.url,
518
- protocol: this.protocolHandler.getProtocolState(),
519
- term: this._protocolHandler.attributes.term,
520
- clientId: this.clientId,
521
- };
522
- this.close();
523
- return JSON.stringify(pendingState);
524
- }
525
- get attachState() {
526
- return this._attachState;
527
- }
528
- serialize() {
529
- (0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Detached, 0x0d3 /* "Should only be called in detached container" */);
530
- const appSummary = this.context.createSummary();
531
- const protocolSummary = this.captureProtocolSummary();
532
- const combinedSummary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
533
- if (this.loader.services.detachedBlobStorage && this.loader.services.detachedBlobStorage.size > 0) {
534
- combinedSummary.tree[".hasAttachmentBlobs"] = { type: protocol_definitions_1.SummaryType.Blob, content: "true" };
535
- }
536
- return JSON.stringify(combinedSummary);
537
- }
538
- async attach(request) {
539
- await telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
540
- if (this._lifecycleState !== "loaded") {
541
- // pre-0.58 error message: containerNotValidForAttach
542
- throw new container_utils_1.UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}]`);
543
- }
544
- // If container is already attached or attach is in progress, throw an error.
545
- (0, common_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
546
- this.attachStarted = true;
547
- // If attachment blobs were uploaded in detached state we will go through a different attach flow
548
- const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
549
- && this.loader.services.detachedBlobStorage.size > 0;
550
- try {
551
- (0, common_utils_1.assert)(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
552
- let summary;
553
- if (!hasAttachmentBlobs) {
554
- // Get the document state post attach - possibly can just call attach but we need to change the
555
- // semantics around what the attach means as far as async code goes.
556
- const appSummary = this.context.createSummary();
557
- const protocolSummary = this.captureProtocolSummary();
558
- summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
559
- // Set the state as attaching as we are starting the process of attaching container.
560
- // This should be fired after taking the summary because it is the place where we are
561
- // starting to attach the container to storage.
562
- // Also, this should only be fired in detached container.
563
- this._attachState = container_definitions_1.AttachState.Attaching;
564
- this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
565
- }
566
- // Actually go and create the resolved document
567
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
568
- (0, driver_utils_1.ensureFluidResolvedUrl)(createNewResolvedUrl);
569
- if (this.service === undefined) {
570
- (0, common_utils_1.assert)(this.client.details.type !== summarizerClientType, 0x2c4 /* "client should not be summarizer before container is created" */);
571
- this.service = await (0, driver_utils_1.runWithRetry)(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
572
- cancel: this.closeSignal,
573
- });
574
- }
575
- const resolvedUrl = this.service.resolvedUrl;
576
- (0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
577
- this._resolvedUrl = resolvedUrl;
578
- await this.connectStorageService();
579
- if (hasAttachmentBlobs) {
580
- // upload blobs to storage
581
- (0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
582
- // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
583
- // support blob handles that only know about the local IDs
584
- const redirectTable = new Map();
585
- // if new blobs are added while uploading, upload them too
586
- while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
587
- const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
588
- for (const id of newIds) {
589
- const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
590
- const response = await this.storageService.createBlob(blob);
591
- redirectTable.set(id, response.id);
592
- }
593
- }
594
- // take summary and upload
595
- const appSummary = this.context.createSummary(redirectTable);
596
- const protocolSummary = this.captureProtocolSummary();
597
- summary = (0, driver_utils_1.combineAppAndProtocolSummary)(appSummary, protocolSummary);
598
- this._attachState = container_definitions_1.AttachState.Attaching;
599
- this.context.notifyAttaching((0, utils_1.getSnapshotTreeFromSerializedContainer)(summary));
600
- await this.storageService.uploadSummaryWithContext(summary, {
601
- referenceSequenceNumber: 0,
602
- ackHandle: undefined,
603
- proposalHandle: undefined,
604
- });
605
- }
606
- this._attachState = container_definitions_1.AttachState.Attached;
607
- this.emit("attached");
608
- // Propagate current connection state through the system.
609
- this.propagateConnectionState();
610
- if (!this.closed) {
611
- this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
612
- }
613
- }
614
- catch (error) {
615
- // add resolved URL on error object so that host has the ability to find this document and delete it
616
- const newError = (0, telemetry_utils_1.normalizeError)(error);
617
- const resolvedUrl = this.resolvedUrl;
618
- if ((0, driver_utils_1.isFluidResolvedUrl)(resolvedUrl)) {
619
- newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
620
- }
621
- this.close(newError);
622
- throw newError;
623
- }
624
- }, { start: true, end: true, cancel: "generic" });
625
- }
626
- async request(path) {
627
- return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
628
- }
629
- setAutoReconnectInternal(mode) {
630
- const currentMode = this._deltaManager.connectionManager.reconnectMode;
631
- if (currentMode === mode) {
632
- return;
633
- }
634
- const now = common_utils_1.performance.now();
635
- const duration = now - this.setAutoReconnectTime;
636
- this.setAutoReconnectTime = now;
637
- this.mc.logger.sendTelemetryEvent({
638
- eventName: mode === contracts_1.ReconnectMode.Enabled ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
639
- connectionMode: this.connectionMode,
640
- connectionState: connectionState_1.ConnectionState[this.connectionState],
641
- duration,
642
- });
643
- this._deltaManager.connectionManager.setAutoReconnect(mode);
644
- }
645
- connect() {
646
- if (this.closed) {
647
- throw new container_utils_1.UsageError(`The Container is closed and cannot be connected`);
648
- }
649
- else if (this._attachState !== container_definitions_1.AttachState.Attached) {
650
- throw new container_utils_1.UsageError(`The Container is not attached and cannot be connected`);
651
- }
652
- else if (!this.connected) {
653
- // Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
654
- // If there is gap, we will learn about it once connected, but the gap should be small (if any),
655
- // assuming that connect() is called quickly after initial container boot.
656
- this.connectInternal({ reason: "DocumentConnect", fetchOpsFromStorage: false });
657
- }
658
- }
659
- connectInternal(args) {
660
- (0, common_utils_1.assert)(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
661
- (0, common_utils_1.assert)(this._attachState === container_definitions_1.AttachState.Attached, 0x2c6 /* "Attempting to connect() a container that is not attached" */);
662
- // Resume processing ops and connect to delta stream
663
- this.resumeInternal(args);
664
- // Set Auto Reconnect Mode
665
- const mode = contracts_1.ReconnectMode.Enabled;
666
- this.setAutoReconnectInternal(mode);
667
- }
668
- disconnect() {
669
- if (this.closed) {
670
- throw new container_utils_1.UsageError(`The Container is closed and cannot be disconnected`);
671
- }
672
- else {
673
- this.disconnectInternal();
674
- }
675
- }
676
- disconnectInternal() {
677
- (0, common_utils_1.assert)(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
678
- // Set Auto Reconnect Mode
679
- const mode = contracts_1.ReconnectMode.Disabled;
680
- this.setAutoReconnectInternal(mode);
681
- }
682
- resumeInternal(args) {
683
- (0, common_utils_1.assert)(!this.closed, 0x0d9 /* "Attempting to connect() a closed DeltaManager" */);
684
- // Resume processing ops
685
- if (this.inboundQueuePausedFromInit) {
686
- this.inboundQueuePausedFromInit = false;
687
- this._deltaManager.inbound.resume();
688
- this._deltaManager.inboundSignal.resume();
689
- }
690
- // Ensure connection to web socket
691
- this.connectToDeltaStream(args);
692
- }
693
- async getAbsoluteUrl(relativeUrl) {
694
- var _a;
695
- if (this.resolvedUrl === undefined) {
696
- return undefined;
697
- }
698
- return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, (0, contracts_1.getPackageName)((_a = this._context) === null || _a === void 0 ? void 0 : _a.codeDetails));
699
- }
700
- async proposeCodeDetails(codeDetails) {
701
- if (!(0, container_definitions_1.isFluidCodeDetails)(codeDetails)) {
702
- throw new Error("Provided codeDetails are not IFluidCodeDetails");
703
- }
704
- if (this.codeLoader.IFluidCodeDetailsComparer) {
705
- const comparison = await this.codeLoader.IFluidCodeDetailsComparer.compare(codeDetails, this.getCodeDetailsFromQuorum());
706
- if (comparison !== undefined && comparison <= 0) {
707
- throw new Error("Proposed code details should be greater than the current");
708
- }
709
- }
710
- return this.protocolHandler.quorum.propose("code", codeDetails)
711
- .then(() => true)
712
- .catch(() => false);
713
- }
714
- async processCodeProposal() {
715
- const codeDetails = this.getCodeDetailsFromQuorum();
716
- await Promise.all([
717
- this.deltaManager.inbound.pause(),
718
- this.deltaManager.inboundSignal.pause()
719
- ]);
720
- if ((await this.context.satisfies(codeDetails) === true)) {
721
- this.deltaManager.inbound.resume();
722
- this.deltaManager.inboundSignal.resume();
723
- return;
724
- }
725
- // pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
726
- this.close(new container_utils_1.GenericError("Existing context does not satisfy incoming proposal"));
727
- }
728
- async getVersion(version) {
729
- const versions = await this.storageService.getVersions(version, 1);
730
- return versions[0];
731
- }
732
- recordConnectStartTime() {
733
- if (this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] === undefined) {
734
- this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected] = common_utils_1.performance.now();
735
- }
736
- }
737
- connectToDeltaStream(args) {
738
- this.recordConnectStartTime();
739
- // All agents need "write" access, including summarizer.
740
- if (!this._canReconnect || !this.client.details.capabilities.interactive) {
741
- args.mode = "write";
742
- }
743
- this._deltaManager.connect(args);
744
- }
745
- /**
746
- * Load container.
747
- *
748
- * @param specifiedVersion - one of the following
749
- * - undefined - fetch latest snapshot
750
- * - otherwise, version sha to load snapshot
751
- */
752
- async load(specifiedVersion, loadMode, pendingLocalState) {
753
- if (this._resolvedUrl === undefined) {
754
- throw new Error("Attempting to load without a resolved url");
755
- }
756
- this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
757
- // Ideally we always connect as "read" by default.
758
- // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
759
- // We should not rely on it by (one of them will address the issue, but we need to address both)
760
- // 1) switching create new flow to one where we create file by posting snapshot
761
- // 2) Fixing quorum workflows (have retry logic)
762
- // That all said, "read" does not work with memorylicious workflows (that opens two simultaneous
763
- // connections to same file) in two ways:
764
- // A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
765
- // B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
766
- const connectionArgs = { reason: "DocumentOpen", mode: "write", fetchOpsFromStorage: false };
767
- // Start websocket connection as soon as possible. Note that there is no op handler attached yet, but the
768
- // DeltaManager is resilient to this and will wait to start processing ops until after it is attached.
769
- if (loadMode.deltaConnection === undefined) {
770
- this.connectToDeltaStream(connectionArgs);
771
- }
772
- if (!pendingLocalState) {
773
- await this.connectStorageService();
774
- }
775
- else {
776
- // if we have pendingLocalState we can load without storage; don't wait for connection
777
- this.connectStorageService().catch((error) => this.close(error));
778
- }
779
- this._attachState = container_definitions_1.AttachState.Attached;
780
- // Fetch specified snapshot.
781
- const { snapshot, versionId } = pendingLocalState === undefined
782
- ? await this.fetchSnapshotTree(specifiedVersion)
783
- : { snapshot: undefined, versionId: undefined };
784
- (0, common_utils_1.assert)(snapshot !== undefined || pendingLocalState !== undefined, 0x237 /* "Snapshot should exist" */);
785
- const attributes = pendingLocalState === undefined
786
- ? await this.getDocumentAttributes(this.storageService, snapshot)
787
- : {
788
- sequenceNumber: pendingLocalState.protocol.sequenceNumber,
789
- minimumSequenceNumber: pendingLocalState.protocol.minimumSequenceNumber,
790
- term: pendingLocalState.term,
791
- };
792
- let opsBeforeReturnP;
793
- // Attach op handlers to finish initialization and be able to start processing ops
794
- // Kick off any ops fetching if required.
795
- switch (loadMode.opsBeforeReturn) {
796
- case undefined:
797
- // Start prefetch, but not set opsBeforeReturnP - boot is not blocked by it!
798
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
799
- this.attachDeltaManagerOpHandler(attributes, loadMode.deltaConnection !== "none" ? "all" : "none");
800
- break;
801
- case "cached":
802
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(attributes, "cached");
803
- break;
804
- case "all":
805
- opsBeforeReturnP = this.attachDeltaManagerOpHandler(attributes, "all");
806
- break;
807
- default:
808
- (0, common_utils_1.unreachableCase)(loadMode.opsBeforeReturn);
809
- }
810
- // ...load in the existing quorum
811
- // Initialize the protocol handler
812
- this._protocolHandler = pendingLocalState === undefined
813
- ? await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot)
814
- : await this.initializeProtocolState(attributes, pendingLocalState.protocol.members, pendingLocalState.protocol.proposals, pendingLocalState.protocol.values);
815
- const codeDetails = this.getCodeDetailsFromQuorum();
816
- await this.instantiateContext(true, // existing
817
- codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
818
- // We might have hit some failure that did not manifest itself in exception in this flow,
819
- // do not start op processing in such case - static version of Container.load() will handle it correctly.
820
- if (!this.closed) {
821
- if (opsBeforeReturnP !== undefined) {
822
- this._deltaManager.inbound.resume();
823
- await ReportIfTooLong(this.mc.logger, "WaitOps", async () => { await opsBeforeReturnP; return {}; });
824
- await ReportIfTooLong(this.mc.logger, "WaitOpProcessing", async () => this._deltaManager.inbound.waitTillProcessingDone());
825
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
826
- this._deltaManager.inbound.pause();
827
- }
828
- switch (loadMode.deltaConnection) {
829
- case undefined:
830
- case "delayed":
831
- (0, common_utils_1.assert)(this.inboundQueuePausedFromInit, 0x346 /* inboundQueuePausedFromInit should be true */);
832
- this.inboundQueuePausedFromInit = false;
833
- this._deltaManager.inbound.resume();
834
- this._deltaManager.inboundSignal.resume();
835
- break;
836
- case "none":
837
- break;
838
- default:
839
- (0, common_utils_1.unreachableCase)(loadMode.deltaConnection);
840
- }
841
- }
842
- // Safety net: static version of Container.load() should have learned about it through "closed" handler.
843
- // But if that did not happen for some reason, fail load for sure.
844
- // Otherwise we can get into situations where container is closed and does not try to connect to ordering
845
- // service, but caller does not know that (callers do expect container to be not closed on successful path
846
- // and listen only on "closed" event)
847
- if (this.closed) {
848
- throw new Error("Container was closed while load()");
849
- }
850
- // Internal context is fully loaded at this point
851
- this.setLoaded();
852
- return {
853
- sequenceNumber: attributes.sequenceNumber,
854
- version: versionId,
855
- dmLastProcessedSeqNumber: this._deltaManager.lastSequenceNumber,
856
- dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
857
- };
858
- }
859
- async createDetached(source) {
860
- const attributes = {
861
- sequenceNumber: detachedContainerRefSeqNumber,
862
- term: 1,
863
- minimumSequenceNumber: 0,
864
- };
865
- await this.attachDeltaManagerOpHandler(attributes);
866
- // Need to just seed the source data in the code quorum. Quorum itself is empty
867
- const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(source);
868
- this._protocolHandler = await this.initializeProtocolState(attributes, [], // members
869
- [], // proposals
870
- qValues);
871
- // The load context - given we seeded the quorum - will be great
872
- await this.instantiateContextDetached(false);
873
- this.setLoaded();
874
- }
875
- async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
876
- if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
877
- (0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage && this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
878
- delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
879
- }
880
- const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
881
- this._storage.loadSnapshotForRehydratingContainer(snapshotTree);
882
- const attributes = await this.getDocumentAttributes(this._storage, snapshotTree);
883
- await this.attachDeltaManagerOpHandler(attributes);
884
- // Initialize the protocol handler
885
- const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
886
- const qValues = await (0, driver_utils_1.readAndParse)(this._storage, baseTree.blobs.quorumValues);
887
- const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
888
- this._protocolHandler =
889
- await this.initializeProtocolState(attributes, [], // members
890
- [], // proposals
891
- codeDetails !== undefined ? (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails) : []);
892
- await this.instantiateContextDetached(true, // existing
893
- snapshotTree);
894
- this.setLoaded();
895
- }
896
- async connectStorageService() {
897
- var _a, _b;
898
- if (this._storageService !== undefined) {
899
- return;
900
- }
901
- (0, common_utils_1.assert)(this.service !== undefined, 0x1ef /* "services must be defined" */);
902
- const storageService = await this.service.connectToStorage();
903
- this._storageService =
904
- new retriableDocumentStorageService_1.RetriableDocumentStorageService(storageService, this.mc.logger);
905
- if (this.options.summarizeProtocolTree === true) {
906
- this.mc.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
907
- this._storageService =
908
- new protocolTreeDocumentStorageService_1.ProtocolTreeStorageService(this._storageService, () => this.captureProtocolSummary());
909
- }
910
- // ensure we did not lose that policy in the process of wrapping
911
- (0, common_utils_1.assert)(((_a = storageService.policies) === null || _a === void 0 ? void 0 : _a.minBlobSize) === ((_b = this.storageService.policies) === null || _b === void 0 ? void 0 : _b.minBlobSize), 0x0e0 /* "lost minBlobSize policy" */);
912
- }
913
- async getDocumentAttributes(storage, tree) {
914
- if (tree === undefined) {
915
- return {
916
- minimumSequenceNumber: 0,
917
- sequenceNumber: 0,
918
- term: 1,
919
- };
920
- }
921
- // Backward compatibility: old docs would have ".attributes" instead of "attributes"
922
- const attributesHash = ".protocol" in tree.trees
923
- ? tree.trees[".protocol"].blobs.attributes
924
- : tree.blobs[".attributes"];
925
- const attributes = await (0, driver_utils_1.readAndParse)(storage, attributesHash);
926
- // Backward compatibility for older summaries with no term
927
- if (attributes.term === undefined) {
928
- attributes.term = 1;
929
- }
930
- return attributes;
931
- }
932
- async initializeProtocolStateFromSnapshot(attributes, storage, snapshot) {
933
- let members = [];
934
- let proposals = [];
935
- let values = [];
936
- if (snapshot !== undefined) {
937
- const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshot);
938
- [members, proposals, values] = await Promise.all([
939
- (0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumMembers),
940
- (0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumProposals),
941
- (0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumValues),
942
- ]);
943
- }
944
- const protocolHandler = await this.initializeProtocolState(attributes, members, proposals, values);
945
- return protocolHandler;
946
- }
947
- async initializeProtocolState(attributes, members, proposals, values) {
948
- const protocol = new protocol_base_1.ProtocolOpHandlerWithClientValidation(attributes.minimumSequenceNumber, attributes.sequenceNumber, attributes.term, members, proposals, values, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, { key, value }));
949
- const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
950
- protocol.quorum.on("error", (error) => {
951
- protocolLogger.sendErrorEvent(error);
952
- });
953
- // Track membership changes and update connection state accordingly
954
- this.connectionStateHandler.initProtocol(protocol);
955
- protocol.quorum.on("addProposal", (proposal) => {
956
- if (proposal.key === "code" || proposal.key === "code2") {
957
- this.emit("codeDetailsProposed", proposal.value, proposal);
958
- }
959
- });
960
- protocol.quorum.on("approveProposal", (sequenceNumber, key, value) => {
961
- if (key === "code" || key === "code2") {
962
- if (!(0, container_definitions_1.isFluidCodeDetails)(value)) {
963
- this.mc.logger.sendErrorEvent({
964
- eventName: "CodeProposalNotIFluidCodeDetails",
965
- });
966
- }
967
- this.processCodeProposal().catch((error) => {
968
- this.close((0, telemetry_utils_1.normalizeError)(error));
969
- throw error;
970
- });
971
- }
972
- });
973
- return protocol;
974
- }
975
- captureProtocolSummary() {
976
- const quorumSnapshot = this.protocolHandler.snapshot();
977
- const summary = {
978
- tree: {
979
- attributes: {
980
- content: JSON.stringify(this.protocolHandler.attributes),
981
- type: protocol_definitions_1.SummaryType.Blob,
982
- },
983
- quorumMembers: {
984
- content: JSON.stringify(quorumSnapshot.members),
985
- type: protocol_definitions_1.SummaryType.Blob,
986
- },
987
- quorumProposals: {
988
- content: JSON.stringify(quorumSnapshot.proposals),
989
- type: protocol_definitions_1.SummaryType.Blob,
990
- },
991
- quorumValues: {
992
- content: JSON.stringify(quorumSnapshot.values),
993
- type: protocol_definitions_1.SummaryType.Blob,
994
- },
995
- },
996
- type: protocol_definitions_1.SummaryType.Tree,
997
- };
998
- return summary;
999
- }
1000
- getCodeDetailsFromQuorum() {
1001
- const quorum = this.protocolHandler.quorum;
1002
- const pkg = getCodeProposal(quorum);
1003
- return pkg;
1004
- }
1005
- get client() {
1006
- var _a;
1007
- const client = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.client) !== undefined
1008
- ? this.options.client
1009
- : {
1010
- details: {
1011
- capabilities: { interactive: true },
1012
- },
1013
- mode: "read",
1014
- permission: [],
1015
- scopes: [],
1016
- user: { id: "" },
1017
- };
1018
- if (this.clientDetailsOverride !== undefined) {
1019
- (0, merge_1.default)(client.details, this.clientDetailsOverride);
1020
- }
1021
- client.details.environment = [client.details.environment, ` loaderVersion:${packageVersion_1.pkgVersion}`].join(";");
1022
- return client;
1023
- }
1024
- /**
1025
- * Returns true if connection is active, i.e. it's "write" connection and
1026
- * container runtime was notified about this connection (i.e. we are up-to-date and could send ops).
1027
- * This happens after client received its own joinOp and thus is in the quorum.
1028
- * If it's not true, runtime is not in position to send ops.
1029
- */
1030
- activeConnection() {
1031
- return this.connectionState === connectionState_1.ConnectionState.Connected &&
1032
- this.connectionMode === "write";
1033
- }
1034
- createDeltaManager() {
1035
- const serviceProvider = () => this.service;
1036
- const deltaManager = new deltaManager_1.DeltaManager(serviceProvider, telemetry_utils_1.ChildLogger.create(this.subLogger, "DeltaManager"), () => this.activeConnection(), (props) => new connectionManager_1.ConnectionManager(serviceProvider, this.client, this._canReconnect, telemetry_utils_1.ChildLogger.create(this.subLogger, "ConnectionManager"), props));
1037
- // Disable inbound queues as Container is not ready to accept any ops until we are fully loaded!
1038
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1039
- deltaManager.inbound.pause();
1040
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
1041
- deltaManager.inboundSignal.pause();
1042
- deltaManager.on("connect", (details, opsBehind) => {
1043
- var _a;
1044
- // Back-compat for new client and old server.
1045
- this._audience.clear();
1046
- for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
1047
- this._audience.addMember(priorClient.clientId, priorClient.client);
1048
- }
1049
- this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
1050
- });
1051
- deltaManager.on("disconnect", (reason) => {
1052
- var _a;
1053
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1054
- this.connectionStateHandler.receivedDisconnectEvent(reason);
1055
- });
1056
- deltaManager.on("throttled", (warning) => {
1057
- const warn = warning;
1058
- // Some "warning" events come from outside the container and are logged
1059
- // elsewhere (e.g. summarizing container). We shouldn't log these here.
1060
- if (warn.logged !== true) {
1061
- this.logContainerError(warn);
1062
- }
1063
- this.emit("warning", warn);
1064
- });
1065
- deltaManager.on("readonly", (readonly) => {
1066
- this.emit("readonly", readonly);
1067
- });
1068
- deltaManager.on("closed", (error) => {
1069
- this.closeCore(error);
1070
- });
1071
- return deltaManager;
1072
- }
1073
- async attachDeltaManagerOpHandler(attributes, prefetchType) {
1074
- var _a;
1075
- return this._deltaManager.attachOpHandler(attributes.minimumSequenceNumber, attributes.sequenceNumber, (_a = attributes.term) !== null && _a !== void 0 ? _a : 1, {
1076
- process: (message) => this.processRemoteMessage(message),
1077
- processSignal: (message) => {
1078
- this.processSignal(message);
1079
- },
1080
- }, prefetchType);
1081
- }
1082
- logConnectionStateChangeTelemetry(value, oldState, reason) {
1083
- var _a;
1084
- // Log actual event
1085
- const time = common_utils_1.performance.now();
1086
- this.connectionTransitionTimes[value] = time;
1087
- const duration = time - this.connectionTransitionTimes[oldState];
1088
- let durationFromDisconnected;
1089
- let connectionInitiationReason;
1090
- let autoReconnect;
1091
- let checkpointSequenceNumber;
1092
- let opsBehind;
1093
- if (value === connectionState_1.ConnectionState.Disconnected) {
1094
- autoReconnect = this._deltaManager.connectionManager.reconnectMode;
1095
- }
1096
- else {
1097
- if (value === connectionState_1.ConnectionState.Connected) {
1098
- durationFromDisconnected = time - this.connectionTransitionTimes[connectionState_1.ConnectionState.Disconnected];
1099
- durationFromDisconnected = telemetry_utils_1.TelemetryLogger.formatTick(durationFromDisconnected);
1100
- }
1101
- else {
1102
- // This info is of most interest on establishing connection only.
1103
- checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
1104
- if (this.deltaManager.hasCheckpointSequenceNumber) {
1105
- opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
1106
- }
1107
- }
1108
- if (this.firstConnection) {
1109
- connectionInitiationReason = "InitialConnect";
1110
- }
1111
- else {
1112
- connectionInitiationReason = "AutoReconnect";
1113
- }
1114
- }
1115
- this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`, from: connectionState_1.ConnectionState[oldState], duration,
1116
- durationFromDisconnected,
1117
- reason,
1118
- connectionInitiationReason, pendingClientId: this.connectionStateHandler.pendingClientId, clientId: this.clientId, autoReconnect,
1119
- opsBehind, online: driver_utils_1.OnlineStatus[(0, driver_utils_1.isOnline)()], lastVisible: this.lastVisible !== undefined ? common_utils_1.performance.now() - this.lastVisible : undefined, checkpointSequenceNumber, quorumSize: (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.quorum.getMembers().size }, this._deltaManager.connectionProps));
1120
- if (value === connectionState_1.ConnectionState.Connected) {
1121
- this.firstConnection = false;
1122
- }
1123
- }
1124
- propagateConnectionState() {
1125
- var _a;
1126
- const logOpsOnReconnect = this.connectionState === connectionState_1.ConnectionState.Connected &&
1127
- !this.firstConnection &&
1128
- this.connectionMode === "write";
1129
- if (logOpsOnReconnect) {
1130
- this.messageCountAfterDisconnection = 0;
1131
- }
1132
- const state = this.connectionState === connectionState_1.ConnectionState.Connected;
1133
- // Both protocol and context should not be undefined if we got so far.
1134
- if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
1135
- this.context.setConnectionState(state, this.clientId);
1136
- }
1137
- this.protocolHandler.setConnectionState(state, this.clientId);
1138
- (0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId);
1139
- if (logOpsOnReconnect) {
1140
- this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
1141
- }
1142
- }
1143
- submitContainerMessage(type, contents, batch, metadata) {
1144
- const outboundMessageType = type;
1145
- switch (outboundMessageType) {
1146
- case protocol_definitions_1.MessageType.Operation:
1147
- case protocol_definitions_1.MessageType.RemoteHelp:
1148
- break;
1149
- case protocol_definitions_1.MessageType.Summarize: {
1150
- // github #6451: this is only needed for staging so the server
1151
- // know when the protocol tree is included
1152
- // this can be removed once all clients send
1153
- // protocol tree by default
1154
- const summary = contents;
1155
- if (summary.details === undefined) {
1156
- summary.details = {};
1157
- }
1158
- summary.details.includesProtocolTree =
1159
- this.options.summarizeProtocolTree === true;
1160
- break;
1161
- }
1162
- default:
1163
- this.close(new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type }));
1164
- return -1;
1165
- }
1166
- return this.submitMessage(type, contents, batch, metadata);
1167
- }
1168
- submitMessage(type, contents, batch, metadata) {
1169
- var _a;
1170
- if (this.connectionState !== connectionState_1.ConnectionState.Connected) {
1171
- this.mc.logger.sendErrorEvent({ eventName: "SubmitMessageWithNoConnection", type });
1172
- return -1;
1173
- }
1174
- this.messageCountAfterDisconnection += 1;
1175
- (_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
1176
- return this._deltaManager.submit(type, contents, batch, metadata);
1177
- }
1178
- processRemoteMessage(message) {
1179
- const local = this.clientId === message.clientId;
1180
- // Allow the protocol handler to process the message
1181
- let result = { immediateNoOp: false };
1182
- try {
1183
- result = this.protocolHandler.processMessage(message, local);
1184
- }
1185
- catch (error) {
1186
- this.close((0, telemetry_utils_1.wrapError)(error, (errorMessage) => new container_utils_1.DataCorruptionError(errorMessage, (0, container_utils_1.extractSafePropertiesFromMessage)(message))));
1187
- }
1188
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
1189
- if ((0, driver_utils_1.isUnpackedRuntimeMessage)(message) && !(0, driver_utils_1.isRuntimeMessage)(message)) {
1190
- this.mc.logger.sendTelemetryEvent({ eventName: "UnpackedRuntimeMessage", type: message.type });
1191
- }
1192
- // Forward non system messages to the loaded runtime for processing
1193
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
1194
- if ((0, driver_utils_1.isRuntimeMessage)(message) || (0, driver_utils_1.isUnpackedRuntimeMessage)(message)) {
1195
- this.context.process(message, local, undefined);
1196
- }
1197
- // Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
1198
- if (this.activeConnection()) {
1199
- if (this.collabWindowTracker === undefined) {
1200
- // Note that config from first connection will be used for this container's lifetime.
1201
- // That means that if relay service changes settings, such changes will impact only newly booted
1202
- // clients.
1203
- // All existing will continue to use settings they got earlier.
1204
- (0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
1205
- this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type, contents) => {
1206
- (0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
1207
- this.submitMessage(type, contents);
1208
- }, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
1209
- }
1210
- this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
1211
- }
1212
- this.emit("op", message);
1213
- return result;
1214
- }
1215
- submitSignal(message) {
1216
- this._deltaManager.submitSignal(JSON.stringify(message));
1217
- }
1218
- processSignal(message) {
1219
- // No clientId indicates a system signal message.
1220
- if (message.clientId === null) {
1221
- const innerContent = message.content;
1222
- if (innerContent.type === protocol_definitions_1.MessageType.ClientJoin) {
1223
- const newClient = innerContent.content;
1224
- this._audience.addMember(newClient.clientId, newClient.client);
1225
- }
1226
- else if (innerContent.type === protocol_definitions_1.MessageType.ClientLeave) {
1227
- const leftClientId = innerContent.content;
1228
- this._audience.removeMember(leftClientId);
1229
- }
1230
- }
1231
- else {
1232
- const local = this.clientId === message.clientId;
1233
- this.context.processSignal(message, local);
1234
- }
1235
- }
1236
- /**
1237
- * Get the most recent snapshot, or a specific version.
1238
- * @param specifiedVersion - The specific version of the snapshot to retrieve
1239
- * @returns The snapshot requested, or the latest snapshot if no version was specified, plus version ID
1240
- */
1241
- async fetchSnapshotTree(specifiedVersion) {
1242
- var _a;
1243
- const version = await this.getVersion(specifiedVersion !== null && specifiedVersion !== void 0 ? specifiedVersion : null);
1244
- if (version === undefined && specifiedVersion !== undefined) {
1245
- // We should have a defined version to load from if specified version requested
1246
- this.mc.logger.sendErrorEvent({ eventName: "NoVersionFoundWhenSpecified", id: specifiedVersion });
1247
- }
1248
- this._loadedFromVersion = version;
1249
- const snapshot = (_a = await this.storageService.getSnapshotTree(version)) !== null && _a !== void 0 ? _a : undefined;
1250
- if (snapshot === undefined && version !== undefined) {
1251
- this.mc.logger.sendErrorEvent({ eventName: "getSnapshotTreeFailed", id: version.id });
1252
- }
1253
- return { snapshot, versionId: version === null || version === void 0 ? void 0 : version.id };
1254
- }
1255
- async instantiateContextDetached(existing, snapshot) {
1256
- const codeDetails = this.getCodeDetailsFromQuorum();
1257
- if (codeDetails === undefined) {
1258
- throw new Error("pkg should be provided in create flow!!");
1259
- }
1260
- await this.instantiateContext(existing, codeDetails, snapshot);
1261
- }
1262
- async instantiateContext(existing, codeDetails, snapshot, pendingLocalState) {
1263
- var _a;
1264
- (0, common_utils_1.assert)(((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing context not disposed" */);
1265
- // The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
1266
- // are set. Global requests will still go directly to the loader
1267
- const loader = new loader_1.RelativeLoader(this, this.loader);
1268
- this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
1269
- this.emit("contextChanged", codeDetails);
1270
- }
1271
- updateDirtyContainerState(dirty) {
1272
- if (this._dirtyContainer === dirty) {
1273
- return;
1274
- }
1275
- this._dirtyContainer = dirty;
1276
- this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
1277
- }
1278
- logContainerError(warning) {
1279
- this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
1280
- }
1281
- }
1282
- exports.Container = Container;
1283
- Container.version = "^0.1.0";
1284
- //# sourceMappingURL=container.js.map