@fluidframework/container-runtime 2.4.0-299707 → 2.5.0-302463

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 (84) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts.map +1 -1
  4. package/dist/blobManager/blobManager.js +10 -4
  5. package/dist/blobManager/blobManager.js.map +1 -1
  6. package/dist/channelCollection.d.ts.map +1 -1
  7. package/dist/channelCollection.js +10 -2
  8. package/dist/channelCollection.js.map +1 -1
  9. package/dist/containerRuntime.d.ts +6 -2
  10. package/dist/containerRuntime.d.ts.map +1 -1
  11. package/dist/containerRuntime.js +46 -28
  12. package/dist/containerRuntime.js.map +1 -1
  13. package/dist/dataStoreContexts.d.ts.map +1 -1
  14. package/dist/dataStoreContexts.js +14 -6
  15. package/dist/dataStoreContexts.js.map +1 -1
  16. package/dist/opLifecycle/index.d.ts +1 -1
  17. package/dist/opLifecycle/index.d.ts.map +1 -1
  18. package/dist/opLifecycle/index.js +2 -1
  19. package/dist/opLifecycle/index.js.map +1 -1
  20. package/dist/opLifecycle/outbox.d.ts +6 -0
  21. package/dist/opLifecycle/outbox.d.ts.map +1 -1
  22. package/dist/opLifecycle/outbox.js +9 -1
  23. package/dist/opLifecycle/outbox.js.map +1 -1
  24. package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -4
  25. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  26. package/dist/opLifecycle/remoteMessageProcessor.js +6 -18
  27. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  28. package/dist/opProperties.js +1 -1
  29. package/dist/opProperties.js.map +1 -1
  30. package/dist/packageVersion.d.ts +1 -1
  31. package/dist/packageVersion.js +1 -1
  32. package/dist/packageVersion.js.map +1 -1
  33. package/dist/summary/documentSchema.js +2 -2
  34. package/dist/summary/documentSchema.js.map +1 -1
  35. package/dist/summary/summaryCollection.d.ts.map +1 -1
  36. package/dist/summary/summaryCollection.js +3 -4
  37. package/dist/summary/summaryCollection.js.map +1 -1
  38. package/lib/blobManager/blobManager.d.ts.map +1 -1
  39. package/lib/blobManager/blobManager.js +10 -4
  40. package/lib/blobManager/blobManager.js.map +1 -1
  41. package/lib/channelCollection.d.ts.map +1 -1
  42. package/lib/channelCollection.js +11 -3
  43. package/lib/channelCollection.js.map +1 -1
  44. package/lib/containerRuntime.d.ts +6 -2
  45. package/lib/containerRuntime.d.ts.map +1 -1
  46. package/lib/containerRuntime.js +44 -27
  47. package/lib/containerRuntime.js.map +1 -1
  48. package/lib/dataStoreContexts.d.ts.map +1 -1
  49. package/lib/dataStoreContexts.js +15 -7
  50. package/lib/dataStoreContexts.js.map +1 -1
  51. package/lib/opLifecycle/index.d.ts +1 -1
  52. package/lib/opLifecycle/index.d.ts.map +1 -1
  53. package/lib/opLifecycle/index.js +1 -1
  54. package/lib/opLifecycle/index.js.map +1 -1
  55. package/lib/opLifecycle/outbox.d.ts +6 -0
  56. package/lib/opLifecycle/outbox.d.ts.map +1 -1
  57. package/lib/opLifecycle/outbox.js +7 -0
  58. package/lib/opLifecycle/outbox.js.map +1 -1
  59. package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -4
  60. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  61. package/lib/opLifecycle/remoteMessageProcessor.js +6 -18
  62. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  63. package/lib/opProperties.js +1 -1
  64. package/lib/opProperties.js.map +1 -1
  65. package/lib/packageVersion.d.ts +1 -1
  66. package/lib/packageVersion.js +1 -1
  67. package/lib/packageVersion.js.map +1 -1
  68. package/lib/summary/documentSchema.js +2 -2
  69. package/lib/summary/documentSchema.js.map +1 -1
  70. package/lib/summary/summaryCollection.d.ts.map +1 -1
  71. package/lib/summary/summaryCollection.js +3 -4
  72. package/lib/summary/summaryCollection.js.map +1 -1
  73. package/package.json +22 -26
  74. package/src/blobManager/blobManager.ts +10 -4
  75. package/src/channelCollection.ts +16 -7
  76. package/src/containerRuntime.ts +49 -27
  77. package/src/dataStoreContexts.ts +20 -7
  78. package/src/opLifecycle/index.ts +1 -1
  79. package/src/opLifecycle/outbox.ts +11 -0
  80. package/src/opLifecycle/remoteMessageProcessor.ts +8 -22
  81. package/src/opProperties.ts +1 -1
  82. package/src/packageVersion.ts +1 -1
  83. package/src/summary/documentSchema.ts +2 -2
  84. package/src/summary/summaryCollection.ts +3 -4
@@ -105,6 +105,7 @@ import {
105
105
  import type {
106
106
  IFluidErrorBase,
107
107
  ITelemetryGenericEventExt,
108
+ TelemetryEventPropertyTypeExt,
108
109
  } from "@fluidframework/telemetry-utils/internal";
109
110
  import {
110
111
  ITelemetryLoggerExt,
@@ -185,6 +186,7 @@ import {
185
186
  OpSplitter,
186
187
  Outbox,
187
188
  RemoteMessageProcessor,
189
+ serializeOpContents,
188
190
  } from "./opLifecycle/index.js";
189
191
  import { pkgVersion } from "./packageVersion.js";
190
192
  import {
@@ -556,13 +558,6 @@ export interface RuntimeHeaderData {
556
558
  allowTombstone?: boolean;
557
559
  }
558
560
 
559
- /** Default values for Runtime Headers */
560
- export const defaultRuntimeHeaderData: Required<RuntimeHeaderData> = {
561
- wait: true,
562
- viaHandle: false,
563
- allowTombstone: false,
564
- };
565
-
566
561
  /**
567
562
  * Available compression algorithms for op compression.
568
563
  * @legacy
@@ -762,7 +757,7 @@ function lastMessageFromMetadata(metadata: IContainerRuntimeMetadata | undefined
762
757
  * to understand if/when it is hit.
763
758
  * We only want to log this once, to avoid spamming telemetry if we are wrong and these cases are hit commonly.
764
759
  */
765
- let getSingleUseLegacyLogCallback = (logger: ITelemetryLoggerExt, type: string) => {
760
+ export let getSingleUseLegacyLogCallback = (logger: ITelemetryLoggerExt, type: string) => {
766
761
  return (codePath: string) => {
767
762
  logger.sendTelemetryEvent({
768
763
  eventName: "LegacyMessageFormat",
@@ -2710,8 +2705,13 @@ export class ContainerRuntime
2710
2705
  const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
2711
2706
  const logLegacyCase = getSingleUseLegacyLogCallback(this.logger, messageCopy.type);
2712
2707
 
2713
- // We expect runtime messages to have JSON contents - deserialize it in place.
2714
- ensureContentsDeserialized(messageCopy, hasModernRuntimeMessageEnvelope, logLegacyCase);
2708
+ let runtimeBatch: boolean =
2709
+ hasModernRuntimeMessageEnvelope || isUnpackedRuntimeMessage(messageCopy);
2710
+ if (runtimeBatch) {
2711
+ // We expect runtime messages to have JSON contents - deserialize it in place.
2712
+ ensureContentsDeserialized(messageCopy);
2713
+ }
2714
+
2715
2715
  if (hasModernRuntimeMessageEnvelope) {
2716
2716
  // If the message has the modern message envelope, then process it here.
2717
2717
  // Here we unpack the message (decompress, unchunk, and/or ungroup) into a batch of messages with ContainerMessageType
@@ -2749,7 +2749,6 @@ export class ContainerRuntime
2749
2749
  }
2750
2750
  }
2751
2751
 
2752
- let runtimeBatch: boolean = true;
2753
2752
  // Reach out to PendingStateManager, either to zip localOpMetadata into the *local* message list,
2754
2753
  // or to check to ensure the *remote* messages don't match the batchId of a pending local batch.
2755
2754
  // This latter case would indicate that the container has forked - two copies are trying to persist the same local changes.
@@ -2807,12 +2806,23 @@ export class ContainerRuntime
2807
2806
  runtimeBatch,
2808
2807
  );
2809
2808
  } else {
2809
+ if (!runtimeBatch) {
2810
+ // The DeltaManager used to do this, but doesn't anymore as of Loader v2.4
2811
+ // Anyone listening to our "op" event would expect the contents to be parsed per this same logic
2812
+ if (
2813
+ typeof messageCopy.contents === "string" &&
2814
+ messageCopy.contents !== "" &&
2815
+ messageCopy.type !== MessageType.ClientLeave
2816
+ ) {
2817
+ messageCopy.contents = JSON.parse(messageCopy.contents);
2818
+ }
2819
+ }
2810
2820
  this.processInboundMessages(
2811
2821
  [{ message: messageCopy, localOpMetadata: undefined }],
2812
2822
  { batchStart: true, batchEnd: true }, // Single message
2813
2823
  local,
2814
2824
  savedOp,
2815
- isUnpackedRuntimeMessage(messageCopy) /* runtimeBatch */,
2825
+ runtimeBatch,
2816
2826
  );
2817
2827
  }
2818
2828
 
@@ -2999,11 +3009,13 @@ export class ContainerRuntime
2999
3009
  const duration = Date.now() - this._signalTracking.signalTimestamp;
3000
3010
  this.mc.logger.sendPerformanceEvent({
3001
3011
  eventName: "SignalLatency",
3002
- duration, // Roundtrip duration of the tracked signal in milliseconds.
3003
- signalsSent: this._signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.
3004
- signalsLost: this._signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.
3005
- outOfOrderSignals: this._signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.
3006
- reconnectCount: this.consecutiveReconnects, // Container reconnect count.
3012
+ details: {
3013
+ duration, // Roundtrip duration of the tracked signal in milliseconds.
3014
+ sent: this._signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.
3015
+ lost: this._signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.
3016
+ outOfOrder: this._signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.
3017
+ reconnectCount: this.consecutiveReconnects, // Container reconnect count.
3018
+ },
3007
3019
  });
3008
3020
  this._signalTracking.signalsLost = 0;
3009
3021
  this._signalTracking.signalsOutOfOrder = 0;
@@ -3038,9 +3050,11 @@ export class ContainerRuntime
3038
3050
  this._signalTracking.signalsLost += signalsLost;
3039
3051
  this.mc.logger.sendErrorEvent({
3040
3052
  eventName: "SignalLost",
3041
- signalsLost, // Number of lost signals detected.
3042
- trackingSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
3043
- clientBroadcastSignalSequenceNumber, // Actual signal sequence number received.
3053
+ details: {
3054
+ signalsLost, // Number of lost signals detected.
3055
+ expectedSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
3056
+ clientBroadcastSignalSequenceNumber, // Actual signal sequence number received.
3057
+ },
3044
3058
  });
3045
3059
  }
3046
3060
  // Update the tracking signal sequence number to the next expected signal in the sequence.
@@ -3052,11 +3066,18 @@ export class ContainerRuntime
3052
3066
  this._signalTracking.minimumTrackingSignalSequenceNumber
3053
3067
  ) {
3054
3068
  this._signalTracking.signalsOutOfOrder++;
3069
+ const details: TelemetryEventPropertyTypeExt = {
3070
+ expectedSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
3071
+ clientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.
3072
+ };
3073
+ // Only log `contents.type` when address is for container to avoid
3074
+ // chance that contents type is customer data.
3075
+ if (envelope.address === undefined) {
3076
+ details.contentsType = envelope.contents.type; // Type of signal that was received out of order.
3077
+ }
3055
3078
  this.mc.logger.sendTelemetryEvent({
3056
3079
  eventName: "SignalOutOfOrder",
3057
- type: envelope.contents.type, // Type of signal that was received out of order.
3058
- trackingSequenceNumber: this._signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.
3059
- clientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.
3080
+ details,
3060
3081
  });
3061
3082
  }
3062
3083
  if (
@@ -3069,7 +3090,8 @@ export class ContainerRuntime
3069
3090
  ) {
3070
3091
  // Latency tracked signal has been received.
3071
3092
  // We now log the roundtrip duration of the tracked signal.
3072
- // This telemetry event also logs metrics for signals sent, signals lost, and out of order signals received.
3093
+ // This telemetry event also logs metrics for broadcast signals
3094
+ // sent, lost, and out of order.
3073
3095
  // These metrics are reset after logging the telemetry event.
3074
3096
  this.sendSignalTelemetryEvent();
3075
3097
  }
@@ -4212,7 +4234,7 @@ export class ContainerRuntime
4212
4234
  contents: idRange,
4213
4235
  };
4214
4236
  const idAllocationBatchMessage: BatchMessage = {
4215
- contents: JSON.stringify(idAllocationMessage),
4237
+ contents: serializeOpContents(idAllocationMessage),
4216
4238
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
4217
4239
  };
4218
4240
  this.outbox.submitIdAllocation(idAllocationBatchMessage);
@@ -4275,13 +4297,13 @@ export class ContainerRuntime
4275
4297
  contents: schemaChangeMessage,
4276
4298
  };
4277
4299
  this.outbox.submit({
4278
- contents: JSON.stringify(msg),
4300
+ contents: serializeOpContents(msg),
4279
4301
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
4280
4302
  });
4281
4303
  }
4282
4304
 
4283
4305
  const message: BatchMessage = {
4284
- contents: JSON.stringify(containerRuntimeMessage) /* serialized content */,
4306
+ contents: serializeOpContents(containerRuntimeMessage),
4285
4307
  metadata,
4286
4308
  localOpMetadata,
4287
4309
  referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
@@ -7,6 +7,7 @@ import { IDisposable, ITelemetryBaseLogger } from "@fluidframework/core-interfac
7
7
  import { assert, Deferred, Lazy } from "@fluidframework/core-utils/internal";
8
8
  import {
9
9
  ITelemetryLoggerExt,
10
+ PerformanceEvent,
10
11
  createChildLogger,
11
12
  } from "@fluidframework/telemetry-utils/internal";
12
13
 
@@ -40,7 +41,7 @@ export class DataStoreContexts
40
41
  .catch((contextError) => {
41
42
  this._logger.sendErrorEvent(
42
43
  {
43
- eventName: "FluidDataStoreContextDisposeError",
44
+ eventName: "DisposeError",
44
45
  fluidDataStoreId,
45
46
  },
46
47
  contextError,
@@ -52,7 +53,10 @@ export class DataStoreContexts
52
53
  private readonly _logger: ITelemetryLoggerExt;
53
54
 
54
55
  constructor(baseLogger: ITelemetryBaseLogger) {
55
- this._logger = createChildLogger({ logger: baseLogger });
56
+ this._logger = createChildLogger({
57
+ namespace: "FluidDataStoreContexts",
58
+ logger: baseLogger,
59
+ });
56
60
  }
57
61
 
58
62
  [Symbol.iterator](): Iterator<[string, FluidDataStoreContext]> {
@@ -140,11 +144,20 @@ export class DataStoreContexts
140
144
  ): Promise<FluidDataStoreContext | undefined> {
141
145
  const deferredContext = this.ensureDeferred(id);
142
146
 
143
- if (!wait && !deferredContext.isCompleted) {
144
- return undefined;
145
- }
146
-
147
- return deferredContext.promise;
147
+ const existing = deferredContext.isCompleted;
148
+ return PerformanceEvent.timedExecAsync(
149
+ this._logger,
150
+ {
151
+ eventName: "GetBoundOrRemoted",
152
+ wait,
153
+ existing,
154
+ },
155
+ async () => (!wait && !existing ? undefined : deferredContext.promise),
156
+ {
157
+ start: true,
158
+ end: true,
159
+ },
160
+ );
148
161
  }
149
162
 
150
163
  private ensureDeferred(id: string): Deferred<FluidDataStoreContext> {
@@ -14,7 +14,7 @@ export {
14
14
  } from "./batchManager.js";
15
15
  export { BatchMessage, IBatch, IBatchCheckpoint, IChunkedOp } from "./definitions.js";
16
16
  export { DuplicateBatchDetector } from "./duplicateBatchDetector.js";
17
- export { Outbox, getLongStack } from "./outbox.js";
17
+ export { Outbox, getLongStack, serializeOpContents } from "./outbox.js";
18
18
  export { OpCompressor } from "./opCompressor.js";
19
19
  export { OpDecompressor } from "./opDecompressor.js";
20
20
  export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
@@ -15,6 +15,7 @@ import {
15
15
  } from "@fluidframework/telemetry-utils/internal";
16
16
 
17
17
  import { ICompressionRuntimeOptions } from "../containerRuntime.js";
18
+ import { OutboundContainerRuntimeMessage } from "../messageTypes.js";
18
19
  import { PendingMessageResubmitData, PendingStateManager } from "../pendingStateManager.js";
19
20
 
20
21
  import {
@@ -28,6 +29,8 @@ import { BatchMessage, IBatch, IBatchCheckpoint } from "./definitions.js";
28
29
  import { OpCompressor } from "./opCompressor.js";
29
30
  import { OpGroupingManager } from "./opGroupingManager.js";
30
31
  import { OpSplitter } from "./opSplitter.js";
32
+ // eslint-disable-next-line unused-imports/no-unused-imports -- Used by "@link" comment annotation below
33
+ import { ensureContentsDeserialized } from "./remoteMessageProcessor.js";
31
34
 
32
35
  export interface IOutboxConfig {
33
36
  readonly compressionOptions: ICompressionRuntimeOptions;
@@ -54,6 +57,14 @@ export interface IOutboxParameters {
54
57
  readonly closeContainer: (error?: ICriticalContainerError) => void;
55
58
  }
56
59
 
60
+ /**
61
+ * Before submitting an op to the Outbox, its contents must be serialized using this function.
62
+ * @remarks - The deserialization on process happens via the function {@link ensureContentsDeserialized}.
63
+ */
64
+ export function serializeOpContents(contents: OutboundContainerRuntimeMessage): string {
65
+ return JSON.stringify(contents);
66
+ }
67
+
57
68
  /**
58
69
  * Temporarily increase the stack limit while executing the provided action.
59
70
  * If a negative value is provided for `length`, no stack frames will be collected.
@@ -20,6 +20,8 @@ import { asBatchMetadata } from "../metadata.js";
20
20
  import { OpDecompressor } from "./opDecompressor.js";
21
21
  import { OpGroupingManager, isGroupedBatch } from "./opGroupingManager.js";
22
22
  import { OpSplitter, isChunkedMessage } from "./opSplitter.js";
23
+ // eslint-disable-next-line unused-imports/no-unused-imports -- Used by "@link" comment annotation below
24
+ import { serializeOpContents } from "./outbox.js";
23
25
 
24
26
  /** Info about the batch we learn when we process the first message */
25
27
  export interface BatchStartInfo {
@@ -236,32 +238,16 @@ export class RemoteMessageProcessor {
236
238
  }
237
239
 
238
240
  /**
239
- * Takes an incoming message and if the contents is a string, JSON.parse's it in place
241
+ * Takes an incoming runtime message JSON.parse's its contents in place, if needed (old Loader does this for us).
242
+ * Only to be used for runtine messages.
243
+ * @remarks - Serialization during submit happens via {@link serializeOpContents}
240
244
  * @param mutableMessage - op message received
241
- * @param hasModernRuntimeMessageEnvelope - false if the message does not contain the modern op envelop where message.type is MessageType.Operation
242
- * @param logLegacyCase - callback to log when legacy op is encountered
243
245
  */
244
- export function ensureContentsDeserialized(
245
- mutableMessage: ISequencedDocumentMessage,
246
- hasModernRuntimeMessageEnvelope: boolean,
247
- logLegacyCase: (codePath: string) => void,
248
- ): void {
249
- // This should become unconditional once (Loader LTS) DeltaManager.processInboundMessage() stops parsing content (ADO #12052)
250
- // Note: Until that change is made in the loader, this case will never be hit.
251
- // Then there will be a long time of needing both cases, until LTS catches up to the change.
252
- let didParseJsonContents: boolean;
246
+ export function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void {
247
+ // This should become unconditional once Loader LTS reaches 2.4 or later.
248
+ // There will be a long time of needing both cases, until LTS advances to that point.
253
249
  if (typeof mutableMessage.contents === "string" && mutableMessage.contents !== "") {
254
250
  mutableMessage.contents = JSON.parse(mutableMessage.contents);
255
- didParseJsonContents = true;
256
- } else {
257
- didParseJsonContents = false;
258
- }
259
-
260
- // The DeltaManager parses the contents of the message as JSON if it is a string,
261
- // so we should never end up parsing it here.
262
- // Let's observe if we are wrong about this to learn about these cases.
263
- if (didParseJsonContents) {
264
- logLegacyCase("ensureContentsDeserialized_foundJsonContents");
265
251
  }
266
252
  }
267
253
 
@@ -12,7 +12,7 @@ export const opSize = (op: ISequencedDocumentMessage): number => {
12
12
  // Some messages may already have string contents,
13
13
  // so stringifying them again will add inaccurate overhead.
14
14
  const content =
15
- typeof op.contents === "string" ? op.contents : JSON.stringify(op.contents) ?? "";
15
+ typeof op.contents === "string" ? op.contents : (JSON.stringify(op.contents) ?? "");
16
16
  const data = opHasData(op) ? op.data : "";
17
17
  return content.length + data.length;
18
18
  };
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.4.0-299707";
9
+ export const pkgVersion = "2.5.0-302463";
@@ -489,7 +489,7 @@ export class DocumentsSchemaController {
489
489
  // Latter is importnat sure that's what will go into summary.
490
490
  this.documentSchema = !existing
491
491
  ? this.desiredSchema
492
- : (documentMetadataSchema as IDocumentSchemaCurrent) ??
492
+ : ((documentMetadataSchema as IDocumentSchemaCurrent) ??
493
493
  ({
494
494
  version: currentDocumentVersionSchema,
495
495
  // see comment in summarizeDocumentSchema() on why it has to stay zero
@@ -499,7 +499,7 @@ export class DocumentsSchemaController {
499
499
  runtime: {
500
500
  explicitSchemaControl: boolToProp(!existing && features.explicitSchemaControl),
501
501
  },
502
- } satisfies IDocumentSchemaCurrent);
502
+ } satisfies IDocumentSchemaCurrent));
503
503
 
504
504
  checkRuntimeCompatibility(this.documentSchema, "document");
505
505
  this.validateSeqNumber(this.documentSchema.refSeq, snapshotSequenceNumber, "summary");
@@ -353,11 +353,10 @@ export class SummaryCollection extends TypedEventEmitter<ISummaryCollectionOpEve
353
353
  }
354
354
 
355
355
  private parseContent(op: ISequencedDocumentMessage) {
356
- // This should become unconditional once (Loader LTS) DeltaManager.processInboundMessage() stops parsing content (ADO #12052)
357
- // Note: Until that change is made in the loader, this case will never be hit.
358
- // Then there will be a long time of needing both cases, until LTS catches up to the change.
356
+ // This should become unconditional once (Loader LTS) reaches 2.4 or later
357
+ // There will be a long time of needing both cases, until LTS catches up to the change.
359
358
  // That said, we may instead move to listen for "op" events from ContainerRuntime,
360
- // and parsing may not be required at all if ContainerRuntime.process() would parse it for all types of ops.
359
+ // and parsing may not be required at all if ContainerRuntime.process() continues to parse it for all types of ops.
361
360
  if (typeof op.contents === "string") {
362
361
  op.contents = JSON.parse(op.contents);
363
362
  }