@fluidframework/container-loader 2.0.0-internal.5.4.2 → 2.0.0-internal.6.0.1
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 +81 -0
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +24 -25
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +2 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +9 -16
- 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 +161 -99
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +2 -14
- 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 +11 -2
- 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 +16 -3
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +62 -24
- 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.map +1 -1
- package/dist/protocol.js +2 -3
- 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 +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +25 -26
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +2 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +9 -16
- 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 +160 -98
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +2 -14
- 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 +11 -2
- 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 +16 -3
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +62 -24
- 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.map +1 -1
- package/lib/protocol.js +2 -3
- 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 +14 -14
- package/src/connectionManager.ts +7 -3
- package/src/connectionStateHandler.ts +3 -2
- package/src/container.ts +113 -27
- package/src/containerContext.ts +0 -16
- package/src/contracts.ts +16 -5
- package/src/deltaManager.ts +22 -5
- package/src/loader.ts +37 -23
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +0 -1
- package/src/utils.ts +29 -0
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.0.1",
|
|
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.0.1 <2.0.0-internal.6.1.0",
|
|
41
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
|
|
42
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
|
|
43
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
|
|
44
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.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.0.1 <2.0.0-internal.6.1.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.
|
|
56
|
+
"@fluid-internal/test-loader-utils": ">=2.0.0-internal.6.0.1 <2.0.0-internal.6.1.0",
|
|
57
57
|
"@fluid-tools/build-cli": "^0.21.0",
|
|
58
|
-
"@fluidframework/build-common": "^
|
|
58
|
+
"@fluidframework/build-common": "^2.0.0",
|
|
59
59
|
"@fluidframework/build-tools": "^0.21.0",
|
|
60
|
-
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.
|
|
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.0.1 <2.0.0-internal.6.1.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",
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
},
|
|
84
84
|
"typeValidation": {
|
|
85
85
|
"broken": {
|
|
86
|
-
"
|
|
87
|
-
"
|
|
86
|
+
"InterfaceDeclaration_IContainerContext": {
|
|
87
|
+
"forwardCompat": false
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
},
|
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,12 @@ 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
|
+
} from "./contracts";
|
|
48
52
|
import { DeltaQueue } from "./deltaQueue";
|
|
49
53
|
import { SignalType } from "./protocol";
|
|
50
54
|
import { isDeltaStreamConnectionForbiddenError } from "./utils";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
8
|
-
import {
|
|
8
|
+
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
9
9
|
import { IAnyDriverError } from "@fluidframework/driver-definitions";
|
|
10
10
|
import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
|
|
11
11
|
import {
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
PerformanceEvent,
|
|
14
14
|
loggerToMonitoringContext,
|
|
15
15
|
} from "@fluidframework/telemetry-utils";
|
|
16
|
-
import { ConnectionState } from "./connectionState";
|
|
17
16
|
import { CatchUpMonitor, ICatchUpMonitor } from "./catchUpMonitor";
|
|
17
|
+
import { ConnectionState } from "./connectionState";
|
|
18
|
+
import { IConnectionDetailsInternal } 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
|
package/src/container.ts
CHANGED
|
@@ -23,27 +23,26 @@ import {
|
|
|
23
23
|
FluidObject,
|
|
24
24
|
} from "@fluidframework/core-interfaces";
|
|
25
25
|
import {
|
|
26
|
+
AttachState,
|
|
27
|
+
ContainerWarning,
|
|
26
28
|
IAudience,
|
|
27
|
-
|
|
29
|
+
IBatchMessage,
|
|
30
|
+
ICodeDetailsLoader,
|
|
28
31
|
IContainer,
|
|
29
32
|
IContainerEvents,
|
|
30
|
-
IDeltaManager,
|
|
31
|
-
ICriticalContainerError,
|
|
32
|
-
ContainerWarning,
|
|
33
|
-
AttachState,
|
|
34
|
-
IThrottlingWarning,
|
|
35
|
-
ReadOnlyInfo,
|
|
36
33
|
IContainerLoadMode,
|
|
34
|
+
ICriticalContainerError,
|
|
35
|
+
IDeltaManager,
|
|
37
36
|
IFluidCodeDetails,
|
|
38
|
-
isFluidCodeDetails,
|
|
39
|
-
IBatchMessage,
|
|
40
|
-
ICodeDetailsLoader,
|
|
41
37
|
IHostLoader,
|
|
42
38
|
IFluidModuleWithDetails,
|
|
43
39
|
IProvideRuntimeFactory,
|
|
44
40
|
IProvideFluidCodeDetailsComparer,
|
|
45
41
|
IFluidCodeDetailsComparer,
|
|
46
42
|
IRuntime,
|
|
43
|
+
isFluidCodeDetails,
|
|
44
|
+
IThrottlingWarning,
|
|
45
|
+
ReadOnlyInfo,
|
|
47
46
|
} from "@fluidframework/container-definitions";
|
|
48
47
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
49
48
|
import {
|
|
@@ -58,7 +57,6 @@ import {
|
|
|
58
57
|
readAndParse,
|
|
59
58
|
OnlineStatus,
|
|
60
59
|
isOnline,
|
|
61
|
-
combineAppAndProtocolSummary,
|
|
62
60
|
runWithRetry,
|
|
63
61
|
isCombinedAppAndProtocolSummary,
|
|
64
62
|
MessageType2,
|
|
@@ -98,7 +96,12 @@ import {
|
|
|
98
96
|
} from "@fluidframework/telemetry-utils";
|
|
99
97
|
import { Audience } from "./audience";
|
|
100
98
|
import { ContainerContext } from "./containerContext";
|
|
101
|
-
import {
|
|
99
|
+
import {
|
|
100
|
+
ReconnectMode,
|
|
101
|
+
IConnectionManagerFactoryArgs,
|
|
102
|
+
getPackageName,
|
|
103
|
+
IConnectionDetailsInternal,
|
|
104
|
+
} from "./contracts";
|
|
102
105
|
import { DeltaManager, IConnectionArgs } from "./deltaManager";
|
|
103
106
|
import { IDetachedBlobStorage, ILoaderOptions, RelativeLoader } from "./loader";
|
|
104
107
|
import { pkgVersion } from "./packageVersion";
|
|
@@ -109,7 +112,11 @@ import {
|
|
|
109
112
|
ISerializableBlobContents,
|
|
110
113
|
} from "./containerStorageAdapter";
|
|
111
114
|
import { IConnectionStateHandler, createConnectionStateHandler } from "./connectionStateHandler";
|
|
112
|
-
import {
|
|
115
|
+
import {
|
|
116
|
+
combineAppAndProtocolSummary,
|
|
117
|
+
getProtocolSnapshotTree,
|
|
118
|
+
getSnapshotTreeFromSerializedContainer,
|
|
119
|
+
} from "./utils";
|
|
113
120
|
import { initQuorumValuesFromCodeDetails } from "./quorum";
|
|
114
121
|
import { NoopHeuristic } from "./noopHeuristic";
|
|
115
122
|
import { ConnectionManager } from "./connectionManager";
|
|
@@ -150,6 +157,11 @@ export interface IContainerLoadProps {
|
|
|
150
157
|
* The pending state serialized from a pervious container instance
|
|
151
158
|
*/
|
|
152
159
|
readonly pendingLocalState?: IPendingContainerState;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Load the container to at least this sequence number.
|
|
163
|
+
*/
|
|
164
|
+
readonly loadToSequenceNumber?: number;
|
|
153
165
|
}
|
|
154
166
|
|
|
155
167
|
/**
|
|
@@ -368,7 +380,8 @@ export class Container
|
|
|
368
380
|
loadProps: IContainerLoadProps,
|
|
369
381
|
createProps: IContainerCreateProps,
|
|
370
382
|
): Promise<Container> {
|
|
371
|
-
const { version, pendingLocalState, loadMode, resolvedUrl } =
|
|
383
|
+
const { version, pendingLocalState, loadMode, resolvedUrl, loadToSequenceNumber } =
|
|
384
|
+
loadProps;
|
|
372
385
|
|
|
373
386
|
const container = new Container(createProps, loadProps);
|
|
374
387
|
|
|
@@ -397,7 +410,7 @@ export class Container
|
|
|
397
410
|
container.on("closed", onClosed);
|
|
398
411
|
|
|
399
412
|
container
|
|
400
|
-
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
413
|
+
.load(version, mode, resolvedUrl, pendingLocalState, loadToSequenceNumber)
|
|
401
414
|
.finally(() => {
|
|
402
415
|
container.removeListener("closed", onClosed);
|
|
403
416
|
})
|
|
@@ -1065,16 +1078,21 @@ export class Container
|
|
|
1065
1078
|
}
|
|
1066
1079
|
}
|
|
1067
1080
|
|
|
1068
|
-
public closeAndGetPendingLocalState(): string {
|
|
1081
|
+
public async closeAndGetPendingLocalState(): Promise<string> {
|
|
1069
1082
|
// runtime matches pending ops to successful ones by clientId and client seq num, so we need to close the
|
|
1070
1083
|
// container at the same time we get pending state, otherwise this container could reconnect and resubmit with
|
|
1071
1084
|
// a new clientId and a future container using stale pending state without the new clientId would resubmit them
|
|
1072
|
-
|
|
1085
|
+
this.disconnect(); // TODO https://dev.azure.com/fluidframework/internal/_workitems/edit/5127
|
|
1086
|
+
const pendingState = await this.getPendingLocalStateCore({ notifyImminentClosure: true });
|
|
1073
1087
|
this.close();
|
|
1074
1088
|
return pendingState;
|
|
1075
1089
|
}
|
|
1076
1090
|
|
|
1077
|
-
public getPendingLocalState(): string {
|
|
1091
|
+
public async getPendingLocalState(): Promise<string> {
|
|
1092
|
+
return this.getPendingLocalStateCore({ notifyImminentClosure: false });
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
|
|
1078
1096
|
if (!this.offlineLoadEnabled) {
|
|
1079
1097
|
throw new UsageError("Can't get pending local state unless offline load is enabled");
|
|
1080
1098
|
}
|
|
@@ -1093,8 +1111,9 @@ export class Container
|
|
|
1093
1111
|
);
|
|
1094
1112
|
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1095
1113
|
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1114
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
1096
1115
|
const pendingState: IPendingContainerState = {
|
|
1097
|
-
pendingRuntimeState
|
|
1116
|
+
pendingRuntimeState,
|
|
1098
1117
|
baseSnapshot: this.baseSnapshot,
|
|
1099
1118
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1100
1119
|
savedOps: this.savedOps,
|
|
@@ -1479,7 +1498,8 @@ export class Container
|
|
|
1479
1498
|
specifiedVersion: string | undefined,
|
|
1480
1499
|
loadMode: IContainerLoadMode,
|
|
1481
1500
|
resolvedUrl: IResolvedUrl,
|
|
1482
|
-
pendingLocalState
|
|
1501
|
+
pendingLocalState: IPendingContainerState | undefined,
|
|
1502
|
+
loadToSequenceNumber: number | undefined,
|
|
1483
1503
|
) {
|
|
1484
1504
|
this.service = await this.serviceFactory.createDocumentService(
|
|
1485
1505
|
resolvedUrl,
|
|
@@ -1553,6 +1573,57 @@ export class Container
|
|
|
1553
1573
|
|
|
1554
1574
|
let opsBeforeReturnP: Promise<void> | undefined;
|
|
1555
1575
|
|
|
1576
|
+
if (loadMode.pauseAfterLoad === true) {
|
|
1577
|
+
// If we are trying to pause at a specific sequence number, ensure the latest snapshot is not newer than the desired sequence number.
|
|
1578
|
+
if (loadMode.opsBeforeReturn === "sequenceNumber") {
|
|
1579
|
+
assert(
|
|
1580
|
+
loadToSequenceNumber !== undefined,
|
|
1581
|
+
0x727 /* sequenceNumber should be defined */,
|
|
1582
|
+
);
|
|
1583
|
+
// Note: It is possible that we think the latest snapshot is newer than the specified sequence number
|
|
1584
|
+
// due to saved ops that may be replayed after the snapshot.
|
|
1585
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/5055
|
|
1586
|
+
if (dmAttributes.sequenceNumber > loadToSequenceNumber) {
|
|
1587
|
+
throw new Error(
|
|
1588
|
+
"Cannot satisfy request to pause the container at the specified sequence number. Most recent snapshot is newer than the specified sequence number.",
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// Force readonly mode - this will ensure we don't receive an error for the lack of join op
|
|
1594
|
+
this.forceReadonly(true);
|
|
1595
|
+
|
|
1596
|
+
// We need to setup a listener to stop op processing once we reach the desired sequence number (if specified).
|
|
1597
|
+
const opHandler = () => {
|
|
1598
|
+
if (loadToSequenceNumber === undefined) {
|
|
1599
|
+
// If there is no specified sequence number, pause after the inbound queue is empty.
|
|
1600
|
+
if (this.deltaManager.inbound.length !== 0) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
} else {
|
|
1604
|
+
// If there is a specified sequence number, keep processing until we reach it.
|
|
1605
|
+
if (this.deltaManager.lastSequenceNumber < loadToSequenceNumber) {
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// Pause op processing once we have processed the desired number of ops.
|
|
1611
|
+
void this.deltaManager.inbound.pause();
|
|
1612
|
+
void this.deltaManager.outbound.pause();
|
|
1613
|
+
this.off("op", opHandler);
|
|
1614
|
+
};
|
|
1615
|
+
if (
|
|
1616
|
+
(loadToSequenceNumber === undefined && this.deltaManager.inbound.length === 0) ||
|
|
1617
|
+
this.deltaManager.lastSequenceNumber === loadToSequenceNumber
|
|
1618
|
+
) {
|
|
1619
|
+
// If we have already reached the desired sequence number, call opHandler() to pause immediately.
|
|
1620
|
+
opHandler();
|
|
1621
|
+
} else {
|
|
1622
|
+
// If we have not yet reached the desired sequence number, setup a listener to pause once we reach it.
|
|
1623
|
+
this.on("op", opHandler);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1556
1627
|
// Attach op handlers to finish initialization and be able to start processing ops
|
|
1557
1628
|
// Kick off any ops fetching if required.
|
|
1558
1629
|
switch (loadMode.opsBeforeReturn) {
|
|
@@ -1564,6 +1635,9 @@ export class Container
|
|
|
1564
1635
|
loadMode.deltaConnection !== "none" ? "all" : "none",
|
|
1565
1636
|
);
|
|
1566
1637
|
break;
|
|
1638
|
+
case "sequenceNumber":
|
|
1639
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1640
|
+
break;
|
|
1567
1641
|
case "cached":
|
|
1568
1642
|
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1569
1643
|
break;
|
|
@@ -1647,6 +1721,22 @@ export class Container
|
|
|
1647
1721
|
}
|
|
1648
1722
|
}
|
|
1649
1723
|
|
|
1724
|
+
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
1725
|
+
if (
|
|
1726
|
+
loadToSequenceNumber !== undefined &&
|
|
1727
|
+
this.deltaManager.lastSequenceNumber < loadToSequenceNumber
|
|
1728
|
+
) {
|
|
1729
|
+
await new Promise<void>((resolve, reject) => {
|
|
1730
|
+
const opHandler = (message: ISequencedDocumentMessage) => {
|
|
1731
|
+
if (message.sequenceNumber >= loadToSequenceNumber) {
|
|
1732
|
+
resolve();
|
|
1733
|
+
this.off("op", opHandler);
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
this.on("op", opHandler);
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1650
1740
|
// Safety net: static version of Container.load() should have learned about it through "closed" handler.
|
|
1651
1741
|
// But if that did not happen for some reason, fail load for sure.
|
|
1652
1742
|
// Otherwise we can get into situations where container is closed and does not try to connect to ordering
|
|
@@ -1976,7 +2066,7 @@ export class Container
|
|
|
1976
2066
|
|
|
1977
2067
|
private async attachDeltaManagerOpHandler(
|
|
1978
2068
|
attributes: IDocumentAttributes,
|
|
1979
|
-
prefetchType?: "cached" | "all" | "none",
|
|
2069
|
+
prefetchType?: "sequenceNumber" | "cached" | "all" | "none",
|
|
1980
2070
|
) {
|
|
1981
2071
|
return this._deltaManager.attachOpHandler(
|
|
1982
2072
|
attributes.minimumSequenceNumber,
|
|
@@ -2314,7 +2404,6 @@ export class Container
|
|
|
2314
2404
|
this.getAbsoluteUrl,
|
|
2315
2405
|
() => this.resolvedUrl?.id,
|
|
2316
2406
|
() => this.clientId,
|
|
2317
|
-
() => this._deltaManager.serviceConfiguration,
|
|
2318
2407
|
() => this.attachState,
|
|
2319
2408
|
() => this.connected,
|
|
2320
2409
|
getSpecifiedCodeDetails,
|
|
@@ -2323,9 +2412,6 @@ export class Container
|
|
|
2323
2412
|
this.subLogger,
|
|
2324
2413
|
pendingLocalState,
|
|
2325
2414
|
);
|
|
2326
|
-
this._lifecycleEvents.once("disposed", () => {
|
|
2327
|
-
context.dispose();
|
|
2328
|
-
});
|
|
2329
2415
|
|
|
2330
2416
|
this._runtime = await PerformanceEvent.timedExecAsync(
|
|
2331
2417
|
this.subLogger,
|
|
@@ -2377,7 +2463,7 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2377
2463
|
* @experimental misuse of this API can result in duplicate op submission and potential document corruption
|
|
2378
2464
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2379
2465
|
*/
|
|
2380
|
-
getPendingLocalState?(): string
|
|
2466
|
+
getPendingLocalState?(): Promise<string>;
|
|
2381
2467
|
|
|
2382
2468
|
/**
|
|
2383
2469
|
* Closes the container and returns serialized local state intended to be
|
|
@@ -2385,5 +2471,5 @@ export interface IContainerExperimental extends IContainer {
|
|
|
2385
2471
|
* @experimental
|
|
2386
2472
|
* {@link https://github.com/microsoft/FluidFramework/blob/main/packages/loader/container-loader/closeAndGetPendingLocalState.md}
|
|
2387
2473
|
*/
|
|
2388
|
-
closeAndGetPendingLocalState(): string
|
|
2474
|
+
closeAndGetPendingLocalState?(): Promise<string>;
|
|
2389
2475
|
}
|
package/src/containerContext.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { FluidObject } from "@fluidframework/core-interfaces";
|
|
19
19
|
import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
20
20
|
import {
|
|
21
|
-
IClientConfiguration,
|
|
22
21
|
IClientDetails,
|
|
23
22
|
IDocumentMessage,
|
|
24
23
|
IQuorumClients,
|
|
@@ -61,16 +60,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
61
60
|
return this._getConnected();
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
public get serviceConfiguration(): IClientConfiguration | undefined {
|
|
65
|
-
return this._getServiceConfiguration();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private _disposed = false;
|
|
69
|
-
|
|
70
|
-
public get disposed() {
|
|
71
|
-
return this._disposed;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
63
|
constructor(
|
|
75
64
|
public readonly options: ILoaderOptions,
|
|
76
65
|
public readonly scope: FluidObject,
|
|
@@ -103,7 +92,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
103
92
|
public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>,
|
|
104
93
|
private readonly _getContainerDiagnosticId: () => string | undefined,
|
|
105
94
|
private readonly _getClientId: () => string | undefined,
|
|
106
|
-
private readonly _getServiceConfiguration: () => IClientConfiguration | undefined,
|
|
107
95
|
private readonly _getAttachState: () => AttachState,
|
|
108
96
|
private readonly _getConnected: () => boolean,
|
|
109
97
|
public readonly getSpecifiedCodeDetails: () => IFluidCodeDetails | undefined,
|
|
@@ -113,10 +101,6 @@ export class ContainerContext implements IContainerContext {
|
|
|
113
101
|
public readonly pendingLocalState?: unknown,
|
|
114
102
|
) {}
|
|
115
103
|
|
|
116
|
-
public dispose(error?: Error): void {
|
|
117
|
-
this._disposed = true;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
104
|
public getLoadedFromVersion(): IVersion | undefined {
|
|
121
105
|
return this._version;
|
|
122
106
|
}
|
package/src/contracts.ts
CHANGED
|
@@ -5,19 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
ReadOnlyInfo,
|
|
10
|
-
IConnectionDetailsInternal,
|
|
8
|
+
IConnectionDetails,
|
|
11
9
|
ICriticalContainerError,
|
|
10
|
+
IDeltaQueue,
|
|
12
11
|
IFluidCodeDetails,
|
|
13
12
|
isFluidPackage,
|
|
13
|
+
ReadOnlyInfo,
|
|
14
14
|
} from "@fluidframework/container-definitions";
|
|
15
15
|
import {
|
|
16
16
|
ConnectionMode,
|
|
17
|
-
IDocumentMessage,
|
|
18
|
-
ISequencedDocumentMessage,
|
|
19
17
|
IClientConfiguration,
|
|
20
18
|
IClientDetails,
|
|
19
|
+
IDocumentMessage,
|
|
20
|
+
ISequencedDocumentMessage,
|
|
21
|
+
ISignalClient,
|
|
21
22
|
ISignalMessage,
|
|
22
23
|
} from "@fluidframework/protocol-definitions";
|
|
23
24
|
import { IAnyDriverError, IContainerPackageInfo } from "@fluidframework/driver-definitions";
|
|
@@ -28,6 +29,16 @@ export enum ReconnectMode {
|
|
|
28
29
|
Enabled = "Enabled",
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Internal version of IConnectionDetails with props are only exposed internally
|
|
34
|
+
*/
|
|
35
|
+
export interface IConnectionDetailsInternal extends IConnectionDetails {
|
|
36
|
+
mode: ConnectionMode;
|
|
37
|
+
version: string;
|
|
38
|
+
initialClients: ISignalClient[];
|
|
39
|
+
reason: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
/**
|
|
32
43
|
* Connection manager (implements this interface) is responsible for maintaining connection
|
|
33
44
|
* to relay service.
|
package/src/deltaManager.ts
CHANGED
|
@@ -7,13 +7,11 @@ import { v4 as uuid } from "uuid";
|
|
|
7
7
|
import { IEventProvider } from "@fluidframework/common-definitions";
|
|
8
8
|
import { ITelemetryProperties, ITelemetryErrorEvent } from "@fluidframework/core-interfaces";
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
ICriticalContainerError,
|
|
11
11
|
IDeltaManager,
|
|
12
12
|
IDeltaManagerEvents,
|
|
13
13
|
IDeltaQueue,
|
|
14
|
-
ICriticalContainerError,
|
|
15
14
|
IThrottlingWarning,
|
|
16
|
-
IConnectionDetailsInternal,
|
|
17
15
|
} from "@fluidframework/container-definitions";
|
|
18
16
|
import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
19
17
|
import {
|
|
@@ -44,7 +42,11 @@ import {
|
|
|
44
42
|
DataProcessingError,
|
|
45
43
|
UsageError,
|
|
46
44
|
} from "@fluidframework/container-utils";
|
|
47
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
IConnectionDetailsInternal,
|
|
47
|
+
IConnectionManager,
|
|
48
|
+
IConnectionManagerFactoryArgs,
|
|
49
|
+
} from "./contracts";
|
|
48
50
|
import { DeltaQueue } from "./deltaQueue";
|
|
49
51
|
import { OnlyValidTermValue } from "./protocol";
|
|
50
52
|
|
|
@@ -73,6 +75,21 @@ interface IBatchMetadata {
|
|
|
73
75
|
batch?: boolean;
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Interface used to define a strategy for handling incoming delta messages
|
|
80
|
+
*/
|
|
81
|
+
export interface IDeltaHandlerStrategy {
|
|
82
|
+
/**
|
|
83
|
+
* Processes the message.
|
|
84
|
+
*/
|
|
85
|
+
process: (message: ISequencedDocumentMessage) => void;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Processes the signal.
|
|
89
|
+
*/
|
|
90
|
+
processSignal: (message: ISignalMessage) => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
76
93
|
/**
|
|
77
94
|
* Determines if message was sent by client, not service
|
|
78
95
|
*/
|
|
@@ -495,7 +512,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
495
512
|
minSequenceNumber: number,
|
|
496
513
|
sequenceNumber: number,
|
|
497
514
|
handler: IDeltaHandlerStrategy,
|
|
498
|
-
prefetchType: "cached" | "all" | "none" = "none",
|
|
515
|
+
prefetchType: "sequenceNumber" | "cached" | "all" | "none" = "none",
|
|
499
516
|
) {
|
|
500
517
|
this.initSequenceNumber = sequenceNumber;
|
|
501
518
|
this.lastProcessedSequenceNumber = sequenceNumber;
|