@fluidframework/container-loader 2.70.0-361248 → 2.70.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 (86) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/api-report/container-loader.legacy.alpha.api.md +13 -0
  3. package/dist/container.d.ts +7 -4
  4. package/dist/container.d.ts.map +1 -1
  5. package/dist/container.js +92 -16
  6. package/dist/container.js.map +1 -1
  7. package/dist/containerContext.d.ts +1 -0
  8. package/dist/containerContext.d.ts.map +1 -1
  9. package/dist/containerContext.js +1 -0
  10. package/dist/containerContext.js.map +1 -1
  11. package/dist/containerStorageAdapter.d.ts +1 -8
  12. package/dist/containerStorageAdapter.d.ts.map +1 -1
  13. package/dist/containerStorageAdapter.js +0 -9
  14. package/dist/containerStorageAdapter.js.map +1 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/legacyAlpha.d.ts +1 -0
  20. package/dist/loaderLayerCompatState.d.ts +4 -3
  21. package/dist/loaderLayerCompatState.d.ts.map +1 -1
  22. package/dist/loaderLayerCompatState.js +4 -34
  23. package/dist/loaderLayerCompatState.js.map +1 -1
  24. package/dist/packageVersion.d.ts +1 -1
  25. package/dist/packageVersion.d.ts.map +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/dist/pendingLocalStateStore.d.ts +84 -0
  29. package/dist/pendingLocalStateStore.d.ts.map +1 -0
  30. package/dist/pendingLocalStateStore.js +157 -0
  31. package/dist/pendingLocalStateStore.js.map +1 -0
  32. package/dist/protocol.d.ts +1 -0
  33. package/dist/protocol.d.ts.map +1 -1
  34. package/dist/protocol.js +43 -1
  35. package/dist/protocol.js.map +1 -1
  36. package/dist/utils.d.ts +1 -0
  37. package/dist/utils.d.ts.map +1 -1
  38. package/dist/utils.js +0 -4
  39. package/dist/utils.js.map +1 -1
  40. package/lib/container.d.ts +7 -4
  41. package/lib/container.d.ts.map +1 -1
  42. package/lib/container.js +93 -17
  43. package/lib/container.js.map +1 -1
  44. package/lib/containerContext.d.ts +1 -0
  45. package/lib/containerContext.d.ts.map +1 -1
  46. package/lib/containerContext.js +1 -0
  47. package/lib/containerContext.js.map +1 -1
  48. package/lib/containerStorageAdapter.d.ts +1 -8
  49. package/lib/containerStorageAdapter.d.ts.map +1 -1
  50. package/lib/containerStorageAdapter.js +0 -9
  51. package/lib/containerStorageAdapter.js.map +1 -1
  52. package/lib/index.d.ts +1 -0
  53. package/lib/index.d.ts.map +1 -1
  54. package/lib/index.js +1 -0
  55. package/lib/index.js.map +1 -1
  56. package/lib/legacyAlpha.d.ts +1 -0
  57. package/lib/loaderLayerCompatState.d.ts +4 -3
  58. package/lib/loaderLayerCompatState.d.ts.map +1 -1
  59. package/lib/loaderLayerCompatState.js +5 -35
  60. package/lib/loaderLayerCompatState.js.map +1 -1
  61. package/lib/packageVersion.d.ts +1 -1
  62. package/lib/packageVersion.d.ts.map +1 -1
  63. package/lib/packageVersion.js +1 -1
  64. package/lib/packageVersion.js.map +1 -1
  65. package/lib/pendingLocalStateStore.d.ts +84 -0
  66. package/lib/pendingLocalStateStore.d.ts.map +1 -0
  67. package/lib/pendingLocalStateStore.js +153 -0
  68. package/lib/pendingLocalStateStore.js.map +1 -0
  69. package/lib/protocol.d.ts +1 -0
  70. package/lib/protocol.d.ts.map +1 -1
  71. package/lib/protocol.js +41 -0
  72. package/lib/protocol.js.map +1 -1
  73. package/lib/utils.d.ts +1 -0
  74. package/lib/utils.d.ts.map +1 -1
  75. package/lib/utils.js +0 -4
  76. package/lib/utils.js.map +1 -1
  77. package/package.json +11 -11
  78. package/src/container.ts +129 -31
  79. package/src/containerContext.ts +2 -0
  80. package/src/containerStorageAdapter.ts +1 -11
  81. package/src/index.ts +1 -0
  82. package/src/loaderLayerCompatState.ts +21 -36
  83. package/src/packageVersion.ts +1 -1
  84. package/src/pendingLocalStateStore.ts +160 -0
  85. package/src/protocol.ts +49 -0
  86. package/src/utils.ts +6 -0
package/lib/container.js CHANGED
@@ -28,7 +28,7 @@ import { validateDriverCompatibility, validateRuntimeCompatibility, } from "./lo
28
28
  import { createMemoryDetachedBlobStorage, tryInitializeMemoryDetachedBlobStorage, } from "./memoryBlobStorage.js";
29
29
  import { NoopHeuristic } from "./noopHeuristic.js";
30
30
  import { pkgVersion } from "./packageVersion.js";
31
- import { ProtocolHandler, protocolHandlerShouldProcessSignal, } from "./protocol.js";
31
+ import { ProtocolHandler, protocolHandlerShouldProcessSignal, wrapProtocolHandlerBuilder, } from "./protocol.js";
32
32
  import { initQuorumValuesFromCodeDetails } from "./quorum.js";
33
33
  import { SerializedStateManager, } from "./serializedStateManager.js";
34
34
  import { combineAppAndProtocolSummary, combineSnapshotTreeAndSnapshotBlobs, getDetachedContainerStateFromSerializedContainer, getDocumentAttributes, getProtocolSnapshotTree, getISnapshotFromSerializedContainer, runSingle, convertISnapshotToSnapshotWithBlobs, convertSnapshotInfoToSnapshot, } from "./utils.js";
@@ -208,12 +208,16 @@ export class Container extends EventEmitterWithErrorHandling {
208
208
  // in the order that is not possible in real life, that it may not expect.
209
209
  // Ideally, we should supply pendingLocalState?.clientId here as well, not in constructor, but it does not matter (at least today)
210
210
  this.connectionStateHandler.initProtocol(this.protocolHandler);
211
- // Propagate current connection state through the system.
212
- const readonly = this.readOnlyInfo.readonly ?? false;
213
211
  // This call does not look like needed any more, with delaying all connection-related events past loaded phase.
214
212
  // Yet, there could be some customer code that would break if we do not deliver it.
215
213
  // Will be removed in further PRs with proper changeset.
216
- this.setContextConnectedState(false /* connected */, readonly);
214
+ const runtime = this._runtime;
215
+ if (runtime !== undefined &&
216
+ // Check for older runtime that may need this call
217
+ !("setConnectionStatus" in runtime) &&
218
+ runtime.disposed === false) {
219
+ runtime.setConnectionState(false /* canSendOps */, this.clientId);
220
+ }
217
221
  // Deliver delayed calls to DeltaManager - we ignored "connect" events while loading.
218
222
  const cm = this._deltaManager.connectionManager;
219
223
  if (cm.connected) {
@@ -373,6 +377,7 @@ export class Container extends EventEmitterWithErrorHandling {
373
377
  }, error);
374
378
  this.close(normalizeError(error));
375
379
  });
380
+ this.signalAudience = new Audience();
376
381
  /**
377
382
  * Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
378
383
  *
@@ -501,7 +506,8 @@ export class Container extends EventEmitterWithErrorHandling {
501
506
  const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, protocolHandlerBuilder, retryConnectionTimeoutMs, } = createProps;
502
507
  // Validate that the Driver is compatible with this Loader.
503
508
  const maybeDriverCompatDetails = documentServiceFactory;
504
- validateDriverCompatibility(maybeDriverCompatDetails.ILayerCompatDetails, (error) => { } /* disposeFn */);
509
+ validateDriverCompatibility(maybeDriverCompatDetails.ILayerCompatDetails, (error) => { } /* disposeFn */, // There is nothing to dispose here, so just ignore the error.
510
+ subLogger);
505
511
  this.connectionTransitionTimes[ConnectionState.Disconnected] = performanceNow();
506
512
  const pendingLocalState = loadProps?.pendingLocalState;
507
513
  this._canReconnect = canReconnect ?? true;
@@ -515,9 +521,8 @@ export class Container extends EventEmitterWithErrorHandling {
515
521
  // Tracking alternative ways to handle this in AB#4129.
516
522
  this.options = { ...options };
517
523
  this.scope = scope;
518
- this.protocolHandlerBuilder =
519
- protocolHandlerBuilder ??
520
- ((attributes, quorumSnapshot, sendProposal) => new ProtocolHandler(attributes, quorumSnapshot, sendProposal, new Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId)));
524
+ this.protocolHandlerBuilder = wrapProtocolHandlerBuilder(protocolHandlerBuilder ??
525
+ ((attributes, quorumSnapshot, sendProposal) => new ProtocolHandler(attributes, quorumSnapshot, sendProposal, new Audience(), (clientId) => this.clientsWhoShouldHaveLeft.has(clientId))), this.signalAudience);
521
526
  // Note that we capture the createProps here so we can replicate the creation call when we want to clone.
522
527
  this.clone = async (_loadProps, createParamOverrides) => {
523
528
  return Container.load(_loadProps, {
@@ -1321,7 +1326,7 @@ export class Container extends EventEmitterWithErrorHandling {
1321
1326
  });
1322
1327
  deltaManager.on("readonly", (readonly) => {
1323
1328
  if (this.loaded) {
1324
- this.setContextConnectedState(this.connectionState === ConnectionState.Connected, readonly);
1329
+ this.setConnectionStatus(readonly);
1325
1330
  }
1326
1331
  this.emit("readonly", readonly);
1327
1332
  });
@@ -1397,7 +1402,18 @@ export class Container extends EventEmitterWithErrorHandling {
1397
1402
  const clientId = this.connectionStateHandler.clientId;
1398
1403
  assert(clientId !== undefined, 0x96e /* there has to be clientId */);
1399
1404
  this.protocolHandler.audience.setCurrentClientId(clientId);
1405
+ this.signalAudience.setCurrentClientId(clientId);
1406
+ }
1407
+ else if (this.connectionState === ConnectionState.CatchingUp) {
1408
+ // Signal-based Audience does not wait for ops. So provide clientId
1409
+ // as soon as possible.
1410
+ const clientId = this.connectionStateHandler.pendingClientId;
1411
+ assert(clientId !== undefined, 0xc89 /* catching up without clientId */);
1412
+ this.signalAudience.setCurrentClientId(clientId);
1400
1413
  }
1414
+ this.setConnectionStatus(
1415
+ /* readonly */ this.readOnlyInfo.readonly ?? false,
1416
+ /* onlyCallSetConnectionStateIfConnectedOrDisconnected */ true);
1401
1417
  // We communicate only transitions to Connected & Disconnected states, skipping all other states.
1402
1418
  // This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
1403
1419
  if (this.connectionState !== ConnectionState.Connected &&
@@ -1405,7 +1421,6 @@ export class Container extends EventEmitterWithErrorHandling {
1405
1421
  return;
1406
1422
  }
1407
1423
  // Both protocol and context should not be undefined if we got so far.
1408
- this.setContextConnectedState(connected, this.readOnlyInfo.readonly ?? false);
1409
1424
  this.protocolHandler.setConnectionState(connected, this.clientId);
1410
1425
  raiseConnectedEvent(this.mc.logger, this, connected, this.clientId, disconnectedReason?.text);
1411
1426
  }
@@ -1537,6 +1552,7 @@ export class Container extends EventEmitterWithErrorHandling {
1537
1552
  storage: this.storageAdapter,
1538
1553
  quorum: this.protocolHandler.quorum,
1539
1554
  audience: this.protocolHandler.audience,
1555
+ signalAudience: this.signalAudience,
1540
1556
  loader,
1541
1557
  submitFn: (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata),
1542
1558
  submitSummaryFn: (summaryOp, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber),
@@ -1560,7 +1576,7 @@ export class Container extends EventEmitterWithErrorHandling {
1560
1576
  const runtime = await PerformanceEvent.timedExecAsync(this.subLogger, { eventName: "InstantiateRuntime" }, async () => runtimeFactory.instantiateRuntime(context, existing));
1561
1577
  // Validate that the Runtime is compatible with this Loader.
1562
1578
  const maybeRuntimeCompatDetails = runtime;
1563
- validateRuntimeCompatibility(maybeRuntimeCompatDetails.ILayerCompatDetails);
1579
+ validateRuntimeCompatibility(maybeRuntimeCompatDetails.ILayerCompatDetails, this.mc.logger);
1564
1580
  this._runtime = runtime;
1565
1581
  this._lifecycleEvents.emit("runtimeInstantiated");
1566
1582
  this._loadedCodeDetails = codeDetails;
@@ -1571,15 +1587,75 @@ export class Container extends EventEmitterWithErrorHandling {
1571
1587
  }
1572
1588
  }
1573
1589
  /**
1574
- * Set the connected state of the ContainerContext
1575
- * This controls the "connected" state of the ContainerRuntime as well
1576
- * @param connected - Is the container currently connected?
1590
+ * Send the connected status to the runtime.
1577
1591
  * @param readonly - Is the container in readonly mode?
1592
+ * @param onlyCallSetConnectionStateIfConnectedOrDisconnected - If true, only
1593
+ * call older `setConnectionState` on the runtime if the connection state is
1594
+ * either Connected or Disconnected. This exists to preserve older behavior
1595
+ * where the runtime was only notified of these two states.
1578
1596
  */
1579
- setContextConnectedState(connected, readonly) {
1597
+ setConnectionStatus(readonly, onlyCallSetConnectionStateIfConnectedOrDisconnected = false) {
1580
1598
  if (this._runtime?.disposed === false && this.loaded) {
1581
- this.runtime.setConnectionState(connected &&
1582
- !readonly /* container can send ops if connected to service and not in readonly mode */, this.clientId);
1599
+ const setConnectionStatus = this.runtime.setConnectionStatus?.bind(this.runtime);
1600
+ if (setConnectionStatus === undefined) {
1601
+ if (!onlyCallSetConnectionStateIfConnectedOrDisconnected ||
1602
+ this.connectionState === ConnectionState.Connected ||
1603
+ this.connectionState === ConnectionState.Disconnected) {
1604
+ this.runtime.setConnectionState(this.connectionState === ConnectionState.Connected &&
1605
+ !readonly /* container can send ops if connected to service and not in readonly mode */, this.clientId);
1606
+ }
1607
+ }
1608
+ else {
1609
+ const pendingClientConnectionId = this.connectionStateHandler.pendingClientId;
1610
+ const connectionState = this.connectionState;
1611
+ switch (connectionState) {
1612
+ case ConnectionState.EstablishingConnection: {
1613
+ setConnectionStatus({
1614
+ connectionState,
1615
+ canSendOps: false,
1616
+ readonly,
1617
+ });
1618
+ break;
1619
+ }
1620
+ case ConnectionState.CatchingUp: {
1621
+ // When catching up, we have a pending clientId, but it
1622
+ // is not usable for ops. Send clientId with canSendOps false.
1623
+ assert(pendingClientConnectionId !== undefined, 0xc8a /* catching up without clientId */);
1624
+ setConnectionStatus({
1625
+ connectionState,
1626
+ pendingClientConnectionId,
1627
+ canSendOps: false,
1628
+ readonly,
1629
+ });
1630
+ break;
1631
+ }
1632
+ case ConnectionState.Connected: {
1633
+ // When connected, we have an active clientId. Pass it along
1634
+ // with canSendOps true/false based on readonly.
1635
+ const clientConnectionId = this.clientId;
1636
+ assert(clientConnectionId !== undefined, 0xc8b /* connected without clientId */);
1637
+ assert(clientConnectionId === pendingClientConnectionId, 0xc8c /* connected with different clientId than pending */);
1638
+ setConnectionStatus({
1639
+ connectionState,
1640
+ clientConnectionId,
1641
+ canSendOps: !readonly,
1642
+ readonly,
1643
+ });
1644
+ break;
1645
+ }
1646
+ case ConnectionState.Disconnected: {
1647
+ setConnectionStatus({
1648
+ connectionState,
1649
+ priorPendingClientConnectionId: pendingClientConnectionId,
1650
+ priorConnectedClientConnectionId: this.clientId,
1651
+ canSendOps: false,
1652
+ readonly,
1653
+ });
1654
+ break;
1655
+ }
1656
+ // No default
1657
+ }
1658
+ }
1583
1659
  }
1584
1660
  }
1585
1661
  handleDeltaConnectionArg(deltaConnectionArg, connectionArgs) {