@fluidframework/container-loader 1.0.1 → 1.1.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 (83) hide show
  1. package/dist/connectionManager.d.ts.map +1 -1
  2. package/dist/connectionManager.js +3 -1
  3. package/dist/connectionManager.js.map +1 -1
  4. package/dist/connectionStateHandler.d.ts +44 -10
  5. package/dist/connectionStateHandler.d.ts.map +1 -1
  6. package/dist/connectionStateHandler.js +90 -35
  7. package/dist/connectionStateHandler.js.map +1 -1
  8. package/dist/container.d.ts +2 -2
  9. package/dist/container.d.ts.map +1 -1
  10. package/dist/container.js +48 -70
  11. package/dist/container.js.map +1 -1
  12. package/dist/containerStorageAdapter.d.ts +2 -2
  13. package/dist/containerStorageAdapter.d.ts.map +1 -1
  14. package/dist/containerStorageAdapter.js +4 -4
  15. package/dist/containerStorageAdapter.js.map +1 -1
  16. package/dist/contracts.d.ts +1 -1
  17. package/dist/contracts.js +1 -1
  18. package/dist/contracts.js.map +1 -1
  19. package/dist/deltaManager.d.ts.map +1 -1
  20. package/dist/deltaManager.js +9 -1
  21. package/dist/deltaManager.js.map +1 -1
  22. package/dist/deltaQueue.d.ts.map +1 -1
  23. package/dist/deltaQueue.js +8 -3
  24. package/dist/deltaQueue.js.map +1 -1
  25. package/dist/packageVersion.d.ts +1 -1
  26. package/dist/packageVersion.js +1 -1
  27. package/dist/packageVersion.js.map +1 -1
  28. package/dist/protocolTreeDocumentStorageService.d.ts +2 -2
  29. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  30. package/dist/retriableDocumentStorageService.d.ts +2 -2
  31. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  32. package/dist/retriableDocumentStorageService.js +4 -4
  33. package/dist/retriableDocumentStorageService.js.map +1 -1
  34. package/dist/utils.d.ts.map +1 -1
  35. package/dist/utils.js +3 -2
  36. package/dist/utils.js.map +1 -1
  37. package/lib/connectionManager.d.ts.map +1 -1
  38. package/lib/connectionManager.js +4 -2
  39. package/lib/connectionManager.js.map +1 -1
  40. package/lib/connectionStateHandler.d.ts +44 -10
  41. package/lib/connectionStateHandler.d.ts.map +1 -1
  42. package/lib/connectionStateHandler.js +90 -35
  43. package/lib/connectionStateHandler.js.map +1 -1
  44. package/lib/container.d.ts +2 -2
  45. package/lib/container.d.ts.map +1 -1
  46. package/lib/container.js +49 -71
  47. package/lib/container.js.map +1 -1
  48. package/lib/containerStorageAdapter.d.ts +2 -2
  49. package/lib/containerStorageAdapter.d.ts.map +1 -1
  50. package/lib/containerStorageAdapter.js +4 -4
  51. package/lib/containerStorageAdapter.js.map +1 -1
  52. package/lib/contracts.d.ts +1 -1
  53. package/lib/contracts.js +1 -1
  54. package/lib/contracts.js.map +1 -1
  55. package/lib/deltaManager.d.ts.map +1 -1
  56. package/lib/deltaManager.js +9 -1
  57. package/lib/deltaManager.js.map +1 -1
  58. package/lib/deltaQueue.d.ts.map +1 -1
  59. package/lib/deltaQueue.js +8 -3
  60. package/lib/deltaQueue.js.map +1 -1
  61. package/lib/packageVersion.d.ts +1 -1
  62. package/lib/packageVersion.js +1 -1
  63. package/lib/packageVersion.js.map +1 -1
  64. package/lib/protocolTreeDocumentStorageService.d.ts +2 -2
  65. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  66. package/lib/retriableDocumentStorageService.d.ts +2 -2
  67. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  68. package/lib/retriableDocumentStorageService.js +4 -4
  69. package/lib/retriableDocumentStorageService.js.map +1 -1
  70. package/lib/utils.d.ts.map +1 -1
  71. package/lib/utils.js +3 -2
  72. package/lib/utils.js.map +1 -1
  73. package/package.json +15 -28
  74. package/src/connectionManager.ts +4 -6
  75. package/src/connectionStateHandler.ts +113 -54
  76. package/src/container.ts +66 -89
  77. package/src/containerStorageAdapter.ts +4 -4
  78. package/src/contracts.ts +1 -1
  79. package/src/deltaManager.ts +8 -2
  80. package/src/deltaQueue.ts +7 -3
  81. package/src/packageVersion.ts +1 -1
  82. package/src/retriableDocumentStorageService.ts +4 -4
  83. package/src/utils.ts +3 -2
package/src/container.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  import merge from "lodash/merge";
8
8
  import { v4 as uuid } from "uuid";
9
9
  import {
10
- IDisposable,
10
+ IDisposable, ITelemetryProperties,
11
11
  } from "@fluidframework/common-definitions";
12
12
  import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
13
13
  import {
@@ -53,7 +53,8 @@ import {
53
53
  } from "@fluidframework/driver-utils";
54
54
  import {
55
55
  isSystemMessage,
56
- ProtocolOpHandler,
56
+ IProtocolHandler,
57
+ ProtocolOpHandlerWithClientValidation,
57
58
  } from "@fluidframework/protocol-base";
58
59
  import {
59
60
  IClient,
@@ -98,7 +99,7 @@ import { DeltaManager, IConnectionArgs } from "./deltaManager";
98
99
  import { DeltaManagerProxy } from "./deltaManagerProxy";
99
100
  import { ILoaderOptions, Loader, RelativeLoader } from "./loader";
100
101
  import { pkgVersion } from "./packageVersion";
101
- import { ConnectionStateHandler, ILocalSequencedClient } from "./connectionStateHandler";
102
+ import { ConnectionStateHandler } from "./connectionStateHandler";
102
103
  import { RetriableDocumentStorageService } from "./retriableDocumentStorageService";
103
104
  import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
104
105
  import { BlobOnlyStorage, ContainerStorageAdapter } from "./containerStorageAdapter";
@@ -262,7 +263,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
262
263
  container.mc.logger,
263
264
  { eventName: "Load" },
264
265
  async (event) => new Promise<Container>((resolve, reject) => {
265
- container._lifecycleState = "loading";
266
266
  const version = loadOptions.version;
267
267
 
268
268
  const defaultMode: IContainerLoadMode = { opsBeforeReturn: "cached" };
@@ -314,7 +314,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
314
314
  container.mc.logger,
315
315
  { eventName: "CreateDetached" },
316
316
  async (_event) => {
317
- container._lifecycleState = "loading";
318
317
  await container.createDetached(codeDetails);
319
318
  return container;
320
319
  },
@@ -337,7 +336,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
337
336
  { eventName: "RehydrateDetachedFromSnapshot" },
338
337
  async (_event) => {
339
338
  const deserializedSummary = JSON.parse(snapshot) as ISummaryTree;
340
- container._lifecycleState = "loading";
341
339
  await container.rehydrateDetachedFromSnapshot(deserializedSummary);
342
340
  return container;
343
341
  },
@@ -352,19 +350,14 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
352
350
 
353
351
  private readonly mc: MonitoringContext;
354
352
 
355
- private _lifecycleState: "created" | "loading" | "loaded" | "closing" | "closed" = "created";
356
-
357
- private get loaded(): boolean {
358
- return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
359
- }
360
-
361
- private set loaded(t: boolean) {
362
- assert(t, 0x27d /* "Setting loaded state to false is not supported" */);
363
- assert(this._lifecycleState !== "created", 0x27e /* "Must go through loading state before loaded" */);
353
+ private _lifecycleState: "loading" | "loaded" | "closing" | "closed" = "loading";
364
354
 
355
+ private setLoaded() {
365
356
  // It's conceivable the container could be closed when this is called
366
357
  // Only transition states if currently loading
367
358
  if (this._lifecycleState === "loading") {
359
+ // Propagate current connection state through the system.
360
+ this.propagateConnectionState();
368
361
  this._lifecycleState = "loaded";
369
362
  }
370
363
  }
@@ -400,7 +393,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
400
393
  }
401
394
  return this._context;
402
395
  }
403
- private _protocolHandler: ProtocolOpHandler | undefined;
396
+ private _protocolHandler: IProtocolHandler | undefined;
404
397
  private get protocolHandler() {
405
398
  if (this._protocolHandler === undefined) {
406
399
  throw new Error("Attempted to access protocolHandler before it was defined");
@@ -608,16 +601,18 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
608
601
  this.logConnectionStateChangeTelemetry(value, oldState, reason),
609
602
  shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
610
603
  maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
611
- logConnectionIssue: (eventName: string) => {
604
+ logConnectionIssue: (eventName: string, details?: ITelemetryProperties) => {
612
605
  // We get here when socket does not receive any ops on "write" connection, including
613
606
  // its own join op. Attempt recovery option.
614
607
  this._deltaManager.logConnectionIssue({
615
608
  eventName,
616
609
  duration: performance.now() - this.connectionTransitionTimes[ConnectionState.CatchingUp],
610
+ ...(details === undefined ? {} : { details: JSON.stringify(details) }),
617
611
  });
618
612
  },
619
613
  connectionStateChanged: () => {
620
- if (this.loaded) {
614
+ // Fire events only if container is fully loaded and not closed
615
+ if (this._lifecycleState === "loaded") {
621
616
  this.propagateConnectionState();
622
617
  }
623
618
  },
@@ -708,16 +703,34 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
708
703
  }
709
704
 
710
705
  public close(error?: ICriticalContainerError) {
711
- if (this.closed) {
712
- return;
713
- }
706
+ // 1. Ensure that close sequence is exactly the same no matter if it's initiated by host or by DeltaManager
707
+ // 2. We need to ensure that we deliver disconnect event to runtime properly. See connectionStateChanged
708
+ // handler. We only deliver events if container fully loaded. Transitioning from "loading" ->
709
+ // "closing" will lose that info (can also solve by tracking extra state).
710
+ this._deltaManager.close(error);
711
+ assert(this.connectionState === ConnectionState.Disconnected,
712
+ 0x0cf /* "disconnect event was not raised!" */);
714
713
 
715
- try {
716
- this._lifecycleState = "closing";
714
+ assert(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
715
+ }
716
+
717
+ private closeCore(error?: ICriticalContainerError) {
718
+ assert(!this.closed, 0x315 /* re-entrancy */);
717
719
 
720
+ try {
718
721
  // Ensure that we raise all key events even if one of these throws
719
722
  try {
720
- this._deltaManager.close(error);
723
+ // Raise event first, to ensure we capture _lifecycleState before transition.
724
+ // This gives us a chance to know what errors happened on open vs. on fully loaded container.
725
+ this.mc.logger.sendTelemetryEvent(
726
+ {
727
+ eventName: "ContainerClose",
728
+ category: error === undefined ? "generic" : "error",
729
+ },
730
+ error,
731
+ );
732
+
733
+ this._lifecycleState = "closing";
721
734
 
722
735
  this._protocolHandler?.close();
723
736
 
@@ -725,9 +738,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
725
738
 
726
739
  this._context?.dispose(error !== undefined ? new Error(error.message) : undefined);
727
740
 
728
- assert(this.connectionState === ConnectionState.Disconnected,
729
- 0x0cf /* "disconnect event was not raised!" */);
730
-
731
741
  this._storageService?.dispose();
732
742
 
733
743
  // Notify storage about critical errors. They may be due to disconnect between client & server knowledge
@@ -738,14 +748,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
738
748
  this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
739
749
  }
740
750
 
741
- this.mc.logger.sendTelemetryEvent(
742
- {
743
- eventName: "ContainerClose",
744
- category: error === undefined ? "generic" : "error",
745
- },
746
- error,
747
- );
748
-
749
751
  this.emit("closed", error);
750
752
 
751
753
  this.removeAllListeners();
@@ -766,11 +768,13 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
766
768
  assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
767
769
  0x0d2 /* "resolved url should be valid Fluid url" */);
768
770
  assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
771
+ assert(this._protocolHandler.attributes.term !== undefined,
772
+ 0x30b /* Must have a valid protocol handler instance */);
769
773
  const pendingState: IPendingContainerState = {
770
774
  pendingRuntimeState: this.context.getPendingLocalState(),
771
775
  url: this.resolvedUrl.url,
772
- protocol: this._protocolHandler.getProtocolState(),
773
- term: this._protocolHandler.term,
776
+ protocol: this.protocolHandler.getProtocolState(),
777
+ term: this._protocolHandler.attributes.term,
774
778
  clientId: this.clientId,
775
779
  };
776
780
 
@@ -1167,11 +1171,8 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1167
1171
  pendingLocalState?.pendingRuntimeState,
1168
1172
  );
1169
1173
 
1170
- // Propagate current connection state through the system.
1171
- this.propagateConnectionState();
1172
-
1173
1174
  // Internal context is fully loaded at this point
1174
- this.loaded = true;
1175
+ this.setLoaded();
1175
1176
 
1176
1177
  // We might have hit some failure that did not manifest itself in exception in this flow,
1177
1178
  // do not start op processing in such case - static version of Container.load() will handle it correctly.
@@ -1243,9 +1244,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1243
1244
  false, // existing
1244
1245
  );
1245
1246
 
1246
- this.propagateConnectionState();
1247
-
1248
- this.loaded = true;
1247
+ this.setLoaded();
1249
1248
  }
1250
1249
 
1251
1250
  private async rehydrateDetachedFromSnapshot(detachedContainerSnapshot: ISummaryTree) {
@@ -1280,9 +1279,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1280
1279
  snapshotTree,
1281
1280
  );
1282
1281
 
1283
- this.loaded = true;
1284
-
1285
- this.propagateConnectionState();
1282
+ this.setLoaded();
1286
1283
  }
1287
1284
 
1288
1285
  private async connectStorageService(): Promise<void> {
@@ -1338,7 +1335,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1338
1335
  attributes: IDocumentAttributes,
1339
1336
  storage: IDocumentStorageService,
1340
1337
  snapshot: ISnapshotTree | undefined,
1341
- ): Promise<ProtocolOpHandler> {
1338
+ ): Promise<IProtocolHandler> {
1342
1339
  let members: [string, ISequencedClient][] = [];
1343
1340
  let proposals: [number, ISequencedProposal, string[]][] = [];
1344
1341
  let values: [string, any][] = [];
@@ -1366,8 +1363,8 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1366
1363
  members: [string, ISequencedClient][],
1367
1364
  proposals: [number, ISequencedProposal, string[]][],
1368
1365
  values: [string, any][],
1369
- ): Promise<ProtocolOpHandler> {
1370
- const protocol = new ProtocolOpHandler(
1366
+ ): Promise<IProtocolHandler> {
1367
+ const protocol = new ProtocolOpHandlerWithClientValidation(
1371
1368
  attributes.minimumSequenceNumber,
1372
1369
  attributes.sequenceNumber,
1373
1370
  attributes.term,
@@ -1412,19 +1409,11 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1412
1409
  }
1413
1410
 
1414
1411
  private captureProtocolSummary(): ISummaryTree {
1415
- const quorumSnapshot = this.protocolHandler.quorum.snapshot();
1416
-
1417
- // Save attributes for the document
1418
- const documentAttributes: IDocumentAttributes = {
1419
- minimumSequenceNumber: this.protocolHandler.minimumSequenceNumber,
1420
- sequenceNumber: this.protocolHandler.sequenceNumber,
1421
- term: this.protocolHandler.term,
1422
- };
1423
-
1412
+ const quorumSnapshot = this.protocolHandler.snapshot();
1424
1413
  const summary: ISummaryTree = {
1425
1414
  tree: {
1426
1415
  attributes: {
1427
- content: JSON.stringify(documentAttributes),
1416
+ content: JSON.stringify(this.protocolHandler.attributes),
1428
1417
  type: SummaryType.Blob,
1429
1418
  },
1430
1419
  quorumMembers: {
@@ -1539,7 +1528,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1539
1528
  });
1540
1529
 
1541
1530
  deltaManager.on("closed", (error?: ICriticalContainerError) => {
1542
- this.close(error);
1531
+ this.closeCore(error);
1543
1532
  });
1544
1533
 
1545
1534
  return deltaManager;
@@ -1629,11 +1618,13 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1629
1618
  }
1630
1619
 
1631
1620
  const state = this.connectionState === ConnectionState.Connected;
1632
- if (!this.context.disposed) {
1621
+
1622
+ // Both protocol and context should not be undefined if we got so far.
1623
+
1624
+ if (this._context?.disposed === false) {
1633
1625
  this.context.setConnectionState(state, this.clientId);
1634
1626
  }
1635
- assert(this.protocolHandler !== undefined, 0x0dc /* "Protocol handler should be set here" */);
1636
- this.protocolHandler.quorum.setConnectionState(state, this.clientId);
1627
+ this.protocolHandler.setConnectionState(state, this.clientId);
1637
1628
  raiseConnectedEvent(this.mc.logger, this, state, this.clientId);
1638
1629
 
1639
1630
  if (logOpsOnReconnect) {
@@ -1682,36 +1673,22 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1682
1673
  }
1683
1674
 
1684
1675
  private processRemoteMessage(message: ISequencedDocumentMessage): IProcessMessageResult {
1685
- // Check and report if we're getting messages from a clientId that we previously
1686
- // flagged as shouldHaveLeft, or from a client that's not in the quorum but should be
1687
- if (message.clientId != null) {
1688
- let errorMsg: string | undefined;
1689
- const client: ILocalSequencedClient | undefined =
1690
- this.getQuorum().getMember(message.clientId);
1691
- if (client === undefined && message.type !== MessageType.ClientJoin) {
1692
- // pre-0.58 error message: messageClientIdMissingFromQuorum
1693
- errorMsg = "Remote message's clientId is missing from the quorum";
1694
- } else if (client?.shouldHaveLeft === true && message.type !== MessageType.NoOp) {
1695
- // pre-0.58 error message: messageClientIdShouldHaveLeft
1696
- errorMsg = "Remote message's clientId already should have left";
1697
- }
1698
- if (errorMsg !== undefined) {
1699
- const error = new DataCorruptionError(
1700
- errorMsg,
1701
- extractSafePropertiesFromMessage(message));
1702
- this.close(normalizeError(error));
1703
- }
1704
- }
1705
-
1706
1676
  const local = this.clientId === message.clientId;
1707
1677
 
1678
+ // Allow the protocol handler to process the message
1679
+ let result: IProcessMessageResult = { immediateNoOp: false };
1680
+ try {
1681
+ result = this.protocolHandler.processMessage(message, local);
1682
+ } catch (error) {
1683
+ this.close(wrapError(error, (errorMessage) =>
1684
+ new DataCorruptionError(errorMessage, extractSafePropertiesFromMessage(message))));
1685
+ }
1686
+
1708
1687
  // Forward non system messages to the loaded runtime for processing
1709
1688
  if (!isSystemMessage(message)) {
1710
1689
  this.context.process(message, local, undefined);
1711
1690
  }
1712
1691
 
1713
- // Allow the protocol handler to process the message
1714
- const result = this.protocolHandler.processMessage(message, local);
1715
1692
  // Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
1716
1693
  if (this.activeConnection()) {
1717
1694
  if (this.collabWindowTracker === undefined) {
@@ -1728,8 +1705,8 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1728
1705
  0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
1729
1706
  this.submitMessage(type, contents);
1730
1707
  },
1731
- this.serviceConfiguration?.noopTimeFrequency,
1732
- this.serviceConfiguration?.noopCountFrequency,
1708
+ this.serviceConfiguration.noopTimeFrequency,
1709
+ this.serviceConfiguration.noopCountFrequency,
1733
1710
  );
1734
1711
  }
1735
1712
  this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
@@ -53,8 +53,8 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
53
53
  return this.storageGetter().repositoryUrl;
54
54
  }
55
55
 
56
- public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTree | null> {
57
- return this.storageGetter().getSnapshotTree(version);
56
+ public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {
57
+ return this.storageGetter().getSnapshotTree(version, scenarioName);
58
58
  }
59
59
 
60
60
  public async readBlob(id: string): Promise<ArrayBufferLike> {
@@ -65,8 +65,8 @@ export class ContainerStorageAdapter implements IDocumentStorageService {
65
65
  return this.storageGetter().readBlob(id);
66
66
  }
67
67
 
68
- public async getVersions(versionId: string | null, count: number): Promise<IVersion[]> {
69
- return this.storageGetter().getVersions(versionId, count);
68
+ public async getVersions(versionId: string | null, count: number, scenarioName?: string): Promise<IVersion[]> {
69
+ return this.storageGetter().getVersions(versionId, count, scenarioName);
70
70
  }
71
71
 
72
72
  public async uploadSummaryWithContext(summary: ISummaryTree, context: ISummaryContext): Promise<string> {
package/src/contracts.ts CHANGED
@@ -163,7 +163,7 @@ export interface IConnectionManagerFactoryArgs {
163
163
 
164
164
  /**
165
165
  *
166
- * @param codeDetails- Data structure used to describe the code to load on the Fluid document
166
+ * @param codeDetails- - Data structure used to describe the code to load on the Fluid document
167
167
  * @returns The name of the Fluid package
168
168
  */
169
169
  export const getPackageName = (codeDetails: IFluidCodeDetails | undefined): IContainerPackageInfo => {
@@ -267,8 +267,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
267
267
  ) {
268
268
  super();
269
269
  const props: IConnectionManagerFactoryArgs = {
270
- incomingOpHandler: (messages: ISequencedDocumentMessage[], reason: string) =>
271
- this.enqueueMessages(messages, reason),
270
+ incomingOpHandler: (messages: ISequencedDocumentMessage[], reason: string) => {
271
+ try {
272
+ this.enqueueMessages(messages, reason);
273
+ } catch (error) {
274
+ this.logger.sendErrorEvent({ eventName: "EnqueueMessages_Exception" }, error);
275
+ this.close(normalizeError(error));
276
+ }
277
+ },
272
278
  signalHandler: (message: ISignalMessage) => this._inboundSignal.push(message),
273
279
  reconnectionDelayHandler: (delayMs: number, error: unknown) =>
274
280
  this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
package/src/deltaQueue.ts CHANGED
@@ -85,9 +85,13 @@ export class DeltaQueue<T>
85
85
  }
86
86
 
87
87
  public push(task: T) {
88
- this.q.push(task);
89
- this.emit("push", task);
90
- this.ensureProcessing();
88
+ try {
89
+ this.q.push(task);
90
+ this.emit("push", task);
91
+ this.ensureProcessing();
92
+ } catch (error) {
93
+ this.emit("error", error);
94
+ }
91
95
  }
92
96
 
93
97
  public async pause(): Promise<void> {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "1.0.1";
9
+ export const pkgVersion = "1.1.0";
@@ -40,9 +40,9 @@ export class RetriableDocumentStorageService implements IDocumentStorageService,
40
40
  return this.internalStorageService.repositoryUrl;
41
41
  }
42
42
 
43
- public async getSnapshotTree(version?: IVersion): Promise<ISnapshotTree | null> {
43
+ public async getSnapshotTree(version?: IVersion, scenarioName?: string): Promise<ISnapshotTree | null> {
44
44
  return this.runWithRetry(
45
- async () => this.internalStorageService.getSnapshotTree(version),
45
+ async () => this.internalStorageService.getSnapshotTree(version, scenarioName),
46
46
  "storage_getSnapshotTree",
47
47
  );
48
48
  }
@@ -54,9 +54,9 @@ export class RetriableDocumentStorageService implements IDocumentStorageService,
54
54
  );
55
55
  }
56
56
 
57
- public async getVersions(versionId: string | null, count: number): Promise<IVersion[]> {
57
+ public async getVersions(versionId: string | null, count: number, scenarioName?: string): Promise<IVersion[]> {
58
58
  return this.runWithRetry(
59
- async () => this.internalStorageService.getVersions(versionId, count),
59
+ async () => this.internalStorageService.getVersions(versionId, count, scenarioName),
60
60
  "storage_getVersions",
61
61
  );
62
62
  }
package/src/utils.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  unreachableCase,
13
13
  } from "@fluidframework/common-utils";
14
14
  import { ISummaryTree, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
15
+ import { LoggingError } from "@fluidframework/telemetry-utils";
15
16
 
16
17
  // This is used when we rehydrate a container from the snapshot. Here we put the blob contents
17
18
  // in separate property: blobContents.
@@ -35,7 +36,7 @@ export interface IParsedUrl {
35
36
  export function parseUrl(url: string): IParsedUrl | undefined {
36
37
  const parsed = parse(url, true);
37
38
  if (typeof parsed.pathname !== "string") {
38
- throw new Error("Failed to parse pathname");
39
+ throw new LoggingError("Failed to parse pathname");
39
40
  }
40
41
  const query = parsed.search ?? "";
41
42
  const regex = /^\/([^/]*\/[^/]*)(\/?.*)$/;
@@ -86,7 +87,7 @@ function convertSummaryToSnapshotWithEmbeddedBlobContents(
86
87
  break;
87
88
  }
88
89
  case SummaryType.Handle:
89
- throw new Error("No handles should be there in summary in detached container!!");
90
+ throw new LoggingError("No handles should be there in summary in detached container!!");
90
91
  break;
91
92
  default: {
92
93
  unreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);