@fluidframework/odsp-driver 2.112.0 → 2.113.0-411909

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 (38) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/packageVersion.d.ts +1 -1
  6. package/dist/packageVersion.d.ts.map +1 -1
  7. package/dist/packageVersion.js +1 -1
  8. package/dist/packageVersion.js.map +1 -1
  9. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts +37 -0
  10. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -0
  11. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js +70 -0
  12. package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -0
  13. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts +49 -0
  14. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts.map +1 -0
  15. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js +108 -0
  16. package/dist/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js.map +1 -0
  17. package/lib/index.d.ts +1 -0
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +2 -0
  20. package/lib/index.js.map +1 -1
  21. package/lib/packageVersion.d.ts +1 -1
  22. package/lib/packageVersion.d.ts.map +1 -1
  23. package/lib/packageVersion.js +1 -1
  24. package/lib/packageVersion.js.map +1 -1
  25. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts +37 -0
  26. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -0
  27. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js +66 -0
  28. package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -0
  29. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts +49 -0
  30. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.d.ts.map +1 -0
  31. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js +104 -0
  32. package/lib/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.js.map +1 -0
  33. package/package.json +11 -11
  34. package/src/index.ts +2 -0
  35. package/src/odspVersionManager/DEV.md +101 -29
  36. package/src/packageVersion.ts +1 -1
  37. package/src/pointInTimeDriver/odspPointInTimeDocumentService.ts +94 -0
  38. package/src/pointInTimeDriver/odspPointInTimeDocumentServiceFactory.ts +185 -0
@@ -0,0 +1,185 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
7
+ import type {
8
+ IDocumentService,
9
+ IPersistedCache,
10
+ IResolvedUrl,
11
+ } from "@fluidframework/driver-definitions/internal";
12
+ import type {
13
+ HostStoragePolicy,
14
+ IOdspUrlParts,
15
+ OdspResourceTokenFetchOptions,
16
+ TokenFetcher,
17
+ } from "@fluidframework/odsp-driver-definitions/internal";
18
+ import { UsageError, createChildLogger } from "@fluidframework/telemetry-utils/internal";
19
+
20
+ import { createOdspCacheAndTracker } from "../epochTracker.js";
21
+ import { NonPersistentCache } from "../odspCache.js";
22
+ import { OdspDocumentServiceFactoryCore } from "../odspDocumentServiceFactoryCore.js";
23
+ import { OdspDriverUrlResolver } from "../odspDriverUrlResolver.js";
24
+ import {
25
+ createOdspLogger,
26
+ getOdspResolvedUrl,
27
+ toInstrumentedOdspStorageTokenFetcher,
28
+ } from "../odspUtils.js";
29
+ import {
30
+ createOdspVersionManager,
31
+ type IOdspVersionManager,
32
+ } from "../odspVersionManager/index.js";
33
+
34
+ import { OdspPointInTimeDocumentService } from "./odspPointInTimeDocumentService.js";
35
+
36
+ /**
37
+ * ODSP document service factory that additionally supports point-in-time (sequence-number-based)
38
+ * loading.
39
+ *
40
+ * @remarks
41
+ * This extends {@link OdspDocumentServiceFactoryCore} with the ability to materialize a read-only
42
+ * document service at a requested Fluid sequence number. The loader detects this capability via the
43
+ * presence of {@link OdspPointInTimeDocumentServiceFactory.createPointInTimeDocumentService}, so
44
+ * hosts that want to load a container to a target sequence number must construct this factory
45
+ * (rather than the legacy `OdspDocumentServiceFactory`) and pass it to the loader.
46
+ *
47
+ * @internal
48
+ */
49
+ export class OdspPointInTimeDocumentServiceFactory extends OdspDocumentServiceFactoryCore {
50
+ /**
51
+ * The storage token fetcher, captured here because the base class keeps it private.
52
+ */
53
+ private readonly getStorageTokenForVersions: TokenFetcher<OdspResourceTokenFetchOptions>;
54
+
55
+ constructor(
56
+ getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,
57
+ getWebsocketToken: TokenFetcher<OdspResourceTokenFetchOptions> | undefined,
58
+ persistedCache?: IPersistedCache,
59
+ hostPolicy?: HostStoragePolicy,
60
+ ) {
61
+ super(getStorageToken, getWebsocketToken, persistedCache, hostPolicy);
62
+ this.getStorageTokenForVersions = getStorageToken;
63
+ }
64
+
65
+ /**
66
+ * Creates a document service that reads its snapshot from the closest file version at or before
67
+ * the target and its deltas from the live document, materializing a requested sequence number
68
+ * through replay.
69
+ */
70
+ public async createPointInTimeDocumentService(
71
+ resolvedUrl: IResolvedUrl,
72
+ targetSequenceNumber: number,
73
+ logger?: ITelemetryBaseLogger,
74
+ clientIsSummarizer?: boolean,
75
+ ): Promise<IDocumentService> {
76
+ const versionManager = await this.createVersionManager(
77
+ resolvedUrl,
78
+ logger,
79
+ clientIsSummarizer,
80
+ );
81
+ const baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
82
+ if (baseResult.kind === "noBaseVersion") {
83
+ const oldestResolvedSequenceDetail =
84
+ baseResult.oldestResolvedSeq === undefined
85
+ ? ""
86
+ : ` The oldest resolved file version is at sequence number ${baseResult.oldestResolvedSeq}.`;
87
+ throw new UsageError(
88
+ `No ODSP file version is available at or before sequence number ${targetSequenceNumber}.${oldestResolvedSequenceDetail}`,
89
+ );
90
+ }
91
+
92
+ const recoverableResolvedUrl = await this.resolveFileVersion(
93
+ resolvedUrl,
94
+ baseResult.base.versionId,
95
+ );
96
+ const recoverableDocumentService = await this.createDocumentService(
97
+ recoverableResolvedUrl,
98
+ logger,
99
+ clientIsSummarizer,
100
+ );
101
+ const liveDocumentService = await this.createDocumentService(
102
+ resolvedUrl,
103
+ logger,
104
+ clientIsSummarizer,
105
+ );
106
+ return new OdspPointInTimeDocumentService(
107
+ recoverableResolvedUrl,
108
+ recoverableDocumentService,
109
+ liveDocumentService,
110
+ targetSequenceNumber,
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Builds an IOdspVersionManager for the given file, which enumerates the file's stored
116
+ * versions and resolves the closest version at or before a target sequence number.
117
+ *
118
+ * @remarks
119
+ * This wires up the plumbing the version manager needs to talk to ODSP: it resolves the URL to
120
+ * its ODSP parts (site/drive/item), creates a scoped child logger, an epoch tracker (from a fresh
121
+ * NonPersistentCache, since only the tracker is needed for consistency checks), and an
122
+ * instrumented storage-token/auth-header fetcher. The resulting manager is used by
123
+ * {@link OdspPointInTimeDocumentServiceFactory.createPointInTimeDocumentService} to pick the base
124
+ * snapshot for point-in-time loading.
125
+ */
126
+ private async createVersionManager(
127
+ resolvedUrl: IResolvedUrl,
128
+ logger?: ITelemetryBaseLogger,
129
+ clientIsSummarizer?: boolean,
130
+ ): Promise<IOdspVersionManager> {
131
+ const odspLogger = createOdspLogger(logger);
132
+ const extLogger = createChildLogger({ logger: odspLogger });
133
+ const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
134
+ const urlParts: IOdspUrlParts = {
135
+ siteUrl: odspResolvedUrl.siteUrl,
136
+ driveId: odspResolvedUrl.driveId,
137
+ itemId: odspResolvedUrl.itemId,
138
+ };
139
+ // Only the epochTracker from the returned cacheAndTracker is used below, so a fresh
140
+ // NonPersistentCache is sufficient here.
141
+ const cacheAndTracker = createOdspCacheAndTracker(
142
+ this.persistedCache,
143
+ new NonPersistentCache(),
144
+ {
145
+ resolvedUrl: odspResolvedUrl,
146
+ docId: odspResolvedUrl.hashedDocumentId,
147
+ fileVersion: odspResolvedUrl.fileVersion,
148
+ },
149
+ extLogger,
150
+ clientIsSummarizer,
151
+ );
152
+ const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
153
+ extLogger,
154
+ urlParts,
155
+ this.getStorageTokenForVersions,
156
+ );
157
+ return createOdspVersionManager({
158
+ urlParts,
159
+ getAuthHeader,
160
+ epochTracker: cacheAndTracker.epochTracker,
161
+ logger: extLogger,
162
+ });
163
+ }
164
+
165
+ private async resolveFileVersion(
166
+ resolvedUrl: IResolvedUrl,
167
+ fileVersion: string,
168
+ ): Promise<IResolvedUrl> {
169
+ const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
170
+ const query = new URLSearchParams({
171
+ driveId: odspResolvedUrl.driveId,
172
+ itemId: odspResolvedUrl.itemId,
173
+ fileVersion,
174
+ });
175
+ if (odspResolvedUrl.dataStorePath !== undefined) {
176
+ query.set("path", odspResolvedUrl.dataStorePath);
177
+ }
178
+ if (odspResolvedUrl.codeHint?.containerPackageName !== undefined) {
179
+ query.set("containerPackageName", odspResolvedUrl.codeHint.containerPackageName);
180
+ }
181
+ return new OdspDriverUrlResolver().resolve({
182
+ url: `${odspResolvedUrl.siteUrl}?${query.toString()}`,
183
+ });
184
+ }
185
+ }