@fluidframework/container-loader 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.1

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 (148) hide show
  1. package/.eslintrc.js +18 -21
  2. package/.mocharc.js +2 -2
  3. package/README.md +45 -43
  4. package/api-extractor.json +2 -2
  5. package/closeAndGetPendingLocalState.md +51 -0
  6. package/dist/audience.d.ts.map +1 -1
  7. package/dist/audience.js.map +1 -1
  8. package/dist/catchUpMonitor.d.ts.map +1 -1
  9. package/dist/catchUpMonitor.js.map +1 -1
  10. package/dist/collabWindowTracker.d.ts.map +1 -1
  11. package/dist/collabWindowTracker.js.map +1 -1
  12. package/dist/connectionManager.d.ts +2 -2
  13. package/dist/connectionManager.d.ts.map +1 -1
  14. package/dist/connectionManager.js +51 -24
  15. package/dist/connectionManager.js.map +1 -1
  16. package/dist/connectionState.d.ts.map +1 -1
  17. package/dist/connectionState.js.map +1 -1
  18. package/dist/connectionStateHandler.d.ts.map +1 -1
  19. package/dist/connectionStateHandler.js +35 -16
  20. package/dist/connectionStateHandler.js.map +1 -1
  21. package/dist/container.d.ts +1 -10
  22. package/dist/container.d.ts.map +1 -1
  23. package/dist/container.js +89 -44
  24. package/dist/container.js.map +1 -1
  25. package/dist/containerContext.d.ts.map +1 -1
  26. package/dist/containerContext.js +6 -2
  27. package/dist/containerContext.js.map +1 -1
  28. package/dist/containerStorageAdapter.d.ts.map +1 -1
  29. package/dist/containerStorageAdapter.js +2 -4
  30. package/dist/containerStorageAdapter.js.map +1 -1
  31. package/dist/contracts.d.ts.map +1 -1
  32. package/dist/contracts.js.map +1 -1
  33. package/dist/deltaManager.d.ts +3 -3
  34. package/dist/deltaManager.d.ts.map +1 -1
  35. package/dist/deltaManager.js +56 -27
  36. package/dist/deltaManager.js.map +1 -1
  37. package/dist/deltaManagerProxy.d.ts.map +1 -1
  38. package/dist/deltaManagerProxy.js.map +1 -1
  39. package/dist/deltaQueue.d.ts.map +1 -1
  40. package/dist/deltaQueue.js +4 -2
  41. package/dist/deltaQueue.js.map +1 -1
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/loader.d.ts +3 -3
  46. package/dist/loader.d.ts.map +1 -1
  47. package/dist/loader.js +18 -15
  48. package/dist/loader.js.map +1 -1
  49. package/dist/packageVersion.d.ts +1 -1
  50. package/dist/packageVersion.js +1 -1
  51. package/dist/packageVersion.js.map +1 -1
  52. package/dist/protocol.d.ts.map +1 -1
  53. package/dist/protocol.js +2 -1
  54. package/dist/protocol.js.map +1 -1
  55. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  56. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  57. package/dist/quorum.d.ts.map +1 -1
  58. package/dist/quorum.js.map +1 -1
  59. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  60. package/dist/retriableDocumentStorageService.js +6 -2
  61. package/dist/retriableDocumentStorageService.js.map +1 -1
  62. package/dist/utils.d.ts.map +1 -1
  63. package/dist/utils.js +6 -4
  64. package/dist/utils.js.map +1 -1
  65. package/lib/audience.d.ts.map +1 -1
  66. package/lib/audience.js.map +1 -1
  67. package/lib/catchUpMonitor.d.ts.map +1 -1
  68. package/lib/catchUpMonitor.js.map +1 -1
  69. package/lib/collabWindowTracker.d.ts.map +1 -1
  70. package/lib/collabWindowTracker.js.map +1 -1
  71. package/lib/connectionManager.d.ts +2 -2
  72. package/lib/connectionManager.d.ts.map +1 -1
  73. package/lib/connectionManager.js +53 -26
  74. package/lib/connectionManager.js.map +1 -1
  75. package/lib/connectionState.d.ts.map +1 -1
  76. package/lib/connectionState.js.map +1 -1
  77. package/lib/connectionStateHandler.d.ts.map +1 -1
  78. package/lib/connectionStateHandler.js +35 -16
  79. package/lib/connectionStateHandler.js.map +1 -1
  80. package/lib/container.d.ts +1 -10
  81. package/lib/container.d.ts.map +1 -1
  82. package/lib/container.js +93 -48
  83. package/lib/container.js.map +1 -1
  84. package/lib/containerContext.d.ts.map +1 -1
  85. package/lib/containerContext.js +6 -2
  86. package/lib/containerContext.js.map +1 -1
  87. package/lib/containerStorageAdapter.d.ts.map +1 -1
  88. package/lib/containerStorageAdapter.js +2 -4
  89. package/lib/containerStorageAdapter.js.map +1 -1
  90. package/lib/contracts.d.ts.map +1 -1
  91. package/lib/contracts.js.map +1 -1
  92. package/lib/deltaManager.d.ts +3 -3
  93. package/lib/deltaManager.d.ts.map +1 -1
  94. package/lib/deltaManager.js +58 -29
  95. package/lib/deltaManager.js.map +1 -1
  96. package/lib/deltaManagerProxy.d.ts.map +1 -1
  97. package/lib/deltaManagerProxy.js.map +1 -1
  98. package/lib/deltaQueue.d.ts.map +1 -1
  99. package/lib/deltaQueue.js +4 -2
  100. package/lib/deltaQueue.js.map +1 -1
  101. package/lib/index.d.ts +1 -1
  102. package/lib/index.d.ts.map +1 -1
  103. package/lib/index.js.map +1 -1
  104. package/lib/loader.d.ts +3 -3
  105. package/lib/loader.d.ts.map +1 -1
  106. package/lib/loader.js +18 -15
  107. package/lib/loader.js.map +1 -1
  108. package/lib/packageVersion.d.ts +1 -1
  109. package/lib/packageVersion.js +1 -1
  110. package/lib/packageVersion.js.map +1 -1
  111. package/lib/protocol.d.ts.map +1 -1
  112. package/lib/protocol.js +2 -1
  113. package/lib/protocol.js.map +1 -1
  114. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  115. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  116. package/lib/quorum.d.ts.map +1 -1
  117. package/lib/quorum.js.map +1 -1
  118. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  119. package/lib/retriableDocumentStorageService.js +6 -2
  120. package/lib/retriableDocumentStorageService.js.map +1 -1
  121. package/lib/utils.d.ts.map +1 -1
  122. package/lib/utils.js +6 -4
  123. package/lib/utils.js.map +1 -1
  124. package/package.json +115 -114
  125. package/prettier.config.cjs +1 -1
  126. package/src/audience.ts +51 -46
  127. package/src/catchUpMonitor.ts +39 -37
  128. package/src/collabWindowTracker.ts +75 -70
  129. package/src/connectionManager.ts +1006 -944
  130. package/src/connectionState.ts +19 -19
  131. package/src/connectionStateHandler.ts +544 -465
  132. package/src/container.ts +2056 -1909
  133. package/src/containerContext.ts +350 -340
  134. package/src/containerStorageAdapter.ts +163 -153
  135. package/src/contracts.ts +155 -153
  136. package/src/deltaManager.ts +1069 -992
  137. package/src/deltaManagerProxy.ts +143 -137
  138. package/src/deltaQueue.ts +155 -151
  139. package/src/index.ts +14 -17
  140. package/src/loader.ts +428 -430
  141. package/src/packageVersion.ts +1 -1
  142. package/src/protocol.ts +93 -87
  143. package/src/protocolTreeDocumentStorageService.ts +30 -33
  144. package/src/quorum.ts +34 -34
  145. package/src/retriableDocumentStorageService.ts +118 -102
  146. package/src/utils.ts +89 -82
  147. package/tsconfig.esnext.json +6 -6
  148. package/tsconfig.json +8 -12
@@ -4,155 +4,161 @@
4
4
  */
5
5
 
6
6
  import {
7
- IDeltaManager,
8
- IDeltaManagerEvents,
9
- IDeltaQueue,
10
- IDeltaSender,
11
- IDeltaQueueEvents,
12
- ReadOnlyInfo,
7
+ IDeltaManager,
8
+ IDeltaManagerEvents,
9
+ IDeltaQueue,
10
+ IDeltaSender,
11
+ IDeltaQueueEvents,
12
+ ReadOnlyInfo,
13
13
  } from "@fluidframework/container-definitions";
14
14
  import { EventForwarder } from "@fluidframework/common-utils";
15
15
  import {
16
- IClientConfiguration,
17
- IClientDetails,
18
- IDocumentMessage,
19
- ISequencedDocumentMessage,
20
- ISignalMessage,
16
+ IClientConfiguration,
17
+ IClientDetails,
18
+ IDocumentMessage,
19
+ ISequencedDocumentMessage,
20
+ ISignalMessage,
21
21
  } from "@fluidframework/protocol-definitions";
22
22
 
23
23
  /**
24
24
  * Proxy to the real IDeltaQueue - used to restrict access
25
25
  */
26
- export class DeltaQueueProxy<T> extends EventForwarder<IDeltaQueueEvents<T>> implements IDeltaQueue<T> {
27
- public get paused(): boolean {
28
- return this.queue.paused;
29
- }
30
-
31
- public get length(): number {
32
- return this.queue.length;
33
- }
34
-
35
- public get idle(): boolean {
36
- return this.queue.idle;
37
- }
38
-
39
- constructor(private readonly queue: IDeltaQueue<T>) {
40
- super(queue);
41
- }
42
-
43
- public peek(): T | undefined {
44
- return this.queue.peek();
45
- }
46
-
47
- public toArray(): T[] {
48
- return this.queue.toArray();
49
- }
50
-
51
- // back-compat: usage removed in 0.33, remove in future versions
52
- public async systemPause(): Promise<void> {
53
- return this.pause();
54
- }
55
-
56
- public async pause(): Promise<void> {
57
- return this.queue.pause();
58
- }
59
-
60
- // back-compat: usage removed in 0.33, remove in future versions
61
- public async systemResume(): Promise<void> {
62
- return this.resume();
63
- }
64
-
65
- public async resume(): Promise<void> {
66
- this.queue.resume();
67
- }
68
-
69
- public async waitTillProcessingDone() {
70
- return this.queue.waitTillProcessingDone();
71
- }
26
+ export class DeltaQueueProxy<T>
27
+ extends EventForwarder<IDeltaQueueEvents<T>>
28
+ implements IDeltaQueue<T>
29
+ {
30
+ public get paused(): boolean {
31
+ return this.queue.paused;
32
+ }
33
+
34
+ public get length(): number {
35
+ return this.queue.length;
36
+ }
37
+
38
+ public get idle(): boolean {
39
+ return this.queue.idle;
40
+ }
41
+
42
+ constructor(private readonly queue: IDeltaQueue<T>) {
43
+ super(queue);
44
+ }
45
+
46
+ public peek(): T | undefined {
47
+ return this.queue.peek();
48
+ }
49
+
50
+ public toArray(): T[] {
51
+ return this.queue.toArray();
52
+ }
53
+
54
+ // back-compat: usage removed in 0.33, remove in future versions
55
+ public async systemPause(): Promise<void> {
56
+ return this.pause();
57
+ }
58
+
59
+ public async pause(): Promise<void> {
60
+ return this.queue.pause();
61
+ }
62
+
63
+ // back-compat: usage removed in 0.33, remove in future versions
64
+ public async systemResume(): Promise<void> {
65
+ return this.resume();
66
+ }
67
+
68
+ public async resume(): Promise<void> {
69
+ this.queue.resume();
70
+ }
71
+
72
+ public async waitTillProcessingDone() {
73
+ return this.queue.waitTillProcessingDone();
74
+ }
72
75
  }
73
76
 
74
77
  /**
75
78
  * Proxy to the real IDeltaManager - used to restrict access
76
79
  */
77
80
  export class DeltaManagerProxy
78
- extends EventForwarder<IDeltaManagerEvents>
79
- implements IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
80
- public readonly inbound: IDeltaQueue<ISequencedDocumentMessage>;
81
- public readonly outbound: IDeltaQueue<IDocumentMessage[]>;
82
- public readonly inboundSignal: IDeltaQueue<ISignalMessage>;
83
-
84
- public get IDeltaSender(): IDeltaSender {
85
- return this;
86
- }
87
-
88
- public get minimumSequenceNumber(): number {
89
- return this.deltaManager.minimumSequenceNumber;
90
- }
91
-
92
- public get lastSequenceNumber(): number {
93
- return this.deltaManager.lastSequenceNumber;
94
- }
95
-
96
- public get lastMessage() {
97
- return this.deltaManager.lastMessage;
98
- }
99
-
100
- public get lastKnownSeqNumber() {
101
- return this.deltaManager.lastKnownSeqNumber;
102
- }
103
-
104
- public get initialSequenceNumber(): number {
105
- return this.deltaManager.initialSequenceNumber;
106
- }
107
-
108
- public get hasCheckpointSequenceNumber() {
109
- return this.deltaManager.hasCheckpointSequenceNumber;
110
- }
111
-
112
- public get clientDetails(): IClientDetails {
113
- return this.deltaManager.clientDetails;
114
- }
115
-
116
- public get version(): string {
117
- return this.deltaManager.version;
118
- }
119
-
120
- public get maxMessageSize(): number {
121
- return this.deltaManager.maxMessageSize;
122
- }
123
-
124
- public get serviceConfiguration(): IClientConfiguration | undefined {
125
- return this.deltaManager.serviceConfiguration;
126
- }
127
-
128
- public get active(): boolean {
129
- return this.deltaManager.active;
130
- }
131
-
132
- public get readOnlyInfo(): ReadOnlyInfo {
133
- return this.deltaManager.readOnlyInfo;
134
- }
135
-
136
- constructor(private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>) {
137
- super(deltaManager);
138
-
139
- this.inbound = new DeltaQueueProxy(deltaManager.inbound);
140
- this.outbound = new DeltaQueueProxy(deltaManager.outbound);
141
- this.inboundSignal = new DeltaQueueProxy(deltaManager.inboundSignal);
142
- }
143
-
144
- public dispose(): void {
145
- this.inbound.dispose();
146
- this.outbound.dispose();
147
- this.inboundSignal.dispose();
148
- super.dispose();
149
- }
150
-
151
- public submitSignal(content: any): void {
152
- return this.deltaManager.submitSignal(content);
153
- }
154
-
155
- public flush(): void {
156
- return this.deltaManager.flush();
157
- }
81
+ extends EventForwarder<IDeltaManagerEvents>
82
+ implements IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>
83
+ {
84
+ public readonly inbound: IDeltaQueue<ISequencedDocumentMessage>;
85
+ public readonly outbound: IDeltaQueue<IDocumentMessage[]>;
86
+ public readonly inboundSignal: IDeltaQueue<ISignalMessage>;
87
+
88
+ public get IDeltaSender(): IDeltaSender {
89
+ return this;
90
+ }
91
+
92
+ public get minimumSequenceNumber(): number {
93
+ return this.deltaManager.minimumSequenceNumber;
94
+ }
95
+
96
+ public get lastSequenceNumber(): number {
97
+ return this.deltaManager.lastSequenceNumber;
98
+ }
99
+
100
+ public get lastMessage() {
101
+ return this.deltaManager.lastMessage;
102
+ }
103
+
104
+ public get lastKnownSeqNumber() {
105
+ return this.deltaManager.lastKnownSeqNumber;
106
+ }
107
+
108
+ public get initialSequenceNumber(): number {
109
+ return this.deltaManager.initialSequenceNumber;
110
+ }
111
+
112
+ public get hasCheckpointSequenceNumber() {
113
+ return this.deltaManager.hasCheckpointSequenceNumber;
114
+ }
115
+
116
+ public get clientDetails(): IClientDetails {
117
+ return this.deltaManager.clientDetails;
118
+ }
119
+
120
+ public get version(): string {
121
+ return this.deltaManager.version;
122
+ }
123
+
124
+ public get maxMessageSize(): number {
125
+ return this.deltaManager.maxMessageSize;
126
+ }
127
+
128
+ public get serviceConfiguration(): IClientConfiguration | undefined {
129
+ return this.deltaManager.serviceConfiguration;
130
+ }
131
+
132
+ public get active(): boolean {
133
+ return this.deltaManager.active;
134
+ }
135
+
136
+ public get readOnlyInfo(): ReadOnlyInfo {
137
+ return this.deltaManager.readOnlyInfo;
138
+ }
139
+
140
+ constructor(
141
+ private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
142
+ ) {
143
+ super(deltaManager);
144
+
145
+ this.inbound = new DeltaQueueProxy(deltaManager.inbound);
146
+ this.outbound = new DeltaQueueProxy(deltaManager.outbound);
147
+ this.inboundSignal = new DeltaQueueProxy(deltaManager.inboundSignal);
148
+ }
149
+
150
+ public dispose(): void {
151
+ this.inbound.dispose();
152
+ this.outbound.dispose();
153
+ this.inboundSignal.dispose();
154
+ super.dispose();
155
+ }
156
+
157
+ public submitSignal(content: any): void {
158
+ return this.deltaManager.submitSignal(content);
159
+ }
160
+
161
+ public flush(): void {
162
+ return this.deltaManager.flush();
163
+ }
158
164
  }
package/src/deltaQueue.ts CHANGED
@@ -8,158 +8,162 @@ import { assert, performance, TypedEventEmitter } from "@fluidframework/common-u
8
8
  import Deque from "double-ended-queue";
9
9
 
10
10
  export interface IDeltaQueueWriter<T> {
11
- push(task: T): void;
12
- clear(): void;
11
+ push(task: T): void;
12
+ clear(): void;
13
13
  }
14
14
 
15
15
  export class DeltaQueue<T>
16
- extends TypedEventEmitter<IDeltaQueueEvents<T>>
17
- implements IDeltaQueue<T>, IDeltaQueueWriter<T> {
18
- private isDisposed: boolean = false;
19
- private readonly q = new Deque<T>();
20
-
21
- /**
22
- * Tracks the number of pause requests for the queue
23
- * The DeltaQueue is create initially paused.
24
- */
25
- private pauseCount = 1;
26
-
27
- private error: any | undefined;
28
-
29
- /**
30
- * When processing is ongoing, holds a deferred that will resolve once processing stops.
31
- * Undefined when not processing.
32
- */
33
- private processingPromise: Promise<{ count: number; duration: number; }> | undefined;
34
-
35
- public get disposed(): boolean {
36
- return this.isDisposed;
37
- }
38
-
39
- /**
40
- * @returns True if the queue is paused, false if not.
41
- */
42
- public get paused(): boolean {
43
- return this.pauseCount !== 0;
44
- }
45
-
46
- public get length(): number {
47
- return this.q.length;
48
- }
49
-
50
- public get idle(): boolean {
51
- return this.processingPromise === undefined && this.q.length === 0;
52
- }
53
-
54
- public async waitTillProcessingDone() {
55
- return this.processingPromise ?? { count: 0, duration: 0 };
56
- }
57
-
58
- /**
59
- * @param worker - A callback to process a delta.
60
- * @param logger - For logging telemetry.
61
- */
62
- constructor(
63
- private readonly worker: (delta: T) => void,
64
- ) {
65
- super();
66
- }
67
-
68
- public dispose() {
69
- throw new Error("Not implemented.");
70
- this.isDisposed = true;
71
- }
72
-
73
- public clear(): void {
74
- this.q.clear();
75
- }
76
-
77
- public peek(): T | undefined {
78
- return this.q.peekFront();
79
- }
80
-
81
- public toArray(): T[] {
82
- return this.q.toArray();
83
- }
84
-
85
- public push(task: T) {
86
- try {
87
- this.q.push(task);
88
- this.emit("push", task);
89
- this.ensureProcessing();
90
- } catch (error) {
91
- this.emit("error", error);
92
- }
93
- }
94
-
95
- public async pause(): Promise<void> {
96
- this.pauseCount++;
97
- // If called from within the processing loop, we are in the middle of processing an op. Return a promise
98
- // that will resolve when processing has actually stopped.
99
- await this.waitTillProcessingDone();
100
- }
101
-
102
- public resume(): void {
103
- assert(this.pauseCount > 0, 0x0f4 /* "Nonzero pause-count on resume()" */);
104
- this.pauseCount--;
105
- this.ensureProcessing();
106
- }
107
-
108
- /**
109
- * There are several actions that may need to kick off delta processing, so we want to guard against
110
- * accidental reentrancy. ensureProcessing can be called safely to start the processing loop if it is
111
- * not already started.
112
- */
113
- private ensureProcessing() {
114
- if (this.anythingToProcess() && this.processingPromise === undefined) {
115
- // Use a resolved promise to start the processing on a separate stack.
116
- this.processingPromise = Promise.resolve().then(() => {
117
- assert(this.processingPromise !== undefined, 0x37f /* reentrancy? */);
118
- const result = this.processDeltas();
119
- assert(this.processingPromise !== undefined, 0x380 /* reentrancy? */);
120
- // WARNING: Do not move next line to .finally() clause!
121
- // It runs async and creates a race condition where incoming ensureProcessing() call observes
122
- // from previous run while previous run is over (but finally clause was not scheduled yet)
123
- this.processingPromise = undefined;
124
- return result;
125
- }).catch((error) => {
126
- this.error = error;
127
- this.processingPromise = undefined;
128
- this.emit("error", error);
129
- return { count: 0, duration: 0 };
130
- });
131
- assert(this.processingPromise !== undefined, 0x381 /* processDeltas() should run async */);
132
- }
133
- }
134
-
135
- private anythingToProcess() {
136
- return this.q.length !== 0 && !this.paused && this.error === undefined;
137
- }
138
-
139
- /**
140
- * Executes the delta processing loop until a stop condition is reached.
141
- */
142
- private processDeltas() {
143
- const start = performance.now();
144
- let count = 0;
145
-
146
- // For grouping to work we must process all local messages immediately and in the single turn.
147
- // So loop over them until no messages to process, we have become paused, or hit an error.
148
- while (this.anythingToProcess()) {
149
- // Get the next message in the queue
150
- const next = this.q.shift();
151
- count++;
152
- // Process the message.
153
- // We know next is defined since we did a length check just prior to shifting.
154
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
155
- this.worker(next!);
156
- this.emit("op", next);
157
- }
158
-
159
- const duration = performance.now() - start;
160
- if (this.q.length === 0) {
161
- this.emit("idle", count, duration);
162
- }
163
- return { count, duration };
164
- }
16
+ extends TypedEventEmitter<IDeltaQueueEvents<T>>
17
+ implements IDeltaQueue<T>, IDeltaQueueWriter<T>
18
+ {
19
+ private isDisposed: boolean = false;
20
+ private readonly q = new Deque<T>();
21
+
22
+ /**
23
+ * Tracks the number of pause requests for the queue
24
+ * The DeltaQueue is create initially paused.
25
+ */
26
+ private pauseCount = 1;
27
+
28
+ private error: any | undefined;
29
+
30
+ /**
31
+ * When processing is ongoing, holds a deferred that will resolve once processing stops.
32
+ * Undefined when not processing.
33
+ */
34
+ private processingPromise: Promise<{ count: number; duration: number }> | undefined;
35
+
36
+ public get disposed(): boolean {
37
+ return this.isDisposed;
38
+ }
39
+
40
+ /**
41
+ * @returns True if the queue is paused, false if not.
42
+ */
43
+ public get paused(): boolean {
44
+ return this.pauseCount !== 0;
45
+ }
46
+
47
+ public get length(): number {
48
+ return this.q.length;
49
+ }
50
+
51
+ public get idle(): boolean {
52
+ return this.processingPromise === undefined && this.q.length === 0;
53
+ }
54
+
55
+ public async waitTillProcessingDone() {
56
+ return this.processingPromise ?? { count: 0, duration: 0 };
57
+ }
58
+
59
+ /**
60
+ * @param worker - A callback to process a delta.
61
+ * @param logger - For logging telemetry.
62
+ */
63
+ constructor(private readonly worker: (delta: T) => void) {
64
+ super();
65
+ }
66
+
67
+ public dispose() {
68
+ throw new Error("Not implemented.");
69
+ this.isDisposed = true;
70
+ }
71
+
72
+ public clear(): void {
73
+ this.q.clear();
74
+ }
75
+
76
+ public peek(): T | undefined {
77
+ return this.q.peekFront();
78
+ }
79
+
80
+ public toArray(): T[] {
81
+ return this.q.toArray();
82
+ }
83
+
84
+ public push(task: T) {
85
+ try {
86
+ this.q.push(task);
87
+ this.emit("push", task);
88
+ this.ensureProcessing();
89
+ } catch (error) {
90
+ this.emit("error", error);
91
+ }
92
+ }
93
+
94
+ public async pause(): Promise<void> {
95
+ this.pauseCount++;
96
+ // If called from within the processing loop, we are in the middle of processing an op. Return a promise
97
+ // that will resolve when processing has actually stopped.
98
+ await this.waitTillProcessingDone();
99
+ }
100
+
101
+ public resume(): void {
102
+ assert(this.pauseCount > 0, 0x0f4 /* "Nonzero pause-count on resume()" */);
103
+ this.pauseCount--;
104
+ this.ensureProcessing();
105
+ }
106
+
107
+ /**
108
+ * There are several actions that may need to kick off delta processing, so we want to guard against
109
+ * accidental reentrancy. ensureProcessing can be called safely to start the processing loop if it is
110
+ * not already started.
111
+ */
112
+ private ensureProcessing() {
113
+ if (this.anythingToProcess() && this.processingPromise === undefined) {
114
+ // Use a resolved promise to start the processing on a separate stack.
115
+ this.processingPromise = Promise.resolve()
116
+ .then(() => {
117
+ assert(this.processingPromise !== undefined, 0x37f /* reentrancy? */);
118
+ const result = this.processDeltas();
119
+ assert(this.processingPromise !== undefined, 0x380 /* reentrancy? */);
120
+ // WARNING: Do not move next line to .finally() clause!
121
+ // It runs async and creates a race condition where incoming ensureProcessing() call observes
122
+ // from previous run while previous run is over (but finally clause was not scheduled yet)
123
+ this.processingPromise = undefined;
124
+ return result;
125
+ })
126
+ .catch((error) => {
127
+ this.error = error;
128
+ this.processingPromise = undefined;
129
+ this.emit("error", error);
130
+ return { count: 0, duration: 0 };
131
+ });
132
+ assert(
133
+ this.processingPromise !== undefined,
134
+ 0x381 /* processDeltas() should run async */,
135
+ );
136
+ }
137
+ }
138
+
139
+ private anythingToProcess() {
140
+ return this.q.length !== 0 && !this.paused && this.error === undefined;
141
+ }
142
+
143
+ /**
144
+ * Executes the delta processing loop until a stop condition is reached.
145
+ */
146
+ private processDeltas() {
147
+ const start = performance.now();
148
+ let count = 0;
149
+
150
+ // For grouping to work we must process all local messages immediately and in the single turn.
151
+ // So loop over them until no messages to process, we have become paused, or hit an error.
152
+ while (this.anythingToProcess()) {
153
+ // Get the next message in the queue
154
+ const next = this.q.shift();
155
+ count++;
156
+ // Process the message.
157
+ // We know next is defined since we did a length check just prior to shifting.
158
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
+ this.worker(next!);
160
+ this.emit("op", next);
161
+ }
162
+
163
+ const duration = performance.now() - start;
164
+ if (this.q.length === 0) {
165
+ this.emit("idle", count, duration);
166
+ }
167
+ return { count, duration };
168
+ }
165
169
  }
package/src/index.ts CHANGED
@@ -5,23 +5,20 @@
5
5
 
6
6
  export { ConnectionState } from "./connectionState";
7
7
  export {
8
- Container,
9
- IContainerLoadOptions,
10
- IContainerConfig,
11
- IPendingContainerState,
12
- waitContainerToCatchUp,
8
+ Container,
9
+ IContainerLoadOptions,
10
+ IContainerConfig,
11
+ IPendingContainerState,
12
+ waitContainerToCatchUp,
13
13
  } from "./container";
14
14
  export {
15
- ICodeDetailsLoader,
16
- IDetachedBlobStorage,
17
- IFluidModuleWithDetails,
18
- ILoaderOptions,
19
- ILoaderProps,
20
- ILoaderServices,
21
- Loader,
22
- RelativeLoader,
15
+ ICodeDetailsLoader,
16
+ IDetachedBlobStorage,
17
+ IFluidModuleWithDetails,
18
+ ILoaderOptions,
19
+ ILoaderProps,
20
+ ILoaderServices,
21
+ Loader,
22
+ RelativeLoader,
23
23
  } from "./loader";
24
- export {
25
- IProtocolHandler,
26
- ProtocolHandlerBuilder,
27
- } from "./protocol";
24
+ export { IProtocolHandler, ProtocolHandlerBuilder } from "./protocol";