@fluidframework/driver-base 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.6.4.0.191258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.eslintrc.js +3 -0
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +36 -0
  4. package/README.md +4 -3
  5. package/dist/documentDeltaConnection.d.ts +2 -1
  6. package/dist/documentDeltaConnection.d.ts.map +1 -1
  7. package/dist/documentDeltaConnection.js +67 -37
  8. package/dist/documentDeltaConnection.js.map +1 -1
  9. package/dist/driverUtils.d.ts +1 -1
  10. package/dist/driverUtils.d.ts.map +1 -1
  11. package/dist/driverUtils.js +24 -17
  12. package/dist/driverUtils.js.map +1 -1
  13. package/dist/packageVersion.d.ts +1 -1
  14. package/dist/packageVersion.js +1 -1
  15. package/dist/packageVersion.js.map +1 -1
  16. package/lib/documentDeltaConnection.d.ts +2 -1
  17. package/lib/documentDeltaConnection.d.ts.map +1 -1
  18. package/lib/documentDeltaConnection.js +55 -25
  19. package/lib/documentDeltaConnection.js.map +1 -1
  20. package/lib/driverUtils.d.ts +1 -1
  21. package/lib/driverUtils.d.ts.map +1 -1
  22. package/lib/driverUtils.js +24 -17
  23. package/lib/driverUtils.js.map +1 -1
  24. package/lib/packageVersion.d.ts +1 -1
  25. package/lib/packageVersion.js +1 -1
  26. package/lib/packageVersion.js.map +1 -1
  27. package/package.json +56 -15
  28. package/src/documentDeltaConnection.ts +35 -9
  29. package/src/driverUtils.ts +25 -14
  30. package/src/packageVersion.ts +1 -1
  31. package/tsconfig.json +2 -1
  32. package/lib/test/types/validateDriverBasePrevious.generated.d.ts +0 -2
  33. package/lib/test/types/validateDriverBasePrevious.generated.d.ts.map +0 -1
  34. package/lib/test/types/validateDriverBasePrevious.generated.js +0 -8
  35. package/lib/test/types/validateDriverBasePrevious.generated.js.map +0 -1
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { performance } from "@fluidframework/common-utils";
5
+ import { performance } from "@fluid-internal/client-utils";
6
6
  /**
7
7
  * Extract and return the w3c data.
8
8
  * @param url - request url for which w3c data needs to be reported.
@@ -22,7 +22,6 @@ export function getW3CData(url, initiatorType) {
22
22
  // domainLookupEnd: immediately after the browser finishes the domain name lookup for the resource.
23
23
  // redirectStart: start time of the fetch which that initiates the redirect.
24
24
  // redirectEnd: immediately after receiving the last byte of the response of the last redirect.
25
- var _a, _b;
26
25
  // Interval between start and finish of the domain name lookup for the resource.
27
26
  let dnsLookupTime; // domainLookupEnd - domainLookupStart
28
27
  // Interval between the first fetch until the last byte of the last redirect.
@@ -42,7 +41,7 @@ export function getW3CData(url, initiatorType) {
42
41
  let reqStartToResponseEndTime; // responseEnd - requestStart
43
42
  let w3cStartTime; // W3C Start time = fetchStart time
44
43
  // 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 : [];
44
+ const resources1 = performance.getEntriesByType?.("resource") ?? [];
46
45
  // Usually the latest fetch call is to the end of resources, so we start from the end.
47
46
  for (let i = resources1.length - 1; i > 0; i--) {
48
47
  const indResTime = resources1[i];
@@ -95,23 +94,26 @@ export async function promiseRaceWithWinner(promises) {
95
94
  });
96
95
  });
97
96
  }
98
- export function validateMessages(reason, messages, from, logger) {
97
+ export function validateMessages(reason, messages, from, logger, strict = true) {
99
98
  if (messages.length !== 0) {
100
99
  const start = messages[0].sequenceNumber;
101
100
  const length = messages.length;
102
101
  const last = messages[length - 1].sequenceNumber;
103
- if (start !== from) {
104
- logger.sendErrorEvent({
105
- eventName: "OpsFetchViolation",
106
- reason,
107
- from,
108
- start,
109
- last,
110
- length,
111
- });
112
- messages.length = 0;
113
- }
114
102
  if (last + 1 !== from + length) {
103
+ // If not strict, then return the first consecutive sub-block. If strict or start
104
+ // seq number is not what we expected, then return no ops.
105
+ if (strict || from !== start) {
106
+ messages.length = 0;
107
+ }
108
+ else {
109
+ let validOpsCount = 1;
110
+ while (validOpsCount < messages.length &&
111
+ messages[validOpsCount].sequenceNumber ===
112
+ messages[validOpsCount - 1].sequenceNumber + 1) {
113
+ validOpsCount++;
114
+ }
115
+ messages.length = validOpsCount;
116
+ }
115
117
  logger.sendErrorEvent({
116
118
  eventName: "OpsFetchViolation",
117
119
  reason,
@@ -119,9 +121,14 @@ export function validateMessages(reason, messages, from, logger) {
119
121
  start,
120
122
  last,
121
123
  length,
124
+ details: JSON.stringify({
125
+ validLength: messages.length,
126
+ lastValidOpSeqNumber: messages.length > 0
127
+ ? messages[messages.length - 1].sequenceNumber
128
+ : undefined,
129
+ strict,
130
+ }),
122
131
  });
123
- // we can do better here by finding consecutive sub-block and return it
124
- messages.length = 0;
125
132
  }
126
133
  }
127
134
  }
@@ -1 +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\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ITelemetryLoggerExt } from \"@fluidframework/telemetry-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\nexport function validateMessages(\n\treason: string,\n\tmessages: ISequencedDocumentMessage[],\n\tfrom: number,\n\tlogger: ITelemetryLoggerExt,\n) {\n\tif (messages.length !== 0) {\n\t\tconst start = messages[0].sequenceNumber;\n\t\tconst length = messages.length;\n\t\tconst last = messages[length - 1].sequenceNumber;\n\t\tif (start !== from) {\n\t\t\tlogger.sendErrorEvent({\n\t\t\t\teventName: \"OpsFetchViolation\",\n\t\t\t\treason,\n\t\t\t\tfrom,\n\t\t\t\tstart,\n\t\t\t\tlast,\n\t\t\t\tlength,\n\t\t\t});\n\t\t\tmessages.length = 0;\n\t\t}\n\t\tif (last + 1 !== from + length) {\n\t\t\tlogger.sendErrorEvent({\n\t\t\t\teventName: \"OpsFetchViolation\",\n\t\t\t\treason,\n\t\t\t\tfrom,\n\t\t\t\tstart,\n\t\t\t\tlast,\n\t\t\t\tlength,\n\t\t\t});\n\t\t\t// we can do better here by finding consecutive sub-block and return it\n\t\t\tmessages.length = 0;\n\t\t}\n\t}\n}\n"]}
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 \"@fluid-internal/client-utils\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ITelemetryLoggerExt } from \"@fluidframework/telemetry-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\nexport function validateMessages(\n\treason: string,\n\tmessages: ISequencedDocumentMessage[],\n\tfrom: number,\n\tlogger: ITelemetryLoggerExt,\n\tstrict: boolean = true,\n) {\n\tif (messages.length !== 0) {\n\t\tconst start = messages[0].sequenceNumber;\n\t\tconst length = messages.length;\n\t\tconst last = messages[length - 1].sequenceNumber;\n\t\tif (last + 1 !== from + length) {\n\t\t\t// If not strict, then return the first consecutive sub-block. If strict or start\n\t\t\t// seq number is not what we expected, then return no ops.\n\t\t\tif (strict || from !== start) {\n\t\t\t\tmessages.length = 0;\n\t\t\t} else {\n\t\t\t\tlet validOpsCount = 1;\n\t\t\t\twhile (\n\t\t\t\t\tvalidOpsCount < messages.length &&\n\t\t\t\t\tmessages[validOpsCount].sequenceNumber ===\n\t\t\t\t\t\tmessages[validOpsCount - 1].sequenceNumber + 1\n\t\t\t\t) {\n\t\t\t\t\tvalidOpsCount++;\n\t\t\t\t}\n\t\t\t\tmessages.length = validOpsCount;\n\t\t\t}\n\t\t\tlogger.sendErrorEvent({\n\t\t\t\teventName: \"OpsFetchViolation\",\n\t\t\t\treason,\n\t\t\t\tfrom,\n\t\t\t\tstart,\n\t\t\t\tlast,\n\t\t\t\tlength,\n\t\t\t\tdetails: JSON.stringify({\n\t\t\t\t\tvalidLength: messages.length,\n\t\t\t\t\tlastValidOpSeqNumber:\n\t\t\t\t\t\tmessages.length > 0\n\t\t\t\t\t\t\t? messages[messages.length - 1].sequenceNumber\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tstrict,\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\t}\n}\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.5.2.0.169897";
8
+ export declare const pkgVersion = "2.0.0-dev.6.4.0.191258";
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.5.2.0.169897";
8
+ export const pkgVersion = "2.0.0-dev.6.4.0.191258";
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.5.2.0.169897\";\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.6.4.0.191258\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/driver-base",
3
- "version": "2.0.0-dev.5.2.0.169897",
3
+ "version": "2.0.0-dev.6.4.0.191258",
4
4
  "description": "Shared driver code for Fluid driver implementations",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -14,30 +14,66 @@
14
14
  "main": "dist/index.js",
15
15
  "module": "lib/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "c8": {
18
+ "all": true,
19
+ "cache-dir": "nyc/.cache",
20
+ "exclude": [
21
+ "src/test/**/*.ts",
22
+ "dist/test/**/*.js"
23
+ ],
24
+ "exclude-after-remap": false,
25
+ "include": [
26
+ "src/**/*.ts",
27
+ "dist/**/*.js"
28
+ ],
29
+ "report-dir": "nyc/report",
30
+ "reporter": [
31
+ "cobertura",
32
+ "html",
33
+ "text"
34
+ ],
35
+ "temp-directory": "nyc/.nyc_output"
36
+ },
17
37
  "dependencies": {
18
- "@fluidframework/common-definitions": "^0.20.1",
19
- "@fluidframework/common-utils": "^1.1.1",
20
- "@fluidframework/driver-definitions": "2.0.0-dev.5.2.0.169897",
21
- "@fluidframework/driver-utils": "2.0.0-dev.5.2.0.169897",
38
+ "@fluid-internal/client-utils": "2.0.0-dev.6.4.0.191258",
39
+ "@fluidframework/core-interfaces": "2.0.0-dev.6.4.0.191258",
40
+ "@fluidframework/core-utils": "2.0.0-dev.6.4.0.191258",
41
+ "@fluidframework/driver-definitions": "2.0.0-dev.6.4.0.191258",
42
+ "@fluidframework/driver-utils": "2.0.0-dev.6.4.0.191258",
22
43
  "@fluidframework/protocol-definitions": "^1.1.0",
23
- "@fluidframework/telemetry-utils": "2.0.0-dev.5.2.0.169897"
44
+ "@fluidframework/telemetry-utils": "2.0.0-dev.6.4.0.191258"
24
45
  },
25
46
  "devDependencies": {
26
- "@fluid-tools/build-cli": "^0.20.0-169245",
27
- "@fluidframework/build-common": "^1.2.0",
28
- "@fluidframework/build-tools": "^0.20.0-169245",
29
- "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.5.0.0",
30
- "@fluidframework/eslint-config-fluid": "^2.0.0",
47
+ "@fluid-tools/build-cli": "^0.22.0",
48
+ "@fluidframework/build-common": "^2.0.0",
49
+ "@fluidframework/build-tools": "^0.22.0",
50
+ "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.6.3.0",
51
+ "@fluidframework/eslint-config-fluid": "^2.1.0",
52
+ "@fluidframework/mocha-test-setup": "2.0.0-dev.6.4.0.191258",
31
53
  "@microsoft/api-extractor": "^7.34.4",
32
- "@types/node": "^14.18.38",
33
- "concurrently": "^7.6.0",
54
+ "@types/mocha": "^9.1.1",
55
+ "@types/node": "^16.18.38",
56
+ "c8": "^7.7.1",
34
57
  "copyfiles": "^2.4.1",
58
+ "cross-env": "^7.0.3",
35
59
  "eslint": "~8.6.0",
60
+ "mocha": "^10.2.0",
61
+ "mocha-json-output-reporter": "^2.0.1",
62
+ "mocha-multi-reporters": "^1.5.1",
63
+ "moment": "^2.21.0",
36
64
  "prettier": "~2.6.2",
37
65
  "rimraf": "^4.4.0",
38
66
  "socket.io-client": "^4.6.1",
39
67
  "typescript": "~4.5.5"
40
68
  },
69
+ "fluidBuild": {
70
+ "tasks": {
71
+ "tsc": [
72
+ "...",
73
+ "typetests:gen"
74
+ ]
75
+ }
76
+ },
41
77
  "typeValidation": {
42
78
  "broken": {}
43
79
  },
@@ -48,8 +84,9 @@
48
84
  "build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
49
85
  "build:esnext": "tsc --project ./tsconfig.esnext.json",
50
86
  "build:genver": "gen-version",
87
+ "build:test": "tsc --project ./src/test/tsconfig.json",
51
88
  "ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
52
- "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
89
+ "clean": "rimraf --glob 'dist' 'lib' '*.tsbuildinfo' '*.build.log' '_api-extractor-temp' 'nyc'",
53
90
  "eslint": "eslint --format stylish src",
54
91
  "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
55
92
  "format": "npm run prettier:fix",
@@ -57,8 +94,12 @@
57
94
  "lint:fix": "npm run prettier:fix && npm run eslint:fix",
58
95
  "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
59
96
  "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
97
+ "test": "npm run test:mocha",
98
+ "test:coverage": "c8 npm test",
99
+ "test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup",
100
+ "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
60
101
  "tsc": "tsc",
61
102
  "typetests:gen": "fluid-type-test-generator",
62
- "typetests:prepare": "flub generate typetests --prepare --dir . --pin"
103
+ "typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
63
104
  }
64
105
  }
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert } from "@fluidframework/common-utils";
6
+ import { assert } from "@fluidframework/core-utils";
7
7
  import {
8
8
  IAnyDriverError,
9
9
  IDocumentDeltaConnection,
@@ -22,16 +22,15 @@ import {
22
22
  ITokenClaims,
23
23
  ScopeType,
24
24
  } from "@fluidframework/protocol-definitions";
25
- import { IDisposable, ITelemetryProperties } from "@fluidframework/common-definitions";
25
+ import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
26
26
  import {
27
27
  ITelemetryLoggerExt,
28
- ChildLogger,
29
28
  extractLogSafeErrorProperties,
30
29
  getCircularReplacer,
31
- loggerToMonitoringContext,
32
30
  MonitoringContext,
33
31
  EventEmitterWithErrorHandling,
34
32
  normalizeError,
33
+ createChildMonitoringContext,
35
34
  } from "@fluidframework/telemetry-utils";
36
35
  import type { Socket } from "socket.io-client";
37
36
  // For now, this package is versioned and released in unison with the specific drivers
@@ -73,6 +72,8 @@ export class DocumentDeltaConnection
73
72
 
74
73
  private _details: IConnected | undefined;
75
74
 
75
+ private trackLatencyTimeout: number | undefined;
76
+
76
77
  // Listeners only needed while the connection is in progress
77
78
  private readonly connectionListeners: Map<string, (...args: any[]) => void> = new Map();
78
79
  // Listeners used throughout the lifetime of the DocumentDeltaConnection
@@ -135,9 +136,9 @@ export class DocumentDeltaConnection
135
136
  );
136
137
  });
137
138
 
138
- this.mc = loggerToMonitoringContext(ChildLogger.create(logger, "DeltaConnection"));
139
+ this.mc = createChildMonitoringContext({ logger, namespace: "DeltaConnection" });
139
140
 
140
- this.on("newListener", (event, listener) => {
141
+ this.on("newListener", (event, _listener) => {
141
142
  assert(!this.disposed, 0x20a /* "register for event on disposed object" */);
142
143
 
143
144
  // Some events are already forwarded - see this.addTrackedListener() calls in initialize().
@@ -160,9 +161,29 @@ export class DocumentDeltaConnection
160
161
  0x20b /* "mismatch" */,
161
162
  );
162
163
  if (!this.trackedListeners.has(event)) {
163
- this.addTrackedListener(event, (...args: any[]) => {
164
- this.emit(event, ...args);
165
- });
164
+ if (event === "pong") {
165
+ // Empty callback for tracking purposes in this class
166
+ this.trackedListeners.set("pong", () => {});
167
+
168
+ const sendPingLoop = () => {
169
+ const start = Date.now();
170
+
171
+ this.socket.volatile?.emit("ping", () => {
172
+ this.emit("pong", Date.now() - start);
173
+
174
+ // Schedule another ping event in 1 minute
175
+ this.trackLatencyTimeout = setTimeout(() => {
176
+ sendPingLoop();
177
+ }, 1000 * 60);
178
+ });
179
+ };
180
+
181
+ sendPingLoop();
182
+ } else {
183
+ this.addTrackedListener(event, (...args: any[]) => {
184
+ this.emit(event, ...args);
185
+ });
186
+ }
166
187
  }
167
188
  });
168
189
  }
@@ -361,6 +382,11 @@ export class DocumentDeltaConnection
361
382
  return;
362
383
  }
363
384
 
385
+ if (this.trackLatencyTimeout !== undefined) {
386
+ clearTimeout(this.trackLatencyTimeout);
387
+ this.trackLatencyTimeout = undefined;
388
+ }
389
+
364
390
  // We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by
365
391
  // DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is
366
392
  // important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { performance } from "@fluidframework/common-utils";
6
+ import { performance } from "@fluid-internal/client-utils";
7
7
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
8
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
9
9
 
@@ -111,23 +111,28 @@ export function validateMessages(
111
111
  messages: ISequencedDocumentMessage[],
112
112
  from: number,
113
113
  logger: ITelemetryLoggerExt,
114
+ strict: boolean = true,
114
115
  ) {
115
116
  if (messages.length !== 0) {
116
117
  const start = messages[0].sequenceNumber;
117
118
  const length = messages.length;
118
119
  const last = messages[length - 1].sequenceNumber;
119
- if (start !== from) {
120
- logger.sendErrorEvent({
121
- eventName: "OpsFetchViolation",
122
- reason,
123
- from,
124
- start,
125
- last,
126
- length,
127
- });
128
- messages.length = 0;
129
- }
130
120
  if (last + 1 !== from + length) {
121
+ // If not strict, then return the first consecutive sub-block. If strict or start
122
+ // seq number is not what we expected, then return no ops.
123
+ if (strict || from !== start) {
124
+ messages.length = 0;
125
+ } else {
126
+ let validOpsCount = 1;
127
+ while (
128
+ validOpsCount < messages.length &&
129
+ messages[validOpsCount].sequenceNumber ===
130
+ messages[validOpsCount - 1].sequenceNumber + 1
131
+ ) {
132
+ validOpsCount++;
133
+ }
134
+ messages.length = validOpsCount;
135
+ }
131
136
  logger.sendErrorEvent({
132
137
  eventName: "OpsFetchViolation",
133
138
  reason,
@@ -135,9 +140,15 @@ export function validateMessages(
135
140
  start,
136
141
  last,
137
142
  length,
143
+ details: JSON.stringify({
144
+ validLength: messages.length,
145
+ lastValidOpSeqNumber:
146
+ messages.length > 0
147
+ ? messages[messages.length - 1].sequenceNumber
148
+ : undefined,
149
+ strict,
150
+ }),
138
151
  });
139
- // we can do better here by finding consecutive sub-block and return it
140
- messages.length = 0;
141
152
  }
142
153
  }
143
154
  }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/driver-base";
9
- export const pkgVersion = "2.0.0-dev.5.2.0.169897";
9
+ export const pkgVersion = "2.0.0-dev.6.4.0.191258";
package/tsconfig.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "extends": "@fluidframework/build-common/ts-common-config.json",
3
- "exclude": ["dist", "node_modules"],
3
+ "exclude": ["src/test/**/*"],
4
4
  "compilerOptions": {
5
5
  "rootDir": "./src",
6
6
  "outDir": "./dist",
7
+ "composite": true,
7
8
  },
8
9
  "include": ["src/**/*"],
9
10
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=validateDriverBasePrevious.generated.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"validateDriverBasePrevious.generated.d.ts","sourceRoot":"","sources":["../../../src/test/types/validateDriverBasePrevious.generated.ts"],"names":[],"mappings":""}
@@ -1,8 +0,0 @@
1
- use_current_ClassDeclaration_DocumentDeltaConnection(get_old_ClassDeclaration_DocumentDeltaConnection());
2
- use_old_ClassDeclaration_DocumentDeltaConnection(get_current_ClassDeclaration_DocumentDeltaConnection());
3
- use_current_FunctionDeclaration_getW3CData(get_old_FunctionDeclaration_getW3CData());
4
- use_old_FunctionDeclaration_getW3CData(get_current_FunctionDeclaration_getW3CData());
5
- use_current_FunctionDeclaration_promiseRaceWithWinner(get_old_FunctionDeclaration_promiseRaceWithWinner());
6
- use_old_FunctionDeclaration_promiseRaceWithWinner(get_current_FunctionDeclaration_promiseRaceWithWinner());
7
- export {};
8
- //# sourceMappingURL=validateDriverBasePrevious.generated.js.map
@@ -1 +0,0 @@
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;AAW5D,0CAA0C,CACtC,sCAAsC,EAAE,CAAC,CAAC;AAW9C,sCAAsC,CAClC,0CAA0C,EAAE,CAAC,CAAC;AAWlD,qDAAqD,CACjD,iDAAiD,EAAE,CAAC,CAAC;AAWzD,iDAAiD,CAC7C,qDAAqD,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\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* \"FunctionDeclaration_getW3CData\": {\"forwardCompat\": false}\n*/\ndeclare function get_old_FunctionDeclaration_getW3CData():\n TypeOnly<typeof old.getW3CData>;\ndeclare function use_current_FunctionDeclaration_getW3CData(\n use: TypeOnly<typeof current.getW3CData>);\nuse_current_FunctionDeclaration_getW3CData(\n get_old_FunctionDeclaration_getW3CData());\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* \"FunctionDeclaration_getW3CData\": {\"backCompat\": false}\n*/\ndeclare function get_current_FunctionDeclaration_getW3CData():\n TypeOnly<typeof current.getW3CData>;\ndeclare function use_old_FunctionDeclaration_getW3CData(\n use: TypeOnly<typeof old.getW3CData>);\nuse_old_FunctionDeclaration_getW3CData(\n get_current_FunctionDeclaration_getW3CData());\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* \"FunctionDeclaration_promiseRaceWithWinner\": {\"forwardCompat\": false}\n*/\ndeclare function get_old_FunctionDeclaration_promiseRaceWithWinner():\n TypeOnly<typeof old.promiseRaceWithWinner>;\ndeclare function use_current_FunctionDeclaration_promiseRaceWithWinner(\n use: TypeOnly<typeof current.promiseRaceWithWinner>);\nuse_current_FunctionDeclaration_promiseRaceWithWinner(\n get_old_FunctionDeclaration_promiseRaceWithWinner());\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* \"FunctionDeclaration_promiseRaceWithWinner\": {\"backCompat\": false}\n*/\ndeclare function get_current_FunctionDeclaration_promiseRaceWithWinner():\n TypeOnly<typeof current.promiseRaceWithWinner>;\ndeclare function use_old_FunctionDeclaration_promiseRaceWithWinner(\n use: TypeOnly<typeof old.promiseRaceWithWinner>);\nuse_old_FunctionDeclaration_promiseRaceWithWinner(\n get_current_FunctionDeclaration_promiseRaceWithWinner());\n"]}