@fluidframework/container-loader 2.0.0-internal.5.4.0 → 2.0.0-internal.6.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 (102) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/dist/connectionManager.d.ts +4 -4
  3. package/dist/connectionManager.d.ts.map +1 -1
  4. package/dist/connectionManager.js +57 -49
  5. package/dist/connectionManager.js.map +1 -1
  6. package/dist/connectionStateHandler.d.ts +15 -14
  7. package/dist/connectionStateHandler.d.ts.map +1 -1
  8. package/dist/connectionStateHandler.js +26 -28
  9. package/dist/connectionStateHandler.js.map +1 -1
  10. package/dist/container.d.ts +10 -5
  11. package/dist/container.d.ts.map +1 -1
  12. package/dist/container.js +183 -134
  13. package/dist/container.js.map +1 -1
  14. package/dist/containerContext.d.ts +2 -12
  15. package/dist/containerContext.d.ts.map +1 -1
  16. package/dist/containerContext.js +1 -20
  17. package/dist/containerContext.js.map +1 -1
  18. package/dist/containerStorageAdapter.js +3 -5
  19. package/dist/containerStorageAdapter.js.map +1 -1
  20. package/dist/contracts.d.ts +20 -7
  21. package/dist/contracts.d.ts.map +1 -1
  22. package/dist/contracts.js +3 -3
  23. package/dist/contracts.js.map +1 -1
  24. package/dist/debugLogger.js +2 -3
  25. package/dist/debugLogger.js.map +1 -1
  26. package/dist/deltaManager.d.ts +19 -6
  27. package/dist/deltaManager.d.ts.map +1 -1
  28. package/dist/deltaManager.js +67 -28
  29. package/dist/deltaManager.js.map +1 -1
  30. package/dist/deltaQueue.js +1 -2
  31. package/dist/deltaQueue.js.map +1 -1
  32. package/dist/loader.d.ts +12 -0
  33. package/dist/loader.d.ts.map +1 -1
  34. package/dist/loader.js +57 -42
  35. package/dist/loader.js.map +1 -1
  36. package/dist/packageVersion.d.ts +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/protocol.d.ts +4 -2
  40. package/dist/protocol.d.ts.map +1 -1
  41. package/dist/protocol.js +25 -4
  42. package/dist/protocol.js.map +1 -1
  43. package/dist/utils.d.ts +8 -1
  44. package/dist/utils.d.ts.map +1 -1
  45. package/dist/utils.js +24 -6
  46. package/dist/utils.js.map +1 -1
  47. package/lib/connectionManager.d.ts +4 -4
  48. package/lib/connectionManager.d.ts.map +1 -1
  49. package/lib/connectionManager.js +58 -50
  50. package/lib/connectionManager.js.map +1 -1
  51. package/lib/connectionStateHandler.d.ts +15 -14
  52. package/lib/connectionStateHandler.d.ts.map +1 -1
  53. package/lib/connectionStateHandler.js +26 -28
  54. package/lib/connectionStateHandler.js.map +1 -1
  55. package/lib/container.d.ts +10 -5
  56. package/lib/container.d.ts.map +1 -1
  57. package/lib/container.js +182 -133
  58. package/lib/container.js.map +1 -1
  59. package/lib/containerContext.d.ts +2 -12
  60. package/lib/containerContext.d.ts.map +1 -1
  61. package/lib/containerContext.js +1 -20
  62. package/lib/containerContext.js.map +1 -1
  63. package/lib/containerStorageAdapter.js +3 -5
  64. package/lib/containerStorageAdapter.js.map +1 -1
  65. package/lib/contracts.d.ts +20 -7
  66. package/lib/contracts.d.ts.map +1 -1
  67. package/lib/contracts.js +3 -3
  68. package/lib/contracts.js.map +1 -1
  69. package/lib/debugLogger.js +2 -3
  70. package/lib/debugLogger.js.map +1 -1
  71. package/lib/deltaManager.d.ts +19 -6
  72. package/lib/deltaManager.d.ts.map +1 -1
  73. package/lib/deltaManager.js +67 -28
  74. package/lib/deltaManager.js.map +1 -1
  75. package/lib/deltaQueue.js +1 -2
  76. package/lib/deltaQueue.js.map +1 -1
  77. package/lib/loader.d.ts +12 -0
  78. package/lib/loader.d.ts.map +1 -1
  79. package/lib/loader.js +57 -42
  80. package/lib/loader.js.map +1 -1
  81. package/lib/packageVersion.d.ts +1 -1
  82. package/lib/packageVersion.js +1 -1
  83. package/lib/packageVersion.js.map +1 -1
  84. package/lib/protocol.d.ts +4 -2
  85. package/lib/protocol.d.ts.map +1 -1
  86. package/lib/protocol.js +25 -4
  87. package/lib/protocol.js.map +1 -1
  88. package/lib/utils.d.ts +8 -1
  89. package/lib/utils.d.ts.map +1 -1
  90. package/lib/utils.js +22 -5
  91. package/lib/utils.js.map +1 -1
  92. package/package.json +15 -19
  93. package/src/connectionManager.ts +53 -34
  94. package/src/connectionStateHandler.ts +30 -37
  95. package/src/container.ts +156 -76
  96. package/src/containerContext.ts +0 -24
  97. package/src/contracts.ts +27 -10
  98. package/src/deltaManager.ts +41 -18
  99. package/src/loader.ts +37 -23
  100. package/src/packageVersion.ts +1 -1
  101. package/src/protocol.ts +33 -2
  102. package/src/utils.ts +29 -0
package/lib/utils.js CHANGED
@@ -10,18 +10,35 @@ import { LoggingError } from "@fluidframework/telemetry-utils";
10
10
  import { isCombinedAppAndProtocolSummary, } from "@fluidframework/driver-utils";
11
11
  import { DriverErrorType } from "@fluidframework/driver-definitions";
12
12
  export function parseUrl(url) {
13
- var _a;
14
13
  const parsed = parse(url, true);
15
14
  if (typeof parsed.pathname !== "string") {
16
15
  throw new LoggingError("Failed to parse pathname");
17
16
  }
18
- const query = (_a = parsed.search) !== null && _a !== void 0 ? _a : "";
17
+ const query = parsed.search ?? "";
19
18
  const regex = /^\/([^/]*\/[^/]*)(\/?.*)$/;
20
19
  const match = regex.exec(parsed.pathname);
21
- return (match === null || match === void 0 ? void 0 : match.length) === 3
20
+ return match?.length === 3
22
21
  ? { id: match[1], path: match[2], query, version: parsed.query.version }
23
22
  : undefined;
24
23
  }
24
+ /**
25
+ * Combine the app summary and protocol summary in 1 tree.
26
+ * @param appSummary - Summary of the app.
27
+ * @param protocolSummary - Summary of the protocol.
28
+ * @internal
29
+ */
30
+ export function combineAppAndProtocolSummary(appSummary, protocolSummary) {
31
+ assert(!isCombinedAppAndProtocolSummary(appSummary), 0x5a8 /* app summary is already a combined tree! */);
32
+ assert(!isCombinedAppAndProtocolSummary(protocolSummary), 0x5a9 /* protocol summary is already a combined tree! */);
33
+ const createNewSummary = {
34
+ type: SummaryType.Tree,
35
+ tree: {
36
+ ".protocol": protocolSummary,
37
+ ".app": appSummary,
38
+ },
39
+ };
40
+ return createNewSummary;
41
+ }
25
42
  /**
26
43
  * Converts summary tree (for upload) to snapshot tree (for download).
27
44
  * Summary tree blobs contain contents, but snapshot tree blobs normally
@@ -80,7 +97,7 @@ export function convertProtocolAndAppSummaryToSnapshotTree(protocolSummaryTree,
80
97
  // Shallow copy is fine, since we are doing a deep clone below.
81
98
  const combinedSummary = {
82
99
  type: SummaryType.Tree,
83
- tree: Object.assign({}, appSummaryTree.tree),
100
+ tree: { ...appSummaryTree.tree },
84
101
  };
85
102
  combinedSummary.tree[".protocol"] = protocolSummaryTree;
86
103
  const snapshotTreeWithBlobContents = convertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);
@@ -101,6 +118,6 @@ export function getProtocolSnapshotTree(snapshot) {
101
118
  export function isDeltaStreamConnectionForbiddenError(error) {
102
119
  return (typeof error === "object" &&
103
120
  error !== null &&
104
- (error === null || error === void 0 ? void 0 : error.errorType) === DriverErrorType.deltaStreamConnectionForbidden);
121
+ error?.errorType === DriverErrorType.deltaStreamConnectionForbidden);
105
122
  }
106
123
  //# sourceMappingURL=utils.js.map
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\tassert,\n\tstringToBuffer,\n\tUint8ArrayToArrayBuffer,\n\tunreachableCase,\n} from \"@fluidframework/common-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n\tDeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n} from \"@fluidframework/driver-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-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\tblobsContents: { [path: string]: ArrayBufferLike };\n\ttrees: { [path: string]: ISnapshotTreeWithBlobContents };\n}\n\nexport interface IParsedUrl {\n\tid: string;\n\tpath: string;\n\tquery: string;\n\t/**\n\t * Null means do not use snapshots, undefined means load latest snapshot\n\t * otherwise it's version ID passed to IDocumentStorageService.getVersions() to figure out what snapshot to use.\n\t * If needed, can add undefined which is treated by Container.load() as load latest snapshot.\n\t */\n\tversion: string | null | undefined;\n}\n\nexport function parseUrl(url: string): IParsedUrl | undefined {\n\tconst parsed = parse(url, true);\n\tif (typeof parsed.pathname !== \"string\") {\n\t\tthrow new LoggingError(\"Failed to parse pathname\");\n\t}\n\tconst query = parsed.search ?? \"\";\n\tconst regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n\tconst match = regex.exec(parsed.pathname);\n\treturn match?.length === 3\n\t\t? { id: match[1], path: match[2], query, version: parsed.query.version as string }\n\t\t: 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\tsummary: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\tconst treeNode: ISnapshotTreeWithBlobContents = {\n\t\tblobs: {},\n\t\tblobsContents: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t};\n\tconst keys = Object.keys(summary.tree);\n\tfor (const key of keys) {\n\t\tconst summaryObject = summary.tree[key];\n\n\t\tswitch (summaryObject.type) {\n\t\t\tcase SummaryType.Tree: {\n\t\t\t\ttreeNode.trees[key] =\n\t\t\t\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Attachment:\n\t\t\t\ttreeNode.blobs[key] = summaryObject.id;\n\t\t\t\tbreak;\n\t\t\tcase SummaryType.Blob: {\n\t\t\t\tconst blobId = uuid();\n\t\t\t\ttreeNode.blobs[key] = blobId;\n\t\t\t\tconst contentBuffer =\n\t\t\t\t\ttypeof summaryObject.content === \"string\"\n\t\t\t\t\t\t? stringToBuffer(summaryObject.content, \"utf8\")\n\t\t\t\t\t\t: Uint8ArrayToArrayBuffer(summaryObject.content);\n\t\t\t\ttreeNode.blobsContents[blobId] = contentBuffer;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Handle:\n\t\t\t\tthrow new LoggingError(\n\t\t\t\t\t\"No handles should be there in summary in detached container!!\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tunreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn 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\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\t// Shallow copy is fine, since we are doing a deep clone below.\n\tconst combinedSummary: ISummaryTree = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { ...appSummaryTree.tree },\n\t};\n\n\tcombinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n\tconst snapshotTreeWithBlobContents =\n\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);\n\treturn 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 = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): ISnapshotTreeWithBlobContents => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x1e0 /* \"Protocol and App summary trees should be present\" */,\n\t);\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"];\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"];\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(\n\t\tprotocolSummaryTree,\n\t\tappSummaryTree,\n\t);\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree {\n\treturn \".protocol\" in snapshot.trees ? snapshot.trees[\".protocol\"] : snapshot;\n}\n\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: any,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\terror?.errorType === DriverErrorType.deltaStreamConnectionForbidden\n\t);\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\tassert,\n\tstringToBuffer,\n\tUint8ArrayToArrayBuffer,\n\tunreachableCase,\n} from \"@fluidframework/common-utils\";\nimport { ISummaryTree, ISnapshotTree, SummaryType } from \"@fluidframework/protocol-definitions\";\nimport { LoggingError } from \"@fluidframework/telemetry-utils\";\nimport {\n\tCombinedAppAndProtocolSummary,\n\tDeltaStreamConnectionForbiddenError,\n\tisCombinedAppAndProtocolSummary,\n} from \"@fluidframework/driver-utils\";\nimport { DriverErrorType } from \"@fluidframework/driver-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\tblobsContents: { [path: string]: ArrayBufferLike };\n\ttrees: { [path: string]: ISnapshotTreeWithBlobContents };\n}\n\nexport interface IParsedUrl {\n\tid: string;\n\tpath: string;\n\tquery: string;\n\t/**\n\t * Null means do not use snapshots, undefined means load latest snapshot\n\t * otherwise it's version ID passed to IDocumentStorageService.getVersions() to figure out what snapshot to use.\n\t * If needed, can add undefined which is treated by Container.load() as load latest snapshot.\n\t */\n\tversion: string | null | undefined;\n}\n\nexport function parseUrl(url: string): IParsedUrl | undefined {\n\tconst parsed = parse(url, true);\n\tif (typeof parsed.pathname !== \"string\") {\n\t\tthrow new LoggingError(\"Failed to parse pathname\");\n\t}\n\tconst query = parsed.search ?? \"\";\n\tconst regex = /^\\/([^/]*\\/[^/]*)(\\/?.*)$/;\n\tconst match = regex.exec(parsed.pathname);\n\treturn match?.length === 3\n\t\t? { id: match[1], path: match[2], query, version: parsed.query.version as string }\n\t\t: undefined;\n}\n\n/**\n * Combine the app summary and protocol summary in 1 tree.\n * @param appSummary - Summary of the app.\n * @param protocolSummary - Summary of the protocol.\n * @internal\n */\nexport function combineAppAndProtocolSummary(\n\tappSummary: ISummaryTree,\n\tprotocolSummary: ISummaryTree,\n): CombinedAppAndProtocolSummary {\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(appSummary),\n\t\t0x5a8 /* app summary is already a combined tree! */,\n\t);\n\tassert(\n\t\t!isCombinedAppAndProtocolSummary(protocolSummary),\n\t\t0x5a9 /* protocol summary is already a combined tree! */,\n\t);\n\tconst createNewSummary: CombinedAppAndProtocolSummary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: {\n\t\t\t\".protocol\": protocolSummary,\n\t\t\t\".app\": appSummary,\n\t\t},\n\t};\n\treturn createNewSummary;\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\tsummary: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\tconst treeNode: ISnapshotTreeWithBlobContents = {\n\t\tblobs: {},\n\t\tblobsContents: {},\n\t\ttrees: {},\n\t\tid: uuid(),\n\t\tunreferenced: summary.unreferenced,\n\t};\n\tconst keys = Object.keys(summary.tree);\n\tfor (const key of keys) {\n\t\tconst summaryObject = summary.tree[key];\n\n\t\tswitch (summaryObject.type) {\n\t\t\tcase SummaryType.Tree: {\n\t\t\t\ttreeNode.trees[key] =\n\t\t\t\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(summaryObject);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Attachment:\n\t\t\t\ttreeNode.blobs[key] = summaryObject.id;\n\t\t\t\tbreak;\n\t\t\tcase SummaryType.Blob: {\n\t\t\t\tconst blobId = uuid();\n\t\t\t\ttreeNode.blobs[key] = blobId;\n\t\t\t\tconst contentBuffer =\n\t\t\t\t\ttypeof summaryObject.content === \"string\"\n\t\t\t\t\t\t? stringToBuffer(summaryObject.content, \"utf8\")\n\t\t\t\t\t\t: Uint8ArrayToArrayBuffer(summaryObject.content);\n\t\t\t\ttreeNode.blobsContents[blobId] = contentBuffer;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SummaryType.Handle:\n\t\t\t\tthrow new LoggingError(\n\t\t\t\t\t\"No handles should be there in summary in detached container!!\",\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tunreachableCase(summaryObject, `Unknown tree type ${(summaryObject as any).type}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn 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\tprotocolSummaryTree: ISummaryTree,\n\tappSummaryTree: ISummaryTree,\n): ISnapshotTreeWithBlobContents {\n\t// Shallow copy is fine, since we are doing a deep clone below.\n\tconst combinedSummary: ISummaryTree = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { ...appSummaryTree.tree },\n\t};\n\n\tcombinedSummary.tree[\".protocol\"] = protocolSummaryTree;\n\tconst snapshotTreeWithBlobContents =\n\t\tconvertSummaryToSnapshotWithEmbeddedBlobContents(combinedSummary);\n\treturn 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 = (\n\tdetachedContainerSnapshot: ISummaryTree,\n): ISnapshotTreeWithBlobContents => {\n\tassert(\n\t\tisCombinedAppAndProtocolSummary(detachedContainerSnapshot),\n\t\t0x1e0 /* \"Protocol and App summary trees should be present\" */,\n\t);\n\tconst protocolSummaryTree = detachedContainerSnapshot.tree[\".protocol\"];\n\tconst appSummaryTree = detachedContainerSnapshot.tree[\".app\"];\n\tconst snapshotTreeWithBlobContents = convertProtocolAndAppSummaryToSnapshotTree(\n\t\tprotocolSummaryTree,\n\t\tappSummaryTree,\n\t);\n\treturn snapshotTreeWithBlobContents;\n};\n\nexport function getProtocolSnapshotTree(snapshot: ISnapshotTree): ISnapshotTree {\n\treturn \".protocol\" in snapshot.trees ? snapshot.trees[\".protocol\"] : snapshot;\n}\n\nexport function isDeltaStreamConnectionForbiddenError(\n\terror: any,\n): error is DeltaStreamConnectionForbiddenError {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\terror?.errorType === DriverErrorType.deltaStreamConnectionForbidden\n\t);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-loader",
3
- "version": "2.0.0-internal.5.4.0",
3
+ "version": "2.0.0-internal.6.1.0",
4
4
  "description": "Fluid container loader",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -37,14 +37,14 @@
37
37
  "dependencies": {
38
38
  "@fluidframework/common-definitions": "^0.20.1",
39
39
  "@fluidframework/common-utils": "^1.1.1",
40
- "@fluidframework/container-definitions": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
41
- "@fluidframework/container-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
42
- "@fluidframework/core-interfaces": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
43
- "@fluidframework/driver-definitions": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
44
- "@fluidframework/driver-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
45
- "@fluidframework/protocol-base": "^0.1039.1000",
40
+ "@fluidframework/container-definitions": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
41
+ "@fluidframework/container-utils": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
42
+ "@fluidframework/core-interfaces": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
43
+ "@fluidframework/driver-definitions": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
44
+ "@fluidframework/driver-utils": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
45
+ "@fluidframework/protocol-base": "^1.0.0",
46
46
  "@fluidframework/protocol-definitions": "^1.1.0",
47
- "@fluidframework/telemetry-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
47
+ "@fluidframework/telemetry-utils": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
48
48
  "debug": "^4.1.1",
49
49
  "double-ended-queue": "^2.1.0-0",
50
50
  "events": "^3.1.0",
@@ -53,13 +53,13 @@
53
53
  "uuid": "^8.3.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@fluid-internal/test-loader-utils": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
57
- "@fluid-tools/build-cli": "^0.21.0",
58
- "@fluidframework/build-common": "^1.2.0",
59
- "@fluidframework/build-tools": "^0.21.0",
60
- "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.5.2.0",
56
+ "@fluid-internal/test-loader-utils": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
57
+ "@fluid-tools/build-cli": "^0.22.0",
58
+ "@fluidframework/build-common": "^2.0.0",
59
+ "@fluidframework/build-tools": "^0.22.0",
60
+ "@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.6.0.0",
61
61
  "@fluidframework/eslint-config-fluid": "^2.0.0",
62
- "@fluidframework/mocha-test-setup": ">=2.0.0-internal.5.4.0 <2.0.0-internal.5.5.0",
62
+ "@fluidframework/mocha-test-setup": ">=2.0.0-internal.6.1.0 <2.0.0-internal.6.2.0",
63
63
  "@microsoft/api-extractor": "^7.34.4",
64
64
  "@types/double-ended-queue": "^2.1.0",
65
65
  "@types/events": "^3.0.0",
@@ -82,11 +82,7 @@
82
82
  "typescript": "~4.5.5"
83
83
  },
84
84
  "typeValidation": {
85
- "broken": {
86
- "InterfaceDeclaration_IContainerExperimental": {
87
- "backCompat": false
88
- }
89
- }
85
+ "broken": {}
90
86
  },
91
87
  "scripts": {
92
88
  "build": "fluid-build . --task build",
@@ -6,10 +6,9 @@
6
6
  import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
7
7
  import { assert, performance, TypedEventEmitter } from "@fluidframework/common-utils";
8
8
  import {
9
+ ICriticalContainerError,
9
10
  IDeltaQueue,
10
11
  ReadOnlyInfo,
11
- IConnectionDetailsInternal,
12
- ICriticalContainerError,
13
12
  } from "@fluidframework/container-definitions";
14
13
  import { GenericError, UsageError } from "@fluidframework/container-utils";
15
14
  import {
@@ -44,7 +43,13 @@ import {
44
43
  ISequencedDocumentSystemMessage,
45
44
  } from "@fluidframework/protocol-definitions";
46
45
  import { ITelemetryLoggerExt, formatTick, normalizeError } from "@fluidframework/telemetry-utils";
47
- import { ReconnectMode, IConnectionManager, IConnectionManagerFactoryArgs } from "./contracts";
46
+ import {
47
+ ReconnectMode,
48
+ IConnectionManager,
49
+ IConnectionManagerFactoryArgs,
50
+ IConnectionDetailsInternal,
51
+ IConnectionStateChangeReason,
52
+ } from "./contracts";
48
53
  import { DeltaQueue } from "./deltaQueue";
49
54
  import { SignalType } from "./protocol";
50
55
  import { isDeltaStreamConnectionForbiddenError } from "./utils";
@@ -332,7 +337,7 @@ export class ConnectionManager implements IConnectionManager {
332
337
 
333
338
  private static detailsFromConnection(
334
339
  connection: IDocumentDeltaConnection,
335
- reason: string,
340
+ reason: IConnectionStateChangeReason,
336
341
  ): IConnectionDetailsInternal {
337
342
  return {
338
343
  claims: connection.claims,
@@ -385,7 +390,10 @@ export class ConnectionManager implements IConnectionManager {
385
390
 
386
391
  this._outbound.clear();
387
392
 
388
- const disconnectReason = "Closing DeltaManager";
393
+ const disconnectReason: IConnectionStateChangeReason = {
394
+ text: "Closing DeltaManager",
395
+ error,
396
+ };
389
397
 
390
398
  // This raises "disconnect" event if we have active connection.
391
399
  this.disconnectFromDeltaStream(disconnectReason);
@@ -402,7 +410,7 @@ export class ConnectionManager implements IConnectionManager {
402
410
  * Enables or disables automatic reconnecting.
403
411
  * Will throw an error if reconnectMode set to Never.
404
412
  */
405
- public setAutoReconnect(mode: ReconnectMode): void {
413
+ public setAutoReconnect(mode: ReconnectMode, reason: IConnectionStateChangeReason): void {
406
414
  assert(
407
415
  mode !== ReconnectMode.Never && this._reconnectMode !== ReconnectMode.Never,
408
416
  0x278 /* "API is not supported for non-connecting or closed container" */,
@@ -412,7 +420,7 @@ export class ConnectionManager implements IConnectionManager {
412
420
 
413
421
  if (mode !== ReconnectMode.Enabled) {
414
422
  // immediately disconnect - do not rely on service eventually dropping connection.
415
- this.disconnectFromDeltaStream("setAutoReconnect");
423
+ this.disconnectFromDeltaStream(reason);
416
424
  }
417
425
  }
418
426
 
@@ -459,12 +467,12 @@ export class ConnectionManager implements IConnectionManager {
459
467
  this.logger.sendErrorEvent({ eventName: "ForceReadonlyPendingChanged" });
460
468
  }
461
469
 
462
- reconnect = this.disconnectFromDeltaStream("Force readonly");
470
+ reconnect = this.disconnectFromDeltaStream({ text: "Force readonly" });
463
471
  }
464
472
  this.props.readonlyChangeHandler(this.readonly);
465
473
  if (reconnect) {
466
474
  // reconnect if we disconnected from before.
467
- this.triggerConnect("Force Readonly", "read");
475
+ this.triggerConnect({ text: "Force Readonly" }, "read");
468
476
  }
469
477
  }
470
478
  }
@@ -477,14 +485,17 @@ export class ConnectionManager implements IConnectionManager {
477
485
  }
478
486
  }
479
487
 
480
- public connect(reason: string, connectionMode?: ConnectionMode) {
481
- this.connectCore(reason, connectionMode).catch((error) => {
482
- const normalizedError = normalizeError(error, { props: fatalConnectErrorProp });
488
+ public connect(reason: IConnectionStateChangeReason, connectionMode?: ConnectionMode) {
489
+ this.connectCore(reason, connectionMode).catch((e) => {
490
+ const normalizedError = normalizeError(e, { props: fatalConnectErrorProp });
483
491
  this.props.closeHandler(normalizedError);
484
492
  });
485
493
  }
486
494
 
487
- private async connectCore(reason: string, connectionMode?: ConnectionMode): Promise<void> {
495
+ private async connectCore(
496
+ reason: IConnectionStateChangeReason,
497
+ connectionMode?: ConnectionMode,
498
+ ): Promise<void> {
488
499
  assert(!this._disposed, 0x26a /* "not closed" */);
489
500
 
490
501
  if (this.connection !== undefined) {
@@ -660,7 +671,7 @@ export class ConnectionManager implements IConnectionManager {
660
671
  * And report the error if it escapes for any reason.
661
672
  * @param args - The connection arguments
662
673
  */
663
- private triggerConnect(reason: string, connectionMode: ConnectionMode) {
674
+ private triggerConnect(reason: IConnectionStateChangeReason, connectionMode: ConnectionMode) {
664
675
  // reconnect() includes async awaits, and that causes potential race conditions
665
676
  // where we might already have a connection. If it were to happen, it's possible that we will connect
666
677
  // with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
@@ -680,7 +691,7 @@ export class ConnectionManager implements IConnectionManager {
680
691
  * @param error - Error causing the disconnect if any.
681
692
  * @returns A boolean that indicates if there was an existing connection (or pending connection) to disconnect
682
693
  */
683
- private disconnectFromDeltaStream(reason: string, error?: IAnyDriverError): boolean {
694
+ private disconnectFromDeltaStream(reason: IConnectionStateChangeReason): boolean {
684
695
  this.pendingReconnect = false;
685
696
 
686
697
  if (this.connection === undefined) {
@@ -713,7 +724,7 @@ export class ConnectionManager implements IConnectionManager {
713
724
  this._outbound.clear();
714
725
  connection.dispose();
715
726
 
716
- this.props.disconnectHandler(reason, error);
727
+ this.props.disconnectHandler(reason);
717
728
 
718
729
  this._connectionVerboseProps = {};
719
730
 
@@ -723,7 +734,7 @@ export class ConnectionManager implements IConnectionManager {
723
734
  /**
724
735
  * Cancel in-progress connection attempt.
725
736
  */
726
- private cancelConnection(reason: string) {
737
+ private cancelConnection(reason: IConnectionStateChangeReason) {
727
738
  assert(
728
739
  this.pendingConnection !== undefined,
729
740
  0x345 /* this.pendingConnection is undefined when trying to cancel */,
@@ -731,7 +742,10 @@ export class ConnectionManager implements IConnectionManager {
731
742
  this.pendingConnection.abort();
732
743
  this.pendingConnection = undefined;
733
744
  this.logger.sendTelemetryEvent({ eventName: "ConnectionCancelReceived" });
734
- this.props.cancelConnectionHandler(`Cancel Pending Connection due to ${reason}`);
745
+ this.props.cancelConnectionHandler({
746
+ text: `Cancel Pending Connection due to ${reason.text}`,
747
+ error: reason.error,
748
+ });
735
749
  }
736
750
 
737
751
  /**
@@ -742,7 +756,7 @@ export class ConnectionManager implements IConnectionManager {
742
756
  private setupNewSuccessfulConnection(
743
757
  connection: IDocumentDeltaConnection,
744
758
  requestedMode: ConnectionMode,
745
- reason: string,
759
+ reason: IConnectionStateChangeReason,
746
760
  ) {
747
761
  // Old connection should have been cleaned up before establishing a new one
748
762
  assert(
@@ -785,7 +799,7 @@ export class ConnectionManager implements IConnectionManager {
785
799
 
786
800
  if (this._disposed) {
787
801
  // Raise proper events, Log telemetry event and close connection.
788
- this.disconnectFromDeltaStream("ConnectionManager already closed");
802
+ this.disconnectFromDeltaStream({ text: "ConnectionManager already closed" });
789
803
  return;
790
804
  }
791
805
 
@@ -890,7 +904,9 @@ export class ConnectionManager implements IConnectionManager {
890
904
  * @returns A promise that resolves when the connection is reestablished or we stop trying
891
905
  */
892
906
  private reconnectOnError(requestedMode: ConnectionMode, error: IAnyDriverError) {
893
- this.reconnect(requestedMode, error.message, error).catch(this.props.closeHandler);
907
+ this.reconnect(requestedMode, { text: error.message, error }).catch(
908
+ this.props.closeHandler,
909
+ );
894
910
  }
895
911
 
896
912
  /**
@@ -902,26 +918,25 @@ export class ConnectionManager implements IConnectionManager {
902
918
  */
903
919
  private async reconnect(
904
920
  requestedMode: ConnectionMode,
905
- disconnectMessage: string,
906
- error?: IAnyDriverError,
921
+ reason: IConnectionStateChangeReason<IAnyDriverError>,
907
922
  ) {
908
923
  // We quite often get protocol errors before / after observing nack/disconnect
909
924
  // we do not want to run through same sequence twice.
910
925
  // If we're already disconnected/disconnecting it's not appropriate to call this again.
911
926
  assert(this.connection !== undefined, 0x0eb /* "Missing connection for reconnect" */);
912
927
 
913
- this.disconnectFromDeltaStream(disconnectMessage, error);
928
+ this.disconnectFromDeltaStream(reason);
914
929
 
915
930
  // We will always trigger reconnect, even if canRetry is false.
916
931
  // Any truly fatal error state will result in container close upon attempted reconnect,
917
932
  // which is a preferable to closing abruptly when a live connection fails.
918
- if (error !== undefined && !error.canRetry) {
933
+ if (reason.error?.canRetry === false) {
919
934
  this.logger.sendTelemetryEvent(
920
935
  {
921
936
  eventName: "reconnectingDespiteFatalError",
922
937
  reconnectMode: this.reconnectMode,
923
938
  },
924
- error,
939
+ reason.error,
925
940
  );
926
941
  }
927
942
 
@@ -938,9 +953,9 @@ export class ConnectionManager implements IConnectionManager {
938
953
  }
939
954
 
940
955
  // If the error tells us to wait before retrying, then do so.
941
- const delayMs = getRetryDelayFromError(error);
942
- if (error !== undefined && delayMs !== undefined) {
943
- this.props.reconnectionDelayHandler(delayMs, error);
956
+ const delayMs = getRetryDelayFromError(reason.error);
957
+ if (reason.error !== undefined && delayMs !== undefined) {
958
+ this.props.reconnectionDelayHandler(delayMs, reason.error);
944
959
  await new Promise<void>((resolve) => {
945
960
  setTimeout(resolve, delayMs);
946
961
  });
@@ -952,9 +967,13 @@ export class ConnectionManager implements IConnectionManager {
952
967
  await waitForOnline();
953
968
 
954
969
  this.triggerConnect(
955
- error !== undefined
956
- ? "Reconnecting due to Error"
957
- : `Reconnecting due to: ${disconnectMessage}`,
970
+ {
971
+ text:
972
+ reason.error !== undefined
973
+ ? "Reconnecting due to Error"
974
+ : `Reconnecting due to: ${reason.text}`,
975
+ error: reason.error,
976
+ },
958
977
  requestedMode,
959
978
  );
960
979
  }
@@ -1025,7 +1044,7 @@ export class ConnectionManager implements IConnectionManager {
1025
1044
  // still valid?
1026
1045
  await this.reconnect(
1027
1046
  "write", // connectionMode
1028
- "Switch to write", // message
1047
+ { text: "Switch to write" }, // message
1029
1048
  );
1030
1049
  }
1031
1050
  })
@@ -1079,7 +1098,7 @@ export class ConnectionManager implements IConnectionManager {
1079
1098
  // Note - this may close container!
1080
1099
  this.reconnect(
1081
1100
  "read", // connectionMode
1082
- "Switch to read", // message
1101
+ { text: "Switch to read" }, // message
1083
1102
  ).catch((error) => {
1084
1103
  this.logger.sendErrorEvent({ eventName: "SwitchToReadConnection" }, error);
1085
1104
  });
@@ -5,16 +5,17 @@
5
5
 
6
6
  import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
7
7
  import { assert, Timer } from "@fluidframework/common-utils";
8
- import { IConnectionDetailsInternal, IDeltaManager } from "@fluidframework/container-definitions";
9
- import { IAnyDriverError } from "@fluidframework/driver-definitions";
8
+ import { IDeltaManager } from "@fluidframework/container-definitions";
10
9
  import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
11
10
  import {
12
11
  ITelemetryLoggerExt,
13
12
  PerformanceEvent,
14
13
  loggerToMonitoringContext,
15
14
  } from "@fluidframework/telemetry-utils";
16
- import { ConnectionState } from "./connectionState";
15
+ import { IAnyDriverError } from "@fluidframework/driver-definitions";
17
16
  import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
17
+ import { ConnectionState } from "./connectionState";
18
+ import { IConnectionDetailsInternal, IConnectionStateChangeReason } from "./contracts";
18
19
  import { IProtocolHandler } from "./protocol";
19
20
 
20
21
  // Based on recent data, it looks like majority of cases where we get stuck are due to really slow or
@@ -32,8 +33,7 @@ export interface IConnectionStateHandlerInputs {
32
33
  connectionStateChanged: (
33
34
  value: ConnectionState,
34
35
  oldState: ConnectionState,
35
- reason?: string | undefined,
36
- error?: IAnyDriverError,
36
+ reason?: IConnectionStateChangeReason,
37
37
  ) => void;
38
38
  /** Whether to expect the client to join in write mode on next connection */
39
39
  shouldClientJoinWrite: () => boolean;
@@ -60,14 +60,14 @@ export interface IConnectionStateHandler {
60
60
  dispose(): void;
61
61
  initProtocol(protocol: IProtocolHandler): void;
62
62
  receivedConnectEvent(details: IConnectionDetailsInternal): void;
63
- receivedDisconnectEvent(reason: string, error?: IAnyDriverError): void;
64
- establishingConnection(reason: string): void;
63
+ receivedDisconnectEvent(reason: IConnectionStateChangeReason): void;
64
+ establishingConnection(reason: IConnectionStateChangeReason): void;
65
65
  /**
66
66
  * Switches state to disconnected when we are still establishing connection during container.load(),
67
67
  * container connect() or reconnect and the container gets closed or disposed or disconnect happens.
68
68
  * @param reason - reason for cancelling the connection.
69
69
  */
70
- cancelEstablishingConnection(reason: string): void;
70
+ cancelEstablishingConnection(reason: IConnectionStateChangeReason): void;
71
71
  }
72
72
 
73
73
  export function createConnectionStateHandler(
@@ -149,15 +149,15 @@ class ConnectionStateHandlerPassThrough
149
149
  public initProtocol(protocol: IProtocolHandler) {
150
150
  return this.pimpl.initProtocol(protocol);
151
151
  }
152
- public receivedDisconnectEvent(reason: string, error?: IAnyDriverError) {
153
- return this.pimpl.receivedDisconnectEvent(reason, error);
152
+ public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
153
+ return this.pimpl.receivedDisconnectEvent(reason);
154
154
  }
155
155
 
156
- public establishingConnection(reason: string) {
156
+ public establishingConnection(reason: IConnectionStateChangeReason) {
157
157
  return this.pimpl.establishingConnection(reason);
158
158
  }
159
159
 
160
- public cancelEstablishingConnection(reason: string) {
160
+ public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
161
161
  return this.pimpl.cancelEstablishingConnection(reason);
162
162
  }
163
163
 
@@ -175,10 +175,9 @@ class ConnectionStateHandlerPassThrough
175
175
  public connectionStateChanged(
176
176
  value: ConnectionState,
177
177
  oldState: ConnectionState,
178
- reason?: string | undefined,
179
- error?: IAnyDriverError,
178
+ reason?: IConnectionStateChangeReason,
180
179
  ) {
181
- return this.inputs.connectionStateChanged(value, oldState, reason, error);
180
+ return this.inputs.connectionStateChanged(value, oldState, reason);
182
181
  }
183
182
  public shouldClientJoinWrite() {
184
183
  return this.inputs.shouldClientJoinWrite();
@@ -222,8 +221,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
222
221
  public connectionStateChanged(
223
222
  value: ConnectionState,
224
223
  oldState: ConnectionState,
225
- reason?: string | undefined,
226
- error?: IAnyDriverError,
224
+ reason?: IConnectionStateChangeReason<IAnyDriverError>,
227
225
  ) {
228
226
  switch (value) {
229
227
  case ConnectionState.Connected:
@@ -267,7 +265,7 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
267
265
  default:
268
266
  }
269
267
  this._connectionState = value;
270
- this.inputs.connectionStateChanged(value, oldState, reason, error);
268
+ this.inputs.connectionStateChanged(value, oldState, reason);
271
269
  }
272
270
 
273
271
  private readonly transitionToConnectedState = () => {
@@ -276,11 +274,9 @@ class ConnectionStateCatchup extends ConnectionStateHandlerPassThrough {
276
274
  assert(state === ConnectionState.Connected, 0x3e5 /* invariant broken */);
277
275
  assert(this._connectionState === ConnectionState.CatchingUp, 0x3e6 /* invariant broken */);
278
276
  this._connectionState = ConnectionState.Connected;
279
- this.inputs.connectionStateChanged(
280
- ConnectionState.Connected,
281
- ConnectionState.CatchingUp,
282
- "caught up",
283
- );
277
+ this.inputs.connectionStateChanged(ConnectionState.Connected, ConnectionState.CatchingUp, {
278
+ text: "caught up",
279
+ });
284
280
  };
285
281
  }
286
282
 
@@ -494,12 +490,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
494
490
  }
495
491
  }
496
492
 
497
- public receivedDisconnectEvent(reason: string, error?: IAnyDriverError) {
493
+ public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
498
494
  this.connection = undefined;
499
- this.setConnectionState(ConnectionState.Disconnected, reason, error);
495
+ this.setConnectionState(ConnectionState.Disconnected, reason);
500
496
  }
501
497
 
502
- public cancelEstablishingConnection(reason: string) {
498
+ public cancelEstablishingConnection(reason: IConnectionStateChangeReason) {
503
499
  assert(
504
500
  this._connectionState === ConnectionState.EstablishingConnection,
505
501
  0x6d3 /* Connection state should be EstablishingConnection */,
@@ -510,14 +506,13 @@ class ConnectionStateHandler implements IConnectionStateHandler {
510
506
  this.handler.connectionStateChanged(ConnectionState.Disconnected, oldState, reason);
511
507
  }
512
508
 
513
- public establishingConnection(reason: string) {
509
+ public establishingConnection(reason: IConnectionStateChangeReason) {
514
510
  const oldState = this._connectionState;
515
511
  this._connectionState = ConnectionState.EstablishingConnection;
516
- this.handler.connectionStateChanged(
517
- ConnectionState.EstablishingConnection,
518
- oldState,
519
- `Establishing Connection due to ${reason}`,
520
- );
512
+ this.handler.connectionStateChanged(ConnectionState.EstablishingConnection, oldState, {
513
+ text: `Establishing Connection due to ${reason.text}`,
514
+ error: reason.error,
515
+ });
521
516
  }
522
517
 
523
518
  private shouldWaitForJoinSignal() {
@@ -582,14 +577,12 @@ class ConnectionStateHandler implements IConnectionStateHandler {
582
577
 
583
578
  private setConnectionState(
584
579
  value: ConnectionState.Disconnected,
585
- reason: string,
586
- error?: IAnyDriverError,
580
+ reason: IConnectionStateChangeReason,
587
581
  ): void;
588
582
  private setConnectionState(value: ConnectionState.Connected): void;
589
583
  private setConnectionState(
590
584
  value: ConnectionState.Disconnected | ConnectionState.Connected,
591
- reason?: string,
592
- error?: IAnyDriverError,
585
+ reason?: IConnectionStateChangeReason,
593
586
  ): void {
594
587
  if (this.connectionState === value) {
595
588
  // Already in the desired state - exit early
@@ -650,7 +643,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
650
643
  }
651
644
 
652
645
  // Report transition before we propagate event across layers
653
- this.handler.connectionStateChanged(this._connectionState, oldState, reason, error);
646
+ this.handler.connectionStateChanged(this._connectionState, oldState, reason);
654
647
  }
655
648
 
656
649
  // Helper method to switch between quorum and audience.