@fluidframework/container-loader 0.52.0-44610 → 0.53.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 (55) hide show
  1. package/dist/connectionStateHandler.d.ts +1 -0
  2. package/dist/connectionStateHandler.d.ts.map +1 -1
  3. package/dist/connectionStateHandler.js +6 -0
  4. package/dist/connectionStateHandler.js.map +1 -1
  5. package/dist/container.d.ts +0 -21
  6. package/dist/container.d.ts.map +1 -1
  7. package/dist/container.js +96 -123
  8. package/dist/container.js.map +1 -1
  9. package/dist/containerContext.d.ts +1 -0
  10. package/dist/containerContext.d.ts.map +1 -1
  11. package/dist/containerContext.js +4 -0
  12. package/dist/containerContext.js.map +1 -1
  13. package/dist/deltaManager.d.ts +0 -7
  14. package/dist/deltaManager.d.ts.map +1 -1
  15. package/dist/deltaManager.js +31 -33
  16. package/dist/deltaManager.js.map +1 -1
  17. package/dist/loader.d.ts +5 -0
  18. package/dist/loader.d.ts.map +1 -1
  19. package/dist/loader.js +6 -1
  20. package/dist/loader.js.map +1 -1
  21. package/dist/packageVersion.d.ts +1 -1
  22. package/dist/packageVersion.d.ts.map +1 -1
  23. package/dist/packageVersion.js +1 -1
  24. package/dist/packageVersion.js.map +1 -1
  25. package/lib/connectionStateHandler.d.ts +1 -0
  26. package/lib/connectionStateHandler.d.ts.map +1 -1
  27. package/lib/connectionStateHandler.js +6 -0
  28. package/lib/connectionStateHandler.js.map +1 -1
  29. package/lib/container.d.ts +0 -21
  30. package/lib/container.d.ts.map +1 -1
  31. package/lib/container.js +97 -124
  32. package/lib/container.js.map +1 -1
  33. package/lib/containerContext.d.ts +1 -0
  34. package/lib/containerContext.d.ts.map +1 -1
  35. package/lib/containerContext.js +4 -0
  36. package/lib/containerContext.js.map +1 -1
  37. package/lib/deltaManager.d.ts +0 -7
  38. package/lib/deltaManager.d.ts.map +1 -1
  39. package/lib/deltaManager.js +31 -33
  40. package/lib/deltaManager.js.map +1 -1
  41. package/lib/loader.d.ts +5 -0
  42. package/lib/loader.d.ts.map +1 -1
  43. package/lib/loader.js +6 -1
  44. package/lib/loader.js.map +1 -1
  45. package/lib/packageVersion.d.ts +1 -1
  46. package/lib/packageVersion.d.ts.map +1 -1
  47. package/lib/packageVersion.js +1 -1
  48. package/lib/packageVersion.js.map +1 -1
  49. package/package.json +11 -11
  50. package/src/connectionStateHandler.ts +8 -0
  51. package/src/container.ts +126 -148
  52. package/src/containerContext.ts +4 -0
  53. package/src/deltaManager.ts +35 -35
  54. package/src/loader.ts +29 -19
  55. package/src/packageVersion.ts +1 -1
package/lib/container.js CHANGED
@@ -9,7 +9,7 @@ import { assert, performance, unreachableCase } from "@fluidframework/common-uti
9
9
  import { isFluidCodeDetails, } from "@fluidframework/core-interfaces";
10
10
  import { AttachState, } from "@fluidframework/container-definitions";
11
11
  import { DataCorruptionError, extractSafePropertiesFromMessage, GenericError, UsageError, } from "@fluidframework/container-utils";
12
- import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, canRetryOnError, } from "@fluidframework/driver-utils";
12
+ import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, } from "@fluidframework/driver-utils";
13
13
  import { isSystemMessage, ProtocolOpHandler, } from "@fluidframework/protocol-base";
14
14
  import { FileMode, MessageType, TreeEntry, SummaryType, } from "@fluidframework/protocol-definitions";
15
15
  import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, } from "@fluidframework/telemetry-utils";
@@ -137,7 +137,6 @@ export class Container extends EventEmitterWithErrorHandling {
137
137
  this.subLogger = ChildLogger.create(loader.services.subLogger, undefined, {
138
138
  all: {
139
139
  clientType,
140
- loaderVersion: pkgVersion,
141
140
  containerId: uuid(),
142
141
  docId: () => this.id,
143
142
  containerAttachState: () => this._attachState,
@@ -222,22 +221,22 @@ export class Container extends EventEmitterWithErrorHandling {
222
221
  switch (event) {
223
222
  case dirtyContainerEvent:
224
223
  if (this._dirtyContainer) {
225
- listener(dirtyContainerEvent);
224
+ listener();
226
225
  }
227
226
  break;
228
227
  case savedContainerEvent:
229
228
  if (!this._dirtyContainer) {
230
- listener(savedContainerEvent);
229
+ listener();
231
230
  }
232
231
  break;
233
232
  case connectedEventName:
234
233
  if (this.connected) {
235
- listener(event, this.clientId);
234
+ listener(this.clientId);
236
235
  }
237
236
  break;
238
237
  case disconnectedEventName:
239
238
  if (!this.connected) {
240
- listener(event);
239
+ listener();
241
240
  }
242
241
  break;
243
242
  default:
@@ -284,16 +283,18 @@ export class Container extends EventEmitterWithErrorHandling {
284
283
  container.close(err);
285
284
  onClosed(err);
286
285
  });
287
- }), { start: true, end: true, cancel: "error" });
286
+ }), { start: true, end: true, cancel: "generic" });
288
287
  }
289
288
  /**
290
289
  * Create a new container in a detached state.
291
290
  */
292
291
  static async createDetached(loader, codeDetails) {
293
292
  const container = new Container(loader, {});
294
- container._lifecycleState = "loading";
295
- await container.createDetached(codeDetails);
296
- return container;
293
+ return PerformanceEvent.timedExecAsync(container.logger, { eventName: "CreateDetached" }, async (_event) => {
294
+ container._lifecycleState = "loading";
295
+ await container.createDetached(codeDetails);
296
+ return container;
297
+ }, { start: true, end: true, cancel: "generic" });
297
298
  }
298
299
  /**
299
300
  * Create a new container in a detached state that is initialized with a
@@ -301,10 +302,12 @@ export class Container extends EventEmitterWithErrorHandling {
301
302
  */
302
303
  static async rehydrateDetachedFromSnapshot(loader, snapshot) {
303
304
  const container = new Container(loader, {});
304
- const deserializedSummary = JSON.parse(snapshot);
305
- container._lifecycleState = "loading";
306
- await container.rehydrateDetachedFromSnapshot(deserializedSummary);
307
- return container;
305
+ return PerformanceEvent.timedExecAsync(container.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
306
+ const deserializedSummary = JSON.parse(snapshot);
307
+ container._lifecycleState = "loading";
308
+ await container.rehydrateDetachedFromSnapshot(deserializedSummary);
309
+ return container;
310
+ }, { start: true, end: true, cancel: "generic" });
308
311
  }
309
312
  get loaded() {
310
313
  return (this._lifecycleState !== "created" && this._lifecycleState !== "loading");
@@ -349,31 +352,6 @@ export class Container extends EventEmitterWithErrorHandling {
349
352
  get loadedFromVersion() {
350
353
  return this._loadedFromVersion;
351
354
  }
352
- /**
353
- * Tells if container is in read-only mode.
354
- * Data stores should listen for "readonly" notifications and disallow user making changes to data stores.
355
- * Readonly state can be because of no storage write permission,
356
- * or due to host forcing readonly mode for container.
357
- *
358
- * We do not differentiate here between no write access to storage vs. host disallowing changes to container -
359
- * in all cases container runtime and data stores should respect readonly state and not allow local changes.
360
- *
361
- * It is undefined if we have not yet established websocket connection
362
- * and do not know if user has write access to a file.
363
- * @deprecated - use readOnlyInfo
364
- */
365
- get readonly() {
366
- return this._deltaManager.readonly;
367
- }
368
- /**
369
- * Tells if user has no write permissions for file in storage
370
- * It is undefined if we have not yet established websocket connection
371
- * and do not know if user has write access to a file.
372
- * @deprecated - use readOnlyInfo
373
- */
374
- get readonlyPermissions() {
375
- return this._deltaManager.readonlyPermissions;
376
- }
377
355
  get readOnlyInfo() {
378
356
  return this._deltaManager.readOnlyInfo;
379
357
  }
@@ -482,6 +460,7 @@ export class Container extends EventEmitterWithErrorHandling {
482
460
  try {
483
461
  this._deltaManager.close(error);
484
462
  (_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
463
+ this.connectionStateHandler.dispose();
485
464
  (_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
486
465
  assert(this.connectionState === ConnectionState.Disconnected, 0x0cf /* "disconnect event was not raised!" */);
487
466
  (_c = this._storageService) === null || _c === void 0 ? void 0 : _c.dispose();
@@ -531,89 +510,88 @@ export class Container extends EventEmitterWithErrorHandling {
531
510
  return JSON.stringify(combinedSummary);
532
511
  }
533
512
  async attach(request) {
534
- if (this._lifecycleState !== "loaded") {
535
- throw new UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
536
- }
537
- // If container is already attached or attach is in progress, throw an error.
538
- assert(this._attachState === AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
539
- this.attachStarted = true;
540
- // If attachment blobs were uploaded in detached state we will go through a different attach flow
541
- const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
542
- && this.loader.services.detachedBlobStorage.size > 0;
543
- try {
544
- assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
545
- let summary;
546
- if (!hasAttachmentBlobs) {
547
- // Get the document state post attach - possibly can just call attach but we need to change the
548
- // semantics around what the attach means as far as async code goes.
549
- const appSummary = this.context.createSummary();
550
- const protocolSummary = this.captureProtocolSummary();
551
- summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
552
- // Set the state as attaching as we are starting the process of attaching container.
553
- // This should be fired after taking the summary because it is the place where we are
554
- // starting to attach the container to storage.
555
- // Also, this should only be fired in detached container.
556
- this._attachState = AttachState.Attaching;
557
- this.context.notifyAttaching();
558
- }
559
- // Actually go and create the resolved document
560
- const createNewResolvedUrl = await this.urlResolver.resolve(request);
561
- ensureFluidResolvedUrl(createNewResolvedUrl);
562
- if (this.service === undefined) {
563
- this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
513
+ await PerformanceEvent.timedExecAsync(this.logger, { eventName: "Attach" }, async () => {
514
+ if (this._lifecycleState !== "loaded") {
515
+ throw new UsageError(`containerNotValidForAttach [${this._lifecycleState}]`);
564
516
  }
565
- const resolvedUrl = this.service.resolvedUrl;
566
- ensureFluidResolvedUrl(resolvedUrl);
567
- this._resolvedUrl = resolvedUrl;
568
- await this.connectStorageService();
569
- if (hasAttachmentBlobs) {
570
- // upload blobs to storage
571
- assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
572
- // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
573
- // support blob handles that only know about the local IDs
574
- const redirectTable = new Map();
575
- // if new blobs are added while uploading, upload them too
576
- while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
577
- const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
578
- for (const id of newIds) {
579
- const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
580
- const response = await this.storageService.createBlob(blob);
581
- redirectTable.set(id, response.id);
517
+ // If container is already attached or attach is in progress, throw an error.
518
+ assert(this._attachState === AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
519
+ this.attachStarted = true;
520
+ // If attachment blobs were uploaded in detached state we will go through a different attach flow
521
+ const hasAttachmentBlobs = this.loader.services.detachedBlobStorage !== undefined
522
+ && this.loader.services.detachedBlobStorage.size > 0;
523
+ try {
524
+ assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
525
+ let summary;
526
+ if (!hasAttachmentBlobs) {
527
+ // Get the document state post attach - possibly can just call attach but we need to change the
528
+ // semantics around what the attach means as far as async code goes.
529
+ const appSummary = this.context.createSummary();
530
+ const protocolSummary = this.captureProtocolSummary();
531
+ summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
532
+ // Set the state as attaching as we are starting the process of attaching container.
533
+ // This should be fired after taking the summary because it is the place where we are
534
+ // starting to attach the container to storage.
535
+ // Also, this should only be fired in detached container.
536
+ this._attachState = AttachState.Attaching;
537
+ this.context.notifyAttaching();
538
+ }
539
+ // Actually go and create the resolved document
540
+ const createNewResolvedUrl = await this.urlResolver.resolve(request);
541
+ ensureFluidResolvedUrl(createNewResolvedUrl);
542
+ if (this.service === undefined) {
543
+ this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger), "containerAttach", this.logger, {});
544
+ }
545
+ const resolvedUrl = this.service.resolvedUrl;
546
+ ensureFluidResolvedUrl(resolvedUrl);
547
+ this._resolvedUrl = resolvedUrl;
548
+ await this.connectStorageService();
549
+ if (hasAttachmentBlobs) {
550
+ // upload blobs to storage
551
+ assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
552
+ // build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
553
+ // support blob handles that only know about the local IDs
554
+ const redirectTable = new Map();
555
+ // if new blobs are added while uploading, upload them too
556
+ while (redirectTable.size < this.loader.services.detachedBlobStorage.size) {
557
+ const newIds = this.loader.services.detachedBlobStorage.getBlobIds().filter((id) => !redirectTable.has(id));
558
+ for (const id of newIds) {
559
+ const blob = await this.loader.services.detachedBlobStorage.readBlob(id);
560
+ const response = await this.storageService.createBlob(blob);
561
+ redirectTable.set(id, response.id);
562
+ }
582
563
  }
564
+ // take summary and upload
565
+ const appSummary = this.context.createSummary(redirectTable);
566
+ const protocolSummary = this.captureProtocolSummary();
567
+ summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
568
+ this._attachState = AttachState.Attaching;
569
+ this.context.notifyAttaching();
570
+ await this.storageService.uploadSummaryWithContext(summary, {
571
+ referenceSequenceNumber: 0,
572
+ ackHandle: undefined,
573
+ proposalHandle: undefined,
574
+ });
575
+ }
576
+ this._attachState = AttachState.Attached;
577
+ this.emit("attached");
578
+ // Propagate current connection state through the system.
579
+ this.propagateConnectionState();
580
+ if (!this.closed) {
581
+ this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
583
582
  }
584
- // take summary and upload
585
- const appSummary = this.context.createSummary(redirectTable);
586
- const protocolSummary = this.captureProtocolSummary();
587
- summary = combineAppAndProtocolSummary(appSummary, protocolSummary);
588
- this._attachState = AttachState.Attaching;
589
- this.context.notifyAttaching();
590
- await this.storageService.uploadSummaryWithContext(summary, {
591
- referenceSequenceNumber: 0,
592
- ackHandle: undefined,
593
- proposalHandle: undefined,
594
- });
595
- }
596
- this._attachState = AttachState.Attached;
597
- this.emit("attached");
598
- // Propagate current connection state through the system.
599
- this.propagateConnectionState();
600
- if (!this.closed) {
601
- this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
602
583
  }
603
- }
604
- catch (error) {
605
- // we should retry upon any retriable errors, so we shouldn't see them here
606
- assert(!canRetryOnError(error), 0x24f /* "retriable error thrown from attach()" */);
607
- // add resolved URL on error object so that host has the ability to find this document and delete it
608
- const newError = normalizeError(error);
609
- const resolvedUrl = this.resolvedUrl;
610
- if (resolvedUrl) {
611
- ensureFluidResolvedUrl(resolvedUrl);
612
- newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
584
+ catch (error) {
585
+ // add resolved URL on error object so that host has the ability to find this document and delete it
586
+ const newError = normalizeError(error);
587
+ const resolvedUrl = this.resolvedUrl;
588
+ if (isFluidResolvedUrl(resolvedUrl)) {
589
+ newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
590
+ }
591
+ this.close(newError);
592
+ throw newError;
613
593
  }
614
- this.close(newError);
615
- throw newError;
616
- }
594
+ }, { start: true, end: true, cancel: "generic" });
617
595
  }
618
596
  async request(path) {
619
597
  return PerformanceEvent.timedExecAsync(this.logger, { eventName: "Request" }, async () => this.context.request(path), { end: true, cancel: "error" });
@@ -1107,6 +1085,7 @@ export class Container extends EventEmitterWithErrorHandling {
1107
1085
  if (this.clientDetailsOverride !== undefined) {
1108
1086
  merge(client.details, this.clientDetailsOverride);
1109
1087
  }
1088
+ client.details.environment = [client.details.environment, ` loaderVersion:${pkgVersion}`].join(";");
1110
1089
  return client;
1111
1090
  }
1112
1091
  /**
@@ -1116,14 +1095,8 @@ export class Container extends EventEmitterWithErrorHandling {
1116
1095
  * If it's not true, runtime is not in position to send ops.
1117
1096
  */
1118
1097
  activeConnection() {
1119
- const active = this.connectionState === ConnectionState.Connected &&
1098
+ return this.connectionState === ConnectionState.Connected &&
1120
1099
  this._deltaManager.connectionMode === "write";
1121
- // Check for presence of current client in quorum for "write" connections - inactive clients
1122
- // would get leave op after some long timeout (5 min) and that should automatically transition
1123
- // state to "read" mode.
1124
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1125
- assert(!active || this.getQuorum().getMember(this.clientId) !== undefined, 0x276 /* "active connection not present in quorum" */);
1126
- return active;
1127
1100
  }
1128
1101
  createDeltaManager() {
1129
1102
  const deltaManager = new DeltaManager(() => this.service, this.client, ChildLogger.create(this.subLogger, "DeltaManager"), this._canReconnect, () => this.activeConnection());