@fluidframework/odsp-driver 0.59.1000 → 0.59.2000-63294

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 (53) hide show
  1. package/dist/epochTracker.js.map +1 -1
  2. package/dist/fetchSnapshot.d.ts +10 -2
  3. package/dist/fetchSnapshot.d.ts.map +1 -1
  4. package/dist/fetchSnapshot.js +22 -10
  5. package/dist/fetchSnapshot.js.map +1 -1
  6. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  7. package/dist/odspDocumentService.d.ts.map +1 -1
  8. package/dist/odspDocumentService.js +1 -4
  9. package/dist/odspDocumentService.js.map +1 -1
  10. package/dist/odspDocumentStorageManager.d.ts +3 -1
  11. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  12. package/dist/odspDocumentStorageManager.js +3 -2
  13. package/dist/odspDocumentStorageManager.js.map +1 -1
  14. package/dist/packageVersion.d.ts +1 -1
  15. package/dist/packageVersion.d.ts.map +1 -1
  16. package/dist/packageVersion.js +1 -1
  17. package/dist/packageVersion.js.map +1 -1
  18. package/dist/prefetchLatestSnapshot.d.ts +5 -1
  19. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  20. package/dist/prefetchLatestSnapshot.js +5 -2
  21. package/dist/prefetchLatestSnapshot.js.map +1 -1
  22. package/dist/retryUtils.js.map +1 -1
  23. package/lib/epochTracker.js.map +1 -1
  24. package/lib/fetchSnapshot.d.ts +10 -2
  25. package/lib/fetchSnapshot.d.ts.map +1 -1
  26. package/lib/fetchSnapshot.js +21 -9
  27. package/lib/fetchSnapshot.js.map +1 -1
  28. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  29. package/lib/odspDocumentService.d.ts.map +1 -1
  30. package/lib/odspDocumentService.js +1 -4
  31. package/lib/odspDocumentService.js.map +1 -1
  32. package/lib/odspDocumentStorageManager.d.ts +3 -1
  33. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  34. package/lib/odspDocumentStorageManager.js +3 -2
  35. package/lib/odspDocumentStorageManager.js.map +1 -1
  36. package/lib/packageVersion.d.ts +1 -1
  37. package/lib/packageVersion.d.ts.map +1 -1
  38. package/lib/packageVersion.js +1 -1
  39. package/lib/packageVersion.js.map +1 -1
  40. package/lib/prefetchLatestSnapshot.d.ts +5 -1
  41. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  42. package/lib/prefetchLatestSnapshot.js +5 -2
  43. package/lib/prefetchLatestSnapshot.js.map +1 -1
  44. package/lib/retryUtils.js.map +1 -1
  45. package/package.json +17 -13
  46. package/src/epochTracker.ts +1 -1
  47. package/src/fetchSnapshot.ts +22 -8
  48. package/src/odspDocumentDeltaConnection.ts +1 -1
  49. package/src/odspDocumentService.ts +1 -2
  50. package/src/odspDocumentStorageManager.ts +4 -3
  51. package/src/packageVersion.ts +1 -1
  52. package/src/prefetchLatestSnapshot.ts +6 -2
  53. package/src/retryUtils.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAUtD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,8BAA8B,GACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAG5E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CACxC,WAAyB,EACzB,eAA4D,EAC5D,cAA+B,EAC/B,sCAA+C,EAC/C,MAA4B,EAC5B,wBAAsD,EACtD,oBAA8B,EAC9B,yBAAmC;IAEnC,MAAM,UAAU,GAAG,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAExD,MAAM,eAAe,GAAkB;QACnC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,MAAM,EAAE,eAAe,CAAC,MAAM;KACjC,CAAC;IACF,MAAM,mBAAmB,GAAG,8BAA8B,CACtD,UAAU,EACV,eAAe,EACf,eAAe,EACf,IAAI,CAAC,sBAAsB,CAC9B,CAAC;IAEF,MAAM,kBAAkB,GAAG,KAAK,EAC5B,oBAAsC,EACtC,YAAoB,EACpB,eAA6C,EAC7C,UAA4B,EAC9B,EAAE;QACA,OAAO,gBAAgB,CACnB,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,yBAAyB,EAAE,UAAU,CAAC,CAAC;IAChH,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,sBAAsB,CAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,MAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,EAAE,cAAwC,EAAE,EAAE;QAClE,MAAM,GAAG,cAAc,CAAC,GAAG,CACvB,WAAW,EACX,cAAc,CACjB,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC,cAAc,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO,gBAAgB,CAAC,cAAc,CAClC,UAAU,EACV,EAAE,SAAS,EAAE,wBAAwB,EAAE,EACvC,KAAK,IAAI,EAAE;QACP,MAAM,uBAAuB,CACrB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,sCAAsC,EACtC,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,oBAAoB,CACvB,CAAC;QACN,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC;QACb,OAAO,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { default as AbortController } from \"abort-controller\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IResolvedUrl } from \"@fluidframework/driver-definitions\";\nimport {\n IOdspResolvedUrl,\n IPersistedCache,\n ISnapshotOptions,\n OdspResourceTokenFetchOptions,\n TokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport {\n createCacheSnapshotKey,\n createOdspLogger,\n getOdspResolvedUrl,\n toInstrumentedOdspTokenFetcher,\n} from \"./odspUtils\";\nimport { downloadSnapshot, fetchSnapshotWithRedeem } from \"./fetchSnapshot\";\nimport { IVersionedValueWithEpoch } from \"./contracts\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * @returns - True if the snapshot is cached, false otherwise.\n */\nexport async function prefetchLatestSnapshot(\n resolvedUrl: IResolvedUrl,\n getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n persistedCache: IPersistedCache,\n forceAccessTokenViaAuthorizationHeader: boolean,\n logger: ITelemetryBaseLogger,\n hostSnapshotFetchOptions: ISnapshotOptions | undefined,\n enableRedeemFallback?: boolean,\n fetchBinarySnapshotFormat?: boolean,\n): Promise<boolean> {\n const odspLogger = createOdspLogger(ChildLogger.create(logger, \"PrefetchSnapshot\"));\n const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n const resolvedUrlData: IOdspUrlParts = {\n siteUrl: odspResolvedUrl.siteUrl,\n driveId: odspResolvedUrl.driveId,\n itemId: odspResolvedUrl.itemId,\n };\n const storageTokenFetcher = toInstrumentedOdspTokenFetcher(\n odspLogger,\n resolvedUrlData,\n getStorageToken,\n true /* throwOnNullToken */,\n );\n\n const snapshotDownloader = async (\n finalOdspResolvedUrl: IOdspResolvedUrl,\n storageToken: string,\n snapshotOptions: ISnapshotOptions | undefined,\n controller?: AbortController,\n ) => {\n return downloadSnapshot(\n finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, fetchBinarySnapshotFormat, controller);\n };\n const snapshotKey = createCacheSnapshotKey(odspResolvedUrl);\n let cacheP: Promise<void> | undefined;\n const putInCache = async (valueWithEpoch: IVersionedValueWithEpoch) => {\n cacheP = persistedCache.put(\n snapshotKey,\n valueWithEpoch,\n );\n return cacheP;\n };\n const removeEntries = async () => persistedCache.removeEntries(snapshotKey.file);\n return PerformanceEvent.timedExecAsync(\n odspLogger,\n { eventName: \"PrefetchLatestSnapshot\" },\n async () => {\n await fetchSnapshotWithRedeem(\n odspResolvedUrl,\n storageTokenFetcher,\n hostSnapshotFetchOptions,\n forceAccessTokenViaAuthorizationHeader,\n odspLogger,\n snapshotDownloader,\n putInCache,\n removeEntries,\n enableRedeemFallback,\n );\n assert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n await cacheP;\n return true;\n }).catch(async (error) => false);\n}\n"]}
1
+ {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAUtD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,8BAA8B,GACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAA6B,MAAM,iBAAiB,CAAC;AAGvG;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CACxC,WAAyB,EACzB,eAA4D,EAC5D,cAA+B,EAC/B,sCAA+C,EAC/C,MAA4B,EAC5B,wBAAsD,EACtD,oBAA8B,EAC9B,yBAAmC,EACnC,uBAAmD;IAEnD,MAAM,UAAU,GAAG,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAExD,MAAM,eAAe,GAAkB;QACnC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,MAAM,EAAE,eAAe,CAAC,MAAM;KACjC,CAAC;IACF,MAAM,mBAAmB,GAAG,8BAA8B,CACtD,UAAU,EACV,eAAe,EACf,eAAe,EACf,IAAI,CAAC,sBAAsB,CAC9B,CAAC;IAEF,MAAM,kBAAkB,GAAG,KAAK,EAC5B,oBAAsC,EACtC,YAAoB,EACpB,eAA6C,EAC7C,UAA4B,EAC9B,EAAE;QACA,OAAO,gBAAgB,CACnB,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,uBAAuB,EAAE,UAAU,CAAC,CAAC;IAC9G,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,sBAAsB,CAAC,eAAe,CAAC,CAAC;IAC5D,IAAI,MAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,EAAE,cAAwC,EAAE,EAAE;QAClE,MAAM,GAAG,cAAc,CAAC,GAAG,CACvB,WAAW,EACX,cAAc,CACjB,CAAC;QACF,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC,cAAc,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO,gBAAgB,CAAC,cAAc,CAClC,UAAU,EACV,EAAE,SAAS,EAAE,wBAAwB,EAAE,EACvC,KAAK,IAAI,EAAE;QACP,MAAM,uBAAuB,CACrB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,sCAAsC,EACtC,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,oBAAoB,CACvB,CAAC;QACN,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC;QACb,OAAO,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { default as AbortController } from \"abort-controller\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IResolvedUrl } from \"@fluidframework/driver-definitions\";\nimport {\n IOdspResolvedUrl,\n IPersistedCache,\n ISnapshotOptions,\n OdspResourceTokenFetchOptions,\n TokenFetcher,\n IOdspUrlParts,\n} from \"@fluidframework/odsp-driver-definitions\";\nimport { ChildLogger, PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport {\n createCacheSnapshotKey,\n createOdspLogger,\n getOdspResolvedUrl,\n toInstrumentedOdspTokenFetcher,\n} from \"./odspUtils\";\nimport { downloadSnapshot, fetchSnapshotWithRedeem, SnapshotFormatSupportType } from \"./fetchSnapshot\";\nimport { IVersionedValueWithEpoch } from \"./contracts\";\n\n/**\n * Function to prefetch the snapshot and cached it in the persistant cache, so that when the container is loaded\n * the cached latest snapshot could be used and removes the network call from the critical path.\n * @param resolvedUrl - Resolved url to fetch the snapshot.\n * @param getStorageToken - function that can provide the storage token for a given site. This is\n * is also referred to as the \"VROOM\" token in SPO.\n * @param persistedCache - Cache to store the fetched snapshot.\n * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.\n * @param logger - Logger to have telemetry events.\n * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.\n * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem\n * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make\n * the Trees latest call.\n * @deprecated - This will be replaced with snapshotFormatFetchType.\n * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.\n * @param snapshotFormatFetchType - Snapshot format to fetch.\n * @returns - True if the snapshot is cached, false otherwise.\n */\nexport async function prefetchLatestSnapshot(\n resolvedUrl: IResolvedUrl,\n getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n persistedCache: IPersistedCache,\n forceAccessTokenViaAuthorizationHeader: boolean,\n logger: ITelemetryBaseLogger,\n hostSnapshotFetchOptions: ISnapshotOptions | undefined,\n enableRedeemFallback?: boolean,\n fetchBinarySnapshotFormat?: boolean,\n snapshotFormatFetchType?: SnapshotFormatSupportType,\n): Promise<boolean> {\n const odspLogger = createOdspLogger(ChildLogger.create(logger, \"PrefetchSnapshot\"));\n const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n const resolvedUrlData: IOdspUrlParts = {\n siteUrl: odspResolvedUrl.siteUrl,\n driveId: odspResolvedUrl.driveId,\n itemId: odspResolvedUrl.itemId,\n };\n const storageTokenFetcher = toInstrumentedOdspTokenFetcher(\n odspLogger,\n resolvedUrlData,\n getStorageToken,\n true /* throwOnNullToken */,\n );\n\n const snapshotDownloader = async (\n finalOdspResolvedUrl: IOdspResolvedUrl,\n storageToken: string,\n snapshotOptions: ISnapshotOptions | undefined,\n controller?: AbortController,\n ) => {\n return downloadSnapshot(\n finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, snapshotFormatFetchType, controller);\n };\n const snapshotKey = createCacheSnapshotKey(odspResolvedUrl);\n let cacheP: Promise<void> | undefined;\n const putInCache = async (valueWithEpoch: IVersionedValueWithEpoch) => {\n cacheP = persistedCache.put(\n snapshotKey,\n valueWithEpoch,\n );\n return cacheP;\n };\n const removeEntries = async () => persistedCache.removeEntries(snapshotKey.file);\n return PerformanceEvent.timedExecAsync(\n odspLogger,\n { eventName: \"PrefetchLatestSnapshot\" },\n async () => {\n await fetchSnapshotWithRedeem(\n odspResolvedUrl,\n storageTokenFetcher,\n hostSnapshotFetchOptions,\n forceAccessTokenViaAuthorizationHeader,\n odspLogger,\n snapshotDownloader,\n putInCache,\n removeEntries,\n enableRedeemFallback,\n );\n assert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n await cacheP;\n return true;\n }).catch(async (error) => false);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,GAAqB,EACrB,QAAgB,EAChB,MAAwB,EACxB,aAA0B;IAE1B,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAI,QAAQ,EAAE,EAAE;QACjC,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,aAAa,EAAE,CAAC;SACnB;QACD,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACd,MAAM,CAAC,kBAAkB,CACrB;oBACI,SAAS,EAAE,iBAAiB;oBAC5B,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,SAAS,CAAC,CAAC;aAClB;YACD,OAAO,MAAM,CAAC;SACjB;QAAC,OAAO,KAAK,EAAE;YACZ,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YAExC,MAAM,cAAc,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,YAAY,OAAM,IAAI,CAAC;YACtD,MAAM,oBAAoB,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,SAAS,MAAK,aAAa,CAAC,eAAe,CAAC;YAChF,4DAA4D;YAC5D,IAAI,CAAC,CAAC,cAAc,IAAI,oBAAoB,CAAC,EAAE;gBAC3C,MAAM,KAAK,CAAC;aACf;YAED,+EAA+E;YAC/E,oFAAoF;YACpF,uEAAuE;YACvE,IAAI,QAAQ,KAAK,CAAC,EAAE;gBAChB,MAAM,CAAC,cAAc,CACjB;oBACI,SAAS,EAAE,cAAc,CAAC,CAAC;wBACvB,8BAA8B,CAAC,CAAC,CAAC,oCAAoC;oBACzE,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,KAAK,CAAC,CAAC;gBACX,aAAa;gBACb,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACvB,MAAM,KAAK,CAAC;aACf;YAED,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1C,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,UAAU,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,SAAS,GAAG,KAAK,CAAC;SACrB;KACJ;AACL,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert, delay, performance } from \"@fluidframework/common-utils\";\nimport { canRetryOnError } from \"@fluidframework/driver-utils\";\nimport { OdspErrorType } from \"@fluidframework/odsp-driver-definitions\";\nimport { Odsp409Error } from \"./epochTracker\";\n\n/**\n * This method retries only for retriable coherency and service read only errors.\n */\nexport async function runWithRetry<T>(\n api: () => Promise<T>,\n callName: string,\n logger: ITelemetryLogger,\n checkDisposed?: () => void,\n): Promise<T> {\n let retryAfter = 1000;\n const start = performance.now();\n let lastError: any;\n for (let attempts = 1; ; attempts++) {\n if (checkDisposed !== undefined) {\n checkDisposed();\n }\n try {\n const result = await api();\n if (attempts > 1) {\n logger.sendTelemetryEvent(\n {\n eventName: \"MultipleRetries\",\n callName,\n attempts,\n duration: performance.now() - start,\n },\n lastError);\n }\n return result;\n } catch (error) {\n const canRetry = canRetryOnError(error);\n\n const coherencyError = error?.[Odsp409Error] === true;\n const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;\n // Retry for 409 coherency errors or serviceReadOnly errors.\n if (!(coherencyError || serviceReadonlyError)) {\n throw error;\n }\n\n // SPO itself does number of retries internally before returning 409 to client.\n // That multiplied to 5 suggests need to reconsider current design, as client spends\n // too much time / bandwidth doing the same thing without any progress.\n if (attempts === 5) {\n logger.sendErrorEvent(\n {\n eventName: coherencyError ?\n \"CoherencyErrorTooManyRetries\" : \"ServiceReadonlyErrorTooManyRetries\",\n callName,\n attempts,\n duration: performance.now() - start, // record total wait time.\n },\n error);\n // Fail hard.\n error.canRetry = false;\n throw error;\n }\n\n assert(canRetry, 0x24d /* \"can retry\" */);\n await delay(Math.floor(retryAfter));\n retryAfter += retryAfter / 4 * (1 + Math.random());\n lastError = error;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,GAAqB,EACrB,QAAgB,EAChB,MAAwB,EACxB,aAA0B;IAE1B,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAI,QAAQ,EAAE,EAAE;QACjC,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,aAAa,EAAE,CAAC;SACnB;QACD,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACd,MAAM,CAAC,kBAAkB,CACrB;oBACI,SAAS,EAAE,iBAAiB;oBAC5B,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,SAAS,CAAC,CAAC;aAClB;YACD,OAAO,MAAM,CAAC;SACjB;QAAC,OAAO,KAAU,EAAE;YACjB,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YAExC,MAAM,cAAc,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,YAAY,OAAM,IAAI,CAAC;YACtD,MAAM,oBAAoB,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,SAAS,MAAK,aAAa,CAAC,eAAe,CAAC;YAChF,4DAA4D;YAC5D,IAAI,CAAC,CAAC,cAAc,IAAI,oBAAoB,CAAC,EAAE;gBAC3C,MAAM,KAAK,CAAC;aACf;YAED,+EAA+E;YAC/E,oFAAoF;YACpF,uEAAuE;YACvE,IAAI,QAAQ,KAAK,CAAC,EAAE;gBAChB,MAAM,CAAC,cAAc,CACjB;oBACI,SAAS,EAAE,cAAc,CAAC,CAAC;wBACvB,8BAA8B,CAAC,CAAC,CAAC,oCAAoC;oBACzE,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,KAAK,CAAC,CAAC;gBACX,aAAa;gBACb,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACvB,MAAM,KAAK,CAAC;aACf;YAED,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC1C,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,UAAU,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,SAAS,GAAG,KAAK,CAAC;SACrB;KACJ;AACL,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { assert, delay, performance } from \"@fluidframework/common-utils\";\nimport { canRetryOnError } from \"@fluidframework/driver-utils\";\nimport { OdspErrorType } from \"@fluidframework/odsp-driver-definitions\";\nimport { Odsp409Error } from \"./epochTracker\";\n\n/**\n * This method retries only for retriable coherency and service read only errors.\n */\nexport async function runWithRetry<T>(\n api: () => Promise<T>,\n callName: string,\n logger: ITelemetryLogger,\n checkDisposed?: () => void,\n): Promise<T> {\n let retryAfter = 1000;\n const start = performance.now();\n let lastError: any;\n for (let attempts = 1; ; attempts++) {\n if (checkDisposed !== undefined) {\n checkDisposed();\n }\n try {\n const result = await api();\n if (attempts > 1) {\n logger.sendTelemetryEvent(\n {\n eventName: \"MultipleRetries\",\n callName,\n attempts,\n duration: performance.now() - start,\n },\n lastError);\n }\n return result;\n } catch (error: any) {\n const canRetry = canRetryOnError(error);\n\n const coherencyError = error?.[Odsp409Error] === true;\n const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;\n // Retry for 409 coherency errors or serviceReadOnly errors.\n if (!(coherencyError || serviceReadonlyError)) {\n throw error;\n }\n\n // SPO itself does number of retries internally before returning 409 to client.\n // That multiplied to 5 suggests need to reconsider current design, as client spends\n // too much time / bandwidth doing the same thing without any progress.\n if (attempts === 5) {\n logger.sendErrorEvent(\n {\n eventName: coherencyError ?\n \"CoherencyErrorTooManyRetries\" : \"ServiceReadonlyErrorTooManyRetries\",\n callName,\n attempts,\n duration: performance.now() - start, // record total wait time.\n },\n error);\n // Fail hard.\n error.canRetry = false;\n throw error;\n }\n\n assert(canRetry, 0x24d /* \"can retry\" */);\n await delay(Math.floor(retryAfter));\n retryAfter += retryAfter / 4 * (1 + Math.random());\n lastError = error;\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/odsp-driver",
3
- "version": "0.59.1000",
3
+ "version": "0.59.2000-63294",
4
4
  "description": "Socket storage implementation for SPO and ODC",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -16,7 +16,7 @@
16
16
  "types": "dist/index.d.ts",
17
17
  "scripts": {
18
18
  "build": "npm run build:genver && concurrently npm:build:compile npm:lint && npm run build:docs",
19
- "build:commonjs": "npm run tsc && npm run build:test",
19
+ "build:commonjs": "npm run tsc && npm run typetests:gen && npm run build:test",
20
20
  "build:compile": "concurrently npm:build:commonjs npm:build:esnext",
21
21
  "build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
22
22
  "build:esnext": "tsc --project ./tsconfig.esnext.json",
@@ -36,7 +36,8 @@
36
36
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
37
37
  "tsc": "tsc",
38
38
  "tsfmt": "tsfmt --verify",
39
- "tsfmt:fix": "tsfmt --replace"
39
+ "tsfmt:fix": "tsfmt --replace",
40
+ "typetests:gen": "fluid-type-validator -g -d ."
40
41
  },
41
42
  "nyc": {
42
43
  "all": true,
@@ -62,15 +63,15 @@
62
63
  "@fluidframework/common-definitions": "^0.20.1",
63
64
  "@fluidframework/common-utils": "^0.32.1",
64
65
  "@fluidframework/core-interfaces": "^0.43.1000",
65
- "@fluidframework/driver-base": "^0.59.1000",
66
+ "@fluidframework/driver-base": "0.59.2000-63294",
66
67
  "@fluidframework/driver-definitions": "^0.46.1000",
67
- "@fluidframework/driver-utils": "^0.59.1000",
68
+ "@fluidframework/driver-utils": "0.59.2000-63294",
68
69
  "@fluidframework/gitresources": "^0.1036.1000",
69
- "@fluidframework/odsp-doclib-utils": "^0.59.1000",
70
- "@fluidframework/odsp-driver-definitions": "^0.59.1000",
70
+ "@fluidframework/odsp-doclib-utils": "0.59.2000-63294",
71
+ "@fluidframework/odsp-driver-definitions": "0.59.2000-63294",
71
72
  "@fluidframework/protocol-base": "^0.1036.1000",
72
73
  "@fluidframework/protocol-definitions": "^0.1028.1000",
73
- "@fluidframework/telemetry-utils": "^0.59.1000",
74
+ "@fluidframework/telemetry-utils": "0.59.2000-63294",
74
75
  "abort-controller": "^3.0.0",
75
76
  "node-fetch": "^2.6.1",
76
77
  "socket.io-client": "^4.4.1",
@@ -79,9 +80,9 @@
79
80
  "devDependencies": {
80
81
  "@fluidframework/build-common": "^0.23.0",
81
82
  "@fluidframework/eslint-config-fluid": "^0.28.1000",
82
- "@fluidframework/mocha-test-setup": "^0.59.1000",
83
- "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@^0.58.0",
84
- "@microsoft/api-extractor": "^7.16.1",
83
+ "@fluidframework/mocha-test-setup": "0.59.2000-63294",
84
+ "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@0.59.1000",
85
+ "@microsoft/api-extractor": "^7.22.2",
85
86
  "@rushstack/eslint-config": "^2.5.1",
86
87
  "@types/mocha": "^8.2.2",
87
88
  "@types/node-fetch": "^2.5.10",
@@ -95,8 +96,11 @@
95
96
  "eslint-plugin-editorconfig": "~3.2.0",
96
97
  "eslint-plugin-eslint-comments": "~3.2.0",
97
98
  "eslint-plugin-import": "~2.25.4",
98
- "eslint-plugin-no-null": "~1.0.2",
99
+ "eslint-plugin-jest": "~26.1.3",
100
+ "eslint-plugin-mocha": "~10.0.3",
101
+ "eslint-plugin-promise": "~6.0.0",
99
102
  "eslint-plugin-react": "~7.28.0",
103
+ "eslint-plugin-tsdoc": "~0.2.14",
100
104
  "eslint-plugin-unicorn": "~40.0.0",
101
105
  "mocha": "^8.4.0",
102
106
  "nyc": "^15.0.0",
@@ -106,7 +110,7 @@
106
110
  "typescript-formatter": "7.1.0"
107
111
  },
108
112
  "typeValidation": {
109
- "version": "0.59.1000",
113
+ "version": "0.59.2000",
110
114
  "broken": {}
111
115
  }
112
116
  }
@@ -423,7 +423,7 @@ export class EpochTrackerWithRedemption extends EpochTracker {
423
423
 
424
424
  try {
425
425
  return await super.fetchAndParseAsJSON<T>(url, fetchOptions, fetchType, addInBody, fetchReason);
426
- } catch (error) {
426
+ } catch (error: any) {
427
427
  // Only handling here treesLatest. If createFile failed, we should never try to do joinSession.
428
428
  // Similar, if getVersions failed, we should not do any further storage calls.
429
429
  // So treesLatest is the only call that can have parallel joinSession request.
@@ -33,6 +33,15 @@ import { currentReadVersion, parseCompactSnapshotResponse } from "./compactSnaps
33
33
  import { ReadBuffer } from "./ReadBufferUtils";
34
34
  import { EpochTracker } from "./epochTracker";
35
35
 
36
+ /**
37
+ * Enum to support different types of snapshot formats.
38
+ */
39
+ export enum SnapshotFormatSupportType {
40
+ Json = 0,
41
+ Binary = 1,
42
+ JsonAndBinary = 2,
43
+ }
44
+
36
45
  /**
37
46
  * Fetches a snapshot from the server with a given version id.
38
47
  * @param snapshotUrl - snapshot url from where the odsp snapshot will be fetched
@@ -428,7 +437,7 @@ function countTreesInSnapshotTree(snapshotTree: ISnapshotTree): number {
428
437
  * @param storageToken - token to do the auth for network request.
429
438
  * @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
430
439
  * @param logger - logger
431
- * @param fetchBinarySnapshotFormat - whether to fetch binary snapshot or not.
440
+ * @param snapshotFormatFetchType - Snapshot format to fetch.
432
441
  * @param controller - abort controller if caller needs to abort the network call.
433
442
  * @param epochTracker - epoch tracker used to add/validate epoch in the network call.
434
443
  * @returns fetched snapshot.
@@ -438,7 +447,7 @@ export async function downloadSnapshot(
438
447
  storageToken: string,
439
448
  logger: ITelemetryLogger,
440
449
  snapshotOptions: ISnapshotOptions | undefined,
441
- fetchBinarySnapshotFormat?: boolean,
450
+ snapshotFormatFetchType?: SnapshotFormatSupportType,
442
451
  controller?: AbortController,
443
452
  epochTracker?: EpochTracker,
444
453
  ): Promise<ISnapshotRequestAndResponseOptions> {
@@ -463,13 +472,18 @@ export async function downloadSnapshot(
463
472
  signal: controller?.signal,
464
473
  method: "POST",
465
474
  };
466
- // For now we are keeping both json and ms-fluid values in accept header while fetching as we want to let
467
- // the server decide what format it wants to send to the client as we roll it out slowly.
468
- if (fetchBinarySnapshotFormat) {
469
- headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
470
- } else {
471
- headers.accept = "application/json";
475
+ // Decide what snapshot format to fetch as per the feature gate.
476
+ switch (snapshotFormatFetchType) {
477
+ case SnapshotFormatSupportType.JsonAndBinary:
478
+ headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
479
+ break;
480
+ case SnapshotFormatSupportType.Binary:
481
+ headers.accept = `application/ms-fluid; v=${currentReadVersion}`;
482
+ break;
483
+ default:
484
+ headers.accept = "application/json";
472
485
  }
486
+
473
487
  const response = await (epochTracker?.fetch(url, fetchOptions, "treesLatest", true) ??
474
488
  fetchHelper(url, fetchOptions));
475
489
 
@@ -250,7 +250,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
250
250
  try {
251
251
  await deltaConnection.initialize(connectMessage, timeoutMs);
252
252
  await epochTracker.validateEpochFromPush(deltaConnection.details);
253
- } catch (errorObject) {
253
+ } catch (errorObject: any) {
254
254
  if (errorObject !== null && typeof errorObject === "object") {
255
255
  // We have to special-case error types here in terms of what is re-triable.
256
256
  // These errors have to re-retried, we just need new joinSession result to connect to right server:
@@ -151,8 +151,6 @@ export class OdspDocumentService implements IDocumentService {
151
151
  }));
152
152
 
153
153
  this.hostPolicy = hostPolicy;
154
- this.hostPolicy.fetchBinarySnapshotFormat ??=
155
- this.mc.config.getBoolean("Fluid.Driver.Odsp.binaryFormatSnapshot");
156
154
  if (this.clientIsSummarizer) {
157
155
  this.hostPolicy = { ...this.hostPolicy, summarizerClient: true };
158
156
  }
@@ -187,6 +185,7 @@ export class OdspDocumentService implements IDocumentService {
187
185
  }
188
186
  throw new Error("Disconnected while uploading summary (attempt to perform flush())");
189
187
  },
188
+ this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
190
189
  );
191
190
  }
192
191
 
@@ -32,7 +32,7 @@ import {
32
32
  IVersionedValueWithEpoch,
33
33
  ISnapshotCachedEntry,
34
34
  } from "./contracts";
35
- import { downloadSnapshot, fetchSnapshot, fetchSnapshotWithRedeem } from "./fetchSnapshot";
35
+ import { downloadSnapshot, fetchSnapshot, fetchSnapshotWithRedeem, SnapshotFormatSupportType } from "./fetchSnapshot";
36
36
  import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
37
37
  import { IOdspCache } from "./odspCache";
38
38
  import {
@@ -221,6 +221,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
221
221
  private readonly hostPolicy: HostStoragePolicyInternal,
222
222
  private readonly epochTracker: EpochTracker,
223
223
  private readonly flushCallback: () => Promise<FlushResult>,
224
+ private readonly snapshotFormatFetchType?: SnapshotFormatSupportType,
224
225
  ) {
225
226
  this.documentId = this.odspResolvedUrl.hashedDocumentId;
226
227
  this.snapshotUrl = this.odspResolvedUrl.endpoints.snapshotStorageUrl;
@@ -578,7 +579,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
578
579
  storageToken,
579
580
  this.logger,
580
581
  options,
581
- this.hostPolicy.fetchBinarySnapshotFormat,
582
+ this.snapshotFormatFetchType,
582
583
  controller,
583
584
  this.epochTracker,
584
585
  );
@@ -604,7 +605,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
604
605
  this.hostPolicy.enableRedeemFallback,
605
606
  );
606
607
  return odspSnapshot;
607
- } catch (error) {
608
+ } catch (error: any) {
608
609
  const errorType = error.errorType;
609
610
  // If the snapshot size is too big and the host specified the size limitation(specified in hostSnapshotOptions), then don't try to fetch the snapshot again.
610
611
  if ((errorType === OdspErrorType.snapshotTooBig && hostSnapshotOptions?.mds !== undefined) && (this.hostPolicy.summarizerClient !== true)) {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "0.59.1000";
9
+ export const pkgVersion = "0.59.2000-63294";
@@ -22,7 +22,7 @@ import {
22
22
  getOdspResolvedUrl,
23
23
  toInstrumentedOdspTokenFetcher,
24
24
  } from "./odspUtils";
25
- import { downloadSnapshot, fetchSnapshotWithRedeem } from "./fetchSnapshot";
25
+ import { downloadSnapshot, fetchSnapshotWithRedeem, SnapshotFormatSupportType } from "./fetchSnapshot";
26
26
  import { IVersionedValueWithEpoch } from "./contracts";
27
27
 
28
28
  /**
@@ -38,6 +38,9 @@ import { IVersionedValueWithEpoch } from "./contracts";
38
38
  * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem
39
39
  * 1RT call fails with redeem error. During fallback it will first redeem the sharing link and then make
40
40
  * the Trees latest call.
41
+ * @deprecated - This will be replaced with snapshotFormatFetchType.
42
+ * @param fetchBinarySnapshotFormat - Control if we want to fetch binary format snapshot.
43
+ * @param snapshotFormatFetchType - Snapshot format to fetch.
41
44
  * @returns - True if the snapshot is cached, false otherwise.
42
45
  */
43
46
  export async function prefetchLatestSnapshot(
@@ -49,6 +52,7 @@ export async function prefetchLatestSnapshot(
49
52
  hostSnapshotFetchOptions: ISnapshotOptions | undefined,
50
53
  enableRedeemFallback?: boolean,
51
54
  fetchBinarySnapshotFormat?: boolean,
55
+ snapshotFormatFetchType?: SnapshotFormatSupportType,
52
56
  ): Promise<boolean> {
53
57
  const odspLogger = createOdspLogger(ChildLogger.create(logger, "PrefetchSnapshot"));
54
58
  const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
@@ -72,7 +76,7 @@ export async function prefetchLatestSnapshot(
72
76
  controller?: AbortController,
73
77
  ) => {
74
78
  return downloadSnapshot(
75
- finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, fetchBinarySnapshotFormat, controller);
79
+ finalOdspResolvedUrl, storageToken, odspLogger, snapshotOptions, snapshotFormatFetchType, controller);
76
80
  };
77
81
  const snapshotKey = createCacheSnapshotKey(odspResolvedUrl);
78
82
  let cacheP: Promise<void> | undefined;
package/src/retryUtils.ts CHANGED
@@ -38,7 +38,7 @@ export async function runWithRetry<T>(
38
38
  lastError);
39
39
  }
40
40
  return result;
41
- } catch (error) {
41
+ } catch (error: any) {
42
42
  const canRetry = canRetryOnError(error);
43
43
 
44
44
  const coherencyError = error?.[Odsp409Error] === true;