@fluidframework/container-loader 0.58.2001 → 0.59.1000-61898

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 (74) hide show
  1. package/dist/connectionManager.d.ts +0 -2
  2. package/dist/connectionManager.d.ts.map +1 -1
  3. package/dist/connectionManager.js +14 -11
  4. package/dist/connectionManager.js.map +1 -1
  5. package/dist/container.d.ts +22 -10
  6. package/dist/container.d.ts.map +1 -1
  7. package/dist/container.js +81 -39
  8. package/dist/container.js.map +1 -1
  9. package/dist/containerContext.d.ts +5 -7
  10. package/dist/containerContext.d.ts.map +1 -1
  11. package/dist/containerContext.js +6 -4
  12. package/dist/containerContext.js.map +1 -1
  13. package/dist/contracts.d.ts +8 -1
  14. package/dist/contracts.d.ts.map +1 -1
  15. package/dist/contracts.js +21 -1
  16. package/dist/contracts.js.map +1 -1
  17. package/dist/deltaManager.d.ts +2 -1
  18. package/dist/deltaManager.d.ts.map +1 -1
  19. package/dist/deltaManager.js +1 -1
  20. package/dist/deltaManager.js.map +1 -1
  21. package/dist/loader.d.ts +4 -4
  22. package/dist/loader.d.ts.map +1 -1
  23. package/dist/loader.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/retriableDocumentStorageService.js +1 -1
  29. package/dist/retriableDocumentStorageService.js.map +1 -1
  30. package/dist/utils.d.ts.map +1 -1
  31. package/dist/utils.js +0 -1
  32. package/dist/utils.js.map +1 -1
  33. package/lib/connectionManager.d.ts +0 -2
  34. package/lib/connectionManager.d.ts.map +1 -1
  35. package/lib/connectionManager.js +14 -11
  36. package/lib/connectionManager.js.map +1 -1
  37. package/lib/container.d.ts +22 -10
  38. package/lib/container.d.ts.map +1 -1
  39. package/lib/container.js +81 -39
  40. package/lib/container.js.map +1 -1
  41. package/lib/containerContext.d.ts +5 -7
  42. package/lib/containerContext.d.ts.map +1 -1
  43. package/lib/containerContext.js +6 -4
  44. package/lib/containerContext.js.map +1 -1
  45. package/lib/contracts.d.ts +8 -1
  46. package/lib/contracts.d.ts.map +1 -1
  47. package/lib/contracts.js +19 -0
  48. package/lib/contracts.js.map +1 -1
  49. package/lib/deltaManager.d.ts +2 -1
  50. package/lib/deltaManager.d.ts.map +1 -1
  51. package/lib/deltaManager.js +1 -1
  52. package/lib/deltaManager.js.map +1 -1
  53. package/lib/loader.d.ts +4 -4
  54. package/lib/loader.d.ts.map +1 -1
  55. package/lib/loader.js.map +1 -1
  56. package/lib/packageVersion.d.ts +1 -1
  57. package/lib/packageVersion.d.ts.map +1 -1
  58. package/lib/packageVersion.js +1 -1
  59. package/lib/packageVersion.js.map +1 -1
  60. package/lib/retriableDocumentStorageService.js +1 -1
  61. package/lib/retriableDocumentStorageService.js.map +1 -1
  62. package/lib/utils.d.ts.map +1 -1
  63. package/lib/utils.js +0 -1
  64. package/lib/utils.js.map +1 -1
  65. package/package.json +25 -13
  66. package/src/connectionManager.ts +16 -12
  67. package/src/container.ts +98 -39
  68. package/src/containerContext.ts +11 -13
  69. package/src/contracts.ts +20 -0
  70. package/src/deltaManager.ts +3 -2
  71. package/src/loader.ts +5 -6
  72. package/src/packageVersion.ts +1 -1
  73. package/src/retriableDocumentStorageService.ts +1 -1
  74. package/src/utils.ts +0 -1
package/lib/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { parse } from \"url\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n assert,\n stringToBuffer,\n Uint8ArrayToArrayBuffer,\n unreachableCase,\n} from \"@fluidframework/common-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\n\n// This is used when we rehydrate a container from the snapshot. Here we put the blob contents\n// in separate property: blobContents.\nexport interface ISnapshotTreeWithBlobContents extends ISnapshotTree {\n blobsContents: {[path: string]: ArrayBufferLike},\n trees: {[path: string]: ISnapshotTreeWithBlobContents},\n}\n\nexport interface IParsedUrl {\n id: string;\n path: string;\n query: string;\n /**\n * Null means do not use snapshots, undefined means load latest snapshot\n * otherwise it's version ID passed to IDocumentStorageService.getVersions() to figure out what snapshot to use.\n * If needed, can add undefined which is treated by Container.load() as load latest snapshot.\n */\n version: string | null | undefined;\n}\n\nexport function parseUrl(url: string): IParsedUrl | undefined {\n const parsed = parse(url, true);\n if (typeof parsed.pathname !== \"string\") {\n throw new Error(\"Failed to parse pathname\");\n }\n const query = parsed.search ?? \"\";\n const regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n const match = regex.exec(parsed.pathname);\n return (match?.length === 3)\n ? { id: match[1], path: match[2], query, version: parsed.query.version as string }\n : undefined;\n}\n\n/**\n * Converts summary tree (for upload) to snapshot tree (for download).\n * Summary tree blobs contain contents, but snapshot tree blobs normally\n * contain IDs pointing to storage. This will create 2 blob entries in the\n * snapshot tree for each blob in the summary tree. One will be the regular\n * path pointing to a uniquely generated ID. Then there will be another\n * entry with the path as that uniquely generated ID, and value as the\n * blob contents as a base-64 string.\n * @param summary - summary to convert\n */\nfunction convertSummaryToSnapshotWithEmbeddedBlobContents(\n summary: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n const treeNode: ISnapshotTreeWithBlobContents = {\n blobs: {},\n blobsContents: {},\n trees: {},\n commits: {},\n id: uuid(),\n unreferenced: summary.unreferenced,\n };\n const keys = Object.keys(summary.tree);\n for (const key of keys) {\n const summaryObject = summary.tree[key];\n\n switch (summaryObject.type) {\n case SummaryType.Tree: {\n treeNode.trees[key] = convertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);\n break;\n }\n case SummaryType.Attachment:\n treeNode.blobs[key] = summaryObject.id;\n break;\n case SummaryType.Blob: {\n const blobId = uuid();\n treeNode.blobs[key] = blobId;\n const contentBuffer = typeof summaryObject.content === \"string\" ?\n stringToBuffer(summaryObject.content, \"utf8\") : Uint8ArrayToArrayBuffer(summaryObject.content);\n treeNode.blobsContents[blobId] = contentBuffer;\n break;\n }\n case SummaryType.Handle:\n throw new Error(\"No handles should be there in summary in detached container!!\");\n break;\n default: {\n unreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n }\n }\n }\n return treeNode;\n}\n\n/**\n * Combine and convert protocol and app summary tree to format which is readable by container while rehydrating.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nexport function convertProtocolAndAppSummaryToSnapshotTree(\n protocolSummaryTree: ISummaryTree,\n appSummaryTree: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n // Shallow copy is fine, since we are doing a deep clone below.\n const combinedSummary: ISummaryTree = {\n type: SummaryType.Tree,\n tree: { ...appSummaryTree.tree },\n };\n\n combinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n const snapshotTreeWithBlobContents =\n convertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);\n return snapshotTreeWithBlobContents;\n}\n\n// This function converts the snapshot taken in detached container(by serialize api) to snapshotTree with which\n// a detached container can be rehydrated.\nexport const getSnapshotTreeFromSerializedContainer = (detachedContainerSnapshot: ISummaryTree) => {\n const protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"] as ISummaryTree;\n const appSummaryTree = detachedContainerSnapshot.tree[\".app\"] as ISummaryTree;\n assert(protocolSummaryTree !== undefined && appSummaryTree !== undefined,\n 0x1e0 /* \"Protocol and App summary trees should be present\" */);\n const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(\n protocolSummaryTree,\n appSummaryTree,\n );\n return snapshotTreeWithBlobContents;\n};\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { parse } from \"url\";\nimport { v4 as uuid } from \"uuid\";\nimport {\n assert,\n stringToBuffer,\n Uint8ArrayToArrayBuffer,\n unreachableCase,\n} from \"@fluidframework/common-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\n\n// This is used when we rehydrate a container from the snapshot. Here we put the blob contents\n// in separate property: blobContents.\nexport interface ISnapshotTreeWithBlobContents extends ISnapshotTree {\n blobsContents: {[path: string]: ArrayBufferLike},\n trees: {[path: string]: ISnapshotTreeWithBlobContents},\n}\n\nexport interface IParsedUrl {\n id: string;\n path: string;\n query: string;\n /**\n * Null means do not use snapshots, undefined means load latest snapshot\n * otherwise it's version ID passed to IDocumentStorageService.getVersions() to figure out what snapshot to use.\n * If needed, can add undefined which is treated by Container.load() as load latest snapshot.\n */\n version: string | null | undefined;\n}\n\nexport function parseUrl(url: string): IParsedUrl | undefined {\n const parsed = parse(url, true);\n if (typeof parsed.pathname !== \"string\") {\n throw new Error(\"Failed to parse pathname\");\n }\n const query = parsed.search ?? \"\";\n const regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n const match = regex.exec(parsed.pathname);\n return (match?.length === 3)\n ? { id: match[1], path: match[2], query, version: parsed.query.version as string }\n : undefined;\n}\n\n/**\n * Converts summary tree (for upload) to snapshot tree (for download).\n * Summary tree blobs contain contents, but snapshot tree blobs normally\n * contain IDs pointing to storage. This will create 2 blob entries in the\n * snapshot tree for each blob in the summary tree. One will be the regular\n * path pointing to a uniquely generated ID. Then there will be another\n * entry with the path as that uniquely generated ID, and value as the\n * blob contents as a base-64 string.\n * @param summary - summary to convert\n */\nfunction convertSummaryToSnapshotWithEmbeddedBlobContents(\n summary: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n const treeNode: ISnapshotTreeWithBlobContents = {\n blobs: {},\n blobsContents: {},\n trees: {},\n id: uuid(),\n unreferenced: summary.unreferenced,\n };\n const keys = Object.keys(summary.tree);\n for (const key of keys) {\n const summaryObject = summary.tree[key];\n\n switch (summaryObject.type) {\n case SummaryType.Tree: {\n treeNode.trees[key] = convertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);\n break;\n }\n case SummaryType.Attachment:\n treeNode.blobs[key] = summaryObject.id;\n break;\n case SummaryType.Blob: {\n const blobId = uuid();\n treeNode.blobs[key] = blobId;\n const contentBuffer = typeof summaryObject.content === \"string\" ?\n stringToBuffer(summaryObject.content, \"utf8\") : Uint8ArrayToArrayBuffer(summaryObject.content);\n treeNode.blobsContents[blobId] = contentBuffer;\n break;\n }\n case SummaryType.Handle:\n throw new Error(\"No handles should be there in summary in detached container!!\");\n break;\n default: {\n unreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n }\n }\n }\n return treeNode;\n}\n\n/**\n * Combine and convert protocol and app summary tree to format which is readable by container while rehydrating.\n * @param protocolSummaryTree - Protocol Summary Tree\n * @param appSummaryTree - App Summary Tree\n */\nexport function convertProtocolAndAppSummaryToSnapshotTree(\n protocolSummaryTree: ISummaryTree,\n appSummaryTree: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n // Shallow copy is fine, since we are doing a deep clone below.\n const combinedSummary: ISummaryTree = {\n type: SummaryType.Tree,\n tree: { ...appSummaryTree.tree },\n };\n\n combinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n const snapshotTreeWithBlobContents =\n convertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);\n return snapshotTreeWithBlobContents;\n}\n\n// This function converts the snapshot taken in detached container(by serialize api) to snapshotTree with which\n// a detached container can be rehydrated.\nexport const getSnapshotTreeFromSerializedContainer = (detachedContainerSnapshot: ISummaryTree) => {\n const protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"] as ISummaryTree;\n const appSummaryTree = detachedContainerSnapshot.tree[\".app\"] as ISummaryTree;\n assert(protocolSummaryTree !== undefined && appSummaryTree !== undefined,\n 0x1e0 /* \"Protocol and App summary trees should be present\" */);\n const snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(\n protocolSummaryTree,\n appSummaryTree,\n );\n return snapshotTreeWithBlobContents;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "0.58.2001",
3
+ "version": "0.59.1000-61898",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "lint:fix": "npm run eslint:fix",
33
33
  "test": "npm run test:mocha",
34
34
  "test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
35
- "test:mocha": "mocha --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
35
+ "test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
36
36
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
37
37
  "tsc": "tsc",
38
38
  "tsc:watch": "tsc --watch",
@@ -62,14 +62,14 @@
62
62
  "dependencies": {
63
63
  "@fluidframework/common-definitions": "^0.20.1",
64
64
  "@fluidframework/common-utils": "^0.32.1",
65
- "@fluidframework/container-definitions": "^0.47.1000",
66
- "@fluidframework/container-utils": "^0.58.2001",
67
- "@fluidframework/core-interfaces": "^0.42.0",
68
- "@fluidframework/driver-definitions": "^0.45.1000",
69
- "@fluidframework/driver-utils": "^0.58.2001",
70
- "@fluidframework/protocol-base": "^0.1035.1000",
71
- "@fluidframework/protocol-definitions": "^0.1027.1000",
72
- "@fluidframework/telemetry-utils": "^0.58.2001",
65
+ "@fluidframework/container-definitions": "^0.48.1000-0",
66
+ "@fluidframework/container-utils": "0.59.1000-61898",
67
+ "@fluidframework/core-interfaces": "^0.43.1000-0",
68
+ "@fluidframework/driver-definitions": "^0.46.1000-0",
69
+ "@fluidframework/driver-utils": "0.59.1000-61898",
70
+ "@fluidframework/protocol-base": "^0.1036.1000-0",
71
+ "@fluidframework/protocol-definitions": "^0.1028.1000-0",
72
+ "@fluidframework/telemetry-utils": "0.59.1000-61898",
73
73
  "abort-controller": "^3.0.0",
74
74
  "double-ended-queue": "^2.1.0-0",
75
75
  "lodash": "^4.17.21",
@@ -77,9 +77,10 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@fluidframework/build-common": "^0.23.0",
80
- "@fluidframework/eslint-config-fluid": "^0.27.0",
81
- "@fluidframework/mocha-test-setup": "^0.58.2001",
82
- "@fluidframework/test-loader-utils": "^0.58.2001",
80
+ "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@^0.58.0",
81
+ "@fluidframework/eslint-config-fluid": "^0.28.1000-61189",
82
+ "@fluidframework/mocha-test-setup": "0.59.1000-61898",
83
+ "@fluidframework/test-loader-utils": "0.59.1000-61898",
83
84
  "@microsoft/api-extractor": "^7.16.1",
84
85
  "@rushstack/eslint-config": "^2.5.1",
85
86
  "@types/double-ended-queue": "^2.1.0",
@@ -105,5 +106,16 @@
105
106
  "sinon": "^7.4.2",
106
107
  "typescript": "~4.1.3",
107
108
  "typescript-formatter": "7.1.0"
109
+ },
110
+ "typeValidation": {
111
+ "version": "0.59.1000",
112
+ "broken": {
113
+ "0.58.2002": {
114
+ "ClassDeclaration_Container": {
115
+ "forwardCompat": false,
116
+ "backCompat": false
117
+ }
118
+ }
119
+ }
108
120
  }
109
121
  }
@@ -147,9 +147,6 @@ export class ConnectionManager implements IConnectionManager {
147
147
  /** True if there is pending (async) reconnection from "read" to "write" */
148
148
  private pendingReconnect = false;
149
149
 
150
- /** downgrade "write" connection to "read" */
151
- private downgradedConnection = false;
152
-
153
150
  private clientSequenceNumber = 0;
154
151
  private clientSequenceNumberObserved = 0;
155
152
  /** Counts the number of noops sent by the client which may not be acked. */
@@ -176,12 +173,7 @@ export class ConnectionManager implements IConnectionManager {
176
173
  * The current connection mode, initially read.
177
174
  */
178
175
  public get connectionMode(): ConnectionMode {
179
- assert(!this.downgradedConnection || this.connection?.mode === "write",
180
- 0x277 /* "Did we forget to reset downgradedConnection on new connection?" */);
181
- if (this.connection === undefined) {
182
- return "read";
183
- }
184
- return this.connection.mode;
176
+ return this.connection?.mode ?? "read";
185
177
  }
186
178
 
187
179
  public get connected() { return this.connection !== undefined; }
@@ -490,6 +482,7 @@ export class ConnectionManager implements IConnectionManager {
490
482
  throw error;
491
483
  }
492
484
 
485
+ // Since the error is retryable this will not log to the error table
493
486
  logNetworkFailure(
494
487
  this.logger,
495
488
  {
@@ -547,7 +540,6 @@ export class ConnectionManager implements IConnectionManager {
547
540
  */
548
541
  private disconnectFromDeltaStream(reason: string): boolean {
549
542
  this.pendingReconnect = false;
550
- this.downgradedConnection = false;
551
543
 
552
544
  if (this.connection === undefined) {
553
545
  return false;
@@ -798,7 +790,7 @@ export class ConnectionManager implements IConnectionManager {
798
790
  // Note that we also want nacks to be rare and be treated as catastrophic failures.
799
791
  // Be careful with reentrancy though - disconnected event should not be be raised in the
800
792
  // middle of the current workflow, but rather on clean stack!
801
- if (this.connectionMode === "read" || this.downgradedConnection) {
793
+ if (this.connectionMode === "read") {
802
794
  if (!this.pendingReconnect) {
803
795
  this.pendingReconnect = true;
804
796
  Promise.resolve().then(async () => {
@@ -841,7 +833,19 @@ export class ConnectionManager implements IConnectionManager {
841
833
  if (clientId === this.clientId) {
842
834
  // We have been kicked out from quorum
843
835
  this.logger.sendPerformanceEvent({ eventName: "ReadConnectionTransition" });
844
- this.downgradedConnection = true;
836
+
837
+ // Please see #8483 for more details on why maintaining connection further as is would not work.
838
+ // Short story - connection properties are immutable, and many processes (consensus DDSs, summarizer)
839
+ // assume that connection stays "write" connection until disconnect, and act accordingly, which may
840
+ // not work well with de-facto "read" connection we are in after receiving own leave op on timeout.
841
+ // Clients need to be able to transition to "read" state after some time of inactivity!
842
+ // Note - this may close container!
843
+ this.reconnect(
844
+ "read", // connectionMode
845
+ "Switch to read", // message
846
+ ).catch((error) => {
847
+ this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection"}, error);
848
+ });
845
849
  }
846
850
  }
847
851
  }
package/src/container.ts CHANGED
@@ -14,8 +14,6 @@ import {
14
14
  IRequest,
15
15
  IResponse,
16
16
  IFluidRouter,
17
- IFluidCodeDetails,
18
- isFluidCodeDetails,
19
17
  } from "@fluidframework/core-interfaces";
20
18
  import {
21
19
  IAudience,
@@ -30,6 +28,8 @@ import {
30
28
  IPendingLocalState,
31
29
  ReadOnlyInfo,
32
30
  IContainerLoadMode,
31
+ IFluidCodeDetails,
32
+ isFluidCodeDetails,
33
33
  } from "@fluidframework/container-definitions";
34
34
  import {
35
35
  DataCorruptionError,
@@ -92,7 +92,7 @@ import {
92
92
  } from "@fluidframework/telemetry-utils";
93
93
  import { Audience } from "./audience";
94
94
  import { ContainerContext } from "./containerContext";
95
- import { ReconnectMode, IConnectionManagerFactoryArgs } from "./contracts";
95
+ import { ReconnectMode, IConnectionManagerFactoryArgs, getPackageName } from "./contracts";
96
96
  import { DeltaManager, IConnectionArgs } from "./deltaManager";
97
97
  import { DeltaManagerProxy } from "./deltaManagerProxy";
98
98
  import { ILoaderOptions, Loader, RelativeLoader } from "./loader";
@@ -224,6 +224,8 @@ const getCodeProposal =
224
224
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
225
225
  (quorum: IQuorumProposals) => quorum.get("code") ?? quorum.get("code2");
226
226
 
227
+ const summarizerClientType = "summarizer";
228
+
227
229
  export class Container extends EventEmitterWithErrorHandling<IContainerEvents> implements IContainer {
228
230
  public static version = "^0.1.0";
229
231
 
@@ -381,7 +383,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
381
383
  private _context: ContainerContext | undefined;
382
384
  private get context() {
383
385
  if (this._context === undefined) {
384
- throw new Error("Attempted to access context before it was defined");
386
+ throw new GenericError("Attempted to access context before it was defined");
385
387
  }
386
388
  return this._context;
387
389
  }
@@ -395,7 +397,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
395
397
 
396
398
  private resumedOpProcessingAfterLoad = false;
397
399
  private firstConnection = true;
398
- private manualReconnectInProgress = false;
399
400
  private readonly connectionTransitionTimes: number[] = [];
400
401
  private messageCountAfterDisconnection: number = 0;
401
402
  private _loadedFromVersion: IVersion | undefined;
@@ -432,6 +433,10 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
432
433
  return this._deltaManager.readOnlyInfo;
433
434
  }
434
435
 
436
+ public get closeSignal(): AbortSignal {
437
+ return this._deltaManager.closeAbortController.signal;
438
+ }
439
+
435
440
  /**
436
441
  * Tracks host requiring read-only mode.
437
442
  */
@@ -818,15 +823,20 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
818
823
  const createNewResolvedUrl = await this.urlResolver.resolve(request);
819
824
  ensureFluidResolvedUrl(createNewResolvedUrl);
820
825
  if (this.service === undefined) {
826
+ assert(this.client.details.type !== summarizerClientType,
827
+ 0x2c4 /* "client should not be summarizer before container is created" */);
821
828
  this.service = await runWithRetry(
822
829
  async () => this.serviceFactory.createContainer(
823
830
  summary,
824
831
  createNewResolvedUrl,
825
832
  this.subLogger,
833
+ false, // clientIsSummarizer
826
834
  ),
827
835
  "containerAttach",
828
836
  this.mc.logger,
829
- {}, // progress
837
+ {
838
+ cancel: this.closeSignal,
839
+ }, // progress
830
840
  );
831
841
  }
832
842
  const resolvedUrl = this.service.resolvedUrl;
@@ -898,11 +908,31 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
898
908
  );
899
909
  }
900
910
 
911
+ /**
912
+ * Dictates whether or not the current container will automatically attempt to reconnect to the delta stream
913
+ * after receiving a disconnect event
914
+ * @param reconnect - Boolean indicating if reconnect should automatically occur
915
+ * @deprecated - 0.58, This API will be removed in 1.0
916
+ * Use `connect()` and `disconnect()` instead of `setAutoReconnect(true)` and `setAutoReconnect(false)` respectively
917
+ * See https://github.com/microsoft/FluidFramework/issues/9167 for context
918
+ */
901
919
  public setAutoReconnect(reconnect: boolean) {
902
920
  if (this.closed) {
903
921
  throw new Error("Attempting to setAutoReconnect() a closed Container");
904
922
  }
923
+
905
924
  const mode = reconnect ? ReconnectMode.Enabled : ReconnectMode.Disabled;
925
+ this.setAutoReconnectInternal(mode);
926
+
927
+ // If container state is not attached and resumed, then don't connect to delta stream. Also don't set the
928
+ // manual reconnection flag to true as we haven't made the initial connection yet.
929
+ if (reconnect && this._attachState === AttachState.Attached && this.resumedOpProcessingAfterLoad) {
930
+ // Ensure connection to web socket
931
+ this.connectToDeltaStream({ reason: "autoReconnect" });
932
+ }
933
+ }
934
+
935
+ private setAutoReconnectInternal(mode: ReconnectMode) {
906
936
  const currentMode = this._deltaManager.connectionManager.reconnectMode;
907
937
 
908
938
  if (currentMode === mode) {
@@ -914,27 +944,66 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
914
944
  this.setAutoReconnectTime = now;
915
945
 
916
946
  this.mc.logger.sendTelemetryEvent({
917
- eventName: reconnect ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
947
+ eventName: mode === ReconnectMode.Enabled ? "AutoReconnectEnabled" : "AutoReconnectDisabled",
918
948
  connectionMode: this.connectionMode,
919
949
  connectionState: ConnectionState[this.connectionState],
920
950
  duration,
921
951
  });
922
952
 
923
953
  this._deltaManager.connectionManager.setAutoReconnect(mode);
954
+ }
924
955
 
925
- // If container state is not attached and resumed, then don't connect to delta stream. Also don't set the
926
- // manual reconnection flag to true as we haven't made the initial connection yet.
927
- if (reconnect && this._attachState === AttachState.Attached && this.resumedOpProcessingAfterLoad) {
928
- if (this.connectionState === ConnectionState.Disconnected) {
929
- // Only track this as a manual reconnection if we are truly the ones kicking it off.
930
- this.manualReconnectInProgress = true;
931
- }
956
+ public connect() {
957
+ if (this.closed) {
958
+ throw new UsageError(`The Container is closed and cannot be connected`);
959
+ }
960
+ else if (this._attachState !== AttachState.Attached) {
961
+ throw new UsageError(`The Container is not attached and cannot be connected`);
962
+ }
963
+ else if (!this.connected) {
964
+ // Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
965
+ // If there is gap, we will learn about it once connected, but the gap should be small (if any),
966
+ // assuming that connect() is called quickly after initial container boot.
967
+ this.connectInternal({ reason: "DocumentConnect", fetchOpsFromStorage: false });
968
+ }
969
+ }
932
970
 
933
- // Ensure connection to web socket
934
- this.connectToDeltaStream({ reason: "autoReconnect" });
971
+ private connectInternal(args: IConnectionArgs) {
972
+ assert(!this.closed, 0x2c5 /* "Attempting to connect() a closed Container" */);
973
+ assert(this._attachState === AttachState.Attached,
974
+ 0x2c6 /* "Attempting to connect() a container that is not attached" */);
975
+
976
+ // Resume processing ops and connect to delta stream
977
+ this.resumeInternal(args);
978
+
979
+ // Set Auto Reconnect Mode
980
+ const mode = ReconnectMode.Enabled;
981
+ this.setAutoReconnectInternal(mode);
982
+ }
983
+
984
+ public disconnect() {
985
+ if (this.closed) {
986
+ throw new UsageError(`The Container is closed and cannot be disconnected`);
987
+ }
988
+ else {
989
+ this.disconnectInternal();
935
990
  }
936
991
  }
937
992
 
993
+ private disconnectInternal() {
994
+ assert(!this.closed, 0x2c7 /* "Attempting to disconnect() a closed Container" */);
995
+
996
+ // Set Auto Reconnect Mode
997
+ const mode = ReconnectMode.Disabled;
998
+ this.setAutoReconnectInternal(mode);
999
+ }
1000
+
1001
+ /**
1002
+ * Have the container attempt to resume processing ops
1003
+ * @deprecated - 0.58, This API will be removed in 1.0
1004
+ * Use `connect()` instead
1005
+ * See https://github.com/microsoft/FluidFramework/issues/9167 for context
1006
+ */
938
1007
  public resume() {
939
1008
  if (!this.closed) {
940
1009
  // Note: no need to fetch ops as we do it preemptively as part of DeltaManager.attachOpHandler().
@@ -958,21 +1027,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
958
1027
  this.connectToDeltaStream(args);
959
1028
  }
960
1029
 
961
- /**
962
- * @deprecated 0.56, will be removed in next release from IContainerContext
963
- * Raise non-critical error to host. Calling this API will not close container.
964
- * For critical errors, please call Container.close(error).
965
- * @param error - an error to raise
966
- */
967
- public raiseContainerWarning(warning: ContainerWarning) {
968
- // Some "warning" events come from outside the container and are logged
969
- // elsewhere (e.g. summarizing container). We shouldn't log these here.
970
- if (warning.logged !== true) {
971
- this.logContainerError(warning);
972
- }
973
- this.emit("warning", warning);
974
- }
975
-
976
1030
  public async getAbsoluteUrl(relativeUrl: string): Promise<string | undefined> {
977
1031
  if (this.resolvedUrl === undefined) {
978
1032
  return undefined;
@@ -981,7 +1035,7 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
981
1035
  return this.urlResolver.getAbsoluteUrl(
982
1036
  this.resolvedUrl,
983
1037
  relativeUrl,
984
- this._context?.codeDetails);
1038
+ getPackageName(this._context?.codeDetails));
985
1039
  }
986
1040
 
987
1041
  public async proposeCodeDetails(codeDetails: IFluidCodeDetails) {
@@ -1057,7 +1111,11 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1057
1111
  if (this._resolvedUrl === undefined) {
1058
1112
  throw new Error("Attempting to load without a resolved url");
1059
1113
  }
1060
- this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger);
1114
+ this.service = await this.serviceFactory.createDocumentService(
1115
+ this._resolvedUrl,
1116
+ this.subLogger,
1117
+ this.client.details.type === summarizerClientType,
1118
+ );
1061
1119
 
1062
1120
  // Ideally we always connect as "read" by default.
1063
1121
  // Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
@@ -1476,13 +1534,18 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1476
1534
  });
1477
1535
 
1478
1536
  deltaManager.on("disconnect", (reason: string) => {
1479
- this.manualReconnectInProgress = false;
1480
1537
  this.collabWindowTracker.stopSequenceNumberUpdate();
1481
1538
  this.connectionStateHandler.receivedDisconnectEvent(reason);
1482
1539
  });
1483
1540
 
1484
1541
  deltaManager.on("throttled", (warning: IThrottlingWarning) => {
1485
- this.raiseContainerWarning(warning);
1542
+ const warn = warning as ContainerWarning;
1543
+ // Some "warning" events come from outside the container and are logged
1544
+ // elsewhere (e.g. summarizing container). We shouldn't log these here.
1545
+ if (warn.logged !== true) {
1546
+ this.logContainerError(warn);
1547
+ }
1548
+ this.emit("warning", warn);
1486
1549
  });
1487
1550
 
1488
1551
  deltaManager.on("readonly", (readonly) => {
@@ -1543,8 +1606,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1543
1606
  }
1544
1607
  if (this.firstConnection) {
1545
1608
  connectionInitiationReason = "InitialConnect";
1546
- } else if (this.manualReconnectInProgress) {
1547
- connectionInitiationReason = "ManualReconnect";
1548
1609
  } else {
1549
1610
  connectionInitiationReason = "AutoReconnect";
1550
1611
  }
@@ -1569,7 +1630,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1569
1630
 
1570
1631
  if (value === ConnectionState.Connected) {
1571
1632
  this.firstConnection = false;
1572
- this.manualReconnectInProgress = false;
1573
1633
  }
1574
1634
  }
1575
1635
 
@@ -1755,7 +1815,6 @@ export class Container extends EventEmitterWithErrorHandling<IContainerEvents> i
1755
1815
  new DeltaManagerProxy(this._deltaManager),
1756
1816
  new QuorumProxy(this.protocolHandler.quorum),
1757
1817
  loader,
1758
- (warning: ContainerWarning) => this.raiseContainerWarning(warning),
1759
1818
  (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata),
1760
1819
  (message) => this.submitSignal(message),
1761
1820
  (error?: ICriticalContainerError) => this.close(error),
@@ -12,20 +12,19 @@ import {
12
12
  ILoader,
13
13
  IRuntime,
14
14
  ICriticalContainerError,
15
- ContainerWarning,
16
15
  AttachState,
17
16
  ILoaderOptions,
18
17
  IRuntimeFactory,
19
- ICodeLoader,
20
18
  IProvideRuntimeFactory,
19
+ IFluidCodeDetails,
20
+ IFluidCodeDetailsComparer,
21
+ IProvideFluidCodeDetailsComparer,
22
+ ICodeDetailsLoader,
23
+ IFluidModuleWithDetails,
21
24
  } from "@fluidframework/container-definitions";
22
25
  import {
23
- IFluidObject,
24
26
  IRequest,
25
27
  IResponse,
26
- IFluidCodeDetails,
27
- IFluidCodeDetailsComparer,
28
- IProvideFluidCodeDetailsComparer,
29
28
  FluidObject,
30
29
  } from "@fluidframework/core-interfaces";
31
30
  import { IDocumentStorageService } from "@fluidframework/driver-definitions";
@@ -45,7 +44,6 @@ import {
45
44
  } from "@fluidframework/protocol-definitions";
46
45
  import { PerformanceEvent } from "@fluidframework/telemetry-utils";
47
46
  import { Container } from "./container";
48
- import { ICodeDetailsLoader, IFluidModuleWithDetails } from "./loader";
49
47
 
50
48
  const PackageNotFactoryError = "Code package does not implement IRuntimeFactory";
51
49
 
@@ -53,13 +51,12 @@ export class ContainerContext implements IContainerContext {
53
51
  public static async createOrLoad(
54
52
  container: Container,
55
53
  scope: FluidObject,
56
- codeLoader: ICodeDetailsLoader | ICodeLoader,
54
+ codeLoader: ICodeDetailsLoader,
57
55
  codeDetails: IFluidCodeDetails,
58
56
  baseSnapshot: ISnapshotTree | undefined,
59
57
  deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
60
58
  quorum: IQuorum,
61
59
  loader: ILoader,
62
- raiseContainerWarning: (warning: ContainerWarning) => void,
63
60
  submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
64
61
  submitSignalFn: (contents: any) => void,
65
62
  closeFn: (error?: ICriticalContainerError) => void,
@@ -77,7 +74,6 @@ export class ContainerContext implements IContainerContext {
77
74
  deltaManager,
78
75
  quorum,
79
76
  loader,
80
- raiseContainerWarning,
81
77
  submitFn,
82
78
  submitSignalFn,
83
79
  closeFn,
@@ -159,14 +155,13 @@ export class ContainerContext implements IContainerContext {
159
155
 
160
156
  constructor(
161
157
  private readonly container: Container,
162
- public readonly scope: IFluidObject & FluidObject,
163
- private readonly codeLoader: ICodeDetailsLoader | ICodeLoader,
158
+ public readonly scope: FluidObject,
159
+ private readonly codeLoader: ICodeDetailsLoader,
164
160
  private readonly _codeDetails: IFluidCodeDetails,
165
161
  private readonly _baseSnapshot: ISnapshotTree | undefined,
166
162
  public readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,
167
163
  quorum: IQuorum,
168
164
  public readonly loader: ILoader,
169
- public readonly raiseContainerWarning: (warning: ContainerWarning) => void,
170
165
  public readonly submitFn: (type: MessageType, contents: any, batch: boolean, appData: any) => number,
171
166
  public readonly submitSignalFn: (contents: any) => void,
172
167
  public readonly closeFn: (error?: ICriticalContainerError) => void,
@@ -333,6 +328,9 @@ export class ContainerContext implements IContainerContext {
333
328
  details: details ?? codeDetails,
334
329
  };
335
330
  } else {
331
+ // If "module" is not in the result, we are using a legacy ICodeLoader. Fix the result up with details.
332
+ // Once usage drops to 0 we can remove this compat path.
333
+ this.taggedLogger.sendTelemetryEvent({ eventName: "LegacyCodeLoader" });
336
334
  return { module: loadCodeResult, details: codeDetails };
337
335
  }
338
336
  }
package/src/contracts.ts CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  ReadOnlyInfo,
12
12
  IConnectionDetails,
13
13
  ICriticalContainerError,
14
+ IFluidCodeDetails,
15
+ isFluidPackage,
14
16
  } from "@fluidframework/container-definitions";
15
17
  import {
16
18
  ConnectionMode,
@@ -20,6 +22,7 @@ import {
20
22
  IClientDetails,
21
23
  ISignalMessage,
22
24
  } from "@fluidframework/protocol-definitions";
25
+ import { IContainerPackageInfo } from "@fluidframework/driver-definitions";
23
26
 
24
27
  export enum ReconnectMode {
25
28
  Never = "Never",
@@ -155,3 +158,20 @@ export interface IConnectionManagerFactoryArgs {
155
158
  */
156
159
  readonly readonlyChangeHandler: (readonly?: boolean) => void,
157
160
  }
161
+
162
+ /**
163
+ *
164
+ * @param codeDetails- Data structure used to describe the code to load on the Fluid document
165
+ * @returns The name of the Fluid package
166
+ */
167
+ export const getPackageName = (codeDetails: IFluidCodeDetails | undefined): IContainerPackageInfo => {
168
+ let containerPackageName;
169
+ if (codeDetails && "name" in codeDetails) {
170
+ containerPackageName = codeDetails;
171
+ } else if (isFluidPackage(codeDetails?.package)) {
172
+ containerPackageName = codeDetails?.package.name;
173
+ } else {
174
+ containerPackageName = codeDetails?.package;
175
+ }
176
+ return { name: containerPackageName };
177
+ };
@@ -123,7 +123,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
123
123
  private readonly throttlingIdSet = new Set<string>();
124
124
  private timeTillThrottling: number = 0;
125
125
 
126
- private readonly closeAbortController = new AbortController();
126
+ public readonly closeAbortController = new AbortController();
127
127
 
128
128
  private readonly deltaStorageDelayId = uuid();
129
129
  private readonly deltaStreamDelayId = uuid();
@@ -201,11 +201,12 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
201
201
 
202
202
  this.messageBuffer.push(message);
203
203
 
204
+ this.emit("submitOp", message);
205
+
204
206
  if (!batch) {
205
207
  this.flush();
206
208
  }
207
209
 
208
- this.emit("submitOp", message);
209
210
  return message.clientSequenceNumber;
210
211
  }
211
212
 
package/src/loader.ts CHANGED
@@ -7,15 +7,12 @@ import { v4 as uuid } from "uuid";
7
7
  import { ITelemetryBaseLogger, ITelemetryLogger } from "@fluidframework/common-definitions";
8
8
  import {
9
9
  FluidObject,
10
- IFluidCodeDetails,
11
10
  IFluidRouter,
12
- IProvideFluidCodeDetailsComparer,
13
11
  IRequest,
14
12
  IRequestHeader,
15
13
  IResponse,
16
14
  } from "@fluidframework/core-interfaces";
17
15
  import {
18
- ICodeLoader,
19
16
  IContainer,
20
17
  IFluidModule,
21
18
  IHostLoader,
@@ -24,6 +21,8 @@ import {
24
21
  ILoaderOptions as ILoaderOptions1,
25
22
  IProxyLoaderFactory,
26
23
  LoaderHeader,
24
+ IProvideFluidCodeDetailsComparer,
25
+ IFluidCodeDetails,
27
26
  } from "@fluidframework/container-definitions";
28
27
  import {
29
28
  ChildLogger,
@@ -187,7 +186,7 @@ export interface ILoaderProps {
187
186
  * The code loader handles loading the necessary code
188
187
  * for running a container once it is loaded.
189
188
  */
190
- readonly codeLoader: ICodeDetailsLoader | ICodeLoader;
189
+ readonly codeLoader: ICodeDetailsLoader;
191
190
 
192
191
  /**
193
192
  * A property bag of options used by various layers
@@ -243,7 +242,7 @@ export interface ILoaderServices {
243
242
  * The code loader handles loading the necessary code
244
243
  * for running a container once it is loaded.
245
244
  */
246
- readonly codeLoader: ICodeDetailsLoader | ICodeLoader;
245
+ readonly codeLoader: ICodeDetailsLoader;
247
246
 
248
247
  /**
249
248
  * A property bag of options used by various layers
@@ -295,7 +294,7 @@ export class Loader implements IHostLoader {
295
294
  private readonly mc: MonitoringContext;
296
295
 
297
296
  constructor(loaderProps: ILoaderProps) {
298
- const scope = { ...loaderProps.scope as FluidObject<ILoader> };
297
+ const scope: FluidObject<ILoader> = { ...loaderProps.scope };
299
298
  if (loaderProps.options?.provideScopeLoader !== false) {
300
299
  scope.ILoader = this;
301
300
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-loader";
9
- export const pkgVersion = "0.58.2001";
9
+ export const pkgVersion = "0.59.1000-61898";