@fluidframework/driver-base 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.
@@ -0,0 +1,98 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { performance } from "@fluidframework/common-utils";
6
+ /**
7
+ * Extract and return the w3c data.
8
+ * @param url - request url for which w3c data needs to be reported.
9
+ * @param initiatorType - type of the network call
10
+ */
11
+ export function getW3CData(url, initiatorType) {
12
+ // From: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
13
+ // fetchStart: immediately before the browser starts to fetch the resource.
14
+ // requestStart: immediately before the browser starts requesting the resource from the server
15
+ // responseStart: immediately after the browser receives the first byte of the response from the server.
16
+ // responseEnd: immediately after the browser receives the last byte of the resource
17
+ // or immediately before the transport connection is closed, whichever comes first.
18
+ // secureConnectionStart: immediately before the browser starts the handshake process to secure the
19
+ // current connection. If a secure connection is not used, this property returns zero.
20
+ // startTime: Time when the resource fetch started. This value is equivalent to fetchStart.
21
+ // domainLookupStart: immediately before the browser starts the domain name lookup for the resource.
22
+ // domainLookupEnd: immediately after the browser finishes the domain name lookup for the resource.
23
+ // redirectStart: start time of the fetch which that initiates the redirect.
24
+ // redirectEnd: immediately after receiving the last byte of the response of the last redirect.
25
+ var _a, _b;
26
+ // Interval between start and finish of the domain name lookup for the resource.
27
+ let dnsLookupTime; // domainLookupEnd - domainLookupStart
28
+ // Interval between the first fetch until the last byte of the last redirect.
29
+ let redirectTime; // redirectEnd - redirectStart
30
+ // Time to establish the connection to the server to retrieve the resource.
31
+ let tcpHandshakeTime; // connectEnd - connectStart
32
+ // Time from the end of the connection until the inital handshake process to secure the connection.
33
+ // If 0, then no time is spent here.
34
+ let secureConnectionTime; // connectEnd - secureConnectionStart
35
+ // Interval to receive all (first to last) bytes form the server.
36
+ let responseNetworkTime; // responsEnd - responseStart
37
+ // Interval between the initial fetch until the last byte is received.
38
+ // Likely same as fetchTime + receiveContentTime.
39
+ let fetchStartToResponseEndTime; // responseEnd - fetchStart
40
+ // reqStartToResponseEndTime = fetchStartToResponseEndTime - <initial TCP handshake>
41
+ // Interval between starting the request for the resource until receiving the last byte.
42
+ let reqStartToResponseEndTime; // responseEnd - requestStart
43
+ let w3cStartTime; // W3C Start time = fetchStart time
44
+ // getEntriesByType is only available in browser performance object
45
+ const resources1 = (_b = (_a = performance.getEntriesByType) === null || _a === void 0 ? void 0 : _a.call(performance, "resource")) !== null && _b !== void 0 ? _b : [];
46
+ // Usually the latest fetch call is to the end of resources, so we start from the end.
47
+ for (let i = resources1.length - 1; i > 0; i--) {
48
+ const indResTime = resources1[i];
49
+ const resource_name = indResTime.name.toString();
50
+ const resource_initiatortype = indResTime.initiatorType;
51
+ if (resource_initiatortype.localeCompare(initiatorType) === 0 &&
52
+ resource_name.includes(url)) {
53
+ redirectTime = indResTime.redirectEnd - indResTime.redirectStart;
54
+ w3cStartTime = indResTime.fetchStart;
55
+ dnsLookupTime = indResTime.domainLookupEnd - indResTime.domainLookupStart;
56
+ tcpHandshakeTime = indResTime.connectEnd - indResTime.connectStart;
57
+ secureConnectionTime =
58
+ indResTime.secureConnectionStart > 0
59
+ ? indResTime.connectEnd - indResTime.secureConnectionStart
60
+ : 0;
61
+ responseNetworkTime =
62
+ indResTime.responseStart > 0
63
+ ? indResTime.responseEnd - indResTime.responseStart
64
+ : undefined;
65
+ fetchStartToResponseEndTime =
66
+ indResTime.fetchStart > 0
67
+ ? indResTime.responseEnd - indResTime.fetchStart
68
+ : undefined;
69
+ reqStartToResponseEndTime =
70
+ indResTime.requestStart > 0
71
+ ? indResTime.responseEnd - indResTime.requestStart
72
+ : undefined;
73
+ break;
74
+ }
75
+ }
76
+ return {
77
+ dnsLookupTime,
78
+ w3cStartTime,
79
+ redirectTime,
80
+ tcpHandshakeTime,
81
+ secureConnectionTime,
82
+ responseNetworkTime,
83
+ fetchStartToResponseEndTime,
84
+ reqStartToResponseEndTime,
85
+ };
86
+ }
87
+ /*
88
+ * An implementation of Promise.race that gives you the winner of the promise race.
89
+ * If one of the promises is rejected before any other is resolved, this method will return the error/reason from that rejection.
90
+ */
91
+ export async function promiseRaceWithWinner(promises) {
92
+ return new Promise((resolve, reject) => {
93
+ promises.forEach((p, index) => {
94
+ p.then((v) => resolve({ index, value: v })).catch(reject);
95
+ });
96
+ });
97
+ }
98
+ //# sourceMappingURL=driverUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driverUtils.js","sourceRoot":"","sources":["../src/driverUtils.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 { performance } from \"@fluidframework/common-utils\";\n\n/**\n * Extract and return the w3c data.\n * @param url - request url for which w3c data needs to be reported.\n * @param initiatorType - type of the network call\n */\nexport function getW3CData(url: string, initiatorType: string) {\n\t// From: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming\n\t// fetchStart: immediately before the browser starts to fetch the resource.\n\t// requestStart: immediately before the browser starts requesting the resource from the server\n\t// responseStart: immediately after the browser receives the first byte of the response from the server.\n\t// responseEnd: immediately after the browser receives the last byte of the resource\n\t// or immediately before the transport connection is closed, whichever comes first.\n\t// secureConnectionStart: immediately before the browser starts the handshake process to secure the\n\t// current connection. If a secure connection is not used, this property returns zero.\n\t// startTime: Time when the resource fetch started. This value is equivalent to fetchStart.\n\t// domainLookupStart: immediately before the browser starts the domain name lookup for the resource.\n\t// domainLookupEnd: immediately after the browser finishes the domain name lookup for the resource.\n\t// redirectStart: start time of the fetch which that initiates the redirect.\n\t// redirectEnd: immediately after receiving the last byte of the response of the last redirect.\n\n\t// Interval between start and finish of the domain name lookup for the resource.\n\tlet dnsLookupTime: number | undefined; // domainLookupEnd - domainLookupStart\n\t// Interval between the first fetch until the last byte of the last redirect.\n\tlet redirectTime: number | undefined; // redirectEnd - redirectStart\n\t// Time to establish the connection to the server to retrieve the resource.\n\tlet tcpHandshakeTime: number | undefined; // connectEnd - connectStart\n\t// Time from the end of the connection until the inital handshake process to secure the connection.\n\t// If 0, then no time is spent here.\n\tlet secureConnectionTime: number | undefined; // connectEnd - secureConnectionStart\n\t// Interval to receive all (first to last) bytes form the server.\n\tlet responseNetworkTime: number | undefined; // responsEnd - responseStart\n\t// Interval between the initial fetch until the last byte is received.\n\t// Likely same as fetchTime + receiveContentTime.\n\tlet fetchStartToResponseEndTime: number | undefined; // responseEnd - fetchStart\n\t// reqStartToResponseEndTime = fetchStartToResponseEndTime - <initial TCP handshake>\n\t// Interval between starting the request for the resource until receiving the last byte.\n\tlet reqStartToResponseEndTime: number | undefined; // responseEnd - requestStart\n\tlet w3cStartTime: number | undefined; // W3C Start time = fetchStart time\n\n\t// getEntriesByType is only available in browser performance object\n\tconst resources1 = performance.getEntriesByType?.(\"resource\") ?? [];\n\t// Usually the latest fetch call is to the end of resources, so we start from the end.\n\tfor (let i = resources1.length - 1; i > 0; i--) {\n\t\tconst indResTime = resources1[i] as PerformanceResourceTiming;\n\t\tconst resource_name = indResTime.name.toString();\n\t\tconst resource_initiatortype = indResTime.initiatorType;\n\t\tif (\n\t\t\tresource_initiatortype.localeCompare(initiatorType) === 0 &&\n\t\t\tresource_name.includes(url)\n\t\t) {\n\t\t\tredirectTime = indResTime.redirectEnd - indResTime.redirectStart;\n\t\t\tw3cStartTime = indResTime.fetchStart;\n\t\t\tdnsLookupTime = indResTime.domainLookupEnd - indResTime.domainLookupStart;\n\t\t\ttcpHandshakeTime = indResTime.connectEnd - indResTime.connectStart;\n\t\t\tsecureConnectionTime =\n\t\t\t\tindResTime.secureConnectionStart > 0\n\t\t\t\t\t? indResTime.connectEnd - indResTime.secureConnectionStart\n\t\t\t\t\t: 0;\n\t\t\tresponseNetworkTime =\n\t\t\t\tindResTime.responseStart > 0\n\t\t\t\t\t? indResTime.responseEnd - indResTime.responseStart\n\t\t\t\t\t: undefined;\n\t\t\tfetchStartToResponseEndTime =\n\t\t\t\tindResTime.fetchStart > 0\n\t\t\t\t\t? indResTime.responseEnd - indResTime.fetchStart\n\t\t\t\t\t: undefined;\n\t\t\treqStartToResponseEndTime =\n\t\t\t\tindResTime.requestStart > 0\n\t\t\t\t\t? indResTime.responseEnd - indResTime.requestStart\n\t\t\t\t\t: undefined;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn {\n\t\tdnsLookupTime,\n\t\tw3cStartTime,\n\t\tredirectTime,\n\t\ttcpHandshakeTime,\n\t\tsecureConnectionTime,\n\t\tresponseNetworkTime,\n\t\tfetchStartToResponseEndTime,\n\t\treqStartToResponseEndTime,\n\t};\n}\n\n/*\n * An implementation of Promise.race that gives you the winner of the promise race.\n * If one of the promises is rejected before any other is resolved, this method will return the error/reason from that rejection.\n */\nexport async function promiseRaceWithWinner<T>(\n\tpromises: Promise<T>[],\n): Promise<{ index: number; value: T }> {\n\treturn new Promise((resolve, reject) => {\n\t\tpromises.forEach((p, index) => {\n\t\t\tp.then((v) => resolve({ index, value: v })).catch(reject);\n\t\t});\n\t});\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -3,4 +3,5 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
6
+ export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
package/lib/index.js CHANGED
@@ -3,4 +3,5 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
6
+ export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
6
7
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { DocumentDeltaConnection } from \"./documentDeltaConnection\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { DocumentDeltaConnection } from \"./documentDeltaConnection\";\nexport { getW3CData, promiseRaceWithWinner } from \"./driverUtils\";\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/driver-base";
8
- export declare const pkgVersion = "2.0.0-dev.3.1.0.125672";
8
+ export declare const pkgVersion = "2.0.0-dev.4.2.0.153917";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/driver-base";
8
- export const pkgVersion = "2.0.0-dev.3.1.0.125672";
8
+ export const pkgVersion = "2.0.0-dev.4.2.0.153917";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/driver-base\";\nexport const pkgVersion = \"2.0.0-dev.3.1.0.125672\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/driver-base\";\nexport const pkgVersion = \"2.0.0-dev.4.2.0.153917\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"validateDriverBasePrevious.generated.js","sourceRoot":"","sources":["../../../src/test/types/validateDriverBasePrevious.generated.ts"],"names":[],"mappings":"AAwBA,oDAAoD,CAChD,gDAAgD,EAAE,CAAC,CAAC;AAWxD,gDAAgD,CAC5C,oDAAoD,EAAE,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n/*\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n * Generated by fluid-type-validator in @fluidframework/build-tools.\n */\nimport * as old from \"@fluidframework/driver-base-previous\";\nimport * as current from \"../../index\";\n\ntype TypeOnly<T> = {\n [P in keyof T]: TypeOnly<T[P]>;\n};\n\n/*\n* Validate forward compat by using old type in place of current type\n* If breaking change required, add in package.json under typeValidation.broken:\n* \"ClassDeclaration_DocumentDeltaConnection\": {\"forwardCompat\": false}\n*/\ndeclare function get_old_ClassDeclaration_DocumentDeltaConnection():\n TypeOnly<old.DocumentDeltaConnection>;\ndeclare function use_current_ClassDeclaration_DocumentDeltaConnection(\n use: TypeOnly<current.DocumentDeltaConnection>);\nuse_current_ClassDeclaration_DocumentDeltaConnection(\n get_old_ClassDeclaration_DocumentDeltaConnection());\n\n/*\n* Validate back compat by using current type in place of old type\n* If breaking change required, add in package.json under typeValidation.broken:\n* \"ClassDeclaration_DocumentDeltaConnection\": {\"backCompat\": false}\n*/\ndeclare function get_current_ClassDeclaration_DocumentDeltaConnection():\n TypeOnly<current.DocumentDeltaConnection>;\ndeclare function use_old_ClassDeclaration_DocumentDeltaConnection(\n use: TypeOnly<old.DocumentDeltaConnection>);\nuse_old_ClassDeclaration_DocumentDeltaConnection(\n get_current_ClassDeclaration_DocumentDeltaConnection());\n"]}
1
+ {"version":3,"file":"validateDriverBasePrevious.generated.js","sourceRoot":"","sources":["../../../src/test/types/validateDriverBasePrevious.generated.ts"],"names":[],"mappings":"AAwBA,oDAAoD,CAChD,gDAAgD,EAAE,CAAC,CAAC;AAWxD,gDAAgD,CAC5C,oDAAoD,EAAE,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n/*\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n * Generated by fluid-type-test-generator in @fluidframework/build-tools.\n */\nimport * as old from \"@fluidframework/driver-base-previous\";\nimport * as current from \"../../index\";\n\ntype TypeOnly<T> = {\n [P in keyof T]: TypeOnly<T[P]>;\n};\n\n/*\n* Validate forward compat by using old type in place of current type\n* If breaking change required, add in package.json under typeValidation.broken:\n* \"ClassDeclaration_DocumentDeltaConnection\": {\"forwardCompat\": false}\n*/\ndeclare function get_old_ClassDeclaration_DocumentDeltaConnection():\n TypeOnly<old.DocumentDeltaConnection>;\ndeclare function use_current_ClassDeclaration_DocumentDeltaConnection(\n use: TypeOnly<current.DocumentDeltaConnection>);\nuse_current_ClassDeclaration_DocumentDeltaConnection(\n get_old_ClassDeclaration_DocumentDeltaConnection());\n\n/*\n* Validate back compat by using current type in place of old type\n* If breaking change required, add in package.json under typeValidation.broken:\n* \"ClassDeclaration_DocumentDeltaConnection\": {\"backCompat\": false}\n*/\ndeclare function get_current_ClassDeclaration_DocumentDeltaConnection():\n TypeOnly<current.DocumentDeltaConnection>;\ndeclare function use_old_ClassDeclaration_DocumentDeltaConnection(\n use: TypeOnly<old.DocumentDeltaConnection>);\nuse_old_ClassDeclaration_DocumentDeltaConnection(\n get_current_ClassDeclaration_DocumentDeltaConnection());\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/driver-base",
3
- "version": "2.0.0-dev.3.1.0.125672",
3
+ "version": "2.0.0-dev.4.2.0.153917",
4
4
  "description": "Shared driver code for Fluid driver implementations",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -14,9 +14,36 @@
14
14
  "main": "dist/index.js",
15
15
  "module": "lib/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "dependencies": {
18
+ "@fluidframework/common-definitions": "^0.20.1",
19
+ "@fluidframework/common-utils": "^1.1.1",
20
+ "@fluidframework/driver-definitions": "2.0.0-dev.4.2.0.153917",
21
+ "@fluidframework/driver-utils": "2.0.0-dev.4.2.0.153917",
22
+ "@fluidframework/protocol-definitions": "^1.1.0",
23
+ "@fluidframework/telemetry-utils": "2.0.0-dev.4.2.0.153917"
24
+ },
25
+ "devDependencies": {
26
+ "@fluid-tools/build-cli": "^0.15.0",
27
+ "@fluidframework/build-common": "^1.1.0",
28
+ "@fluidframework/build-tools": "^0.15.0",
29
+ "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.4.0.0",
30
+ "@fluidframework/eslint-config-fluid": "^2.0.0",
31
+ "@microsoft/api-extractor": "^7.34.4",
32
+ "@types/node": "^14.18.38",
33
+ "concurrently": "^7.6.0",
34
+ "copyfiles": "^2.4.1",
35
+ "eslint": "~8.6.0",
36
+ "prettier": "~2.6.2",
37
+ "rimraf": "^4.4.0",
38
+ "socket.io-client": "^4.4.1",
39
+ "typescript": "~4.5.5"
40
+ },
41
+ "typeValidation": {
42
+ "broken": {}
43
+ },
17
44
  "scripts": {
18
45
  "build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
19
- "build:compile": "concurrently npm:tsc npm:build:esnext",
46
+ "build:compile": "npm run typetests:gen && concurrently npm:tsc npm:build:esnext",
20
47
  "build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
21
48
  "build:esnext": "tsc --project ./tsconfig.esnext.json",
22
49
  "build:full": "npm run build",
@@ -31,37 +58,8 @@
31
58
  "lint:fix": "npm run prettier:fix && npm run eslint:fix",
32
59
  "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
33
60
  "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
34
- "tsc": "tsc"
35
- },
36
- "dependencies": {
37
- "@fluidframework/common-definitions": "^0.20.1",
38
- "@fluidframework/common-utils": "^1.0.0",
39
- "@fluidframework/driver-definitions": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
40
- "@fluidframework/driver-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0",
41
- "@fluidframework/protocol-definitions": "^1.1.0",
42
- "@fluidframework/telemetry-utils": ">=2.0.0-dev.3.1.0.125672 <2.0.0-dev.4.0.0"
43
- },
44
- "devDependencies": {
45
- "@fluid-tools/build-cli": "^0.8.0",
46
- "@fluidframework/build-common": "^1.1.0",
47
- "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.3.0.0",
48
- "@fluidframework/eslint-config-fluid": "^2.0.0",
49
- "@microsoft/api-extractor": "^7.22.2",
50
- "@rushstack/eslint-config": "^2.5.1",
51
- "@types/node": "^14.18.36",
52
- "concurrently": "^6.2.0",
53
- "copyfiles": "^2.4.1",
54
- "eslint": "~8.6.0",
55
- "prettier": "~2.6.2",
56
- "rimraf": "^2.6.2",
57
- "socket.io-client": "^4.4.1",
58
- "typescript": "~4.5.5"
59
- },
60
- "typeValidation": {
61
- "version": "2.0.0-internal.3.1.0",
62
- "previousVersionStyle": "~previousMinor",
63
- "baselineRange": ">=2.0.0-internal.3.0.0 <2.0.0-internal.3.1.0",
64
- "baselineVersion": "2.0.0-internal.3.0.0",
65
- "broken": {}
61
+ "tsc": "tsc",
62
+ "typetests:gen": "fluid-type-test-generator",
63
+ "typetests:prepare": "flub generate typetests --prepare --dir . --pin"
66
64
  }
67
- }
65
+ }
@@ -30,6 +30,7 @@ import {
30
30
  loggerToMonitoringContext,
31
31
  MonitoringContext,
32
32
  EventEmitterWithErrorHandling,
33
+ normalizeError,
33
34
  } from "@fluidframework/telemetry-utils";
34
35
  import type { Socket } from "socket.io-client";
35
36
  // For now, this package is versioned and released in unison with the specific drivers
@@ -81,10 +82,21 @@ export class DocumentDeltaConnection
81
82
  }
82
83
 
83
84
  public get disposed() {
84
- assert(
85
- this._disposed || this.socket.connected,
86
- 0x244 /* "Socket is closed, but connection is not!" */,
87
- );
85
+ // Increase the stack trace limit temporarily, so as to debug better in case it occurs.
86
+ // We are seeing this in telemetry and we are unable to figure out why it is happening, so this should help.
87
+ const originalStackTraceLimit = (Error as any).stackTraceLimit;
88
+ try {
89
+ (Error as any).stackTraceLimit = 50;
90
+ assert(
91
+ this._disposed || this.socket.connected,
92
+ 0x244 /* "Socket is closed, but connection is not!" */,
93
+ );
94
+ } catch (error) {
95
+ const normalizedError = this.addPropsToError(error);
96
+ throw normalizedError;
97
+ } finally {
98
+ (Error as any).stackTraceLimit = originalStackTraceLimit;
99
+ }
88
100
  return this._disposed;
89
101
  }
90
102
 
@@ -120,6 +132,7 @@ export class DocumentDeltaConnection
120
132
  public documentId: string,
121
133
  logger: ITelemetryLogger,
122
134
  private readonly enableLongPollingDowngrades: boolean = false,
135
+ protected readonly connectionId?: string,
123
136
  ) {
124
137
  super((name, error) => {
125
138
  logger.sendErrorEvent(
@@ -223,7 +236,18 @@ export class DocumentDeltaConnection
223
236
  }
224
237
 
225
238
  private checkNotClosed() {
226
- assert(!this.disposed, 0x20c /* "connection disposed" */);
239
+ // Increase the stack trace limit temporarily, so as to debug better in case it occurs.
240
+ // We are seeing this in telemetry and we are unable to figure out why it is happening, so this should help.
241
+ const originalStackTraceLimit = (Error as any).stackTraceLimit;
242
+ try {
243
+ (Error as any).stackTraceLimit = 50;
244
+ assert(!this.disposed, 0x20c /* "connection disposed" */);
245
+ } catch (error) {
246
+ const normalizedError = this.addPropsToError(error);
247
+ throw normalizedError;
248
+ } finally {
249
+ (Error as any).stackTraceLimit = originalStackTraceLimit;
250
+ }
227
251
  }
228
252
 
229
253
  /**
@@ -318,7 +342,26 @@ export class DocumentDeltaConnection
318
342
  /**
319
343
  * Disconnect from the websocket and close the websocket too.
320
344
  */
321
- protected closeSocket(error: IAnyDriverError) {
345
+ private closeSocket(error: IAnyDriverError) {
346
+ if (this._disposed) {
347
+ // This would be rare situation due to complexity around socket emitting events.
348
+ this.logger.sendTelemetryEvent(
349
+ {
350
+ eventName: "SocketCloseOnDisposedConnection",
351
+ driverVersion,
352
+ details: JSON.stringify({
353
+ ...this.getConnectionDetailsProps(),
354
+ trackedListenerCount: this.trackedListeners.size,
355
+ }),
356
+ },
357
+ error,
358
+ );
359
+ return;
360
+ }
361
+ this.closeSocketCore(error);
362
+ }
363
+
364
+ protected closeSocketCore(error: IAnyDriverError) {
322
365
  this.disconnect(error);
323
366
  }
324
367
 
@@ -332,8 +375,7 @@ export class DocumentDeltaConnection
332
375
  eventName: "ClientClosingDeltaConnection",
333
376
  driverVersion,
334
377
  details: JSON.stringify({
335
- disposed: this._disposed,
336
- socketConnected: this.socket.connected,
378
+ ...this.getConnectionDetailsProps(),
337
379
  }),
338
380
  });
339
381
  this.disconnect(
@@ -361,23 +403,24 @@ export class DocumentDeltaConnection
361
403
  // to prevent normal messages from being emitted.
362
404
  this._disposed = true;
363
405
 
364
- // Let user of connection object know about disconnect. This has to happen in between setting _disposed and
365
- // removing all listeners!
406
+ // Remove all listeners listening on the socket. These are listeners on socket and not on this connection
407
+ // object. Anyway since we have disposed this connection object, nobody should listen to event on socket
408
+ // anymore.
409
+ this.removeTrackedListeners();
410
+
411
+ // Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.
412
+ this.disconnectCore();
413
+
414
+ // Let user of connection object know about disconnect.
366
415
  this.emit("disconnect", err);
367
416
  this.logger.sendTelemetryEvent({
368
417
  eventName: "AfterDisconnectEvent",
369
418
  driverVersion,
370
419
  details: JSON.stringify({
371
- socketConnected: this.socket.connected,
420
+ ...this.getConnectionDetailsProps(),
372
421
  disconnectListenerCount: this.listenerCount("disconnect"),
373
422
  }),
374
423
  });
375
- // user of DeltaConnection should have processed "disconnect" event and removed all listeners. Not clear
376
- // if we want to enforce that, as some users (like LocalDocumentService) do not unregister any handlers
377
- // assert(this.listenerCount("disconnect") === 0, "'disconnect` events should be processed synchronously");
378
-
379
- this.removeTrackedListeners();
380
- this.disconnectCore();
381
424
  }
382
425
 
383
426
  /**
@@ -403,14 +446,34 @@ export class DocumentDeltaConnection
403
446
 
404
447
  this._details = await new Promise<IConnected>((resolve, reject) => {
405
448
  const failAndCloseSocket = (err: IAnyDriverError) => {
406
- this.closeSocket(err);
449
+ try {
450
+ this.closeSocket(err);
451
+ } catch (failError) {
452
+ const normalizedError = this.addPropsToError(failError);
453
+ this.logger.sendErrorEvent({ eventName: "CloseSocketError" }, normalizedError);
454
+ }
407
455
  reject(err);
408
456
  };
409
457
 
410
458
  const failConnection = (err: IAnyDriverError) => {
411
- this.disconnect(err);
459
+ try {
460
+ this.disconnect(err);
461
+ } catch (failError) {
462
+ const normalizedError = this.addPropsToError(failError);
463
+ this.logger.sendErrorEvent(
464
+ { eventName: "FailConnectionError" },
465
+ normalizedError,
466
+ );
467
+ }
412
468
  reject(err);
413
469
  };
470
+
471
+ // Immediately set the connection timeout.
472
+ // Give extra 2 seconds for handshake on top of socket connection timeout.
473
+ this.socketConnectionTimeout = setTimeout(() => {
474
+ failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
475
+ }, timeout + 2000);
476
+
414
477
  // Listen for connection issues
415
478
  this.addConnectionListener("connect_error", (error) => {
416
479
  internalSocketConnectionFailureCount++;
@@ -560,16 +623,31 @@ export class DocumentDeltaConnection
560
623
  });
561
624
 
562
625
  this.socket.emit("connect_document", connectMessage);
563
-
564
- // Give extra 2 seconds for handshake on top of socket connection timeout
565
- this.socketConnectionTimeout = setTimeout(() => {
566
- failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
567
- }, timeout + 2000);
568
626
  });
569
627
 
570
628
  assert(!this.disposed, 0x246 /* "checking consistency of socket & _disposed flags" */);
571
629
  }
572
630
 
631
+ private addPropsToError(errorToBeNormalized: unknown) {
632
+ const normalizedError = normalizeError(errorToBeNormalized, {
633
+ props: {
634
+ details: JSON.stringify({
635
+ ...this.getConnectionDetailsProps(),
636
+ }),
637
+ },
638
+ });
639
+ return normalizedError;
640
+ }
641
+
642
+ private getConnectionDetailsProps() {
643
+ return {
644
+ disposed: this._disposed,
645
+ socketConnected: this.socket?.connected,
646
+ clientId: this._details?.clientId,
647
+ connectionId: this.connectionId,
648
+ };
649
+ }
650
+
573
651
  protected earlyOpHandler = (documentId: string, msgs: ISequencedDocumentMessage[]) => {
574
652
  this.queuedMessages.push(...msgs);
575
653
  };
@@ -654,7 +732,12 @@ export class DocumentDeltaConnection
654
732
  const errorObj = createGenericNetworkError(
655
733
  `socket.io (${handler}): ${message}`,
656
734
  { canRetry },
657
- { driverVersion },
735
+ {
736
+ driverVersion,
737
+ details: JSON.stringify({
738
+ ...this.getConnectionDetailsProps(),
739
+ }),
740
+ },
658
741
  );
659
742
 
660
743
  return errorObj;
@@ -0,0 +1,105 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { performance } from "@fluidframework/common-utils";
7
+
8
+ /**
9
+ * Extract and return the w3c data.
10
+ * @param url - request url for which w3c data needs to be reported.
11
+ * @param initiatorType - type of the network call
12
+ */
13
+ export function getW3CData(url: string, initiatorType: string) {
14
+ // From: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
15
+ // fetchStart: immediately before the browser starts to fetch the resource.
16
+ // requestStart: immediately before the browser starts requesting the resource from the server
17
+ // responseStart: immediately after the browser receives the first byte of the response from the server.
18
+ // responseEnd: immediately after the browser receives the last byte of the resource
19
+ // or immediately before the transport connection is closed, whichever comes first.
20
+ // secureConnectionStart: immediately before the browser starts the handshake process to secure the
21
+ // current connection. If a secure connection is not used, this property returns zero.
22
+ // startTime: Time when the resource fetch started. This value is equivalent to fetchStart.
23
+ // domainLookupStart: immediately before the browser starts the domain name lookup for the resource.
24
+ // domainLookupEnd: immediately after the browser finishes the domain name lookup for the resource.
25
+ // redirectStart: start time of the fetch which that initiates the redirect.
26
+ // redirectEnd: immediately after receiving the last byte of the response of the last redirect.
27
+
28
+ // Interval between start and finish of the domain name lookup for the resource.
29
+ let dnsLookupTime: number | undefined; // domainLookupEnd - domainLookupStart
30
+ // Interval between the first fetch until the last byte of the last redirect.
31
+ let redirectTime: number | undefined; // redirectEnd - redirectStart
32
+ // Time to establish the connection to the server to retrieve the resource.
33
+ let tcpHandshakeTime: number | undefined; // connectEnd - connectStart
34
+ // Time from the end of the connection until the inital handshake process to secure the connection.
35
+ // If 0, then no time is spent here.
36
+ let secureConnectionTime: number | undefined; // connectEnd - secureConnectionStart
37
+ // Interval to receive all (first to last) bytes form the server.
38
+ let responseNetworkTime: number | undefined; // responsEnd - responseStart
39
+ // Interval between the initial fetch until the last byte is received.
40
+ // Likely same as fetchTime + receiveContentTime.
41
+ let fetchStartToResponseEndTime: number | undefined; // responseEnd - fetchStart
42
+ // reqStartToResponseEndTime = fetchStartToResponseEndTime - <initial TCP handshake>
43
+ // Interval between starting the request for the resource until receiving the last byte.
44
+ let reqStartToResponseEndTime: number | undefined; // responseEnd - requestStart
45
+ let w3cStartTime: number | undefined; // W3C Start time = fetchStart time
46
+
47
+ // getEntriesByType is only available in browser performance object
48
+ const resources1 = performance.getEntriesByType?.("resource") ?? [];
49
+ // Usually the latest fetch call is to the end of resources, so we start from the end.
50
+ for (let i = resources1.length - 1; i > 0; i--) {
51
+ const indResTime = resources1[i] as PerformanceResourceTiming;
52
+ const resource_name = indResTime.name.toString();
53
+ const resource_initiatortype = indResTime.initiatorType;
54
+ if (
55
+ resource_initiatortype.localeCompare(initiatorType) === 0 &&
56
+ resource_name.includes(url)
57
+ ) {
58
+ redirectTime = indResTime.redirectEnd - indResTime.redirectStart;
59
+ w3cStartTime = indResTime.fetchStart;
60
+ dnsLookupTime = indResTime.domainLookupEnd - indResTime.domainLookupStart;
61
+ tcpHandshakeTime = indResTime.connectEnd - indResTime.connectStart;
62
+ secureConnectionTime =
63
+ indResTime.secureConnectionStart > 0
64
+ ? indResTime.connectEnd - indResTime.secureConnectionStart
65
+ : 0;
66
+ responseNetworkTime =
67
+ indResTime.responseStart > 0
68
+ ? indResTime.responseEnd - indResTime.responseStart
69
+ : undefined;
70
+ fetchStartToResponseEndTime =
71
+ indResTime.fetchStart > 0
72
+ ? indResTime.responseEnd - indResTime.fetchStart
73
+ : undefined;
74
+ reqStartToResponseEndTime =
75
+ indResTime.requestStart > 0
76
+ ? indResTime.responseEnd - indResTime.requestStart
77
+ : undefined;
78
+ break;
79
+ }
80
+ }
81
+ return {
82
+ dnsLookupTime,
83
+ w3cStartTime,
84
+ redirectTime,
85
+ tcpHandshakeTime,
86
+ secureConnectionTime,
87
+ responseNetworkTime,
88
+ fetchStartToResponseEndTime,
89
+ reqStartToResponseEndTime,
90
+ };
91
+ }
92
+
93
+ /*
94
+ * An implementation of Promise.race that gives you the winner of the promise race.
95
+ * If one of the promises is rejected before any other is resolved, this method will return the error/reason from that rejection.
96
+ */
97
+ export async function promiseRaceWithWinner<T>(
98
+ promises: Promise<T>[],
99
+ ): Promise<{ index: number; value: T }> {
100
+ return new Promise((resolve, reject) => {
101
+ promises.forEach((p, index) => {
102
+ p.then((v) => resolve({ index, value: v })).catch(reject);
103
+ });
104
+ });
105
+ }
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@
4
4
  */
5
5
 
6
6
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
7
+ export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/driver-base";
9
- export const pkgVersion = "2.0.0-dev.3.1.0.125672";
9
+ export const pkgVersion = "2.0.0-dev.4.2.0.153917";