@fluidframework/container-runtime 2.114.0 → 2.115.0

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 (78) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-extractor/api-extractor-lint-legacyAlpha.cjs.json +5 -0
  3. package/api-extractor/api-extractor-lint-legacyAlpha.esm.json +5 -0
  4. package/api-extractor/api-extractor.legacy.json +5 -1
  5. package/api-report/container-runtime.legacy.alpha.api.md +453 -0
  6. package/container-runtime.test-files.tar +0 -0
  7. package/dist/containerRuntime.d.ts +7 -0
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +100 -43
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/legacy.d.ts +1 -1
  15. package/dist/legacyAlpha.d.ts +76 -0
  16. package/dist/packageVersion.d.ts +1 -1
  17. package/dist/packageVersion.js +1 -1
  18. package/dist/packageVersion.js.map +1 -1
  19. package/dist/pendingStateManager.d.ts +7 -0
  20. package/dist/pendingStateManager.d.ts.map +1 -1
  21. package/dist/pendingStateManager.js +15 -0
  22. package/dist/pendingStateManager.js.map +1 -1
  23. package/dist/public.d.ts +1 -1
  24. package/dist/runtimeLayerCompatState.d.ts +2 -2
  25. package/dist/versionMarks/inboundBatch.d.ts +36 -0
  26. package/dist/versionMarks/inboundBatch.d.ts.map +1 -0
  27. package/dist/versionMarks/inboundBatch.js +50 -0
  28. package/dist/versionMarks/inboundBatch.js.map +1 -0
  29. package/dist/versionMarks/index.d.ts +7 -0
  30. package/dist/versionMarks/index.d.ts.map +1 -0
  31. package/dist/versionMarks/index.js +12 -0
  32. package/dist/versionMarks/index.js.map +1 -0
  33. package/dist/versionMarks/versionMarkResolver.d.ts +162 -0
  34. package/dist/versionMarks/versionMarkResolver.d.ts.map +1 -0
  35. package/dist/versionMarks/versionMarkResolver.js +198 -0
  36. package/dist/versionMarks/versionMarkResolver.js.map +1 -0
  37. package/internal.d.ts +1 -1
  38. package/legacy/alpha.d.ts +11 -0
  39. package/legacy.d.ts +1 -1
  40. package/lib/containerRuntime.d.ts +7 -0
  41. package/lib/containerRuntime.d.ts.map +1 -1
  42. package/lib/containerRuntime.js +57 -0
  43. package/lib/containerRuntime.js.map +1 -1
  44. package/lib/index.d.ts +1 -0
  45. package/lib/index.d.ts.map +1 -1
  46. package/lib/index.js.map +1 -1
  47. package/lib/legacy.d.ts +1 -1
  48. package/lib/legacyAlpha.d.ts +76 -0
  49. package/lib/packageVersion.d.ts +1 -1
  50. package/lib/packageVersion.js +1 -1
  51. package/lib/packageVersion.js.map +1 -1
  52. package/lib/pendingStateManager.d.ts +7 -0
  53. package/lib/pendingStateManager.d.ts.map +1 -1
  54. package/lib/pendingStateManager.js +15 -0
  55. package/lib/pendingStateManager.js.map +1 -1
  56. package/lib/public.d.ts +1 -1
  57. package/lib/runtimeLayerCompatState.d.ts +2 -2
  58. package/lib/versionMarks/inboundBatch.d.ts +36 -0
  59. package/lib/versionMarks/inboundBatch.d.ts.map +1 -0
  60. package/lib/versionMarks/inboundBatch.js +46 -0
  61. package/lib/versionMarks/inboundBatch.js.map +1 -0
  62. package/lib/versionMarks/index.d.ts +7 -0
  63. package/lib/versionMarks/index.d.ts.map +1 -0
  64. package/lib/versionMarks/index.js +7 -0
  65. package/lib/versionMarks/index.js.map +1 -0
  66. package/lib/versionMarks/versionMarkResolver.d.ts +162 -0
  67. package/lib/versionMarks/versionMarkResolver.d.ts.map +1 -0
  68. package/lib/versionMarks/versionMarkResolver.js +194 -0
  69. package/lib/versionMarks/versionMarkResolver.js.map +1 -0
  70. package/package.json +32 -20
  71. package/src/containerRuntime.ts +87 -0
  72. package/src/index.ts +5 -0
  73. package/src/packageVersion.ts +1 -1
  74. package/src/pendingStateManager.ts +18 -0
  75. package/src/versionMarks/DEV.md +329 -0
  76. package/src/versionMarks/inboundBatch.ts +70 -0
  77. package/src/versionMarks/index.ts +13 -0
  78. package/src/versionMarks/versionMarkResolver.ts +341 -0
@@ -39,8 +39,9 @@ const pendingStateManager_js_1 = require("./pendingStateManager.js");
39
39
  const runCounter_js_1 = require("./runCounter.js");
40
40
  const runtimeLayerCompatState_js_1 = require("./runtimeLayerCompatState.js");
41
41
  const signalTelemetryProcessing_js_1 = require("./signalTelemetryProcessing.js");
42
+ const index_js_4 = require("./versionMarks/index.js");
42
43
  // These types are imported as types here because they are present in summaryDelayLoadedModule, which is loaded dynamically when required.
43
- const index_js_4 = require("./summary/index.js");
44
+ const index_js_5 = require("./summary/index.js");
44
45
  const throttler_js_1 = require("./throttler.js");
45
46
  /**
46
47
  * ContainerRuntime's compatibility details that is exposed to Container Extensions.
@@ -345,12 +346,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
345
346
  }
346
347
  };
347
348
  const [chunks, recentBatchInfo, metadata, electedSummarizerData, aliases, serializedIdCompressor,] = await Promise.all([
348
- tryFetchBlob(index_js_4.chunksBlobName),
349
- tryFetchBlob(index_js_4.recentBatchInfoBlobName),
350
- tryFetchBlob(index_js_4.metadataBlobName),
351
- tryFetchBlob(index_js_4.electedSummarizerBlobName),
352
- tryFetchBlob(index_js_4.aliasBlobName),
353
- tryFetchBlob(index_js_4.idCompressorBlobName),
349
+ tryFetchBlob(index_js_5.chunksBlobName),
350
+ tryFetchBlob(index_js_5.recentBatchInfoBlobName),
351
+ tryFetchBlob(index_js_5.metadataBlobName),
352
+ tryFetchBlob(index_js_5.electedSummarizerBlobName),
353
+ tryFetchBlob(index_js_5.aliasBlobName),
354
+ tryFetchBlob(index_js_5.idCompressorBlobName),
354
355
  ]);
355
356
  // read snapshot blobs needed for BlobManager to load
356
357
  const blobManagerLoadInfo = await (0, index_js_1.loadBlobManagerLoadInfo)(context);
@@ -456,7 +457,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
456
457
  };
457
458
  const compressionLz4 = compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY &&
458
459
  compressionOptions.compressionAlgorithm === "lz4";
459
- const documentSchemaController = new index_js_4.DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
460
+ const documentSchemaController = new index_js_5.DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
460
461
  explicitSchemaControl,
461
462
  compressionLz4,
462
463
  idCompressorMode,
@@ -592,6 +593,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
592
593
  get disposed() {
593
594
  return this._disposed;
594
595
  }
596
+ /**
597
+ * Host-facing version mark resolver.
598
+ */
599
+ get versionMarkResolver() {
600
+ return this.versionMarkResolverInternal;
601
+ }
595
602
  /**
596
603
  * If false, loading or using a Tombstoned object should merely log, not fail.
597
604
  * @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
@@ -622,7 +629,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
622
629
  // // eslint-disable-next-line unicorn/no-object-as-default-parameter
623
630
  summaryConfiguration = {
624
631
  // the defaults
625
- ...index_js_4.DefaultSummaryConfiguration,
632
+ ...index_js_5.DefaultSummaryConfiguration,
626
633
  // the runtime configuration overrides
627
634
  ...runtimeOptions.summaryOptions?.summaryConfigOverrides,
628
635
  }, recentBatchInfo) {
@@ -902,7 +909,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
902
909
  // Values are generally expected to be set from the runtime side.
903
910
  this.options = options ?? {};
904
911
  this.clientDetails = clientDetails;
905
- this.isSummarizerClient = this.clientDetails.type === index_js_4.summarizerClientType;
912
+ this.isSummarizerClient = this.clientDetails.type === index_js_5.summarizerClientType;
906
913
  this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
907
914
  this._getClientId = () => context.clientId;
908
915
  this._getAttachState = () => context.attachState;
@@ -991,6 +998,42 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
991
998
  this.notifyReadOnlyState();
992
999
  },
993
1000
  });
1001
+ // fetchOps is an internal-only capability the loader injects (IContainerContextInternal), not part
1002
+ // of the public IContainerContext contract, so narrow to read it.
1003
+ const fetchOps = context.fetchOps;
1004
+ this.versionMarkResolverInternal = new index_js_4.VersionMarkResolver({
1005
+ getCurrentSequenceNumber: () => this.deltaManager.lastSequenceNumber,
1006
+ getCurrentMinimumSequenceNumber: () => this.deltaManager.minimumSequenceNumber,
1007
+ getCurrentPendingBatchId: () => this.pendingStateManager.getMostRecentPendingBatchId(),
1008
+ logger: (0, internal_8.createChildLogger)({
1009
+ logger: this.baseLogger,
1010
+ namespace: "VersionMarkResolver",
1011
+ }),
1012
+ // Seal the current outbound batch so a just-submitted edit gets a stable batchId in the pending
1013
+ // state before sealAndCaptureVersionMark reads it (a batchId is only assigned when flushed).
1014
+ flushPendingBatch: () => this.flush(),
1015
+ // Wire the container-provided op reader (if any) so resolution can read historical ops.
1016
+ getHistoricalOpReader: fetchOps ? () => ({ fetchMessages: fetchOps }) : undefined,
1017
+ // Unpack scanned historical ops through the same pipeline the live inbound path uses, so a
1018
+ // chunked batch's batchId (only restored after reassembly) is observed by the history scan too.
1019
+ createHistoricalOpUnpacker: fetchOps
1020
+ ? () => {
1021
+ // A fresh processor per scan: it holds chunk-reassembly state, so it must not share
1022
+ // the live inbound processor's state. submit/size args are unused on the read path.
1023
+ const scanProcessor = new index_js_3.RemoteMessageProcessor(new index_js_3.OpSplitter([], submitBatchFn, runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger), new index_js_3.OpDecompressor(this.mc.logger), new index_js_3.OpGroupingManager({ groupedBatchingEnabled: this.groupedBatchingEnabled }, this.mc.logger));
1024
+ return (op) => {
1025
+ // Mirror the live path: only modern runtime-envelope ops (type Operation, with a
1026
+ // client id) carry batches; skip system/server ops.
1027
+ if (op.type !== internal_3.MessageType.Operation || typeof op.clientId !== "string") {
1028
+ return undefined;
1029
+ }
1030
+ const messageCopy = { ...op };
1031
+ (0, index_js_3.ensureContentsDeserialized)(messageCopy);
1032
+ return scanProcessor.process(messageCopy, (0, exports.getSingleUseLegacyLogCallback)(this.mc.logger, messageCopy.type));
1033
+ };
1034
+ }
1035
+ : undefined,
1036
+ });
994
1037
  let outerDeltaManager = this.innerDeltaManager;
995
1038
  this.useDeltaManagerOpsProxy =
996
1039
  this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
@@ -1009,10 +1052,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1009
1052
  : new deltaManagerProxies_js_1.BaseDeltaManagerProxy(outerDeltaManager);
1010
1053
  this.handleContext = new containerHandleContext_js_1.ContainerFluidHandleContext("", this);
1011
1054
  if (this.summaryConfiguration.state === "enabled") {
1012
- (0, index_js_4.validateSummaryHeuristicConfiguration)(this.summaryConfiguration);
1055
+ (0, index_js_5.validateSummaryHeuristicConfiguration)(this.summaryConfiguration);
1013
1056
  }
1014
1057
  this.summariesDisabled =
1015
- (0, index_js_4.isSummariesDisabled)(this.summaryConfiguration) ||
1058
+ (0, index_js_5.isSummariesDisabled)(this.summaryConfiguration) ||
1016
1059
  this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.DisableSummaries") === true;
1017
1060
  this.maxConsecutiveReconnects =
1018
1061
  this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? defaultMaxConsecutiveReconnects;
@@ -1096,7 +1139,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1096
1139
  const summaryReferenceSequenceNumber = baseSnapshot === undefined || metadata?.disableIsolatedChannels === true
1097
1140
  ? undefined
1098
1141
  : loadedFromSequenceNumber;
1099
- this.summarizerNode = (0, index_js_4.createRootSummarizerNodeWithGC)((0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "SummarizerNode" }),
1142
+ this.summarizerNode = (0, index_js_5.createRootSummarizerNodeWithGC)((0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "SummarizerNode" }),
1100
1143
  // Summarize function to call when summarize is called. Summarizer node always tracks summary state.
1101
1144
  async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
1102
1145
  // Latest change sequence number, no changes since summary applied yet
@@ -1337,7 +1380,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1337
1380
  this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
1338
1381
  return;
1339
1382
  }
1340
- const { maxOpsSinceLastSummary = 0, initialSummarizerDelayMs = 0 } = (0, index_js_4.isSummariesDisabled)(this.summaryConfiguration)
1383
+ const { maxOpsSinceLastSummary = 0, initialSummarizerDelayMs = 0 } = (0, index_js_5.isSummariesDisabled)(this.summaryConfiguration)
1341
1384
  ? {}
1342
1385
  : {
1343
1386
  ...this.summaryConfiguration,
@@ -1347,8 +1390,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1347
1390
  this.runtimeOptions.summaryOptions.initialSummarizerDelayMs ??
1348
1391
  this.summaryConfiguration.initialSummarizerDelayMs,
1349
1392
  };
1350
- const summaryCollection = new index_js_4.SummaryCollection(this.deltaManager, this.baseLogger);
1351
- const onRequestMode = (0, index_js_4.isSummaryOnRequest)(this.summaryConfiguration);
1393
+ const summaryCollection = new index_js_5.SummaryCollection(this.deltaManager, this.baseLogger);
1394
+ const onRequestMode = (0, index_js_5.isSummaryOnRequest)(this.summaryConfiguration);
1352
1395
  if (this.isSummarizerClient) {
1353
1396
  // We want to dynamically import any thing inside summaryDelayLoadedModule module only when we are the summarizer client,
1354
1397
  // so that all non summarizer clients don't have to load the code inside this module.
@@ -1370,16 +1413,16 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1370
1413
  () => this.innerDeltaManager.active));
1371
1414
  }
1372
1415
  else if (!onRequestMode &&
1373
- index_js_4.SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
1416
+ index_js_5.SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
1374
1417
  // Only create a SummaryManager and SummarizerClientElection
1375
1418
  // if summaries are enabled and we are not the summarizer client.
1376
1419
  const orderedClientLogger = (0, internal_8.createChildLogger)({
1377
1420
  logger: this.baseLogger,
1378
1421
  namespace: "OrderedClientElection",
1379
1422
  });
1380
- const orderedClientCollection = new index_js_4.OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
1381
- const orderedClientElectionForSummarizer = new index_js_4.OrderedClientElection(orderedClientLogger, orderedClientCollection, this.electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, index_js_4.SummarizerClientElection.isClientEligible, this.mc.config.getBoolean("Fluid.ContainerRuntime.OrderedClientElection.EnablePerformanceEvents"));
1382
- this.summarizerClientElection = new index_js_4.SummarizerClientElection(orderedClientLogger, summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary);
1423
+ const orderedClientCollection = new index_js_5.OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
1424
+ const orderedClientElectionForSummarizer = new index_js_5.OrderedClientElection(orderedClientLogger, orderedClientCollection, this.electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, index_js_5.SummarizerClientElection.isClientEligible, this.mc.config.getBoolean("Fluid.ContainerRuntime.OrderedClientElection.EnablePerformanceEvents"));
1425
+ this.summarizerClientElection = new index_js_5.SummarizerClientElection(orderedClientLogger, summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary);
1383
1426
  const defaultAction = () => {
1384
1427
  if (summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
1385
1428
  this.mc.logger.sendTelemetryEvent({
@@ -1402,8 +1445,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1402
1445
  };
1403
1446
  summaryCollection.on("default", defaultAction);
1404
1447
  // Create the SummaryManager and mark the initial state
1405
- this.summaryManager = new index_js_4.SummaryManager(this.summarizerClientElection, this, // IConnectedState
1406
- summaryCollection, this.baseLogger, (0, index_js_4.formCreateSummarizerFn)(loader), new throttler_js_1.Throttler(60 * 1000, // 60 sec delay window
1448
+ this.summaryManager = new index_js_5.SummaryManager(this.summarizerClientElection, this, // IConnectedState
1449
+ summaryCollection, this.baseLogger, (0, index_js_5.formCreateSummarizerFn)(loader), new throttler_js_1.Throttler(60 * 1000, // 60 sec delay window
1407
1450
  30 * 1000, // 30 sec max delay
1408
1451
  // throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
1409
1452
  (0, throttler_js_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
@@ -1484,7 +1527,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1484
1527
  }),
1485
1528
  });
1486
1529
  // Find the snapshotTree inside the returned snapshot based on the path as given in the request.
1487
- const hasIsolatedChannels = (0, index_js_4.rootHasIsolatedChannels)(this.metadata);
1530
+ const hasIsolatedChannels = (0, index_js_5.rootHasIsolatedChannels)(this.metadata);
1488
1531
  const snapshotTreeForPath = this.getSnapshotTreeForPath(snapshot.snapshotTree, pathParts, hasIsolatedChannels);
1489
1532
  (0, internal_2.assert)(snapshotTreeForPath !== undefined, 0x8ef /* no snapshotTree for the path */);
1490
1533
  const snapshotSeqNumber = snapshot.sequenceNumber;
@@ -1563,7 +1606,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1563
1606
  try {
1564
1607
  const parser = internal_7.RequestParser.create(request);
1565
1608
  const id = parser.pathParts[0];
1566
- if (id === index_js_4.summarizerRequestUrl && parser.pathParts.length === 1) {
1609
+ if (id === index_js_5.summarizerRequestUrl && parser.pathParts.length === 1) {
1567
1610
  if (this._summarizer !== undefined) {
1568
1611
  return {
1569
1612
  status: 200,
@@ -1633,7 +1676,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1633
1676
  addMetadataToSummary(summaryTree) {
1634
1677
  // The last message processed at the time of summary. If there are no new messages, use the message from the
1635
1678
  // last summary.
1636
- const message = (0, index_js_4.extractSummaryMetadataMessage)(this.deltaManager.lastMessage) ??
1679
+ const message = (0, index_js_5.extractSummaryMetadataMessage)(this.deltaManager.lastMessage) ??
1637
1680
  this.messageAtLastSummary;
1638
1681
  const documentSchema = this.documentsSchemaController.summarizeDocumentSchema(this.deltaManager.lastSequenceNumber);
1639
1682
  // Is document schema explicit control on?
@@ -1656,29 +1699,29 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
1656
1699
  lastMessage: explicitSchemaControl ? message : undefined,
1657
1700
  documentSchema,
1658
1701
  };
1659
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.metadataBlobName, JSON.stringify(metadata));
1702
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.metadataBlobName, JSON.stringify(metadata));
1660
1703
  }
1661
1704
  addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
1662
1705
  this.addMetadataToSummary(summaryTree);
1663
1706
  if (this._idCompressor) {
1664
1707
  const idCompressorState = JSON.stringify(this._idCompressor.serialize(false));
1665
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.idCompressorBlobName, idCompressorState);
1708
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.idCompressorBlobName, idCompressorState);
1666
1709
  }
1667
1710
  if (this.remoteMessageProcessor.partialMessages.size > 0) {
1668
1711
  const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
1669
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.chunksBlobName, content);
1712
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.chunksBlobName, content);
1670
1713
  }
1671
1714
  const recentBatchInfo = this.duplicateBatchDetector?.getRecentBatchInfoForSummary(telemetryContext);
1672
1715
  if (recentBatchInfo !== undefined) {
1673
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.recentBatchInfoBlobName, JSON.stringify(recentBatchInfo));
1716
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.recentBatchInfoBlobName, JSON.stringify(recentBatchInfo));
1674
1717
  }
1675
1718
  const dataStoreAliases = this.channelCollection.aliases;
1676
1719
  if (dataStoreAliases.size > 0) {
1677
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.aliasBlobName, JSON.stringify([...dataStoreAliases]));
1720
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.aliasBlobName, JSON.stringify([...dataStoreAliases]));
1678
1721
  }
1679
1722
  if (this.summarizerClientElection) {
1680
1723
  const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
1681
- (0, internal_7.addBlobToSummary)(summaryTree, index_js_4.electedSummarizerBlobName, electedSummarizerContent);
1724
+ (0, internal_7.addBlobToSummary)(summaryTree, index_js_5.electedSummarizerBlobName, electedSummarizerContent);
1682
1725
  }
1683
1726
  const blobManagerSummary = this.blobManager.summarize();
1684
1727
  // Some storage (like git) doesn't allow empty tree, so we can omit it.
@@ -2054,6 +2097,20 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2054
2097
  // or to check to ensure the *remote* messages don't match the batchId of a pending local batch.
2055
2098
  // This latter case would indicate that the container has forked - two copies are trying to persist the same local changes.
2056
2099
  let messagesWithPendingState = this.pendingStateManager.processInboundMessages(inboundResult, local);
2100
+ // Record the version-mark point for a completed batch (its last op's sequence number), carrying
2101
+ // the batch id across a piecemeal batch's messages. This runs only AFTER processInboundMessages
2102
+ // has validated the batch: that call throws (fork detection / pending-content mismatch) for a
2103
+ // batch that must be rejected. processInboundBatch synchronously notifies listeners, which an
2104
+ // app uses to promote a mark in an external store - an irreversible side effect - so it must not
2105
+ // fire for a batch validation is about to reject. Gated on isTracking so a container that never
2106
+ // uses version marks does no per-batch work here (mirrors #22497's DuplicateBatchDetector gating).
2107
+ if (this.versionMarkResolverInternal.isTracking) {
2108
+ const versionMarkUpdate = (0, index_js_4.inboundVersionMarkUpdate)(inboundResult, this.versionMarkInboundBatchId);
2109
+ if (versionMarkUpdate.sequenced !== undefined) {
2110
+ this.versionMarkResolverInternal.processInboundBatch(versionMarkUpdate.sequenced.batchId, versionMarkUpdate.sequenced.sequenceNumber);
2111
+ }
2112
+ this.versionMarkInboundBatchId = versionMarkUpdate.carriedBatchId;
2113
+ }
2057
2114
  if (inboundResult.type !== "fullBatch") {
2058
2115
  (0, internal_2.assert)(messagesWithPendingState.length === 1, 0xa3d /* Partial batch should have exactly one message */);
2059
2116
  }
@@ -2591,7 +2648,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2591
2648
  }
2592
2649
  const summarizeResult = this.channelCollection.getAttachSummary(telemetryContext);
2593
2650
  // Wrap data store summaries in .channels subtree.
2594
- (0, index_js_4.wrapSummaryInChannelsTree)(summarizeResult);
2651
+ (0, index_js_5.wrapSummaryInChannelsTree)(summarizeResult);
2595
2652
  this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
2596
2653
  return summarizeResult.summary;
2597
2654
  }
@@ -2604,7 +2661,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2604
2661
  async summarizeInternal(fullTree, trackState, telemetryContext) {
2605
2662
  const summarizeResult = await this.channelCollection.summarize(fullTree, trackState, telemetryContext);
2606
2663
  // Wrap data store summaries in .channels subtree.
2607
- (0, index_js_4.wrapSummaryInChannelsTree)(summarizeResult);
2664
+ (0, index_js_5.wrapSummaryInChannelsTree)(summarizeResult);
2608
2665
  const pathPartsForChildren = [internal_6.channelsTreeName];
2609
2666
  this.loadIdCompressor();
2610
2667
  this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
@@ -2888,7 +2945,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2888
2945
  stage: "base",
2889
2946
  referenceSequenceNumber: summaryRefSeqNum,
2890
2947
  minimumSequenceNumber,
2891
- error: new index_js_4.RetriableSummaryError(`Summarizer node state inconsistent with summarizer state.`),
2948
+ error: new index_js_5.RetriableSummaryError(`Summarizer node state inconsistent with summarizer state.`),
2892
2949
  };
2893
2950
  }
2894
2951
  }
@@ -2932,7 +2989,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2932
2989
  stage: "base",
2933
2990
  referenceSequenceNumber: summaryRefSeqNum,
2934
2991
  minimumSequenceNumber,
2935
- error: new index_js_4.RetriableSummaryError(continueResult.error),
2992
+ error: new index_js_5.RetriableSummaryError(continueResult.error),
2936
2993
  };
2937
2994
  }
2938
2995
  const trace = client_utils_1.Trace.start();
@@ -2951,14 +3008,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
2951
3008
  stage: "base",
2952
3009
  referenceSequenceNumber: summaryRefSeqNum,
2953
3010
  minimumSequenceNumber,
2954
- error: (0, internal_8.wrapError)(error, (msg) => new index_js_4.RetriableSummaryError(msg)),
3011
+ error: (0, internal_8.wrapError)(error, (msg) => new index_js_5.RetriableSummaryError(msg)),
2955
3012
  };
2956
3013
  }
2957
3014
  // Validate that the summary generated by summarizer nodes is correct before uploading.
2958
3015
  const validateResult = this.summarizerNode.validateSummary();
2959
3016
  if (!validateResult.success) {
2960
3017
  const { success, ...loggingProps } = validateResult;
2961
- const error = new index_js_4.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
3018
+ const error = new index_js_5.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
2962
3019
  return {
2963
3020
  stage: "base",
2964
3021
  referenceSequenceNumber: summaryRefSeqNum,
@@ -3005,7 +3062,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3005
3062
  return {
3006
3063
  stage: "generate",
3007
3064
  ...generateSummaryData,
3008
- error: new index_js_4.RetriableSummaryError(continueResult.error),
3065
+ error: new index_js_5.RetriableSummaryError(continueResult.error),
3009
3066
  };
3010
3067
  }
3011
3068
  const summaryContext = {
@@ -3021,7 +3078,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3021
3078
  return {
3022
3079
  stage: "generate",
3023
3080
  ...generateSummaryData,
3024
- error: (0, internal_8.wrapError)(error, (msg) => new index_js_4.RetriableSummaryError(msg)),
3081
+ error: (0, internal_8.wrapError)(error, (msg) => new index_js_5.RetriableSummaryError(msg)),
3025
3082
  };
3026
3083
  }
3027
3084
  const parent = summaryContext.ackHandle;
@@ -3042,7 +3099,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3042
3099
  return {
3043
3100
  stage: "upload",
3044
3101
  ...uploadData,
3045
- error: new index_js_4.RetriableSummaryError(continueResult.error),
3102
+ error: new index_js_5.RetriableSummaryError(continueResult.error),
3046
3103
  };
3047
3104
  }
3048
3105
  let clientSequenceNumber;
@@ -3053,7 +3110,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3053
3110
  return {
3054
3111
  stage: "upload",
3055
3112
  ...uploadData,
3056
- error: (0, internal_8.wrapError)(error, (msg) => new index_js_4.RetriableSummaryError(msg)),
3113
+ error: (0, internal_8.wrapError)(error, (msg) => new index_js_5.RetriableSummaryError(msg)),
3057
3114
  };
3058
3115
  }
3059
3116
  const submitData = {
@@ -3069,7 +3126,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3069
3126
  return {
3070
3127
  stage: "upload",
3071
3128
  ...uploadData,
3072
- error: (0, internal_8.wrapError)(error, (msg) => new index_js_4.RetriableSummaryError(msg)),
3129
+ error: (0, internal_8.wrapError)(error, (msg) => new index_js_5.RetriableSummaryError(msg)),
3073
3130
  };
3074
3131
  }
3075
3132
  return submitData;
@@ -3117,7 +3174,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
3117
3174
  // based on telemetry while we decide on a stable number.
3118
3175
  const retryDelayMs = this.mc.config.getNumber("Fluid.Summarizer.PendingOpsRetryDelayMs") ??
3119
3176
  exports.defaultPendingOpsRetryDelayMs;
3120
- const error = new index_js_4.RetriableSummaryError("PendingOpsWhileSummarizing", retryDelayMs / 1000, {
3177
+ const error = new index_js_5.RetriableSummaryError("PendingOpsWhileSummarizing", retryDelayMs / 1000, {
3121
3178
  count: this.pendingMessagesCount,
3122
3179
  beforeGenerate: beforeSummaryGeneration,
3123
3180
  });