@fluidframework/container-runtime 2.33.0-333010 → 2.33.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 (180) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +71 -67
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +7 -4
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +38 -12
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/channelCollection.d.ts +4 -0
  9. package/dist/channelCollection.d.ts.map +1 -1
  10. package/dist/channelCollection.js +24 -0
  11. package/dist/channelCollection.js.map +1 -1
  12. package/dist/compatUtils.d.ts +74 -0
  13. package/dist/compatUtils.d.ts.map +1 -0
  14. package/dist/compatUtils.js +151 -0
  15. package/dist/compatUtils.js.map +1 -0
  16. package/dist/compressionDefinitions.d.ts +39 -0
  17. package/dist/compressionDefinitions.d.ts.map +1 -0
  18. package/dist/compressionDefinitions.js +30 -0
  19. package/dist/compressionDefinitions.js.map +1 -0
  20. package/dist/containerRuntime.d.ts +78 -52
  21. package/dist/containerRuntime.d.ts.map +1 -1
  22. package/dist/containerRuntime.js +141 -54
  23. package/dist/containerRuntime.js.map +1 -1
  24. package/dist/dataStoreContext.d.ts +3 -0
  25. package/dist/dataStoreContext.d.ts.map +1 -1
  26. package/dist/dataStoreContext.js +122 -66
  27. package/dist/dataStoreContext.js.map +1 -1
  28. package/dist/deltaManagerProxies.d.ts +55 -12
  29. package/dist/deltaManagerProxies.d.ts.map +1 -1
  30. package/dist/deltaManagerProxies.js +63 -55
  31. package/dist/deltaManagerProxies.js.map +1 -1
  32. package/dist/gc/gcDefinitions.d.ts +2 -0
  33. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  34. package/dist/gc/gcDefinitions.js.map +1 -1
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +3 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/legacy.d.ts +1 -0
  40. package/dist/opLifecycle/batchManager.d.ts +1 -1
  41. package/dist/opLifecycle/batchManager.d.ts.map +1 -1
  42. package/dist/opLifecycle/batchManager.js +4 -1
  43. package/dist/opLifecycle/batchManager.js.map +1 -1
  44. package/dist/opLifecycle/definitions.d.ts +35 -4
  45. package/dist/opLifecycle/definitions.d.ts.map +1 -1
  46. package/dist/opLifecycle/definitions.js.map +1 -1
  47. package/dist/opLifecycle/index.d.ts +1 -1
  48. package/dist/opLifecycle/index.d.ts.map +1 -1
  49. package/dist/opLifecycle/index.js.map +1 -1
  50. package/dist/opLifecycle/opCompressor.js +2 -2
  51. package/dist/opLifecycle/opCompressor.js.map +1 -1
  52. package/dist/opLifecycle/opDecompressor.js +3 -3
  53. package/dist/opLifecycle/opDecompressor.js.map +1 -1
  54. package/dist/opLifecycle/opGroupingManager.d.ts +2 -2
  55. package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
  56. package/dist/opLifecycle/opGroupingManager.js +1 -2
  57. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  58. package/dist/opLifecycle/opSerialization.d.ts +3 -1
  59. package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
  60. package/dist/opLifecycle/opSerialization.js +4 -2
  61. package/dist/opLifecycle/opSerialization.js.map +1 -1
  62. package/dist/opLifecycle/outbox.d.ts +6 -3
  63. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  64. package/dist/opLifecycle/outbox.js +46 -20
  65. package/dist/opLifecycle/outbox.js.map +1 -1
  66. package/dist/packageVersion.d.ts +1 -1
  67. package/dist/packageVersion.d.ts.map +1 -1
  68. package/dist/packageVersion.js +1 -1
  69. package/dist/packageVersion.js.map +1 -1
  70. package/dist/pendingStateManager.d.ts +36 -7
  71. package/dist/pendingStateManager.d.ts.map +1 -1
  72. package/dist/pendingStateManager.js +83 -16
  73. package/dist/pendingStateManager.js.map +1 -1
  74. package/dist/runtimeLayerCompatState.d.ts.map +1 -1
  75. package/dist/runtimeLayerCompatState.js +1 -1
  76. package/dist/runtimeLayerCompatState.js.map +1 -1
  77. package/dist/summary/documentSchema.d.ts +1 -0
  78. package/dist/summary/documentSchema.d.ts.map +1 -1
  79. package/dist/summary/documentSchema.js +2 -0
  80. package/dist/summary/documentSchema.js.map +1 -1
  81. package/lib/blobManager/blobManager.d.ts +7 -4
  82. package/lib/blobManager/blobManager.d.ts.map +1 -1
  83. package/lib/blobManager/blobManager.js +38 -12
  84. package/lib/blobManager/blobManager.js.map +1 -1
  85. package/lib/channelCollection.d.ts +4 -0
  86. package/lib/channelCollection.d.ts.map +1 -1
  87. package/lib/channelCollection.js +24 -0
  88. package/lib/channelCollection.js.map +1 -1
  89. package/lib/compatUtils.d.ts +74 -0
  90. package/lib/compatUtils.d.ts.map +1 -0
  91. package/lib/compatUtils.js +142 -0
  92. package/lib/compatUtils.js.map +1 -0
  93. package/lib/compressionDefinitions.d.ts +39 -0
  94. package/lib/compressionDefinitions.d.ts.map +1 -0
  95. package/lib/compressionDefinitions.js +27 -0
  96. package/lib/compressionDefinitions.js.map +1 -0
  97. package/lib/containerRuntime.d.ts +78 -52
  98. package/lib/containerRuntime.d.ts.map +1 -1
  99. package/lib/containerRuntime.js +143 -56
  100. package/lib/containerRuntime.js.map +1 -1
  101. package/lib/dataStoreContext.d.ts +3 -0
  102. package/lib/dataStoreContext.d.ts.map +1 -1
  103. package/lib/dataStoreContext.js +57 -1
  104. package/lib/dataStoreContext.js.map +1 -1
  105. package/lib/deltaManagerProxies.d.ts +55 -12
  106. package/lib/deltaManagerProxies.d.ts.map +1 -1
  107. package/lib/deltaManagerProxies.js +63 -55
  108. package/lib/deltaManagerProxies.js.map +1 -1
  109. package/lib/gc/gcDefinitions.d.ts +2 -0
  110. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  111. package/lib/gc/gcDefinitions.js.map +1 -1
  112. package/lib/index.d.ts +4 -2
  113. package/lib/index.d.ts.map +1 -1
  114. package/lib/index.js +2 -1
  115. package/lib/index.js.map +1 -1
  116. package/lib/legacy.d.ts +1 -0
  117. package/lib/opLifecycle/batchManager.d.ts +1 -1
  118. package/lib/opLifecycle/batchManager.d.ts.map +1 -1
  119. package/lib/opLifecycle/batchManager.js +4 -1
  120. package/lib/opLifecycle/batchManager.js.map +1 -1
  121. package/lib/opLifecycle/definitions.d.ts +35 -4
  122. package/lib/opLifecycle/definitions.d.ts.map +1 -1
  123. package/lib/opLifecycle/definitions.js.map +1 -1
  124. package/lib/opLifecycle/index.d.ts +1 -1
  125. package/lib/opLifecycle/index.d.ts.map +1 -1
  126. package/lib/opLifecycle/index.js.map +1 -1
  127. package/lib/opLifecycle/opCompressor.js +1 -1
  128. package/lib/opLifecycle/opCompressor.js.map +1 -1
  129. package/lib/opLifecycle/opDecompressor.js +1 -1
  130. package/lib/opLifecycle/opDecompressor.js.map +1 -1
  131. package/lib/opLifecycle/opGroupingManager.d.ts +2 -2
  132. package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
  133. package/lib/opLifecycle/opGroupingManager.js +1 -2
  134. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  135. package/lib/opLifecycle/opSerialization.d.ts +3 -1
  136. package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
  137. package/lib/opLifecycle/opSerialization.js +4 -2
  138. package/lib/opLifecycle/opSerialization.js.map +1 -1
  139. package/lib/opLifecycle/outbox.d.ts +6 -3
  140. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  141. package/lib/opLifecycle/outbox.js +46 -20
  142. package/lib/opLifecycle/outbox.js.map +1 -1
  143. package/lib/packageVersion.d.ts +1 -1
  144. package/lib/packageVersion.d.ts.map +1 -1
  145. package/lib/packageVersion.js +1 -1
  146. package/lib/packageVersion.js.map +1 -1
  147. package/lib/pendingStateManager.d.ts +36 -7
  148. package/lib/pendingStateManager.d.ts.map +1 -1
  149. package/lib/pendingStateManager.js +84 -17
  150. package/lib/pendingStateManager.js.map +1 -1
  151. package/lib/runtimeLayerCompatState.d.ts.map +1 -1
  152. package/lib/runtimeLayerCompatState.js +2 -2
  153. package/lib/runtimeLayerCompatState.js.map +1 -1
  154. package/lib/summary/documentSchema.d.ts +1 -0
  155. package/lib/summary/documentSchema.d.ts.map +1 -1
  156. package/lib/summary/documentSchema.js +2 -0
  157. package/lib/summary/documentSchema.js.map +1 -1
  158. package/lib/tsdoc-metadata.json +1 -1
  159. package/package.json +21 -20
  160. package/src/blobManager/blobManager.ts +48 -15
  161. package/src/channelCollection.ts +27 -0
  162. package/src/compatUtils.ts +211 -0
  163. package/src/compressionDefinitions.ts +47 -0
  164. package/src/containerRuntime.ts +259 -108
  165. package/src/dataStoreContext.ts +82 -2
  166. package/src/deltaManagerProxies.ts +132 -70
  167. package/src/gc/gcDefinitions.ts +2 -0
  168. package/src/index.ts +5 -3
  169. package/src/opLifecycle/batchManager.ts +5 -4
  170. package/src/opLifecycle/definitions.ts +34 -4
  171. package/src/opLifecycle/index.ts +1 -0
  172. package/src/opLifecycle/opCompressor.ts +1 -1
  173. package/src/opLifecycle/opDecompressor.ts +1 -1
  174. package/src/opLifecycle/opGroupingManager.ts +7 -5
  175. package/src/opLifecycle/opSerialization.ts +6 -2
  176. package/src/opLifecycle/outbox.ts +65 -30
  177. package/src/packageVersion.ts +1 -1
  178. package/src/pendingStateManager.ts +135 -21
  179. package/src/runtimeLayerCompatState.ts +5 -2
  180. package/src/summary/documentSchema.ts +3 -0
@@ -5,11 +5,17 @@
5
5
 
6
6
  import { TypedEventEmitter, type ILayerCompatDetails } from "@fluid-internal/client-utils";
7
7
  import { AttachState, IAudience } from "@fluidframework/container-definitions";
8
- import { IDeltaManager } from "@fluidframework/container-definitions/internal";
8
+ import {
9
+ IDeltaManager,
10
+ isIDeltaManagerFull,
11
+ type IDeltaManagerFull,
12
+ type ReadOnlyInfo,
13
+ } from "@fluidframework/container-definitions/internal";
9
14
  import {
10
15
  FluidObject,
11
16
  IDisposable,
12
17
  ITelemetryBaseProperties,
18
+ type IErrorBase,
13
19
  type IEvent,
14
20
  } from "@fluidframework/core-interfaces";
15
21
  import {
@@ -75,6 +81,7 @@ import {
75
81
  tagCodeArtifacts,
76
82
  } from "@fluidframework/telemetry-utils/internal";
77
83
 
84
+ import { BaseDeltaManagerProxy } from "./deltaManagerProxies.js";
78
85
  import {
79
86
  runtimeCompatDetailsForDataStore,
80
87
  validateDatastoreCompatibility,
@@ -188,6 +195,58 @@ export interface IFluidDataStoreContextEvents extends IEvent {
188
195
  (event: "attaching" | "attached", listener: () => void);
189
196
  }
190
197
 
198
+ /**
199
+ * Eventually we should remove the delta manger from being exposed to Datastore runtimes via the context. However to remove that exposure we need to add new
200
+ * features, and those features themselves need forward and back compat. This proxy is here to enable that back compat. Each feature this proxy is used to
201
+ * support should be listed below, and as layer compat support goes away for those feature, we should also remove them from this proxy, with the eventual goal
202
+ * of completely removing this proxy.
203
+ *
204
+ * - Everything regarding readonly is to support older datastore runtimes which do not have the setReadonly function, so they must get their readonly state via the delta manager.
205
+ *
206
+ */
207
+ class ContextDeltaManagerProxy extends BaseDeltaManagerProxy {
208
+ constructor(
209
+ base: IDeltaManagerFull,
210
+ private readonly isReadOnly: () => boolean,
211
+ ) {
212
+ super(base, {
213
+ onReadonly: (): void => {
214
+ /* readonly is controlled from the context which calls setReadonly */
215
+ },
216
+ });
217
+ }
218
+
219
+ public get readOnlyInfo(): ReadOnlyInfo {
220
+ const readonly = this.isReadOnly();
221
+ if (readonly === this.deltaManager.readOnlyInfo.readonly) {
222
+ return this.deltaManager.readOnlyInfo;
223
+ } else {
224
+ return readonly === true
225
+ ? {
226
+ readonly,
227
+ forced: false,
228
+ permissions: undefined,
229
+ storageOnly: false,
230
+ }
231
+ : { readonly };
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Called by the owning datastore context to configure the readonly
237
+ * state of the delta manger that is project down to the datastore
238
+ * runtime. This state may not align with that of the true delta
239
+ * manager if the context wishes to control the read only state
240
+ * differently than the delta manager itself.
241
+ */
242
+ public setReadonly(
243
+ readonly: boolean,
244
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
245
+ ): void {
246
+ this.emit("readonly", readonly, readonlyConnectionReason);
247
+ }
248
+ }
249
+
191
250
  /**
192
251
  * Represents the context for the store. This context is passed to the store runtime.
193
252
  * @internal
@@ -217,10 +276,13 @@ export abstract class FluidDataStoreContext
217
276
  return this.parentContext.baseLogger;
218
277
  }
219
278
 
279
+ private readonly _contextDeltaManagerProxy: ContextDeltaManagerProxy;
220
280
  public get deltaManager(): IDeltaManager<ISequencedDocumentMessage, IDocumentMessage> {
221
- return this.parentContext.deltaManager;
281
+ return this._contextDeltaManagerProxy;
222
282
  }
223
283
 
284
+ public isReadOnly = (): boolean => this.parentContext.isReadOnly?.() ?? false;
285
+
224
286
  public get connected(): boolean {
225
287
  return this.parentContext.connected;
226
288
  }
@@ -420,6 +482,16 @@ export abstract class FluidDataStoreContext
420
482
  // By default, a data store can log maximum 10 local changes telemetry in summarizer.
421
483
  this.localChangesTelemetryCount =
422
484
  this.mc.config.getNumber("Fluid.Telemetry.LocalChangesTelemetryCount") ?? 10;
485
+
486
+ assert(
487
+ isIDeltaManagerFull(this.parentContext.deltaManager),
488
+ 0xb83 /* Invalid delta manager */,
489
+ );
490
+
491
+ this._contextDeltaManagerProxy = new ContextDeltaManagerProxy(
492
+ this.parentContext.deltaManager,
493
+ () => this.isReadOnly(),
494
+ );
423
495
  }
424
496
 
425
497
  public dispose(): void {
@@ -437,6 +509,7 @@ export abstract class FluidDataStoreContext
437
509
  })
438
510
  .catch((error) => {});
439
511
  }
512
+ this._contextDeltaManagerProxy.dispose();
440
513
  }
441
514
 
442
515
  /**
@@ -615,6 +688,13 @@ export abstract class FluidDataStoreContext
615
688
  this.channel!.setConnectionState(connected, clientId);
616
689
  }
617
690
 
691
+ public notifyReadOnlyState(readonly: boolean): void {
692
+ this.verifyNotClosed("notifyReadOnlyState", false /* checkTombstone */);
693
+
694
+ this.channel?.notifyReadOnlyState?.(readonly);
695
+ this._contextDeltaManagerProxy.setReadonly(readonly);
696
+ }
697
+
618
698
  /**
619
699
  * Process messages for this data store. The messages here are contiguous messages for this data store in a batch.
620
700
  * @param messageCollection - The collection of messages to process.
@@ -25,13 +25,68 @@ import {
25
25
  import type { PendingStateManager } from "./pendingStateManager.js";
26
26
  import { summarizerClientType } from "./summary/index.js";
27
27
 
28
+ /**
29
+ * Interface defining event handlers for the BaseDeltaManagerProxy.
30
+ * These handlers allow customization of behavior for various DeltaManager events.
31
+ */
32
+ export interface BaseDeltaManagerProxyEventHandlers {
33
+ /**
34
+ * Event handler triggered when the readonly state changes.
35
+ * @param readonly - Indicates if the connection is readonly.
36
+ * @param readonlyConnectionReason - Optional reason and error details for the readonly state.
37
+ */
38
+ onReadonly: (
39
+ readonly: boolean,
40
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
41
+ ) => void;
42
+
43
+ /**
44
+ * Event handler triggered before sending a message.
45
+ * @param messageBuffer - The buffer of messages to be sent.
46
+ */
47
+ onPrepareSend: (messageBuffer: unknown[]) => void;
48
+
49
+ /**
50
+ * Event handler triggered when an operation is submitted.
51
+ * @param message - The document message being submitted.
52
+ */
53
+ onSubmitOp: (message: IDocumentMessage) => void;
54
+
55
+ /**
56
+ * Event handler triggered when an operation is received.
57
+ * @param message - The sequenced document message received.
58
+ * @param processingTime - The time taken to process the message.
59
+ */
60
+ onOp: (message: ISequencedDocumentMessage, processingTime: number) => void;
61
+
62
+ /**
63
+ * Event handler triggered when a pong response is received.
64
+ * @param latency - The latency of the pong response.
65
+ */
66
+ onPong: (latency: number) => void;
67
+
68
+ /**
69
+ * Event handler triggered when a connection is established.
70
+ * @param details - The connection details.
71
+ * @param opsBehind - Optional number of operations behind the latest state.
72
+ */
73
+ onConnect: (details: IConnectionDetails, opsBehind?: number) => void;
74
+
75
+ /**
76
+ * Event handler triggered when a disconnection occurs.
77
+ * @param reason - The reason for the disconnection.
78
+ * @param error - Optional error details for the disconnection.
79
+ */
80
+ onDisconnect: (reason: string, error?: IAnyDriverError) => void;
81
+ }
82
+
28
83
  /**
29
84
  * Base class for DeltaManager proxy that proxy's access to the real DeltaManager.
30
85
  *
31
86
  * This class allows us to build proxy functionality without actually having to implement all the methods
32
87
  * of the DeltaManager.
33
88
  */
34
- export abstract class BaseDeltaManagerProxy
89
+ export class BaseDeltaManagerProxy
35
90
  extends TypedEventEmitter<IDeltaManagerEvents>
36
91
  implements IDeltaManagerFull
37
92
  {
@@ -99,29 +154,64 @@ export abstract class BaseDeltaManagerProxy
99
154
  return this.deltaManager.readOnlyInfo;
100
155
  }
101
156
 
102
- constructor(protected readonly deltaManager: IDeltaManagerFull) {
157
+ private readonly eventHandlers: BaseDeltaManagerProxyEventHandlers;
158
+
159
+ constructor(
160
+ protected readonly deltaManager: IDeltaManagerFull,
161
+ overrides?: Partial<BaseDeltaManagerProxyEventHandlers>,
162
+ ) {
103
163
  super();
104
164
 
165
+ this.eventHandlers = {
166
+ onPrepareSend: (messageBuffer: unknown[]): void => {
167
+ this.emit("prepareSend", messageBuffer);
168
+ },
169
+ onSubmitOp: (message: IDocumentMessage): void => {
170
+ this.emit("submitOp", message);
171
+ },
172
+ onOp: (message: ISequencedDocumentMessage, processingTime: number): void => {
173
+ this.emit("op", message, processingTime);
174
+ },
175
+ onPong: (latency: number): void => {
176
+ this.emit("pong", latency);
177
+ },
178
+ onConnect: (details: IConnectionDetails, opsBehind?: number): void => {
179
+ this.emit("connect", details, opsBehind);
180
+ },
181
+ onDisconnect: (reason: string, error?: IAnyDriverError): void => {
182
+ this.emit("disconnect", reason, error);
183
+ },
184
+ onReadonly: (
185
+ readonly: boolean,
186
+ readonlyConnectionReason?: { reason: string; error?: IErrorBase },
187
+ ): void => {
188
+ this.emit("readonly", readonly, readonlyConnectionReason);
189
+ },
190
+
191
+ ...overrides,
192
+ };
193
+
105
194
  // We are expecting this class to have many listeners, so we suppress noisy "MaxListenersExceededWarning" logging.
106
195
  super.setMaxListeners(0);
107
196
 
108
- this.deltaManager.on("prepareSend", this.onPrepareSend);
109
- this.deltaManager.on("submitOp", this.onSubmitOp);
110
- this.deltaManager.on("op", this.onOp);
111
- this.deltaManager.on("pong", this.onPong);
112
- this.deltaManager.on("connect", this.onConnect);
113
- this.deltaManager.on("disconnect", this.onDisconnect);
114
- this.deltaManager.on("readonly", this.onReadonly);
197
+ this.deltaManager.on("prepareSend", this.eventHandlers.onPrepareSend);
198
+ this.deltaManager.on("submitOp", this.eventHandlers.onSubmitOp);
199
+ this.deltaManager.on("op", this.eventHandlers.onOp);
200
+ this.deltaManager.on("pong", this.eventHandlers.onPong);
201
+ this.deltaManager.on("connect", this.eventHandlers.onConnect);
202
+ this.deltaManager.on("disconnect", this.eventHandlers.onDisconnect);
203
+ this.deltaManager.on("readonly", this.eventHandlers.onReadonly);
115
204
  }
116
205
 
117
206
  public dispose(): void {
118
- this.deltaManager.off("prepareSend", this.onPrepareSend);
119
- this.deltaManager.off("submitOp", this.onSubmitOp);
120
- this.deltaManager.off("op", this.onOp);
121
- this.deltaManager.off("pong", this.onPong);
122
- this.deltaManager.off("connect", this.onConnect);
123
- this.deltaManager.off("disconnect", this.onDisconnect);
124
- this.deltaManager.off("readonly", this.onReadonly);
207
+ this.deltaManager.off("prepareSend", this.eventHandlers.onPrepareSend);
208
+ this.deltaManager.off("submitOp", this.eventHandlers.onSubmitOp);
209
+ this.deltaManager.off("op", this.eventHandlers.onOp);
210
+ this.deltaManager.off("pong", this.eventHandlers.onPong);
211
+ this.deltaManager.off("connect", this.eventHandlers.onConnect);
212
+ this.deltaManager.off("disconnect", this.eventHandlers.onDisconnect);
213
+ this.deltaManager.off("readonly", this.eventHandlers.onReadonly);
214
+ this.removeAllListeners();
125
215
  }
126
216
 
127
217
  public submitSignal(content: string, targetClientId?: string): void {
@@ -131,34 +221,6 @@ export abstract class BaseDeltaManagerProxy
131
221
  public flush(): void {
132
222
  return this.deltaManager.flush();
133
223
  }
134
-
135
- private readonly onPrepareSend = (messageBuffer: unknown[]): void => {
136
- this.emit("prepareSend", messageBuffer);
137
- };
138
- private readonly onSubmitOp = (message: IDocumentMessage): void => {
139
- this.emit("submitOp", message);
140
- };
141
- protected readonly onOp = (
142
- message: ISequencedDocumentMessage,
143
- processingTime: number,
144
- ): void => {
145
- this.emit("op", message, processingTime);
146
- };
147
- private readonly onPong = (latency: number): void => {
148
- this.emit("pong", latency);
149
- };
150
- private readonly onConnect = (details: IConnectionDetails, opsBehind?: number): void => {
151
- this.emit("connect", details, opsBehind);
152
- };
153
- private readonly onDisconnect = (reason: string, error?: IAnyDriverError): void => {
154
- this.emit("disconnect", reason, error);
155
- };
156
- private readonly onReadonly = (
157
- readonly: boolean,
158
- readonlyConnectionReason?: { reason: string; error?: IErrorBase },
159
- ): void => {
160
- this.emit("readonly", readonly, readonlyConnectionReason);
161
- };
162
224
  }
163
225
 
164
226
  /**
@@ -167,31 +229,34 @@ export abstract class BaseDeltaManagerProxy
167
229
  * - Summarizer client should not be active to layers below the container runtime to restrict local changes.
168
230
  */
169
231
  export class DeltaManagerSummarizerProxy extends BaseDeltaManagerProxy {
232
+ public static wrapIfSummarizer(deltaManager: IDeltaManagerFull): IDeltaManagerFull {
233
+ if (deltaManager.clientDetails.type === summarizerClientType) {
234
+ return new DeltaManagerSummarizerProxy(deltaManager);
235
+ }
236
+ return deltaManager;
237
+ }
238
+
239
+ // eslint-disable-next-line @typescript-eslint/class-literal-property-style
170
240
  public get active(): boolean {
171
241
  // Summarize clients should not be active. There shouldn't be any local changes (writes) in the summarizer
172
242
  // except for the SummarizeOp which is generated by the runtime.
173
- return !this.isSummarizerClient && this.deltaManager.active;
243
+ return false;
174
244
  }
175
245
 
176
246
  public get readOnlyInfo(): ReadOnlyInfo {
177
247
  // Summarizer clients should be read-only as far as the runtime and layers below are concerned. There shouldn't
178
248
  // be any local changes (writes) in the summarizer except for the summarize op which is generated by the runtime.
179
- if (this.isSummarizerClient) {
180
- return {
181
- readonly: true,
182
- forced: false,
183
- permissions: undefined,
184
- storageOnly: false,
185
- };
186
- }
187
- return this.deltaManager.readOnlyInfo;
188
- }
189
249
 
190
- private readonly isSummarizerClient: boolean;
250
+ return {
251
+ readonly: true,
252
+ forced: false,
253
+ permissions: undefined,
254
+ storageOnly: false,
255
+ };
256
+ }
191
257
 
192
258
  constructor(protected readonly deltaManager: IDeltaManagerFull) {
193
- super(deltaManager);
194
- this.isSummarizerClient = this.deltaManager.clientDetails.type === summarizerClientType;
259
+ super(deltaManager, { onReadonly: () => {} });
195
260
  }
196
261
  }
197
262
 
@@ -228,17 +293,6 @@ export class DeltaManagerPendingOpsProxy extends BaseDeltaManagerProxy {
228
293
  };
229
294
  }
230
295
 
231
- protected readonly onOp = (
232
- message: ISequencedDocumentMessage,
233
- processingTime: number,
234
- ): void => {
235
- const messageIntercept = {
236
- ...message,
237
- minimumSequenceNumber: this.minimumSequenceNumber,
238
- };
239
- this.emit("op", messageIntercept, processingTime);
240
- };
241
-
242
296
  constructor(
243
297
  protected readonly deltaManager: IDeltaManagerFull,
244
298
  private readonly pendingStateManager: Pick<
@@ -246,6 +300,14 @@ export class DeltaManagerPendingOpsProxy extends BaseDeltaManagerProxy {
246
300
  "minimumPendingMessageSequenceNumber"
247
301
  >,
248
302
  ) {
249
- super(deltaManager);
303
+ super(deltaManager, {
304
+ onOp: (message: ISequencedDocumentMessage, processingTime: number): void => {
305
+ const messageIntercept = {
306
+ ...message,
307
+ minimumSequenceNumber: this.minimumSequenceNumber,
308
+ };
309
+ this.emit("op", messageIntercept, processingTime);
310
+ },
311
+ });
250
312
  }
251
313
  }
@@ -333,6 +333,8 @@ export type GarbageCollectionMessage = ISweepMessage | ITombstoneLoadedMessage;
333
333
 
334
334
  /**
335
335
  * Defines the APIs for the runtime object to be passed to the garbage collector.
336
+ *
337
+ * @internal
336
338
  */
337
339
  export interface IGarbageCollectionRuntime {
338
340
  /**
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  export {
7
+ ContainerRuntimeOptions,
8
+ ContainerRuntimeOptionsInternal,
7
9
  ISummaryRuntimeOptions,
8
10
  IContainerRuntimeOptions,
9
11
  IContainerRuntimeOptionsInternal,
@@ -14,11 +16,10 @@ export {
14
16
  DeletedResponseHeaderKey,
15
17
  TombstoneResponseHeaderKey,
16
18
  InactiveResponseHeaderKey,
17
- ICompressionRuntimeOptions,
18
- CompressionAlgorithms,
19
19
  RuntimeHeaderData,
20
- disabledCompressionConfig,
21
20
  } from "./containerRuntime.js";
21
+ export type { ICompressionRuntimeOptions } from "./compressionDefinitions.js";
22
+ export { CompressionAlgorithms, disabledCompressionConfig } from "./compressionDefinitions.js";
22
23
  export {
23
24
  ContainerMessageType,
24
25
  UnknownContainerRuntimeMessage,
@@ -35,6 +36,7 @@ export {
35
36
  IGCMetadata,
36
37
  GCFeatureMatrix,
37
38
  GCVersion,
39
+ IGarbageCollectionRuntime,
38
40
  IGCRuntimeOptions,
39
41
  IMarkPhaseStats,
40
42
  ISweepPhaseStats,
@@ -10,11 +10,12 @@ import {
10
10
  TelemetryDataTag,
11
11
  } from "@fluidframework/telemetry-utils/internal";
12
12
 
13
- import { ICompressionRuntimeOptions } from "../containerRuntime.js";
13
+ import { ICompressionRuntimeOptions } from "../compressionDefinitions.js";
14
14
  import { asBatchMetadata, type IBatchMetadata } from "../metadata.js";
15
15
  import type { IPendingMessage } from "../pendingStateManager.js";
16
16
 
17
17
  import { LocalBatchMessage, IBatchCheckpoint, type LocalBatch } from "./definitions.js";
18
+ import { serializeOp } from "./opSerialization.js";
18
19
  import type { BatchStartInfo } from "./remoteMessageProcessor.js";
19
20
 
20
21
  export interface IBatchManagerOptions {
@@ -127,10 +128,12 @@ export class BatchManager {
127
128
  * Gets the pending batch and clears state for the next batch.
128
129
  */
129
130
  public popBatch(batchId?: BatchId): LocalBatch {
131
+ assert(this.pendingBatch[0] !== undefined, 0xb8a /* expected non-empty batch */);
130
132
  const batch: LocalBatch = {
131
133
  messages: this.pendingBatch,
132
134
  referenceSequenceNumber: this.referenceSequenceNumber,
133
135
  hasReentrantOps: this.hasReentrantOps,
136
+ staged: this.pendingBatch[0].staged,
134
137
  };
135
138
 
136
139
  this.pendingBatch = [];
@@ -158,9 +161,7 @@ export class BatchManager {
158
161
  throw new LoggingError("Ops generated during rollback", {
159
162
  count,
160
163
  ...tagData(TelemetryDataTag.UserData, {
161
- ops: JSON.stringify(
162
- this.pendingBatch.slice(startPoint).map((b) => b.serializedOp),
163
- ),
164
+ ops: serializeOp(this.pendingBatch.slice(startPoint).map((b) => b.runtimeOp)),
164
165
  }),
165
166
  });
166
167
  }
@@ -5,17 +5,33 @@
5
5
 
6
6
  import type { IBatchMessage } from "@fluidframework/container-definitions/internal";
7
7
 
8
- import { CompressionAlgorithms } from "../containerRuntime.js";
8
+ import { CompressionAlgorithms } from "../compressionDefinitions.js";
9
+ import type { LocalContainerRuntimeMessage } from "../messageTypes.js";
9
10
 
10
11
  /**
11
12
  * Local Batch message, before it is virtualized and sent to the ordering service
12
13
  */
13
14
  export interface LocalBatchMessage {
14
- serializedOp: string;
15
+ /**
16
+ * The original local op
17
+ */
18
+ runtimeOp: LocalContainerRuntimeMessage;
19
+ /**
20
+ * Optional metadata which is not to be serialized with the op, and is visible to the ordering service
21
+ */
15
22
  metadata?: Record<string, unknown>;
23
+ /**
24
+ * Metadata used by this local client in flows such as rebase
25
+ */
16
26
  localOpMetadata?: unknown;
27
+ /**
28
+ * Reference sequence number this op is based on
29
+ */
17
30
  referenceSequenceNumber: number;
18
- compression?: CompressionAlgorithms;
31
+ /**
32
+ * If true, this op is not to be submitted to the ordering service yet, since it was submitted during Staging Mode
33
+ */
34
+ staged?: boolean;
19
35
 
20
36
  /**
21
37
  * @deprecated Use serializedOp
@@ -23,6 +39,15 @@ export interface LocalBatchMessage {
23
39
  contents?: never; // To ensure we don't leave this one when converting from OutboundBatchMessage
24
40
  }
25
41
 
42
+ /**
43
+ * Placeholder for an empty batch, for tracking the pending local empty batch
44
+ */
45
+ export interface LocalEmptyBatchPlaceholder {
46
+ metadata?: Record<string, unknown>;
47
+ localOpMetadata: { emptyBatch: true };
48
+ referenceSequenceNumber: number;
49
+ }
50
+
26
51
  /**
27
52
  * Virtualized Batch message, on its way out the door to the ordering service
28
53
  */
@@ -40,7 +65,12 @@ export type OutboundBatchMessage = IBatchMessage & {
40
65
  /**
41
66
  * A batch of messages we have accumulated locally, but haven't sent to the ordering service yet.
42
67
  */
43
- export type LocalBatch = IBatch<LocalBatchMessage[]>;
68
+ export interface LocalBatch extends IBatch<LocalBatchMessage[]> {
69
+ /**
70
+ * If true, this batch is not to be submitted to the ordering service yet, since it was submitted during Staging Mode
71
+ */
72
+ staged?: boolean;
73
+ }
44
74
 
45
75
  /**
46
76
  * A batch of messages that has been virtualized as needed (grouped, compressed, chunked)
@@ -14,6 +14,7 @@ export {
14
14
  export {
15
15
  LocalBatch,
16
16
  LocalBatchMessage,
17
+ LocalEmptyBatchPlaceholder,
17
18
  OutboundBatch,
18
19
  OutboundBatchMessage,
19
20
  OutboundSingletonBatch,
@@ -13,7 +13,7 @@ import {
13
13
  } from "@fluidframework/telemetry-utils/internal";
14
14
  import { compress } from "lz4js";
15
15
 
16
- import { CompressionAlgorithms } from "../containerRuntime.js";
16
+ import { CompressionAlgorithms } from "../compressionDefinitions.js";
17
17
 
18
18
  import { type OutboundBatchMessage, type OutboundSingletonBatch } from "./definitions.js";
19
19
  import { estimateSocketSize } from "./outbox.js";
@@ -13,7 +13,7 @@ import {
13
13
  } from "@fluidframework/telemetry-utils/internal";
14
14
  import { decompress } from "lz4js";
15
15
 
16
- import { CompressionAlgorithms } from "../containerRuntime.js";
16
+ import { CompressionAlgorithms } from "../compressionDefinitions.js";
17
17
  import { IBatchMetadata } from "../metadata.js";
18
18
 
19
19
  /**
@@ -12,8 +12,8 @@ import {
12
12
  } from "@fluidframework/telemetry-utils/internal";
13
13
 
14
14
  import {
15
+ type LocalEmptyBatchPlaceholder,
15
16
  type OutboundBatch,
16
- type OutboundBatchMessage,
17
17
  type OutboundSingletonBatch,
18
18
  } from "./definitions.js";
19
19
 
@@ -67,7 +67,10 @@ export class OpGroupingManager {
67
67
  public createEmptyGroupedBatch(
68
68
  resubmittingBatchId: string,
69
69
  referenceSequenceNumber: number,
70
- ): { outboundBatch: OutboundSingletonBatch; placeholderMessage: OutboundBatchMessage } {
70
+ ): {
71
+ outboundBatch: OutboundSingletonBatch;
72
+ placeholderMessage: LocalEmptyBatchPlaceholder;
73
+ } {
71
74
  assert(
72
75
  this.config.groupedBatchingEnabled,
73
76
  0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,
@@ -77,15 +80,14 @@ export class OpGroupingManager {
77
80
  contents: [],
78
81
  });
79
82
 
80
- const placeholderMessage: OutboundBatchMessage = {
83
+ const placeholderMessage: LocalEmptyBatchPlaceholder = {
81
84
  metadata: { batchId: resubmittingBatchId },
82
85
  localOpMetadata: { emptyBatch: true },
83
86
  referenceSequenceNumber,
84
- contents: serializedOp,
85
87
  };
86
88
  const outboundBatch: OutboundSingletonBatch = {
87
89
  contentSizeInBytes: 0,
88
- messages: [placeholderMessage],
90
+ messages: [{ ...placeholderMessage, contents: serializedOp }],
89
91
  referenceSequenceNumber,
90
92
  };
91
93
  return { outboundBatch, placeholderMessage };
@@ -30,10 +30,14 @@ export function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMes
30
30
  /**
31
31
  * Before submitting an op to the Outbox, its contents must be serialized using this function.
32
32
  * @remarks - The deserialization on process happens via the function {@link ensureContentsDeserialized}.
33
+ *
34
+ * @param toSerialize - op message to serialize. Also supports an array of ops.
33
35
  */
34
- export function serializeOp(op: LocalContainerRuntimeMessage): string {
36
+ export function serializeOp(
37
+ toSerialize: LocalContainerRuntimeMessage | LocalContainerRuntimeMessage[],
38
+ ): string {
35
39
  return JSON.stringify(
36
- op,
40
+ toSerialize,
37
41
  // replacer:
38
42
  (key, value: unknown) => {
39
43
  // If 'value' is an IFluidHandle return its encoded form.