@fluidframework/container-loader 1.2.7 → 2.0.0-dev.1.3.0.96595

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 (157) hide show
  1. package/.mocharc.js +12 -0
  2. package/dist/audience.d.ts +2 -6
  3. package/dist/audience.d.ts.map +1 -1
  4. package/dist/audience.js +6 -11
  5. package/dist/audience.js.map +1 -1
  6. package/dist/catchUpMonitor.d.ts +29 -0
  7. package/dist/catchUpMonitor.d.ts.map +1 -0
  8. package/dist/catchUpMonitor.js +43 -0
  9. package/dist/catchUpMonitor.js.map +1 -0
  10. package/dist/collabWindowTracker.d.ts +1 -1
  11. package/dist/collabWindowTracker.d.ts.map +1 -1
  12. package/dist/collabWindowTracker.js +12 -4
  13. package/dist/collabWindowTracker.js.map +1 -1
  14. package/dist/connectionManager.d.ts +5 -5
  15. package/dist/connectionManager.d.ts.map +1 -1
  16. package/dist/connectionManager.js +43 -22
  17. package/dist/connectionManager.js.map +1 -1
  18. package/dist/connectionState.d.ts +0 -5
  19. package/dist/connectionState.d.ts.map +1 -1
  20. package/dist/connectionState.js +0 -5
  21. package/dist/connectionState.js.map +1 -1
  22. package/dist/connectionStateHandler.d.ts +84 -22
  23. package/dist/connectionStateHandler.d.ts.map +1 -1
  24. package/dist/connectionStateHandler.js +172 -59
  25. package/dist/connectionStateHandler.js.map +1 -1
  26. package/dist/container.d.ts +29 -17
  27. package/dist/container.d.ts.map +1 -1
  28. package/dist/container.js +181 -171
  29. package/dist/container.js.map +1 -1
  30. package/dist/containerContext.d.ts +18 -7
  31. package/dist/containerContext.d.ts.map +1 -1
  32. package/dist/containerContext.js +18 -8
  33. package/dist/containerContext.js.map +1 -1
  34. package/dist/containerStorageAdapter.d.ts +11 -25
  35. package/dist/containerStorageAdapter.d.ts.map +1 -1
  36. package/dist/containerStorageAdapter.js +51 -17
  37. package/dist/containerStorageAdapter.js.map +1 -1
  38. package/dist/contracts.d.ts +5 -5
  39. package/dist/contracts.js.map +1 -1
  40. package/dist/deltaManager.d.ts +4 -1
  41. package/dist/deltaManager.d.ts.map +1 -1
  42. package/dist/deltaManager.js +39 -12
  43. package/dist/deltaManager.js.map +1 -1
  44. package/dist/deltaManagerProxy.d.ts +4 -1
  45. package/dist/deltaManagerProxy.d.ts.map +1 -1
  46. package/dist/deltaQueue.d.ts +9 -2
  47. package/dist/deltaQueue.d.ts.map +1 -1
  48. package/dist/deltaQueue.js +31 -26
  49. package/dist/deltaQueue.js.map +1 -1
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/loader.d.ts +8 -1
  54. package/dist/loader.d.ts.map +1 -1
  55. package/dist/loader.js +4 -3
  56. package/dist/loader.js.map +1 -1
  57. package/dist/packageVersion.d.ts +1 -1
  58. package/dist/packageVersion.d.ts.map +1 -1
  59. package/dist/packageVersion.js +1 -1
  60. package/dist/packageVersion.js.map +1 -1
  61. package/dist/protocol.d.ts +27 -0
  62. package/dist/protocol.d.ts.map +1 -0
  63. package/dist/protocol.js +79 -0
  64. package/dist/protocol.js.map +1 -0
  65. package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
  66. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  67. package/dist/retriableDocumentStorageService.d.ts +2 -2
  68. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  69. package/dist/retriableDocumentStorageService.js +2 -2
  70. package/dist/retriableDocumentStorageService.js.map +1 -1
  71. package/lib/audience.d.ts +2 -6
  72. package/lib/audience.d.ts.map +1 -1
  73. package/lib/audience.js +6 -11
  74. package/lib/audience.js.map +1 -1
  75. package/lib/catchUpMonitor.d.ts +29 -0
  76. package/lib/catchUpMonitor.d.ts.map +1 -0
  77. package/lib/catchUpMonitor.js +39 -0
  78. package/lib/catchUpMonitor.js.map +1 -0
  79. package/lib/collabWindowTracker.d.ts +1 -1
  80. package/lib/collabWindowTracker.d.ts.map +1 -1
  81. package/lib/collabWindowTracker.js +13 -5
  82. package/lib/collabWindowTracker.js.map +1 -1
  83. package/lib/connectionManager.d.ts +5 -5
  84. package/lib/connectionManager.d.ts.map +1 -1
  85. package/lib/connectionManager.js +44 -25
  86. package/lib/connectionManager.js.map +1 -1
  87. package/lib/connectionState.d.ts +0 -5
  88. package/lib/connectionState.d.ts.map +1 -1
  89. package/lib/connectionState.js +0 -5
  90. package/lib/connectionState.js.map +1 -1
  91. package/lib/connectionStateHandler.d.ts +84 -22
  92. package/lib/connectionStateHandler.d.ts.map +1 -1
  93. package/lib/connectionStateHandler.js +171 -59
  94. package/lib/connectionStateHandler.js.map +1 -1
  95. package/lib/container.d.ts +29 -17
  96. package/lib/container.d.ts.map +1 -1
  97. package/lib/container.js +184 -174
  98. package/lib/container.js.map +1 -1
  99. package/lib/containerContext.d.ts +18 -7
  100. package/lib/containerContext.d.ts.map +1 -1
  101. package/lib/containerContext.js +19 -9
  102. package/lib/containerContext.js.map +1 -1
  103. package/lib/containerStorageAdapter.d.ts +11 -25
  104. package/lib/containerStorageAdapter.d.ts.map +1 -1
  105. package/lib/containerStorageAdapter.js +51 -16
  106. package/lib/containerStorageAdapter.js.map +1 -1
  107. package/lib/contracts.d.ts +5 -5
  108. package/lib/contracts.js.map +1 -1
  109. package/lib/deltaManager.d.ts +4 -1
  110. package/lib/deltaManager.d.ts.map +1 -1
  111. package/lib/deltaManager.js +41 -14
  112. package/lib/deltaManager.js.map +1 -1
  113. package/lib/deltaManagerProxy.d.ts +4 -1
  114. package/lib/deltaManagerProxy.d.ts.map +1 -1
  115. package/lib/deltaQueue.d.ts +9 -2
  116. package/lib/deltaQueue.d.ts.map +1 -1
  117. package/lib/deltaQueue.js +32 -27
  118. package/lib/deltaQueue.js.map +1 -1
  119. package/lib/index.d.ts +1 -0
  120. package/lib/index.d.ts.map +1 -1
  121. package/lib/index.js.map +1 -1
  122. package/lib/loader.d.ts +8 -1
  123. package/lib/loader.d.ts.map +1 -1
  124. package/lib/loader.js +4 -3
  125. package/lib/loader.js.map +1 -1
  126. package/lib/packageVersion.d.ts +1 -1
  127. package/lib/packageVersion.d.ts.map +1 -1
  128. package/lib/packageVersion.js +1 -1
  129. package/lib/packageVersion.js.map +1 -1
  130. package/lib/protocol.d.ts +27 -0
  131. package/lib/protocol.d.ts.map +1 -0
  132. package/lib/protocol.js +75 -0
  133. package/lib/protocol.js.map +1 -0
  134. package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
  135. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  136. package/lib/retriableDocumentStorageService.d.ts +2 -2
  137. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  138. package/lib/retriableDocumentStorageService.js +2 -2
  139. package/lib/retriableDocumentStorageService.js.map +1 -1
  140. package/package.json +27 -19
  141. package/src/audience.ts +8 -14
  142. package/src/catchUpMonitor.ts +59 -0
  143. package/src/collabWindowTracker.ts +15 -6
  144. package/src/connectionManager.ts +56 -33
  145. package/src/connectionState.ts +0 -6
  146. package/src/connectionStateHandler.ts +235 -70
  147. package/src/container.ts +241 -218
  148. package/src/containerContext.ts +22 -8
  149. package/src/containerStorageAdapter.ts +71 -16
  150. package/src/contracts.ts +7 -7
  151. package/src/deltaManager.ts +48 -15
  152. package/src/deltaQueue.ts +34 -28
  153. package/src/index.ts +4 -0
  154. package/src/loader.ts +14 -3
  155. package/src/packageVersion.ts +1 -1
  156. package/src/protocol.ts +120 -0
  157. package/src/retriableDocumentStorageService.ts +8 -2
@@ -1 +1 @@
1
- {"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport {\n IDocumentStorageService,\n IDocumentStorageServicePolicies,\n ISummaryContext,\n} from \"@fluidframework/driver-definitions\";\nimport {\n ICreateBlobResponse,\n ISnapshotTree,\n ISummaryHandle,\n ISummaryTree,\n IVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { runWithRetry } from \"@fluidframework/driver-utils\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n private _disposed = false;\n constructor(\n private readonly internalStorageService: IDocumentStorageService,\n private readonly logger: ITelemetryLogger,\n ) {\n }\n\n public get policies(): IDocumentStorageServicePolicies | undefined {\n return this.internalStorageService.policies;\n }\n public get disposed() { return this._disposed; }\n public dispose() {\n this._disposed = true;\n }\n\n public get repositoryUrl(): string {\n return this.internalStorageService.repositoryUrl;\n }\n\n public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {\n return this.runWithRetry(\n async () => this.internalStorageService.getSnapshotTree(version, scenarioName),\n \"storage_getSnapshotTree\",\n );\n }\n\n public async readBlob(id: string): Promise<ArrayBufferLike> {\n return this.runWithRetry(\n async () => this.internalStorageService.readBlob(id),\n \"storage_readBlob\",\n );\n }\n\n public async getVersions(versionId: string | null, count: number, scenarioName?: string): Promise<IVersion[]> {\n return this.runWithRetry(\n async () => this.internalStorageService.getVersions(versionId, count, scenarioName),\n \"storage_getVersions\",\n );\n }\n\n public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {\n // Not using retry loop here. Couple reasons:\n // 1. If client lost connectivity, then retry loop will result in uploading stale summary\n // by stale summarizer after connectivity comes back. It will cause failures for this client and for\n // real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n // on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n // 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n // upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n // retryAfter into account!\n // But retry loop is required for creation flow (Container.attach)\n assert((context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n 0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */);\n if (context.referenceSequenceNumber !== 0) {\n return this.internalStorageService.uploadSummaryWithContext(summary, context);\n }\n\n // Creation flow with attachment blobs - need to do retries!\n return this.runWithRetry(\n async () => this.internalStorageService.uploadSummaryWithContext(summary, context),\n \"storage_uploadSummaryWithContext\",\n );\n }\n\n public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n return this.runWithRetry(\n async () => this.internalStorageService.downloadSummary(handle),\n \"storage_downloadSummary\",\n );\n }\n\n public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n return this.runWithRetry(\n async () => this.internalStorageService.createBlob(file),\n \"storage_createBlob\",\n );\n }\n\n private checkStorageDisposed() {\n if (this._disposed) {\n // pre-0.58 error message: storageServiceDisposedCannotRetry\n throw new GenericError(\"Storage Service is disposed. Cannot retry\", { canRetry: false });\n }\n return undefined;\n }\n\n private async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n return runWithRetry(\n api,\n callName,\n this.logger,\n {\n onRetry: () => this.checkStorageDisposed(),\n },\n );\n }\n}\n"]}
1
+ {"version":3,"file":"retriableDocumentStorageService.js","sourceRoot":"","sources":["../src/retriableDocumentStorageService.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport { GenericError } from \"@fluidframework/container-utils\";\nimport {\n FetchSource,\n IDocumentStorageService,\n IDocumentStorageServicePolicies,\n ISummaryContext,\n} from \"@fluidframework/driver-definitions\";\nimport {\n ICreateBlobResponse,\n ISnapshotTree,\n ISummaryHandle,\n ISummaryTree,\n IVersion,\n} from \"@fluidframework/protocol-definitions\";\nimport { IDisposable, ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { runWithRetry } from \"@fluidframework/driver-utils\";\n\nexport class RetriableDocumentStorageService implements IDocumentStorageService, IDisposable {\n private _disposed = false;\n constructor(\n private readonly internalStorageService: IDocumentStorageService,\n private readonly logger: ITelemetryLogger,\n ) {\n }\n\n public get policies(): IDocumentStorageServicePolicies | undefined {\n return this.internalStorageService.policies;\n }\n public get disposed() { return this._disposed; }\n public dispose() {\n this._disposed = true;\n }\n\n public get repositoryUrl(): string {\n return this.internalStorageService.repositoryUrl;\n }\n\n public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {\n return this.runWithRetry(\n async () => this.internalStorageService.getSnapshotTree(version, scenarioName),\n \"storage_getSnapshotTree\",\n );\n }\n\n public async readBlob(id: string): Promise<ArrayBufferLike> {\n return this.runWithRetry(\n async () => this.internalStorageService.readBlob(id),\n \"storage_readBlob\",\n );\n }\n\n public async getVersions(\n versionId: string | null,\n count: number,\n scenarioName?: string,\n fetchSource?: FetchSource,\n ): Promise<IVersion[]> {\n return this.runWithRetry(\n async () => this.internalStorageService.getVersions(versionId, count, scenarioName, fetchSource),\n \"storage_getVersions\",\n );\n }\n\n public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {\n // Not using retry loop here. Couple reasons:\n // 1. If client lost connectivity, then retry loop will result in uploading stale summary\n // by stale summarizer after connectivity comes back. It will cause failures for this client and for\n // real (new) summarizer. This problem in particular should be solved in future by supplying abort handle\n // on all APIs and caller (ContainerRuntime.submitSummary) aborting call on loss of connectivity\n // 2. Similar, if we get 429 with retryAfter = 10 minutes, it's likely not the right call to retry summary\n // upload in 10 minutes - it's better to keep processing ops and retry later. Though caller needs to take\n // retryAfter into account!\n // But retry loop is required for creation flow (Container.attach)\n assert((context.referenceSequenceNumber === 0) === (context.ackHandle === undefined),\n 0x251 /* \"creation summary has to have seq=0 && handle === undefined\" */);\n if (context.referenceSequenceNumber !== 0) {\n return this.internalStorageService.uploadSummaryWithContext(summary, context);\n }\n\n // Creation flow with attachment blobs - need to do retries!\n return this.runWithRetry(\n async () => this.internalStorageService.uploadSummaryWithContext(summary, context),\n \"storage_uploadSummaryWithContext\",\n );\n }\n\n public async downloadSummary(handle: ISummaryHandle): Promise<ISummaryTree> {\n return this.runWithRetry(\n async () => this.internalStorageService.downloadSummary(handle),\n \"storage_downloadSummary\",\n );\n }\n\n public async createBlob(file: ArrayBufferLike): Promise<ICreateBlobResponse> {\n return this.runWithRetry(\n async () => this.internalStorageService.createBlob(file),\n \"storage_createBlob\",\n );\n }\n\n private checkStorageDisposed() {\n if (this._disposed) {\n // pre-0.58 error message: storageServiceDisposedCannotRetry\n throw new GenericError(\"Storage Service is disposed. Cannot retry\", { canRetry: false });\n }\n return undefined;\n }\n\n private async runWithRetry<T>(api: () => Promise<T>, callName: string): Promise<T> {\n return runWithRetry(\n api,\n callName,\n this.logger,\n {\n onRetry: () => this.checkStorageDisposed(),\n },\n );\n }\n}\n"]}
package/lib/audience.d.ts CHANGED
@@ -4,12 +4,12 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  import { EventEmitter } from "events";
7
- import { IAudience } from "@fluidframework/container-definitions";
7
+ import { IAudienceOwner } from "@fluidframework/container-definitions";
8
8
  import { IClient } from "@fluidframework/protocol-definitions";
9
9
  /**
10
10
  * Audience represents all clients connected to the op stream.
11
11
  */
12
- export declare class Audience extends EventEmitter implements IAudience {
12
+ export declare class Audience extends EventEmitter implements IAudienceOwner {
13
13
  private readonly members;
14
14
  on(event: "addMember" | "removeMember", listener: (clientId: string, client: IClient) => void): this;
15
15
  /**
@@ -29,9 +29,5 @@ export declare class Audience extends EventEmitter implements IAudience {
29
29
  * Retrieves a specific member of the audience
30
30
  */
31
31
  getMember(clientId: string): IClient | undefined;
32
- /**
33
- * Clears the audience
34
- */
35
- clear(): void;
36
32
  }
37
33
  //# sourceMappingURL=audience.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audience.d.ts","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAE/D;;GAEG;AACH,qBAAa,QAAS,SAAQ,YAAa,YAAW,SAAS;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAE/C,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK3G;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAKnD;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAW9C;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAIzC;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIvD;;OAEG;IACI,KAAK,IAAI,IAAI;CAMvB"}
1
+ {"version":3,"file":"audience.d.ts","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAE/D;;GAEG;AACH,qBAAa,QAAS,SAAQ,YAAa,YAAW,cAAc;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAE/C,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK3G;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IASnD;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAW9C;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAIzC;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;CAG1D"}
package/lib/audience.js CHANGED
@@ -18,8 +18,12 @@ export class Audience extends EventEmitter {
18
18
  * Adds a new client to the audience
19
19
  */
20
20
  addMember(clientId, details) {
21
- this.members.set(clientId, details);
22
- this.emit("addMember", clientId, details);
21
+ // Given that signal delivery is unreliable process, we might observe same client being added twice
22
+ // In such case we should see exactly same payload (IClient), and should not raise event twice!
23
+ if (!this.members.has(clientId)) {
24
+ this.members.set(clientId, details);
25
+ this.emit("addMember", clientId, details);
26
+ }
23
27
  }
24
28
  /**
25
29
  * Removes a client from the audience. Only emits an event if a client is actually removed
@@ -48,14 +52,5 @@ export class Audience extends EventEmitter {
48
52
  getMember(clientId) {
49
53
  return this.members.get(clientId);
50
54
  }
51
- /**
52
- * Clears the audience
53
- */
54
- clear() {
55
- const clientIds = this.members.keys();
56
- for (const clientId of clientIds) {
57
- this.removeMember(clientId);
58
- }
59
- }
60
55
  }
61
56
  //# sourceMappingURL=audience.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audience.js","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,YAAY;IAA1C;;QACqB,YAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IAqD1D,CAAC;IAlDU,EAAE,CAAC,KAAa,EAAE,QAAkC;QACvD,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB,EAAE,OAAgB;QAC/C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACI,YAAY,CAAC,QAAgB;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;SACf;aAAM;YACH,OAAO,KAAK,CAAC;SAChB;IACL,CAAC;IAED;;OAEG;IACI,UAAU;QACb,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACI,KAAK;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACtC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;YAC9B,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;SAC/B;IACL,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { EventEmitter } from \"events\";\nimport { IAudience } from \"@fluidframework/container-definitions\";\nimport { IClient } from \"@fluidframework/protocol-definitions\";\n\n/**\n * Audience represents all clients connected to the op stream.\n */\nexport class Audience extends EventEmitter implements IAudience {\n private readonly members = new Map<string, IClient>();\n\n public on(event: \"addMember\" | \"removeMember\", listener: (clientId: string, client: IClient) => void): this;\n public on(event: string, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /**\n * Adds a new client to the audience\n */\n public addMember(clientId: string, details: IClient) {\n this.members.set(clientId, details);\n this.emit(\"addMember\", clientId, details);\n }\n\n /**\n * Removes a client from the audience. Only emits an event if a client is actually removed\n * @returns if a client was removed from the audience\n */\n public removeMember(clientId: string): boolean {\n const removedClient = this.members.get(clientId);\n if (removedClient !== undefined) {\n this.members.delete(clientId);\n this.emit(\"removeMember\", clientId, removedClient);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * Retrieves all the members in the audience\n */\n public getMembers(): Map<string, IClient> {\n return new Map(this.members);\n }\n\n /**\n * Retrieves a specific member of the audience\n */\n public getMember(clientId: string): IClient | undefined {\n return this.members.get(clientId);\n }\n\n /**\n * Clears the audience\n */\n public clear(): void {\n const clientIds = this.members.keys();\n for (const clientId of clientIds) {\n this.removeMember(clientId);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"audience.js","sourceRoot":"","sources":["../src/audience.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,YAAY;IAA1C;;QACqB,YAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;IA+C1D,CAAC;IA5CU,EAAE,CAAC,KAAa,EAAE,QAAkC;QACvD,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB,EAAE,OAAgB;QAC/C,mGAAmG;QACnG,+FAA+F;QAC/F,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;SAC7C;IACL,CAAC;IAED;;;OAGG;IACI,YAAY,CAAC,QAAgB;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC;SACf;aAAM;YACH,OAAO,KAAK,CAAC;SAChB;IACL,CAAC;IAED;;OAEG;IACI,UAAU;QACb,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { EventEmitter } from \"events\";\nimport { IAudienceOwner } from \"@fluidframework/container-definitions\";\nimport { IClient } from \"@fluidframework/protocol-definitions\";\n\n/**\n * Audience represents all clients connected to the op stream.\n */\nexport class Audience extends EventEmitter implements IAudienceOwner {\n private readonly members = new Map<string, IClient>();\n\n public on(event: \"addMember\" | \"removeMember\", listener: (clientId: string, client: IClient) => void): this;\n public on(event: string, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /**\n * Adds a new client to the audience\n */\n public addMember(clientId: string, details: IClient) {\n // Given that signal delivery is unreliable process, we might observe same client being added twice\n // In such case we should see exactly same payload (IClient), and should not raise event twice!\n if (!this.members.has(clientId)) {\n this.members.set(clientId, details);\n this.emit(\"addMember\", clientId, details);\n }\n }\n\n /**\n * Removes a client from the audience. Only emits an event if a client is actually removed\n * @returns if a client was removed from the audience\n */\n public removeMember(clientId: string): boolean {\n const removedClient = this.members.get(clientId);\n if (removedClient !== undefined) {\n this.members.delete(clientId);\n this.emit(\"removeMember\", clientId, removedClient);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * Retrieves all the members in the audience\n */\n public getMembers(): Map<string, IClient> {\n return new Map(this.members);\n }\n\n /**\n * Retrieves a specific member of the audience\n */\n public getMember(clientId: string): IClient | undefined {\n return this.members.get(clientId);\n }\n}\n"]}
@@ -0,0 +1,29 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { IDisposable } from "@fluidframework/common-definitions";
6
+ import { IDeltaManager } from "@fluidframework/container-definitions";
7
+ /** @see CatchUpMonitor for usage */
8
+ declare type CaughtUpListener = () => void;
9
+ /** Monitor that emits an event when a Container has caught up to a given point in the op stream */
10
+ export declare type ICatchUpMonitor = IDisposable;
11
+ /**
12
+ * Monitors a Container's DeltaManager, notifying listeners when all ops have been processed
13
+ * that were known at the time the monitor was created.
14
+ */
15
+ export declare class CatchUpMonitor implements ICatchUpMonitor {
16
+ private readonly deltaManager;
17
+ private readonly listener;
18
+ private readonly targetSeqNumber;
19
+ private caughtUp;
20
+ private readonly opHandler;
21
+ /**
22
+ * Create the CatchUpMonitor, setting the target sequence number to wait for based on DeltaManager's current state.
23
+ */
24
+ constructor(deltaManager: IDeltaManager<any, any>, listener: CaughtUpListener);
25
+ disposed: boolean;
26
+ dispose(): void;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=catchUpMonitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catchUpMonitor.d.ts","sourceRoot":"","sources":["../src/catchUpMonitor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAGtE,oCAAoC;AACpC,aAAK,gBAAgB,GAAG,MAAM,IAAI,CAAC;AAEnC,mGAAmG;AACnG,oBAAY,eAAe,GAAG,WAAW,CAAC;AAE1C;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IAe9C,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAf7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAkB;IAElC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKxB;IAEF;;OAEG;gBAEkB,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EACrC,QAAQ,EAAE,gBAAgB;IAaxC,QAAQ,EAAE,OAAO,CAAS;IAC1B,OAAO;CAQjB"}
@@ -0,0 +1,39 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { assert } from "@fluidframework/common-utils";
6
+ /**
7
+ * Monitors a Container's DeltaManager, notifying listeners when all ops have been processed
8
+ * that were known at the time the monitor was created.
9
+ */
10
+ export class CatchUpMonitor {
11
+ /**
12
+ * Create the CatchUpMonitor, setting the target sequence number to wait for based on DeltaManager's current state.
13
+ */
14
+ constructor(deltaManager, listener) {
15
+ this.deltaManager = deltaManager;
16
+ this.listener = listener;
17
+ this.caughtUp = false;
18
+ this.opHandler = (message) => {
19
+ if (!this.caughtUp && message.sequenceNumber >= this.targetSeqNumber) {
20
+ this.caughtUp = true;
21
+ this.listener();
22
+ }
23
+ };
24
+ this.disposed = false;
25
+ this.targetSeqNumber = this.deltaManager.lastKnownSeqNumber;
26
+ assert(this.targetSeqNumber >= this.deltaManager.lastSequenceNumber, 0x37c /* Cannot wait for seqNumber below last processed sequence number */);
27
+ this.deltaManager.on("op", this.opHandler);
28
+ // Simulate the last processed op to set caughtUp in case we already are
29
+ this.opHandler({ sequenceNumber: this.deltaManager.lastSequenceNumber });
30
+ }
31
+ dispose() {
32
+ if (this.disposed) {
33
+ return;
34
+ }
35
+ this.disposed = true;
36
+ this.deltaManager.off("op", this.opHandler);
37
+ }
38
+ }
39
+ //# sourceMappingURL=catchUpMonitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catchUpMonitor.js","sourceRoot":"","sources":["../src/catchUpMonitor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAUtD;;;GAGG;AACH,MAAM,OAAO,cAAc;IAWvB;;OAEG;IACH,YACqB,YAAqC,EACrC,QAA0B;QAD1B,iBAAY,GAAZ,YAAY,CAAyB;QACrC,aAAQ,GAAR,QAAQ,CAAkB;QAdvC,aAAQ,GAAY,KAAK,CAAC;QAEjB,cAAS,GAAG,CAAC,OAA0D,EAAE,EAAE;YACxF,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,eAAe,EAAE;gBAClE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,QAAQ,EAAE,CAAC;aACnB;QACL,CAAC,CAAC;QAoBK,aAAQ,GAAY,KAAK,CAAC;QAX7B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC;QAE5D,MAAM,CAAC,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAC/D,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAEhF,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAE3C,wEAAwE;QACxE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC7E,CAAC;IAGM,OAAO;QACV,IAAI,IAAI,CAAC,QAAQ,EAAE;YACf,OAAO;SACV;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\n\n/** @see CatchUpMonitor for usage */\ntype CaughtUpListener = () => void;\n\n/** Monitor that emits an event when a Container has caught up to a given point in the op stream */\nexport type ICatchUpMonitor = IDisposable;\n\n/**\n * Monitors a Container's DeltaManager, notifying listeners when all ops have been processed\n * that were known at the time the monitor was created.\n */\nexport class CatchUpMonitor implements ICatchUpMonitor {\n private readonly targetSeqNumber: number;\n private caughtUp: boolean = false;\n\n private readonly opHandler = (message: Pick<ISequencedDocumentMessage, \"sequenceNumber\">) => {\n if (!this.caughtUp && message.sequenceNumber >= this.targetSeqNumber) {\n this.caughtUp = true;\n this.listener();\n }\n };\n\n /**\n * Create the CatchUpMonitor, setting the target sequence number to wait for based on DeltaManager's current state.\n */\n constructor(\n private readonly deltaManager: IDeltaManager<any, any>,\n private readonly listener: CaughtUpListener,\n ) {\n this.targetSeqNumber = this.deltaManager.lastKnownSeqNumber;\n\n assert(this.targetSeqNumber >= this.deltaManager.lastSequenceNumber,\n 0x37c /* Cannot wait for seqNumber below last processed sequence number */);\n\n this.deltaManager.on(\"op\", this.opHandler);\n\n // Simulate the last processed op to set caughtUp in case we already are\n this.opHandler({ sequenceNumber: this.deltaManager.lastSequenceNumber });\n }\n\n public disposed: boolean = false;\n public dispose() {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n\n this.deltaManager.off(\"op\", this.opHandler);\n }\n}\n"]}
@@ -8,7 +8,7 @@ export declare class CollabWindowTracker {
8
8
  private readonly NoopCountFrequency;
9
9
  private opsCountSinceNoop;
10
10
  private readonly timer;
11
- constructor(submit: (type: MessageType, contents: any) => void, NoopTimeFrequency?: number, NoopCountFrequency?: number);
11
+ constructor(submit: (type: MessageType) => void, NoopTimeFrequency?: number, NoopCountFrequency?: number);
12
12
  /**
13
13
  * Schedules as ack to the server to update the reference sequence number
14
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"collabWindowTracker.d.ts","sourceRoot":"","sources":["../src/collabWindowTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAyB9F,qBAAa,mBAAmB;IAKxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IANvC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAGrB,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,EACnE,iBAAiB,GAAE,MAAiC,EACnC,kBAAkB,GAAE,MAAkC;IAa3E;;OAEG;IACI,4BAA4B,CAAC,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IA+BrG,OAAO,CAAC,UAAU;IAOX,wBAAwB,IAAI,IAAI;CAS1C"}
1
+ {"version":3,"file":"collabWindowTracker.d.ts","sourceRoot":"","sources":["../src/collabWindowTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAyB9F,qBAAa,mBAAmB;IAKxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IANvC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAGrB,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,EACpD,iBAAiB,GAAE,MAAiC,EACnC,kBAAkB,GAAE,MAAkC;IAa3E;;OAEG;IACI,4BAA4B,CAAC,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,OAAO,GAAG,IAAI;IAuCrG,OAAO,CAAC,UAAU;IAQX,wBAAwB,IAAI,IAAI;CAS1C"}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { assert, Timer } from "@fluidframework/common-utils";
6
6
  import { MessageType } from "@fluidframework/protocol-definitions";
7
- import { isRuntimeMessage } from "@fluidframework/driver-utils";
7
+ import { isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
8
8
  const defaultNoopTimeFrequency = 2000;
9
9
  const defaultNoopCountFrequency = 50;
10
10
  // Here are key considerations when deciding conditions for when to send non-immediate noops:
@@ -59,9 +59,16 @@ export class CollabWindowTracker {
59
59
  return;
60
60
  }
61
61
  this.opsCountSinceNoop++;
62
- if (this.opsCountSinceNoop >= this.NoopCountFrequency) {
63
- this.submitNoop(false /* immediate */);
64
- return;
62
+ if (this.opsCountSinceNoop === this.NoopCountFrequency) {
63
+ // Ensure we only send noop after a batch of many ops is processed
64
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
65
+ Promise.resolve().then(() => {
66
+ assert(this.opsCountSinceNoop >= this.NoopCountFrequency, 0x3ae /* not enough ops were sent to reach the noop frequency */);
67
+ this.submitNoop(false /* immediate */);
68
+ // reset count now that all ops are processed
69
+ this.opsCountSinceNoop = 0;
70
+ return;
71
+ });
65
72
  }
66
73
  if (this.timer !== undefined) {
67
74
  if (this.opsCountSinceNoop === 1) {
@@ -72,7 +79,8 @@ export class CollabWindowTracker {
72
79
  }
73
80
  submitNoop(immediate) {
74
81
  // Anything other than null is immediate noop
75
- this.submit(MessageType.NoOp, immediate ? "" : null);
82
+ // ADO:1385: Remove cast and use MessageType once definition changes propagate
83
+ this.submit(immediate ? MessageType2.Accept : MessageType.NoOp);
76
84
  assert(this.opsCountSinceNoop === 0, 0x243 /* "stopSequenceNumberUpdate should be called as result of sending any op!" */);
77
85
  }
78
86
  stopSequenceNumberUpdate() {
@@ -1 +1 @@
1
- {"version":3,"file":"collabWindowTracker.js","sourceRoot":"","sources":["../src/collabWindowTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAA6B,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAC9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAErC,6FAA6F;AAC7F,4GAA4G;AAC5G,yGAAyG;AACzG,2CAA2C;AAC3C,oHAAoH;AACpH,2FAA2F;AAC3F,kHAAkH;AAClH,+CAA+C;AAC/C,gHAAgH;AAChH,yFAAyF;AACzF,qHAAqH;AACrH,oDAAoD;AACpD,EAAE;AACF,kDAAkD;AAClD,oGAAoG;AACpG,iHAAiH;AACjH,sEAAsE;AACtE,4GAA4G;AAC5G,qGAAqG;AACrG,MAAM,OAAO,mBAAmB;IAI5B,YACqB,MAAkD,EACnE,oBAA4B,wBAAwB,EACnC,qBAA6B,yBAAyB;QAFtD,WAAM,GAAN,MAAM,CAA4C;QAElD,uBAAkB,GAAlB,kBAAkB,CAAoC;QANnE,sBAAiB,GAAG,CAAC,CAAC;QAQ1B,IAAI,iBAAiB,KAAK,QAAQ,EAAE;YAChC,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,EAAE,GAAG,EAAE;gBAC3C,2EAA2E;gBAC3E,mGAAmG;gBACnG,IAAI,IAAI,CAAC,iBAAiB,KAAK,CAAC,EAAE;oBAC9B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;iBAC1C;YACL,CAAC,CAAC,CAAC;SACN;IACL,CAAC;IAED;;OAEG;IACI,4BAA4B,CAAC,OAAkC,EAAE,aAAsB;QAC1F,mEAAmE;QACnE,sDAAsD;QACtD,IAAI,aAAa,EAAE;YACf,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACtC,OAAO;SACV;QAED,gFAAgF;QAChF,+DAA+D;QAC/D,sFAAsF;QACtF,yCAAyC;QACzC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE;YAC5B,OAAO;SACV;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,kBAAkB,EAAE;YACnD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACvC,OAAO;SACV;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;YAC1B,IAAI,IAAI,CAAC,iBAAiB,KAAK,CAAC,EAAE;gBAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;aACxB;YAED,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;SACxD;IACL,CAAC;IAEO,UAAU,CAAC,SAAkB;QACjC,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,iBAAiB,KAAK,CAAC,EAC/B,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAC9F,CAAC;IAEM,wBAAwB;QAC3B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QAC3B,kGAAkG;QAClG,wGAAwG;QACxG,oDAAoD;QACpD,qGAAqG;QACrG,yFAAyF;QACzF,sBAAsB;IAC1B,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils\";\n\nconst defaultNoopTimeFrequency = 2000;\nconst defaultNoopCountFrequency = 50;\n\n// Here are key considerations when deciding conditions for when to send non-immediate noops:\n// 1. Sending them too often results in increase in file size and bandwidth, as well as catch up performance\n// 2. Sending too infrequently ensures that collab window is large, and as result Sequence DDS would have\n// large catchUp blobs - see Issue #6364\n// 3. Similarly, processes that rely on \"core\" snapshot (and can't parse trailing ops, including above), like search\n// parser in SPO, will result in non-accurate results due to presence of catch up blobs.\n// 4. Ordering service used 250ms timeout to coalesce non-immediate noops. It was changed to 2000 ms to allow more\n// aggressive noop sending from client side.\n// 5. Number of ops sent by all clients is proportional to number of \"write\" clients (every client sends noops),\n// but number of sequenced noops is a function of time (one op per 2 seconds at most).\n// We should consider impact to both outbound traffic (might be huge, depends on number of clients) and file size.\n// Please also see Issue #5629 for more discussions.\n//\n// With that, the current algorithm is as follows:\n// 1. Sent noop 2000 ms of receiving an op if no ops were sent by this client within this timeframe.\n// This will ensure that MSN moves forward with reasonable speed. If that results in too many sequenced noops,\n// server timeout of 2000ms should be reconsidered to be increased.\n// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.\n// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.\nexport class CollabWindowTracker {\n private opsCountSinceNoop = 0;\n private readonly timer: Timer | undefined;\n\n constructor(\n private readonly submit: (type: MessageType, contents: any) => void,\n NoopTimeFrequency: number = defaultNoopTimeFrequency,\n private readonly NoopCountFrequency: number = defaultNoopCountFrequency,\n ) {\n if (NoopTimeFrequency !== Infinity) {\n this.timer = new Timer(NoopTimeFrequency, () => {\n // Can get here due to this.stopSequenceNumberUpdate() not resetting timer.\n // Also timer callback can fire even after timer cancellation if it was queued before cancellation.\n if (this.opsCountSinceNoop !== 0) {\n this.submitNoop(false /* immediate */);\n }\n });\n }\n }\n\n /**\n * Schedules as ack to the server to update the reference sequence number\n */\n public scheduleSequenceNumberUpdate(message: ISequencedDocumentMessage, immediateNoOp: boolean): void {\n // While processing a message, an immediate no-op can be requested.\n // i.e. to expedite approve or commit phase of quorum.\n if (immediateNoOp) {\n this.submitNoop(true /* immediate */);\n return;\n }\n\n // We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN\n // update, which updates the MSN, then ack the update, etc...).\n // Intent here is for runtime (and DDSes) not to keep too much tracking state / memory\n // due to runtime ops from other clients.\n if (!isRuntimeMessage(message)) {\n return;\n }\n\n this.opsCountSinceNoop++;\n if (this.opsCountSinceNoop >= this.NoopCountFrequency) {\n this.submitNoop(false /* immediate */);\n return;\n }\n\n if (this.timer !== undefined) {\n if (this.opsCountSinceNoop === 1) {\n this.timer.restart();\n }\n\n assert(this.timer.hasTimer, 0x242 /* \"has timer\" */);\n }\n }\n\n private submitNoop(immediate: boolean) {\n // Anything other than null is immediate noop\n this.submit(MessageType.NoOp, immediate ? \"\" : null);\n assert(this.opsCountSinceNoop === 0,\n 0x243 /* \"stopSequenceNumberUpdate should be called as result of sending any op!\" */);\n }\n\n public stopSequenceNumberUpdate(): void {\n this.opsCountSinceNoop = 0;\n // Ideally, we cancel timer here. But that will result in too often set/reset cycle if this client\n // keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)\n // for nothing, then have a ton of set/reset cycles.\n // Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer\n // expiration - it will restart the timer instead when it fires with adjusted expiration.\n // this.timer.clear();\n }\n}\n"]}
1
+ {"version":3,"file":"collabWindowTracker.js","sourceRoot":"","sources":["../src/collabWindowTracker.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert, Timer } from \"@fluidframework/common-utils\";\nimport { ISequencedDocumentMessage, MessageType } from \"@fluidframework/protocol-definitions\";\nimport { isRuntimeMessage, MessageType2 } from \"@fluidframework/driver-utils\";\n\nconst defaultNoopTimeFrequency = 2000;\nconst defaultNoopCountFrequency = 50;\n\n// Here are key considerations when deciding conditions for when to send non-immediate noops:\n// 1. Sending them too often results in increase in file size and bandwidth, as well as catch up performance\n// 2. Sending too infrequently ensures that collab window is large, and as result Sequence DDS would have\n// large catchUp blobs - see Issue #6364\n// 3. Similarly, processes that rely on \"core\" snapshot (and can't parse trailing ops, including above), like search\n// parser in SPO, will result in non-accurate results due to presence of catch up blobs.\n// 4. Ordering service used 250ms timeout to coalesce non-immediate noops. It was changed to 2000 ms to allow more\n// aggressive noop sending from client side.\n// 5. Number of ops sent by all clients is proportional to number of \"write\" clients (every client sends noops),\n// but number of sequenced noops is a function of time (one op per 2 seconds at most).\n// We should consider impact to both outbound traffic (might be huge, depends on number of clients) and file size.\n// Please also see Issue #5629 for more discussions.\n//\n// With that, the current algorithm is as follows:\n// 1. Sent noop 2000 ms of receiving an op if no ops were sent by this client within this timeframe.\n// This will ensure that MSN moves forward with reasonable speed. If that results in too many sequenced noops,\n// server timeout of 2000ms should be reconsidered to be increased.\n// 2. If there are more than 50 ops received without sending any ops, send noop to keep collab window small.\n// Note that system ops (including noops themselves) are excluded, so it's 1 noop per 50 real ops.\nexport class CollabWindowTracker {\n private opsCountSinceNoop = 0;\n private readonly timer: Timer | undefined;\n\n constructor(\n private readonly submit: (type: MessageType) => void,\n NoopTimeFrequency: number = defaultNoopTimeFrequency,\n private readonly NoopCountFrequency: number = defaultNoopCountFrequency,\n ) {\n if (NoopTimeFrequency !== Infinity) {\n this.timer = new Timer(NoopTimeFrequency, () => {\n // Can get here due to this.stopSequenceNumberUpdate() not resetting timer.\n // Also timer callback can fire even after timer cancellation if it was queued before cancellation.\n if (this.opsCountSinceNoop !== 0) {\n this.submitNoop(false /* immediate */);\n }\n });\n }\n }\n\n /**\n * Schedules as ack to the server to update the reference sequence number\n */\n public scheduleSequenceNumberUpdate(message: ISequencedDocumentMessage, immediateNoOp: boolean): void {\n // While processing a message, an immediate no-op can be requested.\n // i.e. to expedite approve or commit phase of quorum.\n if (immediateNoOp) {\n this.submitNoop(true /* immediate */);\n return;\n }\n\n // We don't acknowledge no-ops to avoid acknowledgement cycles (i.e. ack the MSN\n // update, which updates the MSN, then ack the update, etc...).\n // Intent here is for runtime (and DDSes) not to keep too much tracking state / memory\n // due to runtime ops from other clients.\n if (!isRuntimeMessage(message)) {\n return;\n }\n\n this.opsCountSinceNoop++;\n if (this.opsCountSinceNoop === this.NoopCountFrequency) {\n // Ensure we only send noop after a batch of many ops is processed\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n Promise.resolve().then(() => {\n assert(this.opsCountSinceNoop >= this.NoopCountFrequency,\n 0x3ae /* not enough ops were sent to reach the noop frequency */);\n this.submitNoop(false /* immediate */);\n // reset count now that all ops are processed\n this.opsCountSinceNoop = 0;\n return;\n });\n }\n\n if (this.timer !== undefined) {\n if (this.opsCountSinceNoop === 1) {\n this.timer.restart();\n }\n\n assert(this.timer.hasTimer, 0x242 /* \"has timer\" */);\n }\n }\n\n private submitNoop(immediate: boolean) {\n // Anything other than null is immediate noop\n // ADO:1385: Remove cast and use MessageType once definition changes propagate\n this.submit(immediate ? (MessageType2.Accept as unknown as MessageType) : MessageType.NoOp);\n assert(this.opsCountSinceNoop === 0,\n 0x243 /* \"stopSequenceNumberUpdate should be called as result of sending any op!\" */);\n }\n\n public stopSequenceNumberUpdate(): void {\n this.opsCountSinceNoop = 0;\n // Ideally, we cancel timer here. But that will result in too often set/reset cycle if this client\n // keeps sending ops. In most cases it's actually better to let it expire (at most - 4 times per second)\n // for nothing, then have a ton of set/reset cycles.\n // Note that Timer.restart() is smart and will not change timer expiration if we keep extending timer\n // expiration - it will restart the timer instead when it fires with adjusted expiration.\n // this.timer.clear();\n }\n}\n"]}
@@ -9,8 +9,8 @@ import { ConnectionMode, IClient, IClientConfiguration, IClientDetails, IDocumen
9
9
  import { ReconnectMode, IConnectionManager, IConnectionManagerFactoryArgs } from "./contracts";
10
10
  /**
11
11
  * Implementation of IConnectionManager, used by Container class
12
- * Implements constant connectivity to relay service, by reconnecting in case of loast connection or error.
13
- * Exposes various controls to influecen this process, including manual reconnects, forced read-only mode, etc.
12
+ * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
13
+ * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
14
14
  */
15
15
  export declare class ConnectionManager implements IConnectionManager {
16
16
  private readonly serviceProvider;
@@ -39,7 +39,7 @@ export declare class ConnectionManager implements IConnectionManager {
39
39
  private clientSequenceNumber;
40
40
  private clientSequenceNumberObserved;
41
41
  /** Counts the number of noops sent by the client which may not be acked. */
42
- private trailingNoopCount;
42
+ private localOpsToIgnore;
43
43
  /** track clientId used last time when we sent any ops */
44
44
  private lastSubmittedClientId;
45
45
  private connectFirstConnection;
@@ -112,8 +112,8 @@ export declare class ConnectionManager implements IConnectionManager {
112
112
  connect(connectionMode?: ConnectionMode): void;
113
113
  private connectCore;
114
114
  /**
115
- * Start the connection. Any error should result in container being close.
116
- * And report the error if it excape for any reason.
115
+ * Start the connection. Any error should result in container being closed.
116
+ * And report the error if it escapes for any reason.
117
117
  * @param args - The connection arguments
118
118
  */
119
119
  private triggerConnect;
@@ -1 +1 @@
1
- {"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connectionManager.d.ts","sourceRoot":"","sources":["../src/connectionManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEH,gBAAgB,EAChB,oBAAoB,EACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACH,WAAW,EACX,YAAY,EAEZ,uBAAuB,EAC1B,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EACH,gBAAgB,EAGnB,MAAM,oCAAoC,CAAC;AAY5C,OAAO,EACH,cAAc,EACd,OAAO,EACP,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAGhB,yBAAyB,EAO5B,MAAM,sCAAsC,CAAC;AAK9C,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,6BAA6B,EAChC,MAAM,aAAa,CAAC;AA8ErB;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAyJpD,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IA5J1B,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiB;IAEzD;;;;OAIG;IACH,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,UAAU,CAAuC;IAEzD,kEAAkE;IAClE,OAAO,CAAC,oBAAoB,CAAsB;IAElD,4CAA4C;IAC5C,OAAO,CAAC,cAAc,CAAS;IAE/B;;OAEG;IACH,OAAO,CAAC,cAAc,CAAgB;IAEtC,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,4BAA4B,CAAK;IACzC,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB,CAAK;IAE7B,yDAAyD;IACzD,OAAO,CAAC,qBAAqB,CAAqB;IAElD,OAAO,CAAC,sBAAsB,CAAQ;IAEtC,OAAO,CAAC,uBAAuB,CAAuC;IAEtE,OAAO,CAAC,gBAAgB,CAA4B;IAEpD,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D,IAAW,sBAAsB,oCAA2C;IAE5E,SAAgB,aAAa,EAAE,cAAc,CAAC;IAE9C;;OAEG;IACH,IAAW,cAAc,IAAI,cAAc,CAE1C;IAED,IAAW,SAAS,YAA4C;IAEhE,IAAW,QAAQ,uBAAwC;IAC3D;;;OAGG;IACH,IAAW,aAAa,IAAI,aAAa,CAExC;IAED,IAAW,cAAc,IAAI,MAAM,CAGlC;IAED,IAAW,OAAO,IAAI,MAAM,CAK3B;IAED,IAAW,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAElE;IAED,IAAW,MAAM,IAAI,MAAM,EAAE,GAAG,SAAS,CAExC;IAED,IAAW,QAAQ,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAErD;IAED;;;MAGE;IACF,IAAW,eAAe,IAAI,oBAAoB,CAQjD;IAEM,eAAe,IAAI,OAAO;IAKjC;;;;;;;;OAQG;IACH,OAAO,KAAK,QAAQ,GAKnB;IAED,IAAW,YAAY,IAAI,YAAY,CAYtC;IAED,OAAO,CAAC,MAAM,CAAC,qBAAqB;gBAcf,eAAe,EAAE,MAAM,gBAAgB,GAAG,SAAS,EAC5D,MAAM,EAAE,OAAO,EACvB,gBAAgB,EAAE,OAAO,EACR,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,6BAA6B;IAqBlD,OAAO,CAAC,KAAK,CAAC,EAAE,uBAAuB;IA0B9C;;;MAGE;IACK,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAYlD;;;;;;;;;;;;;;;;OAgBG;IACI,aAAa,CAAC,QAAQ,EAAE,OAAO;IAoCtC,OAAO,CAAC,uBAAuB;IAQxB,OAAO,CAAC,cAAc,CAAC,EAAE,cAAc;YAOhC,WAAW;IAyIzB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;IAqCjC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;;OAIG;IACH,OAAO,CAAC,4BAA4B;IAqHpC;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;OAMG;YACW,SAAS;IA2ChB,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,GAAG,gBAAgB,GAAG,SAAS;IAmC3G,YAAY,CAAC,OAAO,EAAE,GAAG;IAQzB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,EAAE;IA4BzC,0BAA0B,CAAC,OAAO,EAAE,yBAAyB;IAuCpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGxB;IAGF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAmB1B;IAGF,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAOxC;IAEF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAK3B;CACL"}
@@ -5,13 +5,12 @@
5
5
  import { default as AbortController } from "abort-controller";
6
6
  import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
7
7
  import { GenericError, UsageError } from "@fluidframework/container-utils";
8
- import { canRetryOnError, createWriteError, createGenericNetworkError, getRetryDelayFromError, waitForConnectedState, DeltaStreamConnectionForbiddenError, logNetworkFailure,
9
- // isRuntimeMessage,
10
- } from "@fluidframework/driver-utils";
8
+ import { canRetryOnError, createWriteError, createGenericNetworkError, getRetryDelayFromError, waitForConnectedState, DeltaStreamConnectionForbiddenError, logNetworkFailure, isRuntimeMessage, } from "@fluidframework/driver-utils";
11
9
  import { MessageType, ScopeType, } from "@fluidframework/protocol-definitions";
12
10
  import { TelemetryLogger, normalizeError, } from "@fluidframework/telemetry-utils";
13
11
  import { ReconnectMode, } from "./contracts";
14
12
  import { DeltaQueue } from "./deltaQueue";
13
+ import { SignalType } from "./protocol";
15
14
  const MaxReconnectDelayInMs = 8000;
16
15
  const InitialReconnectDelayInMs = 1000;
17
16
  const DefaultChunkSize = 16 * 1024;
@@ -43,7 +42,6 @@ class NoDeltaStream extends TypedEventEmitter {
43
42
  this.serviceConfiguration = {
44
43
  maxMessageSize: 0,
45
44
  blockSize: 0,
46
- summary: undefined,
47
45
  };
48
46
  this.checkpointSequenceNumber = undefined;
49
47
  this._disposed = false;
@@ -67,8 +65,8 @@ class NoDeltaStream extends TypedEventEmitter {
67
65
  }
68
66
  /**
69
67
  * Implementation of IConnectionManager, used by Container class
70
- * Implements constant connectivity to relay service, by reconnecting in case of loast connection or error.
71
- * Exposes various controls to influecen this process, including manual reconnects, forced read-only mode, etc.
68
+ * Implements constant connectivity to relay service, by reconnecting in case of lost connection or error.
69
+ * Exposes various controls to influence this process, including manual reconnects, forced read-only mode, etc.
72
70
  */
73
71
  export class ConnectionManager {
74
72
  constructor(serviceProvider, client, reconnectAllowed, logger, props) {
@@ -83,7 +81,7 @@ export class ConnectionManager {
83
81
  this.clientSequenceNumber = 0;
84
82
  this.clientSequenceNumberObserved = 0;
85
83
  /** Counts the number of noops sent by the client which may not be acked. */
86
- this.trailingNoopCount = 0;
84
+ this.localOpsToIgnore = 0;
87
85
  this.connectFirstConnection = true;
88
86
  this._connectionVerboseProps = {};
89
87
  this._connectionProps = {};
@@ -174,18 +172,15 @@ export class ConnectionManager {
174
172
  * about current or last connection (if there is no connection at the moment)
175
173
  */
176
174
  get connectionProps() {
177
- if (this.connection !== undefined) {
178
- return this._connectionProps;
179
- }
180
- else {
181
- return Object.assign(Object.assign({}, this._connectionProps), {
175
+ return this.connection !== undefined
176
+ ? this._connectionProps
177
+ : Object.assign(Object.assign({}, this._connectionProps), {
182
178
  // Report how many ops this client sent in last disconnected session
183
179
  sentOps: this.clientSequenceNumber });
184
- }
185
180
  }
186
181
  shouldJoinWrite() {
187
182
  // We don't have to wait for ack for topmost NoOps. So subtract those.
188
- return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.trailingNoopCount);
183
+ return this.clientSequenceNumberObserved < (this.clientSequenceNumber - this.localOpsToIgnore);
189
184
  }
190
185
  /**
191
186
  * Tells if container is in read-only mode.
@@ -432,8 +427,8 @@ export class ConnectionManager {
432
427
  this.setupNewSuccessfulConnection(connection, requestedMode);
433
428
  }
434
429
  /**
435
- * Start the connection. Any error should result in container being close.
436
- * And report the error if it excape for any reason.
430
+ * Start the connection. Any error should result in container being closed.
431
+ * And report the error if it escapes for any reason.
437
432
  * @param args - The connection arguments
438
433
  */
439
434
  triggerConnect(connectionMode) {
@@ -491,6 +486,7 @@ export class ConnectionManager {
491
486
  * @param connection - The newly established connection
492
487
  */
493
488
  setupNewSuccessfulConnection(connection, requestedMode) {
489
+ var _a;
494
490
  // Old connection should have been cleaned up before establishing a new one
495
491
  assert(this.connection === undefined, 0x0e6 /* "old connection exists on new connection setup" */);
496
492
  assert(!connection.disposed, 0x28a /* "can't be disposed - Callers need to ensure that!" */);
@@ -549,15 +545,39 @@ export class ConnectionManager {
549
545
  }
550
546
  }
551
547
  this.props.incomingOpHandler(initialMessages, this.connectFirstConnection ? "InitialOps" : "ReconnectOps");
548
+ const details = ConnectionManager.detailsFromConnection(connection);
549
+ details.checkpointSequenceNumber = checkpointSequenceNumber;
550
+ this.props.connectHandler(details);
551
+ this.connectFirstConnection = false;
552
+ // Synthesize clear & join signals out of initialClients state.
553
+ // This allows us to have single way to process signals, and makes it simpler to initialize
554
+ // protocol in Container.
555
+ const clearSignal = {
556
+ clientId: null,
557
+ content: JSON.stringify({
558
+ type: SignalType.Clear,
559
+ }),
560
+ };
561
+ this.props.signalHandler(clearSignal);
562
+ for (const priorClient of (_a = connection.initialClients) !== null && _a !== void 0 ? _a : []) {
563
+ const joinSignal = {
564
+ clientId: null,
565
+ content: JSON.stringify({
566
+ type: SignalType.ClientJoin,
567
+ content: priorClient, // ISignalClient
568
+ }),
569
+ };
570
+ this.props.signalHandler(joinSignal);
571
+ }
572
+ // Unfortunately, there is no defined order between initialSignals (including join & leave signals)
573
+ // and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
574
+ // for "self" and connection.initialClients does not contain "self", so we have to process them after
575
+ // "clear" signal above.
552
576
  if (connection.initialSignals !== undefined) {
553
577
  for (const signal of connection.initialSignals) {
554
578
  this.props.signalHandler(signal);
555
579
  }
556
580
  }
557
- const details = ConnectionManager.detailsFromConnection(connection);
558
- details.checkpointSequenceNumber = checkpointSequenceNumber;
559
- this.props.connectHandler(details);
560
- this.connectFirstConnection = false;
561
581
  }
562
582
  /**
563
583
  * Disconnect the current connection and reconnect. Closes the container if it fails.
@@ -631,11 +651,11 @@ export class ConnectionManager {
631
651
  this.clientSequenceNumber = 0;
632
652
  this.clientSequenceNumberObserved = 0;
633
653
  }
634
- if (message.type === MessageType.NoOp) {
635
- this.trailingNoopCount++;
654
+ if (!isRuntimeMessage(message)) {
655
+ this.localOpsToIgnore++;
636
656
  }
637
657
  else {
638
- this.trailingNoopCount = 0;
658
+ this.localOpsToIgnore = 0;
639
659
  }
640
660
  return Object.assign(Object.assign({}, message), { clientSequenceNumber: ++this.clientSequenceNumber });
641
661
  }
@@ -663,8 +683,7 @@ export class ConnectionManager {
663
683
  await this.reconnect("write", // connectionMode
664
684
  "Switch to write");
665
685
  }
666
- })
667
- .catch(() => { });
686
+ }).catch(() => { });
668
687
  }
669
688
  return;
670
689
  }