@fluidframework/container-loader 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917
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 +14 -0
- package/README.md +45 -4
- package/closeAndGetPendingLocalState.md +51 -0
- package/dist/connectionManager.d.ts +2 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +59 -17
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +4 -4
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +7 -0
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +44 -4
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +160 -105
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -8
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +47 -4
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +41 -2
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +87 -11
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +4 -5
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +7 -10
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +10 -22
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaManagerProxy.js +14 -50
- package/dist/deltaManagerProxy.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +10 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +25 -16
- 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 +1 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -2
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +6 -2
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +7 -4
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +2 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +60 -18
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +4 -4
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +7 -0
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +44 -4
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +164 -109
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -8
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +48 -5
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +41 -2
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +85 -11
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +4 -5
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +7 -10
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +10 -22
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaManagerProxy.js +14 -50
- package/lib/deltaManagerProxy.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +10 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +24 -16
- 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 +1 -0
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +3 -1
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +6 -2
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +7 -4
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -1
- package/lib/utils.js.map +1 -1
- package/package.json +64 -56
- package/src/connectionManager.ts +65 -24
- package/src/connectionStateHandler.ts +17 -5
- package/src/container.ts +239 -137
- package/src/containerContext.ts +74 -11
- package/src/containerStorageAdapter.ts +113 -9
- package/src/contracts.ts +2 -2
- package/src/deltaManager.ts +12 -14
- package/src/deltaManagerProxy.ts +18 -73
- package/src/index.ts +3 -3
- package/src/loader.ts +31 -26
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +4 -1
- package/src/protocolTreeDocumentStorageService.ts +6 -3
- package/src/utils.ts +7 -4
package/lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC;AAC5B,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EACN,MAAM,EACN,cAAc,EACd,uBAAuB,EACvB,eAAe,GACf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAA+B,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAChG,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;
|
|
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 { isCombinedAppAndProtocolSummary } from \"@fluidframework/driver-utils\";\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-loader",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.4.2.0.153917",
|
|
4
4
|
"description": "Fluid container loader",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -14,34 +14,6 @@
|
|
|
14
14
|
"main": "dist/index.js",
|
|
15
15
|
"module": "lib/index.js",
|
|
16
16
|
"types": "dist/index.d.ts",
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
|
|
19
|
-
"build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
|
|
20
|
-
"build:compile": "concurrently npm:build:commonjs npm:build:esnext",
|
|
21
|
-
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
|
|
22
|
-
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
23
|
-
"build:full": "npm run build",
|
|
24
|
-
"build:full:compile": "npm run build:compile",
|
|
25
|
-
"build:genver": "gen-version",
|
|
26
|
-
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
27
|
-
"ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
|
|
28
|
-
"clean": "rimraf dist lib *.tsbuildinfo *.build.log",
|
|
29
|
-
"eslint": "eslint --format stylish src",
|
|
30
|
-
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
31
|
-
"format": "npm run prettier:fix",
|
|
32
|
-
"lint": "npm run prettier && npm run eslint",
|
|
33
|
-
"lint:fix": "npm run prettier:fix && npm run eslint:fix",
|
|
34
|
-
"prettier": "prettier --check . --ignore-path ../../../.prettierignore",
|
|
35
|
-
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
|
|
36
|
-
"test": "npm run test:mocha",
|
|
37
|
-
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
38
|
-
"test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
39
|
-
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
40
|
-
"tsc": "tsc",
|
|
41
|
-
"tsc:watch": "tsc --watch",
|
|
42
|
-
"typetests:gen": "flub generate typetests --generate --dir .",
|
|
43
|
-
"typetests:prepare": "flub generate typetests --prepare --dir . --pin"
|
|
44
|
-
},
|
|
45
17
|
"nyc": {
|
|
46
18
|
"all": true,
|
|
47
19
|
"cache-dir": "nyc/.cache",
|
|
@@ -64,15 +36,15 @@
|
|
|
64
36
|
},
|
|
65
37
|
"dependencies": {
|
|
66
38
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
|
-
"@fluidframework/common-utils": "^1.
|
|
68
|
-
"@fluidframework/container-definitions": "
|
|
69
|
-
"@fluidframework/container-utils": "
|
|
70
|
-
"@fluidframework/core-interfaces": "
|
|
71
|
-
"@fluidframework/driver-definitions": "
|
|
72
|
-
"@fluidframework/driver-utils": "
|
|
73
|
-
"@fluidframework/protocol-base": "^0.
|
|
39
|
+
"@fluidframework/common-utils": "^1.1.1",
|
|
40
|
+
"@fluidframework/container-definitions": "2.0.0-dev.4.2.0.153917",
|
|
41
|
+
"@fluidframework/container-utils": "2.0.0-dev.4.2.0.153917",
|
|
42
|
+
"@fluidframework/core-interfaces": "2.0.0-dev.4.2.0.153917",
|
|
43
|
+
"@fluidframework/driver-definitions": "2.0.0-dev.4.2.0.153917",
|
|
44
|
+
"@fluidframework/driver-utils": "2.0.0-dev.4.2.0.153917",
|
|
45
|
+
"@fluidframework/protocol-base": "^0.1039.1000",
|
|
74
46
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
75
|
-
"@fluidframework/telemetry-utils": "
|
|
47
|
+
"@fluidframework/telemetry-utils": "2.0.0-dev.4.2.0.153917",
|
|
76
48
|
"abort-controller": "^3.0.0",
|
|
77
49
|
"double-ended-queue": "^2.1.0-0",
|
|
78
50
|
"events": "^3.1.0",
|
|
@@ -81,37 +53,73 @@
|
|
|
81
53
|
"uuid": "^8.3.1"
|
|
82
54
|
},
|
|
83
55
|
"devDependencies": {
|
|
84
|
-
"@fluid-
|
|
56
|
+
"@fluid-internal/test-loader-utils": "2.0.0-dev.4.2.0.153917",
|
|
57
|
+
"@fluid-tools/build-cli": "^0.15.0",
|
|
85
58
|
"@fluidframework/build-common": "^1.1.0",
|
|
86
|
-
"@fluidframework/build-tools": "^0.
|
|
87
|
-
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.
|
|
59
|
+
"@fluidframework/build-tools": "^0.15.0",
|
|
60
|
+
"@fluidframework/container-loader-previous": "npm:@fluidframework/container-loader@2.0.0-internal.4.0.0",
|
|
88
61
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
89
|
-
"@fluidframework/mocha-test-setup": "
|
|
90
|
-
"@
|
|
91
|
-
"@microsoft/api-extractor": "^7.22.2",
|
|
92
|
-
"@rushstack/eslint-config": "^2.5.1",
|
|
62
|
+
"@fluidframework/mocha-test-setup": "2.0.0-dev.4.2.0.153917",
|
|
63
|
+
"@microsoft/api-extractor": "^7.34.4",
|
|
93
64
|
"@types/double-ended-queue": "^2.1.0",
|
|
94
65
|
"@types/events": "^3.0.0",
|
|
95
66
|
"@types/lodash": "^4.14.118",
|
|
96
67
|
"@types/mocha": "^9.1.1",
|
|
97
|
-
"@types/node": "^14.18.
|
|
68
|
+
"@types/node": "^14.18.38",
|
|
98
69
|
"@types/sinon": "^7.0.13",
|
|
99
|
-
"concurrently": "^6.
|
|
70
|
+
"concurrently": "^7.6.0",
|
|
100
71
|
"copyfiles": "^2.4.1",
|
|
101
|
-
"cross-env": "^7.0.
|
|
72
|
+
"cross-env": "^7.0.3",
|
|
102
73
|
"eslint": "~8.6.0",
|
|
103
|
-
"mocha": "^10.
|
|
104
|
-
"
|
|
74
|
+
"mocha": "^10.2.0",
|
|
75
|
+
"mocha-json-output-reporter": "^2.0.1",
|
|
76
|
+
"mocha-multi-reporters": "^1.5.1",
|
|
77
|
+
"moment": "^2.21.0",
|
|
78
|
+
"nyc": "^15.1.0",
|
|
105
79
|
"prettier": "~2.6.2",
|
|
106
|
-
"rimraf": "^
|
|
80
|
+
"rimraf": "^4.4.0",
|
|
107
81
|
"sinon": "^7.4.2",
|
|
108
82
|
"typescript": "~4.5.5"
|
|
109
83
|
},
|
|
110
84
|
"typeValidation": {
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
85
|
+
"broken": {
|
|
86
|
+
"InterfaceDeclaration_IContainerConfig": {
|
|
87
|
+
"forwardCompat": false,
|
|
88
|
+
"backCompat": false
|
|
89
|
+
},
|
|
90
|
+
"InterfaceDeclaration_IPendingContainerState": {
|
|
91
|
+
"forwardCompat": false,
|
|
92
|
+
"backCompat": false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
|
|
98
|
+
"build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
|
|
99
|
+
"build:compile": "concurrently npm:build:commonjs npm:build:esnext",
|
|
100
|
+
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
|
|
101
|
+
"build:esnext": "tsc --project ./tsconfig.esnext.json",
|
|
102
|
+
"build:full": "npm run build",
|
|
103
|
+
"build:full:compile": "npm run build:compile",
|
|
104
|
+
"build:genver": "gen-version",
|
|
105
|
+
"build:test": "tsc --project ./src/test/tsconfig.json",
|
|
106
|
+
"ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
|
|
107
|
+
"clean": "rimraf dist lib *.tsbuildinfo *.build.log",
|
|
108
|
+
"eslint": "eslint --format stylish src",
|
|
109
|
+
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
|
110
|
+
"format": "npm run prettier:fix",
|
|
111
|
+
"lint": "npm run prettier && npm run eslint",
|
|
112
|
+
"lint:fix": "npm run prettier:fix && npm run eslint:fix",
|
|
113
|
+
"prettier": "prettier --check . --ignore-path ../../../.prettierignore",
|
|
114
|
+
"prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
|
|
115
|
+
"test": "npm run test:mocha",
|
|
116
|
+
"test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
|
|
117
|
+
"test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
|
|
118
|
+
"test:mocha:multireport": "cross-env FLUID_TEST_MULTIREPORT=1 npm run test:mocha",
|
|
119
|
+
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
120
|
+
"tsc": "tsc",
|
|
121
|
+
"tsc:watch": "tsc --watch",
|
|
122
|
+
"typetests:gen": "fluid-type-test-generator",
|
|
123
|
+
"typetests:prepare": "flub generate typetests --prepare --dir . --pin"
|
|
116
124
|
}
|
|
117
|
-
}
|
|
125
|
+
}
|
package/src/connectionManager.ts
CHANGED
|
@@ -13,11 +13,12 @@ import { assert, performance, TypedEventEmitter } from "@fluidframework/common-u
|
|
|
13
13
|
import {
|
|
14
14
|
IDeltaQueue,
|
|
15
15
|
ReadOnlyInfo,
|
|
16
|
-
|
|
16
|
+
IConnectionDetailsInternal,
|
|
17
17
|
ICriticalContainerError,
|
|
18
18
|
} from "@fluidframework/container-definitions";
|
|
19
19
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
20
20
|
import {
|
|
21
|
+
DriverErrorType,
|
|
21
22
|
IAnyDriverError,
|
|
22
23
|
IDocumentService,
|
|
23
24
|
IDocumentDeltaConnection,
|
|
@@ -28,8 +29,6 @@ import {
|
|
|
28
29
|
createWriteError,
|
|
29
30
|
createGenericNetworkError,
|
|
30
31
|
getRetryDelayFromError,
|
|
31
|
-
waitForConnectedState,
|
|
32
|
-
DeltaStreamConnectionForbiddenError,
|
|
33
32
|
logNetworkFailure,
|
|
34
33
|
isRuntimeMessage,
|
|
35
34
|
} from "@fluidframework/driver-utils";
|
|
@@ -135,6 +134,19 @@ class NoDeltaStream
|
|
|
135
134
|
}
|
|
136
135
|
}
|
|
137
136
|
|
|
137
|
+
const waitForOnline = async (): Promise<void> => {
|
|
138
|
+
// Only wait if we have a strong signal that we're offline - otherwise assume we're online.
|
|
139
|
+
if (globalThis.navigator?.onLine === false && globalThis.addEventListener !== undefined) {
|
|
140
|
+
return new Promise<void>((resolve) => {
|
|
141
|
+
const resolveAndRemoveListener = () => {
|
|
142
|
+
resolve();
|
|
143
|
+
globalThis.removeEventListener("online", resolveAndRemoveListener);
|
|
144
|
+
};
|
|
145
|
+
globalThis.addEventListener("online", resolveAndRemoveListener);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
138
150
|
/**
|
|
139
151
|
* Interface to track the current in-progress connection attempt.
|
|
140
152
|
*/
|
|
@@ -266,9 +278,21 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
266
278
|
|
|
267
279
|
public shouldJoinWrite(): boolean {
|
|
268
280
|
// We don't have to wait for ack for topmost NoOps. So subtract those.
|
|
269
|
-
|
|
270
|
-
this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore
|
|
271
|
-
|
|
281
|
+
const outstandingOps =
|
|
282
|
+
this.clientSequenceNumberObserved < this.clientSequenceNumber - this.localOpsToIgnore;
|
|
283
|
+
|
|
284
|
+
// Previous behavior was to force write mode here only when there are outstanding ops (besides
|
|
285
|
+
// no-ops). The dirty signal from runtime should provide the same behavior, but also support
|
|
286
|
+
// stashed ops that weren't submitted to container layer yet. For safety, we want to retain the
|
|
287
|
+
// same behavior whenever dirty is false.
|
|
288
|
+
const isDirty = this.containerDirty();
|
|
289
|
+
if (outstandingOps !== isDirty) {
|
|
290
|
+
this.logger.sendTelemetryEvent({
|
|
291
|
+
eventName: "DesiredConnectionModeMismatch",
|
|
292
|
+
details: JSON.stringify({ outstandingOps, isDirty }),
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return outstandingOps || isDirty;
|
|
272
296
|
}
|
|
273
297
|
|
|
274
298
|
/**
|
|
@@ -281,10 +305,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
281
305
|
* and do not know if user has write access to a file.
|
|
282
306
|
*/
|
|
283
307
|
private get readonly(): boolean | undefined {
|
|
284
|
-
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
287
|
-
return this._readonlyPermissions;
|
|
308
|
+
return this.readOnlyInfo.readonly;
|
|
288
309
|
}
|
|
289
310
|
|
|
290
311
|
public get readOnlyInfo(): ReadOnlyInfo {
|
|
@@ -302,11 +323,12 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
302
323
|
return { readonly: this._readonlyPermissions };
|
|
303
324
|
}
|
|
304
325
|
|
|
305
|
-
private static detailsFromConnection(
|
|
326
|
+
private static detailsFromConnection(
|
|
327
|
+
connection: IDocumentDeltaConnection,
|
|
328
|
+
): IConnectionDetailsInternal {
|
|
306
329
|
return {
|
|
307
330
|
claims: connection.claims,
|
|
308
331
|
clientId: connection.clientId,
|
|
309
|
-
existing: connection.existing,
|
|
310
332
|
checkpointSequenceNumber: connection.checkpointSequenceNumber,
|
|
311
333
|
get initialClients() {
|
|
312
334
|
return connection.initialClients;
|
|
@@ -319,6 +341,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
319
341
|
|
|
320
342
|
constructor(
|
|
321
343
|
private readonly serviceProvider: () => IDocumentService | undefined,
|
|
344
|
+
public readonly containerDirty: () => boolean,
|
|
322
345
|
private client: IClient,
|
|
323
346
|
reconnectAllowed: boolean,
|
|
324
347
|
private readonly logger: ITelemetryLogger,
|
|
@@ -355,10 +378,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
355
378
|
|
|
356
379
|
this._outbound.clear();
|
|
357
380
|
|
|
358
|
-
const disconnectReason =
|
|
359
|
-
error !== undefined
|
|
360
|
-
? `Closing DeltaManager (${error.message})`
|
|
361
|
-
: "Closing DeltaManager";
|
|
381
|
+
const disconnectReason = "Closing DeltaManager";
|
|
362
382
|
|
|
363
383
|
// This raises "disconnect" event if we have active connection.
|
|
364
384
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
@@ -543,7 +563,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
543
563
|
if (
|
|
544
564
|
typeof origError === "object" &&
|
|
545
565
|
origError !== null &&
|
|
546
|
-
origError?.errorType ===
|
|
566
|
+
origError?.errorType === DriverErrorType.deltaStreamConnectionForbidden
|
|
547
567
|
) {
|
|
548
568
|
connection = new NoDeltaStream();
|
|
549
569
|
requestedMode = "read";
|
|
@@ -572,12 +592,25 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
572
592
|
lastError = origError;
|
|
573
593
|
|
|
574
594
|
const retryDelayFromError = getRetryDelayFromError(origError);
|
|
575
|
-
delayMs = retryDelayFromError ?? Math.min(delayMs * 2, MaxReconnectDelayInMs);
|
|
576
|
-
|
|
577
595
|
if (retryDelayFromError !== undefined) {
|
|
596
|
+
// If the error told us to wait, then we wait.
|
|
578
597
|
this.props.reconnectionDelayHandler(retryDelayFromError, origError);
|
|
598
|
+
await new Promise<void>((resolve) => {
|
|
599
|
+
setTimeout(resolve, retryDelayFromError);
|
|
600
|
+
});
|
|
601
|
+
} else if (globalThis.navigator?.onLine !== false) {
|
|
602
|
+
// If the error didn't tell us to wait, let's still wait a little bit before retrying.
|
|
603
|
+
// We skip this delay if we're confident we're offline, because we probably just need to wait to come back online.
|
|
604
|
+
await new Promise<void>((resolve) => {
|
|
605
|
+
setTimeout(resolve, delayMs);
|
|
606
|
+
delayMs = Math.min(delayMs * 2, MaxReconnectDelayInMs);
|
|
607
|
+
});
|
|
579
608
|
}
|
|
580
|
-
|
|
609
|
+
|
|
610
|
+
// If we believe we're offline, we assume there's no point in trying until we at least think we're online.
|
|
611
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
612
|
+
// should probably live in the driver.
|
|
613
|
+
await waitForOnline();
|
|
581
614
|
}
|
|
582
615
|
}
|
|
583
616
|
|
|
@@ -615,7 +648,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
615
648
|
* @param args - The connection arguments
|
|
616
649
|
*/
|
|
617
650
|
private triggerConnect(connectionMode: ConnectionMode) {
|
|
618
|
-
// reconnect()
|
|
651
|
+
// reconnect() includes async awaits, and that causes potential race conditions
|
|
619
652
|
// where we might already have a connection. If it were to happen, it's possible that we will connect
|
|
620
653
|
// with different mode to `connectionMode`. Glancing through the caller chains, it looks like code should be
|
|
621
654
|
// fine (if needed, reconnect flow will get triggered again). Places where new mode matters should encode it
|
|
@@ -664,10 +697,10 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
664
697
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
665
698
|
this._outbound.pause();
|
|
666
699
|
this._outbound.clear();
|
|
667
|
-
this.props.disconnectHandler(reason);
|
|
668
|
-
|
|
669
700
|
connection.dispose();
|
|
670
701
|
|
|
702
|
+
this.props.disconnectHandler(reason);
|
|
703
|
+
|
|
671
704
|
this._connectionVerboseProps = {};
|
|
672
705
|
|
|
673
706
|
return true;
|
|
@@ -888,12 +921,20 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
888
921
|
return;
|
|
889
922
|
}
|
|
890
923
|
|
|
924
|
+
// If the error tells us to wait before retrying, then do so.
|
|
891
925
|
const delayMs = getRetryDelayFromError(error);
|
|
892
926
|
if (error !== undefined && delayMs !== undefined) {
|
|
893
927
|
this.props.reconnectionDelayHandler(delayMs, error);
|
|
894
|
-
await
|
|
928
|
+
await new Promise<void>((resolve) => {
|
|
929
|
+
setTimeout(resolve, delayMs);
|
|
930
|
+
});
|
|
895
931
|
}
|
|
896
932
|
|
|
933
|
+
// If we believe we're offline, we assume there's no point in trying again until we at least think we're online.
|
|
934
|
+
// NOTE: This isn't strictly true for drivers that don't require network (e.g. local driver). Really this logic
|
|
935
|
+
// should probably live in the driver.
|
|
936
|
+
await waitForOnline();
|
|
937
|
+
|
|
897
938
|
this.triggerConnect(requestedMode);
|
|
898
939
|
}
|
|
899
940
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
TelemetryEventCategory,
|
|
10
10
|
} from "@fluidframework/common-definitions";
|
|
11
11
|
import { assert, Timer } from "@fluidframework/common-utils";
|
|
12
|
-
import {
|
|
12
|
+
import { IConnectionDetailsInternal, IDeltaManager } from "@fluidframework/container-definitions";
|
|
13
13
|
import { ILocalSequencedClient } from "@fluidframework/protocol-base";
|
|
14
14
|
import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
|
|
15
15
|
import { PerformanceEvent, loggerToMonitoringContext } from "@fluidframework/telemetry-utils";
|
|
@@ -56,7 +56,7 @@ export interface IConnectionStateHandler {
|
|
|
56
56
|
containerSaved(): void;
|
|
57
57
|
dispose(): void;
|
|
58
58
|
initProtocol(protocol: IProtocolHandler): void;
|
|
59
|
-
receivedConnectEvent(details:
|
|
59
|
+
receivedConnectEvent(details: IConnectionDetailsInternal): void;
|
|
60
60
|
receivedDisconnectEvent(reason: string): void;
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -143,7 +143,7 @@ class ConnectionStateHandlerPassThrough
|
|
|
143
143
|
return this.pimpl.receivedDisconnectEvent(reason);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
public receivedConnectEvent(details:
|
|
146
|
+
public receivedConnectEvent(details: IConnectionDetailsInternal) {
|
|
147
147
|
return this.pimpl.receivedConnectEvent(details);
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -288,7 +288,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
288
288
|
private readonly prevClientLeftTimer: Timer;
|
|
289
289
|
private readonly joinOpTimer: Timer;
|
|
290
290
|
private protocol?: IProtocolHandler;
|
|
291
|
-
private connection?:
|
|
291
|
+
private connection?: IConnectionDetailsInternal;
|
|
292
292
|
private _clientId?: string;
|
|
293
293
|
|
|
294
294
|
private waitEvent: PerformanceEvent | undefined;
|
|
@@ -398,6 +398,18 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
398
398
|
});
|
|
399
399
|
}
|
|
400
400
|
this.applyForConnectedState("addMemberEvent");
|
|
401
|
+
} else if (clientId === this.clientId) {
|
|
402
|
+
// If we see our clientId and it's not also our pending ID, it's our own join op
|
|
403
|
+
// being replayed, so start the timer in case our previous client is still in quorum
|
|
404
|
+
assert(
|
|
405
|
+
!this.waitingForLeaveOp,
|
|
406
|
+
0x5d2 /* Unexpected join op with current clientId while waiting */,
|
|
407
|
+
);
|
|
408
|
+
assert(
|
|
409
|
+
this.connectionState !== ConnectionState.Connected,
|
|
410
|
+
0x5d3 /* Unexpected join op with current clientId while connected */,
|
|
411
|
+
);
|
|
412
|
+
this.prevClientLeftTimer.restart();
|
|
401
413
|
}
|
|
402
414
|
}
|
|
403
415
|
|
|
@@ -472,7 +484,7 @@ class ConnectionStateHandler implements IConnectionStateHandler {
|
|
|
472
484
|
* @param deltaManager - DeltaManager to be used for delaying Connected transition until caught up.
|
|
473
485
|
* If it's undefined, then don't delay and transition to Connected as soon as Leave/Join op are accounted for
|
|
474
486
|
*/
|
|
475
|
-
public receivedConnectEvent(details:
|
|
487
|
+
public receivedConnectEvent(details: IConnectionDetailsInternal) {
|
|
476
488
|
this.connection = details;
|
|
477
489
|
|
|
478
490
|
const oldState = this._connectionState;
|