@fluidframework/odsp-driver 2.92.0 → 2.100.0

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/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/compactSnapshotParser.d.ts +2 -2
  4. package/dist/compactSnapshotParser.d.ts.map +1 -1
  5. package/dist/compactSnapshotParser.js.map +1 -1
  6. package/dist/epochTracker.d.ts +8 -4
  7. package/dist/epochTracker.d.ts.map +1 -1
  8. package/dist/epochTracker.js +12 -9
  9. package/dist/epochTracker.js.map +1 -1
  10. package/dist/fetchSnapshot.d.ts +10 -7
  11. package/dist/fetchSnapshot.d.ts.map +1 -1
  12. package/dist/fetchSnapshot.js +58 -25
  13. package/dist/fetchSnapshot.js.map +1 -1
  14. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  15. package/dist/odspDocumentStorageManager.js +1 -1
  16. package/dist/odspDocumentStorageManager.js.map +1 -1
  17. package/dist/packageVersion.d.ts +1 -1
  18. package/dist/packageVersion.d.ts.map +1 -1
  19. package/dist/packageVersion.js +1 -1
  20. package/dist/packageVersion.js.map +1 -1
  21. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  22. package/dist/prefetchLatestSnapshot.js +2 -2
  23. package/dist/prefetchLatestSnapshot.js.map +1 -1
  24. package/eslint.config.mts +1 -1
  25. package/lib/compactSnapshotParser.d.ts +2 -2
  26. package/lib/compactSnapshotParser.d.ts.map +1 -1
  27. package/lib/compactSnapshotParser.js.map +1 -1
  28. package/lib/epochTracker.d.ts +8 -4
  29. package/lib/epochTracker.d.ts.map +1 -1
  30. package/lib/epochTracker.js +13 -10
  31. package/lib/epochTracker.js.map +1 -1
  32. package/lib/fetchSnapshot.d.ts +10 -7
  33. package/lib/fetchSnapshot.d.ts.map +1 -1
  34. package/lib/fetchSnapshot.js +37 -4
  35. package/lib/fetchSnapshot.js.map +1 -1
  36. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  37. package/lib/odspDocumentStorageManager.js +1 -1
  38. package/lib/odspDocumentStorageManager.js.map +1 -1
  39. package/lib/packageVersion.d.ts +1 -1
  40. package/lib/packageVersion.d.ts.map +1 -1
  41. package/lib/packageVersion.js +1 -1
  42. package/lib/packageVersion.js.map +1 -1
  43. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  44. package/lib/prefetchLatestSnapshot.js +2 -2
  45. package/lib/prefetchLatestSnapshot.js.map +1 -1
  46. package/lib/tsdoc-metadata.json +1 -1
  47. package/package.json +17 -18
  48. package/src/compactSnapshotParser.ts +2 -2
  49. package/src/epochTracker.ts +30 -11
  50. package/src/fetchSnapshot.ts +53 -8
  51. package/src/odspDocumentStorageManager.ts +1 -0
  52. package/src/packageVersion.ts +1 -1
  53. package/src/prefetchLatestSnapshot.ts +2 -1
@@ -46,7 +46,7 @@ export async function prefetchLatestSnapshot(resolvedUrl, getStorageToken, persi
46
46
  };
47
47
  const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(odspLogger, resolvedUrlData, getStorageToken);
48
48
  const snapshotDownloader = async (finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, controller) => {
49
- return downloadSnapshot(finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, undefined, controller);
49
+ return downloadSnapshot(finalOdspResolvedUrl, storageTokenFetcher, tokenFetchOptions, loadingGroupId, snapshotOptions, odspLogger, undefined, controller);
50
50
  };
51
51
  const snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);
52
52
  let cacheP;
@@ -88,7 +88,7 @@ export async function prefetchLatestSnapshot(resolvedUrl, getStorageToken, persi
88
88
  }, 5000);
89
89
  })
90
90
  .catch((error) => {
91
- // Remove it from the non persistent cache if an error occured.
91
+ // Remove it from the non persistent cache if an error occurred.
92
92
  snapshotNonPersistentCache?.remove(nonPersistentCacheKey);
93
93
  snapshotContentsWithEpochP.reject(error);
94
94
  throw error;
@@ -1 +1 @@
1
- {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAKvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAS5E,OAAO,EACN,gBAAgB,EAChB,4BAA4B,GAC5B,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAGN,gBAAgB,EAChB,uBAAuB,GACvB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mCAAmC,EACnC,qCAAqC,GAErC,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC3C,WAAyB,EACzB,eAA4D,EAC5D,cAA+B,EAC/B,sCAA+C,EAC/C,MAA4B,EAC5B,wBAAsD,EACtD,uBAAgC,IAAI,EACpC,yBAAmC,EACnC,uBAAmD,EACnD,0BAAuD;IAEvD,MAAM,EAAE,GAAG,4BAA4B,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,gBAAgB,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,2BAA2B,GAAG,mCAAmC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IACnF,4FAA4F;IAC5F,kDAAkD;IAClD,MAAM,eAAe,GAAG,2BAA2B,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,eAAe,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAExD,MAAM,eAAe,GAAkB;QACtC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,MAAM,EAAE,eAAe,CAAC,MAAM;KAC9B,CAAC;IACF,MAAM,aAAa,GAAG,qCAAqC,CAC1D,UAAU,EACV,eAAe,EACf,eAAe,CACf,CAAC;IAEF,MAAM,kBAAkB,GAAG,KAAK,EAC/B,oBAAsC,EACtC,mBAAoD,EACpD,iBAAsC,EACtC,cAAoC,EACpC,eAA6C,EAC7C,UAA4B,EACkB,EAAE;QAChD,OAAO,gBAAgB,CACtB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,SAAS,EACT,UAAU,CACV,CAAC;IACH,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,sBAAsB,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;IACzF,IAAI,MAAiC,CAAC;IACtC,IAAI,aAAiC,CAAC;IACtC,MAAM,UAAU,GAAG,KAAK,EAAE,cAAwC,EAAiB,EAAE;QACpF,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC;QAC1C,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE,CAC/C,cAAc,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,gBAAgB,CAAC,cAAc,CACrC,UAAU,EACV,EAAE,SAAS,EAAE,wBAAwB,EAAE,EACvC,KAAK,IAAI,EAAE;QACV,MAAM,iBAAiB,GAAG,cAAc,EAAE,CAAC;QAC3C,kGAAkG;QAClG,MAAM,0BAA0B,GAAG,IAAI,QAAQ,EAA6B,CAAC;QAC7E,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC/D,MAAM,0BAA0B,GAC/B,0BAA0B,EAAE,2BAA2B,CAAC;QACzD,0BAA0B,EAAE,GAAG,CAC9B,qBAAqB,EACrB,KAAK,IAAI,EAAE,CAAC,0BAA0B,CAAC,OAAO,CAC9C,CAAC;QACF,MAAM,uBAAuB,CAC5B,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,sCAAsC,EACtC,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,eAAe,EACf,oBAAoB,CACpB;aACC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACrB,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,KAAK,CAAC,mDAAmD,CAAC,CAAC;YACnF,0BAA0B,CAAC,OAAO,CAAC;gBAClC,GAAG,KAAK;gBACR,UAAU,EAAE,aAAa;gBACzB,iBAAiB;aACjB,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACvE,MAAM,MAAM,CAAC;YACb,6CAA6C;YAC7C,kGAAkG;YAClG,6FAA6F;YAC7F,mGAAmG;YACnG,kGAAkG;YAClG,mGAAmG;YACnG,2FAA2F;YAC3F,+FAA+F;YAC/F,qCAAqC;YACrC,UAAU,CAAC,GAAG,EAAE;gBACf,0BAA0B,EAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAC3D,CAAC,EAAE,IAAI,CAAC,CAAC;QACV,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAChB,+DAA+D;YAC/D,0BAA0B,EAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAC1D,0BAA0B,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzC,MAAM,KAAK,CAAC;QACb,CAAC,CAAC,CAAC;QACJ,OAAO,IAAI,CAAC;IACb,CAAC,CACD,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACvB,UAAU,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,6BAA6B,EAAE,EAAE,KAAK,CAAC,CAAC;QAC/E,OAAO,KAAK,CAAC;IACd,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 { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIPersistedCache,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getKeyForCacheEntry } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n\tISnapshotOptions,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tPerformanceEvent,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IVersionedValueWithEpoch } from \"./contracts.js\";\nimport {\n\ttype ISnapshotRequestAndResponseOptions,\n\ttype SnapshotFormatSupportType,\n\tdownloadSnapshot,\n\tfetchSnapshotWithRedeem,\n} from \"./fetchSnapshot.js\";\nimport type { IPrefetchSnapshotContents } from \"./odspCache.js\";\nimport type { OdspDocumentServiceFactory } from \"./odspDocumentServiceFactory.js\";\nimport {\n\tcreateCacheSnapshotKey,\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\tsnapshotWithLoadingGroupIdSupported,\n\ttoInstrumentedOdspStorageTokenFetcher,\n\ttype TokenFetchOptionsEx,\n} from \"./odspUtils.js\";\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 *\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 - @deprecated Not used, true value always used instead. 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 * Note: this can be considered deprecated - it 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 * @param odspDocumentServiceFactory - factory to access the non persistent cache and store the prefetch promise.\n *\n * @returns `true` if the snapshot is cached, `false` otherwise.\n * @legacy\n * @beta\n */\nexport async function prefetchLatestSnapshot(\n\tresolvedUrl: IResolvedUrl,\n\tgetStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tpersistedCache: IPersistedCache,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tlogger: ITelemetryBaseLogger,\n\thostSnapshotFetchOptions: ISnapshotOptions | undefined,\n\tenableRedeemFallback: boolean = true,\n\tfetchBinarySnapshotFormat?: boolean,\n\tsnapshotFormatFetchType?: SnapshotFormatSupportType,\n\todspDocumentServiceFactory?: OdspDocumentServiceFactory,\n): Promise<boolean> {\n\tconst mc = createChildMonitoringContext({ logger, namespace: \"PrefetchSnapshot\" });\n\tconst odspLogger = createOdspLogger(mc.logger);\n\tconst useGroupIdsForSnapshotFetch = snapshotWithLoadingGroupIdSupported(mc.config);\n\t// For prefetch, we just want to fetch the ungrouped data and want to use the new API if the\n\t// feature gate is set, so provide an empty array.\n\tconst loadingGroupIds = useGroupIdsForSnapshotFetch ? [] : undefined;\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n\tconst resolvedUrlData: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\todspLogger,\n\t\tresolvedUrlData,\n\t\tgetStorageToken,\n\t);\n\n\tconst snapshotDownloader = async (\n\t\tfinalOdspResolvedUrl: IOdspResolvedUrl,\n\t\tstorageTokenFetcher: InstrumentedStorageTokenFetcher,\n\t\ttokenFetchOptions: TokenFetchOptionsEx,\n\t\tloadingGroupId: string[] | undefined,\n\t\tsnapshotOptions: ISnapshotOptions | undefined,\n\t\tcontroller?: AbortController,\n\t): Promise<ISnapshotRequestAndResponseOptions> => {\n\t\treturn downloadSnapshot(\n\t\t\tfinalOdspResolvedUrl,\n\t\t\tstorageTokenFetcher,\n\t\t\ttokenFetchOptions,\n\t\t\tloadingGroupId,\n\t\t\tsnapshotOptions,\n\t\t\tundefined,\n\t\t\tcontroller,\n\t\t);\n\t};\n\tconst snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);\n\tlet cacheP: Promise<void> | undefined;\n\tlet snapshotEpoch: string | undefined;\n\tconst putInCache = async (valueWithEpoch: IVersionedValueWithEpoch): Promise<void> => {\n\t\tsnapshotEpoch = valueWithEpoch.fluidEpoch;\n\t\tcacheP = persistedCache.put(snapshotKey, valueWithEpoch);\n\t\treturn cacheP;\n\t};\n\n\tconst removeEntries = async (): Promise<void> =>\n\t\tpersistedCache.removeEntries(snapshotKey.file);\n\treturn PerformanceEvent.timedExecAsync(\n\t\todspLogger,\n\t\t{ eventName: \"PrefetchLatestSnapshot\" },\n\t\tasync () => {\n\t\t\tconst prefetchStartTime = performanceNow();\n\t\t\t// Add the deferred promise to the cache, so that it can be leveraged while loading the container.\n\t\t\tconst snapshotContentsWithEpochP = new Deferred<IPrefetchSnapshotContents>();\n\t\t\tconst nonPersistentCacheKey = getKeyForCacheEntry(snapshotKey);\n\t\t\tconst snapshotNonPersistentCache =\n\t\t\t\todspDocumentServiceFactory?.snapshotPrefetchResultCache;\n\t\t\tsnapshotNonPersistentCache?.add(\n\t\t\t\tnonPersistentCacheKey,\n\t\t\t\tasync () => snapshotContentsWithEpochP.promise,\n\t\t\t);\n\t\t\tawait fetchSnapshotWithRedeem(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tgetAuthHeader,\n\t\t\t\thostSnapshotFetchOptions,\n\t\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t\t\todspLogger,\n\t\t\t\tsnapshotDownloader,\n\t\t\t\tputInCache,\n\t\t\t\tremoveEntries,\n\t\t\t\tloadingGroupIds,\n\t\t\t\tenableRedeemFallback,\n\t\t\t)\n\t\t\t\t.then(async (value) => {\n\t\t\t\t\tassert(!!snapshotEpoch, 0x585 /* prefetched snapshot should have a valid epoch */);\n\t\t\t\t\tsnapshotContentsWithEpochP.resolve({\n\t\t\t\t\t\t...value,\n\t\t\t\t\t\tfluidEpoch: snapshotEpoch,\n\t\t\t\t\t\tprefetchStartTime,\n\t\t\t\t\t});\n\t\t\t\t\tassert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n\t\t\t\t\tawait cacheP;\n\t\t\t\t\t// Schedule it to remove from cache after 5s.\n\t\t\t\t\t// 1. While it's in snapshotNonPersistentCache: Load flow will use this value and will not attempt\n\t\t\t\t\t// to fetch snapshot from network again. That's the best from perf POV, but cache will not be\n\t\t\t\t\t// updated if we keep it in this cache, thus we want to eventually remove snapshot from this cache.\n\t\t\t\t\t// 2. After it's removed from snapshotNonPersistentCache: snapshot is present in persistent cache,\n\t\t\t\t\t// so we sill still use it (in accordance with cache policy controlled by host). But load flow will\n\t\t\t\t\t// also fetch snapshot (in parallel) from storage and update cache. This is fine long term,\n\t\t\t\t\t// but is an extra cost (unneeded network call). However since it is 5s older, new network call\n\t\t\t\t\t// will update the snapshot in cache.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\t}, 5000);\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\t// Remove it from the non persistent cache if an error occured.\n\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\tsnapshotContentsWithEpochP.reject(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t\treturn true;\n\t\t},\n\t).catch(async (error) => {\n\t\todspLogger.sendErrorEvent({ eventName: \"PrefetchLatestSnapshotError\" }, error);\n\t\treturn false;\n\t});\n}\n"]}
1
+ {"version":3,"file":"prefetchLatestSnapshot.js","sourceRoot":"","sources":["../src/prefetchLatestSnapshot.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 { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Deferred } from \"@fluidframework/core-utils/internal\";\nimport type {\n\tIPersistedCache,\n\tIResolvedUrl,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { getKeyForCacheEntry } from \"@fluidframework/driver-utils/internal\";\nimport type {\n\tIOdspResolvedUrl,\n\tIOdspUrlParts,\n\tISnapshotOptions,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\tInstrumentedStorageTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tPerformanceEvent,\n\tcreateChildMonitoringContext,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IVersionedValueWithEpoch } from \"./contracts.js\";\nimport {\n\ttype ISnapshotRequestAndResponseOptions,\n\ttype SnapshotFormatSupportType,\n\tdownloadSnapshot,\n\tfetchSnapshotWithRedeem,\n} from \"./fetchSnapshot.js\";\nimport type { IPrefetchSnapshotContents } from \"./odspCache.js\";\nimport type { OdspDocumentServiceFactory } from \"./odspDocumentServiceFactory.js\";\nimport {\n\tcreateCacheSnapshotKey,\n\tcreateOdspLogger,\n\tgetOdspResolvedUrl,\n\tsnapshotWithLoadingGroupIdSupported,\n\ttoInstrumentedOdspStorageTokenFetcher,\n\ttype TokenFetchOptionsEx,\n} from \"./odspUtils.js\";\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 *\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 - @deprecated Not used, true value always used instead. 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 * Note: this can be considered deprecated - it 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 * @param odspDocumentServiceFactory - factory to access the non persistent cache and store the prefetch promise.\n *\n * @returns `true` if the snapshot is cached, `false` otherwise.\n * @legacy\n * @beta\n */\nexport async function prefetchLatestSnapshot(\n\tresolvedUrl: IResolvedUrl,\n\tgetStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tpersistedCache: IPersistedCache,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tlogger: ITelemetryBaseLogger,\n\thostSnapshotFetchOptions: ISnapshotOptions | undefined,\n\tenableRedeemFallback: boolean = true,\n\tfetchBinarySnapshotFormat?: boolean,\n\tsnapshotFormatFetchType?: SnapshotFormatSupportType,\n\todspDocumentServiceFactory?: OdspDocumentServiceFactory,\n): Promise<boolean> {\n\tconst mc = createChildMonitoringContext({ logger, namespace: \"PrefetchSnapshot\" });\n\tconst odspLogger = createOdspLogger(mc.logger);\n\tconst useGroupIdsForSnapshotFetch = snapshotWithLoadingGroupIdSupported(mc.config);\n\t// For prefetch, we just want to fetch the ungrouped data and want to use the new API if the\n\t// feature gate is set, so provide an empty array.\n\tconst loadingGroupIds = useGroupIdsForSnapshotFetch ? [] : undefined;\n\tconst odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);\n\n\tconst resolvedUrlData: IOdspUrlParts = {\n\t\tsiteUrl: odspResolvedUrl.siteUrl,\n\t\tdriveId: odspResolvedUrl.driveId,\n\t\titemId: odspResolvedUrl.itemId,\n\t};\n\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\todspLogger,\n\t\tresolvedUrlData,\n\t\tgetStorageToken,\n\t);\n\n\tconst snapshotDownloader = async (\n\t\tfinalOdspResolvedUrl: IOdspResolvedUrl,\n\t\tstorageTokenFetcher: InstrumentedStorageTokenFetcher,\n\t\ttokenFetchOptions: TokenFetchOptionsEx,\n\t\tloadingGroupId: string[] | undefined,\n\t\tsnapshotOptions: ISnapshotOptions | undefined,\n\t\tcontroller?: AbortController,\n\t): Promise<ISnapshotRequestAndResponseOptions> => {\n\t\treturn downloadSnapshot(\n\t\t\tfinalOdspResolvedUrl,\n\t\t\tstorageTokenFetcher,\n\t\t\ttokenFetchOptions,\n\t\t\tloadingGroupId,\n\t\t\tsnapshotOptions,\n\t\t\todspLogger,\n\t\t\tundefined,\n\t\t\tcontroller,\n\t\t);\n\t};\n\tconst snapshotKey = createCacheSnapshotKey(odspResolvedUrl, useGroupIdsForSnapshotFetch);\n\tlet cacheP: Promise<void> | undefined;\n\tlet snapshotEpoch: string | undefined;\n\tconst putInCache = async (valueWithEpoch: IVersionedValueWithEpoch): Promise<void> => {\n\t\tsnapshotEpoch = valueWithEpoch.fluidEpoch;\n\t\tcacheP = persistedCache.put(snapshotKey, valueWithEpoch);\n\t\treturn cacheP;\n\t};\n\n\tconst removeEntries = async (): Promise<void> =>\n\t\tpersistedCache.removeEntries(snapshotKey.file);\n\treturn PerformanceEvent.timedExecAsync(\n\t\todspLogger,\n\t\t{ eventName: \"PrefetchLatestSnapshot\" },\n\t\tasync () => {\n\t\t\tconst prefetchStartTime = performanceNow();\n\t\t\t// Add the deferred promise to the cache, so that it can be leveraged while loading the container.\n\t\t\tconst snapshotContentsWithEpochP = new Deferred<IPrefetchSnapshotContents>();\n\t\t\tconst nonPersistentCacheKey = getKeyForCacheEntry(snapshotKey);\n\t\t\tconst snapshotNonPersistentCache =\n\t\t\t\todspDocumentServiceFactory?.snapshotPrefetchResultCache;\n\t\t\tsnapshotNonPersistentCache?.add(\n\t\t\t\tnonPersistentCacheKey,\n\t\t\t\tasync () => snapshotContentsWithEpochP.promise,\n\t\t\t);\n\t\t\tawait fetchSnapshotWithRedeem(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tgetAuthHeader,\n\t\t\t\thostSnapshotFetchOptions,\n\t\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t\t\todspLogger,\n\t\t\t\tsnapshotDownloader,\n\t\t\t\tputInCache,\n\t\t\t\tremoveEntries,\n\t\t\t\tloadingGroupIds,\n\t\t\t\tenableRedeemFallback,\n\t\t\t)\n\t\t\t\t.then(async (value) => {\n\t\t\t\t\tassert(!!snapshotEpoch, 0x585 /* prefetched snapshot should have a valid epoch */);\n\t\t\t\t\tsnapshotContentsWithEpochP.resolve({\n\t\t\t\t\t\t...value,\n\t\t\t\t\t\tfluidEpoch: snapshotEpoch,\n\t\t\t\t\t\tprefetchStartTime,\n\t\t\t\t\t});\n\t\t\t\t\tassert(cacheP !== undefined, 0x1e7 /* \"caching was not performed!\" */);\n\t\t\t\t\tawait cacheP;\n\t\t\t\t\t// Schedule it to remove from cache after 5s.\n\t\t\t\t\t// 1. While it's in snapshotNonPersistentCache: Load flow will use this value and will not attempt\n\t\t\t\t\t// to fetch snapshot from network again. That's the best from perf POV, but cache will not be\n\t\t\t\t\t// updated if we keep it in this cache, thus we want to eventually remove snapshot from this cache.\n\t\t\t\t\t// 2. After it's removed from snapshotNonPersistentCache: snapshot is present in persistent cache,\n\t\t\t\t\t// so we sill still use it (in accordance with cache policy controlled by host). But load flow will\n\t\t\t\t\t// also fetch snapshot (in parallel) from storage and update cache. This is fine long term,\n\t\t\t\t\t// but is an extra cost (unneeded network call). However since it is 5s older, new network call\n\t\t\t\t\t// will update the snapshot in cache.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\t}, 5000);\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\t// Remove it from the non persistent cache if an error occurred.\n\t\t\t\t\tsnapshotNonPersistentCache?.remove(nonPersistentCacheKey);\n\t\t\t\t\tsnapshotContentsWithEpochP.reject(error);\n\t\t\t\t\tthrow error;\n\t\t\t\t});\n\t\t\treturn true;\n\t\t},\n\t).catch(async (error) => {\n\t\todspLogger.sendErrorEvent({ eventName: \"PrefetchLatestSnapshotError\" }, error);\n\t\treturn false;\n\t});\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.52.11"
8
+ "packageVersion": "7.58.1"
9
9
  }
10
10
  ]
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/odsp-driver",
3
- "version": "2.92.0",
3
+ "version": "2.100.0",
4
4
  "description": "Socket storage implementation for SPO and ODC",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,30 +69,30 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluid-internal/client-utils": "~2.92.0",
73
- "@fluidframework/core-interfaces": "~2.92.0",
74
- "@fluidframework/core-utils": "~2.92.0",
75
- "@fluidframework/driver-base": "~2.92.0",
76
- "@fluidframework/driver-definitions": "~2.92.0",
77
- "@fluidframework/driver-utils": "~2.92.0",
78
- "@fluidframework/odsp-doclib-utils": "~2.92.0",
79
- "@fluidframework/odsp-driver-definitions": "~2.92.0",
80
- "@fluidframework/telemetry-utils": "~2.92.0",
72
+ "@fluid-internal/client-utils": "~2.100.0",
73
+ "@fluidframework/core-interfaces": "~2.100.0",
74
+ "@fluidframework/core-utils": "~2.100.0",
75
+ "@fluidframework/driver-base": "~2.100.0",
76
+ "@fluidframework/driver-definitions": "~2.100.0",
77
+ "@fluidframework/driver-utils": "~2.100.0",
78
+ "@fluidframework/odsp-doclib-utils": "~2.100.0",
79
+ "@fluidframework/odsp-driver-definitions": "~2.100.0",
80
+ "@fluidframework/telemetry-utils": "~2.100.0",
81
81
  "socket.io-client": "^4.8.3",
82
82
  "uuid": "^11.1.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@arethetypeswrong/cli": "^0.18.2",
86
86
  "@biomejs/biome": "~2.4.5",
87
- "@fluid-internal/mocha-test-setup": "~2.92.0",
88
- "@fluid-tools/build-cli": "^0.64.0",
87
+ "@fluid-internal/mocha-test-setup": "~2.100.0",
88
+ "@fluid-tools/build-cli": "^0.65.0",
89
89
  "@fluidframework/build-common": "^2.0.3",
90
- "@fluidframework/build-tools": "^0.64.0",
90
+ "@fluidframework/build-tools": "^0.65.0",
91
91
  "@fluidframework/eslint-config-fluid": "^9.0.0",
92
- "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@2.91.0",
93
- "@microsoft/api-extractor": "7.52.11",
92
+ "@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@2.92.0",
93
+ "@microsoft/api-extractor": "7.58.1",
94
94
  "@types/mocha": "^10.0.10",
95
- "@types/node": "~20.19.30",
95
+ "@types/node": "~22.19.17",
96
96
  "@types/sinon": "^17.0.3",
97
97
  "c8": "^10.1.3",
98
98
  "concurrently": "^9.2.1",
@@ -153,7 +153,6 @@
153
153
  "test:mocha:esm": "mocha",
154
154
  "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
155
155
  "tsc": "fluid-tsc commonjs --project ./tsconfig.cjs.json && copyfiles -f ../../../common/build/build-common/src/cjs/package.json ./dist",
156
- "typetests:gen": "flub generate typetests --dir . -v",
157
- "typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
156
+ "typetests:gen": "flub generate typetests --dir . -v"
158
157
  }
159
158
  }
@@ -9,7 +9,7 @@ import type {
9
9
  ISnapshotTree,
10
10
  ISequencedDocumentMessage,
11
11
  } from "@fluidframework/driver-definitions/internal";
12
- import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
12
+ import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
13
13
 
14
14
  import { ReadBuffer } from "./ReadBufferUtils.js";
15
15
  import { measure } from "./odspUtils.js";
@@ -261,7 +261,7 @@ function readSnapshotSection(node: NodeTypes): {
261
261
  */
262
262
  export function parseCompactSnapshotResponse(
263
263
  buffer: Uint8Array,
264
- logger: ITelemetryLoggerExt,
264
+ logger: TelemetryLoggerExt,
265
265
  ): ISnapshotContentsWithProps {
266
266
  const { builder, telemetryProps } = TreeBuilder.load(new ReadBuffer(buffer), logger);
267
267
  assert(builder.length === 1, 0x219 /* "1 root should be there" */);
@@ -25,14 +25,18 @@ import {
25
25
  snapshotKey,
26
26
  snapshotWithLoadingGroupIdKey,
27
27
  } from "@fluidframework/odsp-driver-definitions/internal";
28
+ import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
28
29
  import {
29
- type ITelemetryLoggerExt,
30
30
  PerformanceEvent,
31
+ extractTelemetryLoggerExt,
31
32
  isFluidError,
32
33
  loggerToMonitoringContext,
33
34
  normalizeError,
35
+ toITelemetryLoggerExt,
34
36
  wrapError,
35
37
  } from "@fluidframework/telemetry-utils/internal";
38
+ // eslint-disable-next-line import-x/no-internal-modules -- Needed to avoid specialized /internal ITelemetryLoggerExt
39
+ import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/legacy";
36
40
  import { v4 as uuid } from "uuid";
37
41
 
38
42
  import { type IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts.js";
@@ -90,6 +94,9 @@ export const Odsp409Error = "Odsp409Error";
90
94
  * then it also clears all the cached entries for the given container.
91
95
  * @legacy
92
96
  * @beta
97
+ *
98
+ * @privateRemarks
99
+ * This class should be hidden and an interface exposed to better manage internal types like telemetry logger.
93
100
  */
94
101
  export class EpochTracker implements IPersistedFileCache {
95
102
  private _fluidEpoch: string | undefined;
@@ -99,12 +106,15 @@ export class EpochTracker implements IPersistedFileCache {
99
106
  private readonly driverId = uuid();
100
107
  // This tracks the request number made by the driver instance.
101
108
  private networkCallNumber = 1;
109
+ private readonly loggerInternal: TelemetryLoggerExt;
102
110
  constructor(
103
111
  protected readonly cache: IPersistedCache,
104
112
  protected readonly fileEntry: IFileEntry,
105
113
  protected readonly logger: ITelemetryLoggerExt,
106
114
  protected readonly clientIsSummarizer?: boolean,
107
115
  ) {
116
+ this.loggerInternal = extractTelemetryLoggerExt(logger);
117
+
108
118
  // Limits the max number of concurrent requests to 24.
109
119
  this.rateLimiter = new RateLimiter(24);
110
120
 
@@ -121,7 +131,7 @@ export class EpochTracker implements IPersistedFileCache {
121
131
  assert(this._fluidEpoch === undefined, 0x1db /* "epoch exists" */);
122
132
  this._fluidEpoch = epoch;
123
133
 
124
- this.logger.sendTelemetryEvent({
134
+ this.loggerInternal.sendTelemetryEvent({
125
135
  eventName: "EpochLearnedFirstTime",
126
136
  epoch,
127
137
  fetchType,
@@ -137,7 +147,7 @@ export class EpochTracker implements IPersistedFileCache {
137
147
  const value = (await this.cache.get(
138
148
  this.fileEntryFromEntry(entry),
139
149
  )) as IVersionedValueWithEpoch;
140
- // Version mismatch between what the runtime expects and what it recieved.
150
+ // Version mismatch between what the runtime expects and what it received.
141
151
  // The cached value should not be used
142
152
  // eslint-disable-next-line @typescript-eslint/prefer-optional-chain -- using ?. could change behavior
143
153
  if (value === undefined || value.version !== persistedCacheValueVersion) {
@@ -161,7 +171,7 @@ export class EpochTracker implements IPersistedFileCache {
161
171
  cacheTime === undefined ||
162
172
  currentTime - cacheTime >= this.snapshotCacheExpiryTimeoutMs
163
173
  ) {
164
- this.logger.sendTelemetryEvent({
174
+ this.loggerInternal.sendTelemetryEvent({
165
175
  eventName: "odspVersionsCacheExpired",
166
176
  duration: currentTime - cacheTime,
167
177
  maxCacheAgeMs: this.snapshotCacheExpiryTimeoutMs,
@@ -172,7 +182,10 @@ export class EpochTracker implements IPersistedFileCache {
172
182
  }
173
183
  return value.value;
174
184
  } catch (error) {
175
- this.logger.sendErrorEvent({ eventName: "cacheFetchError", type: entry.type }, error);
185
+ this.loggerInternal.sendErrorEvent(
186
+ { eventName: "cacheFetchError", type: entry.type },
187
+ error,
188
+ );
176
189
  return undefined;
177
190
  }
178
191
  }
@@ -194,7 +207,10 @@ export class EpochTracker implements IPersistedFileCache {
194
207
  fluidEpoch: this._fluidEpoch,
195
208
  };
196
209
  return this.cache.put(this.fileEntryFromEntry(entry), data).catch((error) => {
197
- this.logger.sendErrorEvent({ eventName: "cachePutError", type: entry.type }, error);
210
+ this.loggerInternal.sendErrorEvent(
211
+ { eventName: "cachePutError", type: entry.type },
212
+ error,
213
+ );
198
214
  throw error;
199
215
  });
200
216
  }
@@ -203,7 +219,7 @@ export class EpochTracker implements IPersistedFileCache {
203
219
  try {
204
220
  return await this.cache.removeEntries(this.fileEntry);
205
221
  } catch (error) {
206
- this.logger.sendErrorEvent({ eventName: "removeCacheEntries" }, error);
222
+ this.loggerInternal.sendErrorEvent({ eventName: "removeCacheEntries" }, error);
207
223
  }
208
224
  }
209
225
 
@@ -456,7 +472,10 @@ export class EpochTracker implements IPersistedFileCache {
456
472
  fromCache,
457
473
  fetchType,
458
474
  });
459
- this.logger.sendErrorEvent({ eventName: "fileOverwrittenInStorage" }, epochError);
475
+ this.loggerInternal.sendErrorEvent(
476
+ { eventName: "fileOverwrittenInStorage" },
477
+ epochError,
478
+ );
460
479
  // If the epoch mismatches, then clear all entries for such file entry from cache.
461
480
  await this.removeEntries();
462
481
  throw epochError;
@@ -502,10 +521,10 @@ export class EpochTrackerWithRedemption extends EpochTracker {
502
521
  constructor(
503
522
  protected readonly cache: IPersistedCache,
504
523
  protected readonly fileEntry: IFileEntry,
505
- protected readonly logger: ITelemetryLoggerExt,
524
+ logger: TelemetryLoggerExt,
506
525
  protected readonly clientIsSummarizer?: boolean,
507
526
  ) {
508
- super(cache, fileEntry, logger, clientIsSummarizer);
527
+ super(cache, fileEntry, toITelemetryLoggerExt(logger), clientIsSummarizer);
509
528
  // Handles the rejected promise within treesLatestDeferral.
510
529
  this.treesLatestDeferral.promise.catch(() => {});
511
530
  }
@@ -635,7 +654,7 @@ export function createOdspCacheAndTracker(
635
654
  persistedCacheArg: IPersistedCache,
636
655
  nonpersistentCache: INonPersistentCache,
637
656
  fileEntry: IFileEntry,
638
- logger: ITelemetryLoggerExt,
657
+ logger: TelemetryLoggerExt,
639
658
  clientIsSummarizer?: boolean,
640
659
  ): ICacheAndTracker {
641
660
  const epochTracker = new EpochTrackerWithRedemption(
@@ -6,7 +6,12 @@
6
6
  import { fromUtf8ToBase64 } from "@fluid-internal/client-utils";
7
7
  import { assert } from "@fluidframework/core-utils/internal";
8
8
  import { getW3CData } from "@fluidframework/driver-base/internal";
9
- import type { ISnapshot, ISnapshotTree } from "@fluidframework/driver-definitions/internal";
9
+ import {
10
+ DriverErrorTypes,
11
+ type ILocationRedirectionError,
12
+ type ISnapshot,
13
+ type ISnapshotTree,
14
+ } from "@fluidframework/driver-definitions/internal";
10
15
  import {
11
16
  type DriverErrorTelemetryProps,
12
17
  NonRetryableError,
@@ -23,8 +28,8 @@ import {
23
28
  type InstrumentedStorageTokenFetcher,
24
29
  OdspErrorTypes,
25
30
  } from "@fluidframework/odsp-driver-definitions/internal";
31
+ import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
26
32
  import {
27
- type ITelemetryLoggerExt,
28
33
  PerformanceEvent,
29
34
  isFluidError,
30
35
  wrapError,
@@ -54,6 +59,7 @@ import {
54
59
  fetchAndParseAsJSONHelper,
55
60
  fetchHelper,
56
61
  getWithRetryForTokenRefresh,
62
+ getOdspResolvedUrl,
57
63
  getWithRetryForTokenRefreshRepeat,
58
64
  isSnapshotFetchForLoadingGroup,
59
65
  measure,
@@ -88,7 +94,7 @@ export async function fetchSnapshot(
88
94
  versionId: string,
89
95
  fetchFullSnapshot: boolean,
90
96
  forceAccessTokenViaAuthorizationHeader: boolean,
91
- logger: ITelemetryLoggerExt,
97
+ logger: TelemetryLoggerExt,
92
98
  snapshotDownloader: (url: string) => Promise<IOdspResponse<unknown>>,
93
99
  ): Promise<ISnapshot> {
94
100
  const path = `/trees/${versionId}`;
@@ -115,7 +121,7 @@ export async function fetchSnapshotWithRedeem(
115
121
  storageTokenFetcher: InstrumentedStorageTokenFetcher,
116
122
  snapshotOptions: ISnapshotOptions | undefined,
117
123
  forceAccessTokenViaAuthorizationHeader: boolean,
118
- logger: ITelemetryLoggerExt,
124
+ logger: TelemetryLoggerExt,
119
125
  snapshotDownloader: (
120
126
  finalOdspResolvedUrl: IOdspResolvedUrl,
121
127
  getAuthHeader: InstrumentedStorageTokenFetcher,
@@ -183,6 +189,31 @@ export async function fetchSnapshotWithRedeem(
183
189
  putInCache,
184
190
  loadingGroupIds,
185
191
  );
192
+ } else if (
193
+ isLocationRedirectionError(error) &&
194
+ odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem !== undefined
195
+ ) {
196
+ try {
197
+ // The redirect itself is handled earlier, but we need to redeem the sharing link
198
+ // now against the redirected URL rather than waiting until the error reaches the
199
+ // resolveWithLocationRedirectionHandling handler as it will call getAbsoluteURL
200
+ // and would fail due to permission issues since it will not attempt to redeem.
201
+ logger.sendTelemetryEvent(
202
+ {
203
+ eventName: "RedirectRedeemFallback",
204
+ errorType: error.errorType,
205
+ },
206
+ error,
207
+ );
208
+ const redirectedResolvedUrl: IOdspResolvedUrl = {
209
+ ...getOdspResolvedUrl(error.redirectUrl),
210
+ shareLinkInfo: odspResolvedUrl.shareLinkInfo,
211
+ };
212
+ await redeemSharingLink(redirectedResolvedUrl, storageTokenFetcher, logger);
213
+ } catch (redeemError) {
214
+ logger.sendErrorEvent({ eventName: "RedirectRedeemFallbackError" }, redeemError);
215
+ }
216
+ throw error;
186
217
  } else {
187
218
  throw error;
188
219
  }
@@ -208,7 +239,7 @@ export async function fetchSnapshotWithRedeem(
208
239
  async function redeemSharingLink(
209
240
  odspResolvedUrl: IOdspResolvedUrl,
210
241
  getAuthHeader: InstrumentedStorageTokenFetcher,
211
- logger: ITelemetryLoggerExt,
242
+ logger: TelemetryLoggerExt,
212
243
  ): Promise<void> {
213
244
  await PerformanceEvent.timedExecAsync(
214
245
  logger,
@@ -275,7 +306,7 @@ async function fetchLatestSnapshotCore(
275
306
  odspResolvedUrl: IOdspResolvedUrl,
276
307
  getAuthHeader: InstrumentedStorageTokenFetcher,
277
308
  snapshotOptions: ISnapshotOptions | undefined,
278
- logger: ITelemetryLoggerExt,
309
+ logger: TelemetryLoggerExt,
279
310
  snapshotDownloader: (
280
311
  finalOdspResolvedUrl: IOdspResolvedUrl,
281
312
  getAuthHeader: InstrumentedStorageTokenFetcher,
@@ -682,15 +713,18 @@ function getTreeStatsCore(snapshotTree: ISnapshotTree, stats: ITreeStats): void
682
713
  /**
683
714
  * This function fetches the snapshot and parse it according to what is mentioned in response headers.
684
715
  * @param odspResolvedUrl - resolved odsp url.
685
- * @param storageToken - token to do the auth for network request.
686
- * @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
716
+ * @param getAuthHeader - function to fetch the auth header for the network request.
717
+ * @param tokenFetchOptions - options for fetching the token.
687
718
  * @param loadingGroupIds - loadingGroupIds for which snapshot needs to be downloaded. Note:
688
719
  * 1.) If undefined, then legacy trees latest call will be used where no groupId query param would be specified.
689
720
  * 2.) If [] is passed, then snapshot with all ungrouped data will be fetched.
690
721
  * 3.) If any groupId is specified like ["g1"], then snapshot for g1 group will be fetched.
722
+ * @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
723
+ * @param logger - logger for sending telemetry events.
691
724
  * @param snapshotFormatFetchType - Snapshot format to fetch.
692
725
  * @param controller - abort controller if caller needs to abort the network call.
693
726
  * @param epochTracker - epoch tracker used to add/validate epoch in the network call.
727
+ * @param scenarioName - scenario name for telemetry.
694
728
  * @returns fetched snapshot.
695
729
  */
696
730
  export const downloadSnapshot = mockify(
@@ -700,6 +734,7 @@ export const downloadSnapshot = mockify(
700
734
  tokenFetchOptions: TokenFetchOptionsEx,
701
735
  loadingGroupIds: string[] | undefined,
702
736
  snapshotOptions: ISnapshotOptions | undefined,
737
+ logger?: TelemetryLoggerExt,
703
738
  snapshotFormatFetchType?: SnapshotFormatSupportType,
704
739
  controller?: AbortController,
705
740
  epochTracker?: EpochTracker,
@@ -756,6 +791,7 @@ export const downloadSnapshot = mockify(
756
791
  "downloadSnapshot",
757
792
  );
758
793
  assert(authHeader !== null, 0x1e5 /* "Storage token should not be null" */);
794
+ logger?.sendTelemetryEvent({ eventName: "SnapshotAuthHeaderObtained" });
759
795
  const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, authHeader, header);
760
796
  const fetchOptions = {
761
797
  body,
@@ -782,6 +818,7 @@ export const downloadSnapshot = mockify(
782
818
  true,
783
819
  scenarioName,
784
820
  ) ?? fetchHelper(url, fetchOptions));
821
+ logger?.sendTelemetryEvent({ eventName: "SnapshotFetchResponseReceived" });
785
822
 
786
823
  return {
787
824
  odspResponse,
@@ -791,6 +828,14 @@ export const downloadSnapshot = mockify(
791
828
  },
792
829
  );
793
830
 
831
+ function isLocationRedirectionError(error: unknown): error is ILocationRedirectionError {
832
+ return (
833
+ typeof error === "object" &&
834
+ error !== null &&
835
+ (error as Partial<IOdspError>).errorType === DriverErrorTypes.locationRedirection
836
+ );
837
+ }
838
+
794
839
  function isRedeemSharingLinkError(
795
840
  odspResolvedUrl: IOdspResolvedUrl,
796
841
  error: Partial<IOdspError>,
@@ -639,6 +639,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
639
639
  tokenFetchOptions,
640
640
  loadingGroupId,
641
641
  options,
642
+ this.logger,
642
643
  this.snapshotFormatFetchType,
643
644
  controller,
644
645
  this.epochTracker,
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.92.0";
9
+ export const pkgVersion = "2.100.0";
@@ -110,6 +110,7 @@ export async function prefetchLatestSnapshot(
110
110
  tokenFetchOptions,
111
111
  loadingGroupId,
112
112
  snapshotOptions,
113
+ odspLogger,
113
114
  undefined,
114
115
  controller,
115
116
  );
@@ -174,7 +175,7 @@ export async function prefetchLatestSnapshot(
174
175
  }, 5000);
175
176
  })
176
177
  .catch((error) => {
177
- // Remove it from the non persistent cache if an error occured.
178
+ // Remove it from the non persistent cache if an error occurred.
178
179
  snapshotNonPersistentCache?.remove(nonPersistentCacheKey);
179
180
  snapshotContentsWithEpochP.reject(error);
180
181
  throw error;