@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.
- package/CHANGELOG.md +85 -0
- package/dist/connectionManager.d.ts +4 -4
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +57 -49
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +15 -14
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +26 -28
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +10 -5
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +183 -134
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -12
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +1 -20
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.js +3 -5
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +20 -7
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -3
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +2 -3
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +19 -6
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +67 -28
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +1 -2
- package/dist/deltaQueue.js.map +1 -1
- package/dist/loader.d.ts +12 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +57 -42
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +4 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +25 -4
- package/dist/protocol.js.map +1 -1
- package/dist/utils.d.ts +8 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +24 -6
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +4 -4
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +58 -50
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +15 -14
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +26 -28
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +10 -5
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +182 -133
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -12
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +1 -20
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.js +3 -5
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +20 -7
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +3 -3
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +2 -3
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +19 -6
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +67 -28
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +1 -2
- package/lib/deltaQueue.js.map +1 -1
- package/lib/loader.d.ts +12 -0
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +57 -42
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +4 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +25 -4
- package/lib/protocol.js.map +1 -1
- package/lib/utils.d.ts +8 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +22 -5
- package/lib/utils.js.map +1 -1
- package/package.json +15 -19
- package/src/connectionManager.ts +53 -34
- package/src/connectionStateHandler.ts +30 -37
- package/src/container.ts +156 -76
- package/src/containerContext.ts +0 -24
- package/src/contracts.ts +27 -10
- package/src/deltaManager.ts +41 -18
- package/src/loader.ts +37 -23
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +33 -2
- 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 =
|
|
17
|
+
const query = parsed.search ?? "";
|
|
19
18
|
const regex = /^\/([^/]*\/[^/]*)(\/?.*)$/;
|
|
20
19
|
const match = regex.exec(parsed.pathname);
|
|
21
|
-
return
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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.
|
|
41
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.
|
|
42
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.
|
|
43
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.
|
|
44
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.
|
|
45
|
-
"@fluidframework/protocol-base": "^0.
|
|
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.
|
|
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.
|
|
57
|
-
"@fluid-tools/build-cli": "^0.
|
|
58
|
-
"@fluidframework/build-common": "^
|
|
59
|
-
"@fluidframework/build-tools": "^0.
|
|
60
|
-
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.
|
|
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.
|
|
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",
|
package/src/connectionManager.ts
CHANGED
|
@@ -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 {
|
|
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:
|
|
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 =
|
|
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(
|
|
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:
|
|
481
|
-
this.connectCore(reason, connectionMode).catch((
|
|
482
|
-
const normalizedError = normalizeError(
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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(
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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 {
|
|
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 {
|
|
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?:
|
|
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:
|
|
64
|
-
establishingConnection(reason:
|
|
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:
|
|
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:
|
|
153
|
-
return this.pimpl.receivedDisconnectEvent(reason
|
|
152
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
153
|
+
return this.pimpl.receivedDisconnectEvent(reason);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
public establishingConnection(reason:
|
|
156
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
157
157
|
return this.pimpl.establishingConnection(reason);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
public cancelEstablishingConnection(reason:
|
|
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?:
|
|
179
|
-
error?: IAnyDriverError,
|
|
178
|
+
reason?: IConnectionStateChangeReason,
|
|
180
179
|
) {
|
|
181
|
-
return this.inputs.connectionStateChanged(value, oldState, reason
|
|
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?:
|
|
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
|
|
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
|
-
|
|
281
|
-
|
|
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:
|
|
493
|
+
public receivedDisconnectEvent(reason: IConnectionStateChangeReason<IAnyDriverError>) {
|
|
498
494
|
this.connection = undefined;
|
|
499
|
-
this.setConnectionState(ConnectionState.Disconnected, reason
|
|
495
|
+
this.setConnectionState(ConnectionState.Disconnected, reason);
|
|
500
496
|
}
|
|
501
497
|
|
|
502
|
-
public cancelEstablishingConnection(reason:
|
|
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:
|
|
509
|
+
public establishingConnection(reason: IConnectionStateChangeReason) {
|
|
514
510
|
const oldState = this._connectionState;
|
|
515
511
|
this._connectionState = ConnectionState.EstablishingConnection;
|
|
516
|
-
this.handler.connectionStateChanged(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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:
|
|
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?:
|
|
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
|
|
646
|
+
this.handler.connectionStateChanged(this._connectionState, oldState, reason);
|
|
654
647
|
}
|
|
655
648
|
|
|
656
649
|
// Helper method to switch between quorum and audience.
|