@fluidframework/driver-base 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.3.2.178189

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 (44) hide show
  1. package/.eslintrc.js +3 -0
  2. package/.mocharc.js +12 -0
  3. package/CHANGELOG.md +20 -0
  4. package/dist/documentDeltaConnection.d.ts +7 -4
  5. package/dist/documentDeltaConnection.d.ts.map +1 -1
  6. package/dist/documentDeltaConnection.js +51 -59
  7. package/dist/documentDeltaConnection.js.map +1 -1
  8. package/dist/driverUtils.d.ts +3 -0
  9. package/dist/driverUtils.d.ts.map +1 -1
  10. package/dist/driverUtils.js +40 -1
  11. package/dist/driverUtils.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/packageVersion.d.ts +1 -1
  17. package/dist/packageVersion.js +1 -1
  18. package/dist/packageVersion.js.map +1 -1
  19. package/dist/tsdoc-metadata.json +11 -0
  20. package/lib/documentDeltaConnection.d.ts +7 -4
  21. package/lib/documentDeltaConnection.d.ts.map +1 -1
  22. package/lib/documentDeltaConnection.js +51 -59
  23. package/lib/documentDeltaConnection.js.map +1 -1
  24. package/lib/driverUtils.d.ts +3 -0
  25. package/lib/driverUtils.d.ts.map +1 -1
  26. package/lib/driverUtils.js +38 -0
  27. package/lib/driverUtils.js.map +1 -1
  28. package/lib/index.d.ts +1 -1
  29. package/lib/index.d.ts.map +1 -1
  30. package/lib/index.js +1 -1
  31. package/lib/index.js.map +1 -1
  32. package/lib/packageVersion.d.ts +1 -1
  33. package/lib/packageVersion.js +1 -1
  34. package/lib/packageVersion.js.map +1 -1
  35. package/package.json +56 -16
  36. package/src/documentDeltaConnection.ts +83 -73
  37. package/src/driverUtils.ts +49 -0
  38. package/src/index.ts +1 -1
  39. package/src/packageVersion.ts +1 -1
  40. package/tsconfig.json +2 -1
  41. package/lib/test/types/validateDriverBasePrevious.generated.d.ts +0 -2
  42. package/lib/test/types/validateDriverBasePrevious.generated.d.ts.map +0 -1
  43. package/lib/test/types/validateDriverBasePrevious.generated.js +0 -6
  44. package/lib/test/types/validateDriverBasePrevious.generated.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"driverUtils.js","sourceRoot":"","sources":["../src/driverUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3D;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,aAAqB;IAC5D,mFAAmF;IACnF,2EAA2E;IAC3E,8FAA8F;IAC9F,wGAAwG;IACxG,oFAAoF;IACpF,gGAAgG;IAChG,mGAAmG;IACnG,mGAAmG;IACnG,2FAA2F;IAC3F,oGAAoG;IACpG,mGAAmG;IACnG,4EAA4E;IAC5E,+FAA+F;;IAE/F,gFAAgF;IAChF,IAAI,aAAiC,CAAC,CAAC,sCAAsC;IAC7E,6EAA6E;IAC7E,IAAI,YAAgC,CAAC,CAAC,8BAA8B;IACpE,2EAA2E;IAC3E,IAAI,gBAAoC,CAAC,CAAC,6BAA6B;IACvE,mGAAmG;IACnG,oCAAoC;IACpC,IAAI,oBAAwC,CAAC,CAAC,sCAAsC;IACpF,iEAAiE;IACjE,IAAI,mBAAuC,CAAC,CAAC,6BAA6B;IAC1E,sEAAsE;IACtE,iDAAiD;IACjD,IAAI,2BAA+C,CAAC,CAAC,4BAA4B;IACjF,oFAAoF;IACpF,wFAAwF;IACxF,IAAI,yBAA6C,CAAC,CAAC,6BAA6B;IAChF,IAAI,YAAgC,CAAC,CAAC,mCAAmC;IAEzE,mEAAmE;IACnE,MAAM,UAAU,GAAG,MAAA,MAAA,WAAW,CAAC,gBAAgB,+CAA5B,WAAW,EAAoB,UAAU,CAAC,mCAAI,EAAE,CAAC;IACpE,sFAAsF;IACtF,KAAK,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAA8B,CAAC;QAC9D,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjD,MAAM,sBAAsB,GAAG,UAAU,CAAC,aAAa,CAAC;QACxD,IACC,sBAAsB,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC;YACzD,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAC1B;YACD,YAAY,GAAG,UAAU,CAAC,WAAW,GAAG,UAAU,CAAC,aAAa,CAAC;YACjE,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC;YACrC,aAAa,GAAG,UAAU,CAAC,eAAe,GAAG,UAAU,CAAC,iBAAiB,CAAC;YAC1E,gBAAgB,GAAG,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,YAAY,CAAC;YACnE,oBAAoB;gBACnB,UAAU,CAAC,qBAAqB,GAAG,CAAC;oBACnC,CAAC,CAAC,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,qBAAqB;oBAC1D,CAAC,CAAC,CAAC,CAAC;YACN,mBAAmB;gBAClB,UAAU,CAAC,aAAa,GAAG,CAAC;oBAC3B,CAAC,CAAC,UAAU,CAAC,WAAW,GAAG,UAAU,CAAC,aAAa;oBACnD,CAAC,CAAC,SAAS,CAAC;YACd,2BAA2B;gBAC1B,UAAU,CAAC,UAAU,GAAG,CAAC;oBACxB,CAAC,CAAC,UAAU,CAAC,WAAW,GAAG,UAAU,CAAC,UAAU;oBAChD,CAAC,CAAC,SAAS,CAAC;YACd,yBAAyB;gBACxB,UAAU,CAAC,YAAY,GAAG,CAAC;oBAC1B,CAAC,CAAC,UAAU,CAAC,WAAW,GAAG,UAAU,CAAC,YAAY;oBAClD,CAAC,CAAC,SAAS,CAAC;YACd,MAAM;SACN;KACD;IACD,OAAO;QACN,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,gBAAgB;QAChB,oBAAoB;QACpB,mBAAmB;QACnB,2BAA2B;QAC3B,yBAAyB;KACzB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,QAAsB;IAEtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;YAC7B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC","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"]}
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\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"]}
package/lib/index.d.ts CHANGED
@@ -3,5 +3,5 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
6
- export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
6
+ export { getW3CData, promiseRaceWithWinner, validateMessages } from "./driverUtils";
7
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;AACpE,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,eAAe,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,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
package/lib/index.js CHANGED
@@ -3,5 +3,5 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
6
- export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
6
+ export { getW3CData, promiseRaceWithWinner, validateMessages } from "./driverUtils";
7
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;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"]}
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,gBAAgB,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, validateMessages } 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.4.4.0.162574";
8
+ export declare const pkgVersion = "2.0.0-dev.5.3.2.178189";
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.4.4.0.162574";
8
+ export const pkgVersion = "2.0.0-dev.5.3.2.178189";
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.4.4.0.162574\";\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.5.3.2.178189\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/driver-base",
3
- "version": "2.0.0-dev.4.4.0.162574",
3
+ "version": "2.0.0-dev.5.3.2.178189",
4
4
  "description": "Shared driver code for Fluid driver implementations",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -14,42 +14,77 @@
14
14
  "main": "dist/index.js",
15
15
  "module": "lib/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "nyc": {
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
38
  "@fluidframework/common-utils": "^1.1.1",
20
- "@fluidframework/driver-definitions": "2.0.0-dev.4.4.0.162574",
21
- "@fluidframework/driver-utils": "2.0.0-dev.4.4.0.162574",
39
+ "@fluidframework/core-interfaces": "2.0.0-dev.5.3.2.178189",
40
+ "@fluidframework/driver-definitions": "2.0.0-dev.5.3.2.178189",
41
+ "@fluidframework/driver-utils": "2.0.0-dev.5.3.2.178189",
22
42
  "@fluidframework/protocol-definitions": "^1.1.0",
23
- "@fluidframework/telemetry-utils": "2.0.0-dev.4.4.0.162574"
43
+ "@fluidframework/telemetry-utils": "2.0.0-dev.5.3.2.178189"
24
44
  },
25
45
  "devDependencies": {
26
- "@fluid-tools/build-cli": "^0.17.0",
27
- "@fluidframework/build-common": "^1.1.0",
28
- "@fluidframework/build-tools": "^0.17.0",
29
- "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.4.1.0",
46
+ "@fluid-tools/build-cli": "^0.21.0",
47
+ "@fluidframework/build-common": "^1.2.0",
48
+ "@fluidframework/build-tools": "^0.21.0",
49
+ "@fluidframework/driver-base-previous": "npm:@fluidframework/driver-base@2.0.0-internal.5.2.0",
30
50
  "@fluidframework/eslint-config-fluid": "^2.0.0",
51
+ "@fluidframework/mocha-test-setup": "2.0.0-dev.5.3.2.178189",
31
52
  "@microsoft/api-extractor": "^7.34.4",
53
+ "@types/mocha": "^9.1.1",
32
54
  "@types/node": "^14.18.38",
33
55
  "concurrently": "^7.6.0",
34
56
  "copyfiles": "^2.4.1",
57
+ "cross-env": "^7.0.3",
35
58
  "eslint": "~8.6.0",
59
+ "mocha": "^10.2.0",
60
+ "mocha-json-output-reporter": "^2.0.1",
61
+ "mocha-multi-reporters": "^1.5.1",
62
+ "moment": "^2.21.0",
63
+ "nyc": "^15.1.0",
36
64
  "prettier": "~2.6.2",
37
65
  "rimraf": "^4.4.0",
38
- "socket.io-client": "^4.4.1",
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
  },
44
80
  "scripts": {
45
- "build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
46
- "build:commonjs": "npm run typetests:gen && npm run tsc",
47
- "build:compile": "concurrently npm:build:commonjs npm:build:esnext",
81
+ "build": "fluid-build . --task build",
82
+ "build:commonjs": "fluid-build . --task commonjs",
83
+ "build:compile": "fluid-build . --task compile",
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
- "build:full": "npm run build",
51
- "build:full:compile": "npm run build:compile",
52
86
  "build:genver": "gen-version",
87
+ "build:test": "tsc --project ./src/test/tsconfig.json",
53
88
  "ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
54
89
  "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
55
90
  "eslint": "eslint --format stylish src",
@@ -59,8 +94,13 @@
59
94
  "lint:fix": "npm run prettier:fix && npm run eslint:fix",
60
95
  "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
61
96
  "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
97
+ "test": "npm run test:mocha",
98
+ "test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
99
+ "test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
100
+ "test:mocha:multireport": "cross-env FLUID_TEST_MULTIREPORT=1 npm run test:mocha",
101
+ "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
62
102
  "tsc": "tsc",
63
103
  "typetests:gen": "fluid-type-test-generator",
64
- "typetests:prepare": "flub generate typetests --prepare --dir . --pin"
104
+ "typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
65
105
  }
66
106
  }
@@ -22,8 +22,9 @@ import {
22
22
  ITokenClaims,
23
23
  ScopeType,
24
24
  } from "@fluidframework/protocol-definitions";
25
- import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
25
+ import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
26
26
  import {
27
+ ITelemetryLoggerExt,
27
28
  ChildLogger,
28
29
  extractLogSafeErrorProperties,
29
30
  getCircularReplacer,
@@ -72,6 +73,8 @@ export class DocumentDeltaConnection
72
73
 
73
74
  private _details: IConnected | undefined;
74
75
 
76
+ private trackLatencyTimeout: number | undefined;
77
+
75
78
  // Listeners only needed while the connection is in progress
76
79
  private readonly connectionListeners: Map<string, (...args: any[]) => void> = new Map();
77
80
  // Listeners used throughout the lifetime of the DocumentDeltaConnection
@@ -82,21 +85,10 @@ export class DocumentDeltaConnection
82
85
  }
83
86
 
84
87
  public get disposed() {
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
+ assert(
89
+ this._disposed || this.socket.connected,
90
+ 0x244 /* "Socket is closed, but connection is not!" */,
91
+ );
100
92
  return this._disposed;
101
93
  }
102
94
 
@@ -110,7 +102,7 @@ export class DocumentDeltaConnection
110
102
  /**
111
103
  * @deprecated Implementors should manage their own logger or monitoring context
112
104
  */
113
- protected get logger(): ITelemetryLogger {
105
+ protected get logger(): ITelemetryLoggerExt {
114
106
  return this.mc.logger;
115
107
  }
116
108
 
@@ -130,11 +122,12 @@ export class DocumentDeltaConnection
130
122
  protected constructor(
131
123
  protected readonly socket: Socket,
132
124
  public documentId: string,
133
- logger: ITelemetryLogger,
125
+ logger: ITelemetryLoggerExt,
134
126
  private readonly enableLongPollingDowngrades: boolean = false,
135
127
  protected readonly connectionId?: string,
136
128
  ) {
137
129
  super((name, error) => {
130
+ this.addPropsToError(error);
138
131
  logger.sendErrorEvent(
139
132
  {
140
133
  eventName: "DeltaConnection:EventException",
@@ -146,7 +139,7 @@ export class DocumentDeltaConnection
146
139
 
147
140
  this.mc = loggerToMonitoringContext(ChildLogger.create(logger, "DeltaConnection"));
148
141
 
149
- this.on("newListener", (event, listener) => {
142
+ this.on("newListener", (event, _listener) => {
150
143
  assert(!this.disposed, 0x20a /* "register for event on disposed object" */);
151
144
 
152
145
  // Some events are already forwarded - see this.addTrackedListener() calls in initialize().
@@ -169,9 +162,29 @@ export class DocumentDeltaConnection
169
162
  0x20b /* "mismatch" */,
170
163
  );
171
164
  if (!this.trackedListeners.has(event)) {
172
- this.addTrackedListener(event, (...args: any[]) => {
173
- this.emit(event, ...args);
174
- });
165
+ if (event === "pong") {
166
+ // Empty callback for tracking purposes in this class
167
+ this.trackedListeners.set("pong", () => {});
168
+
169
+ const sendPingLoop = () => {
170
+ const start = Date.now();
171
+
172
+ this.socket.volatile?.emit("ping", () => {
173
+ this.emit("pong", Date.now() - start);
174
+
175
+ // Schedule another ping event in 1 minute
176
+ this.trackLatencyTimeout = setTimeout(() => {
177
+ sendPingLoop();
178
+ }, 1000 * 60);
179
+ });
180
+ };
181
+
182
+ sendPingLoop();
183
+ } else {
184
+ this.addTrackedListener(event, (...args: any[]) => {
185
+ this.emit(event, ...args);
186
+ });
187
+ }
175
188
  }
176
189
  });
177
190
  }
@@ -236,18 +249,7 @@ export class DocumentDeltaConnection
236
249
  }
237
250
 
238
251
  private checkNotDisposed() {
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
- }
252
+ assert(!this.disposed, 0x20c /* "connection disposed" */);
251
253
  }
252
254
 
253
255
  /**
@@ -341,17 +343,6 @@ export class DocumentDeltaConnection
341
343
  private closeSocket(error: IAnyDriverError) {
342
344
  if (this._disposed) {
343
345
  // This would be rare situation due to complexity around socket emitting events.
344
- this.logger.sendTelemetryEvent(
345
- {
346
- eventName: "SocketCloseOnDisposedConnection",
347
- driverVersion,
348
- details: JSON.stringify({
349
- ...this.getConnectionDetailsProps(),
350
- trackedListenerCount: this.trackedListeners.size,
351
- }),
352
- },
353
- error,
354
- );
355
346
  return;
356
347
  }
357
348
  this.closeSocketCore(error);
@@ -392,6 +383,11 @@ export class DocumentDeltaConnection
392
383
  return;
393
384
  }
394
385
 
386
+ if (this.trackLatencyTimeout !== undefined) {
387
+ clearTimeout(this.trackLatencyTimeout);
388
+ this.trackLatencyTimeout = undefined;
389
+ }
390
+
395
391
  // We set the disposed flag as a part of the contract for overriding the disconnect method. This is used by
396
392
  // DocumentDeltaConnection to determine if emitting messages (ops) on the socket is allowed, which is
397
393
  // important since OdspDocumentDeltaConnection reuses the socket rather than truly disconnecting it. Note that
@@ -409,14 +405,6 @@ export class DocumentDeltaConnection
409
405
 
410
406
  // Let user of connection object know about disconnect.
411
407
  this.emit("disconnect", err);
412
- this.logger.sendTelemetryEvent({
413
- eventName: "AfterDisconnectEvent",
414
- driverVersion,
415
- details: JSON.stringify({
416
- ...this.getConnectionDetailsProps(),
417
- disconnectListenerCount: this.listenerCount("disconnect"),
418
- }),
419
- });
420
408
  }
421
409
 
422
410
  /**
@@ -591,9 +579,14 @@ export class DocumentDeltaConnection
591
579
  // Socket can be disconnected while waiting for Fluid protocol messages
592
580
  // (connect_document_error / connect_document_success), as well as before DeltaManager
593
581
  // had a chance to register its handlers.
594
- this.addTrackedListener("disconnect", (reason) => {
595
- const err = this.createErrorObject("disconnect", reason);
596
- failAndCloseSocket(err);
582
+ this.addTrackedListener("disconnect", (reason, details) => {
583
+ failAndCloseSocket(
584
+ this.createErrorObjectWithProps("disconnect", reason, {
585
+ socketErrorType: details?.context?.type,
586
+ // https://www.rfc-editor.org/rfc/rfc6455#section-7.4
587
+ socketCode: details?.context?.code,
588
+ }),
589
+ );
597
590
  });
598
591
 
599
592
  this.addTrackedListener("error", (error) => {
@@ -705,25 +698,44 @@ export class DocumentDeltaConnection
705
698
  this.connectionListeners.clear();
706
699
  }
707
700
 
701
+ private getErrorMessage(error?: any): string {
702
+ if (error?.type !== "TransportError") {
703
+ return extractLogSafeErrorProperties(error, true).message;
704
+ }
705
+ // JSON.stringify drops Error.message
706
+ const messagePrefix = error?.message !== undefined ? `${error.message}: ` : "";
707
+
708
+ // Websocket errors reported by engine.io-client.
709
+ // They are Error objects with description containing WS error and description = "TransportError"
710
+ // Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
711
+ return `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
712
+ }
713
+
714
+ private createErrorObjectWithProps(
715
+ handler: string,
716
+ error?: any,
717
+ props?: ITelemetryProperties,
718
+ canRetry = true,
719
+ ): IAnyDriverError {
720
+ return createGenericNetworkError(
721
+ `socket.io (${handler}): ${this.getErrorMessage(error)}`,
722
+ { canRetry },
723
+ {
724
+ ...props,
725
+ driverVersion,
726
+ details: JSON.stringify({
727
+ ...this.getConnectionDetailsProps(),
728
+ }),
729
+ },
730
+ );
731
+ }
732
+
708
733
  /**
709
734
  * Error raising for socket.io issues
710
735
  */
711
736
  protected createErrorObject(handler: string, error?: any, canRetry = true): IAnyDriverError {
712
- let message: string;
713
- if (error?.type === "TransportError") {
714
- // JSON.stringify drops Error.message
715
- const messagePrefix = error?.message !== undefined ? `${error.message}: ` : "";
716
-
717
- // Websocket errors reported by engine.io-client.
718
- // They are Error objects with description containing WS error and description = "TransportError"
719
- // Please see https://github.com/socketio/engine.io-client/blob/7245b80/lib/transport.ts#L44,
720
- message = `${messagePrefix}${JSON.stringify(error, getCircularReplacer())}`;
721
- } else {
722
- message = extractLogSafeErrorProperties(error, true).message;
723
- }
724
-
725
- const errorObj = createGenericNetworkError(
726
- `socket.io (${handler}): ${message}`,
737
+ return createGenericNetworkError(
738
+ `socket.io (${handler}): ${this.getErrorMessage(error)}`,
727
739
  { canRetry },
728
740
  {
729
741
  driverVersion,
@@ -732,7 +744,5 @@ export class DocumentDeltaConnection
732
744
  }),
733
745
  },
734
746
  );
735
-
736
- return errorObj;
737
747
  }
738
748
  }
@@ -4,6 +4,8 @@
4
4
  */
5
5
 
6
6
  import { performance } from "@fluidframework/common-utils";
7
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
8
+ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
7
9
 
8
10
  /**
9
11
  * Extract and return the w3c data.
@@ -103,3 +105,50 @@ export async function promiseRaceWithWinner<T>(
103
105
  });
104
106
  });
105
107
  }
108
+
109
+ export function validateMessages(
110
+ reason: string,
111
+ messages: ISequencedDocumentMessage[],
112
+ from: number,
113
+ logger: ITelemetryLoggerExt,
114
+ strict: boolean = true,
115
+ ) {
116
+ if (messages.length !== 0) {
117
+ const start = messages[0].sequenceNumber;
118
+ const length = messages.length;
119
+ const last = messages[length - 1].sequenceNumber;
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
+ }
136
+ logger.sendErrorEvent({
137
+ eventName: "OpsFetchViolation",
138
+ reason,
139
+ from,
140
+ start,
141
+ last,
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
+ }),
151
+ });
152
+ }
153
+ }
154
+ }
package/src/index.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  */
5
5
 
6
6
  export { DocumentDeltaConnection } from "./documentDeltaConnection";
7
- export { getW3CData, promiseRaceWithWinner } from "./driverUtils";
7
+ export { getW3CData, promiseRaceWithWinner, validateMessages } 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.4.4.0.162574";
9
+ export const pkgVersion = "2.0.0-dev.5.3.2.178189";
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,6 +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
- export {};
6
- //# 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","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"]}