@fluidframework/odsp-driver 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.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 (149) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/WriteBufferUtils.d.ts.map +1 -1
  3. package/dist/WriteBufferUtils.js +0 -1
  4. package/dist/WriteBufferUtils.js.map +1 -1
  5. package/dist/contractsPublic.d.ts.map +1 -1
  6. package/dist/contractsPublic.js.map +1 -1
  7. package/dist/createFile.d.ts +2 -6
  8. package/dist/createFile.d.ts.map +1 -1
  9. package/dist/createFile.js +13 -125
  10. package/dist/createFile.js.map +1 -1
  11. package/dist/createNewUtils.d.ts +16 -0
  12. package/dist/createNewUtils.d.ts.map +1 -1
  13. package/dist/createNewUtils.js +131 -1
  14. package/dist/createNewUtils.js.map +1 -1
  15. package/dist/fetchSnapshot.js +1 -1
  16. package/dist/fetchSnapshot.js.map +1 -1
  17. package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
  18. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
  19. package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  20. package/dist/odspDelayLoadedDeltaStream.d.ts +75 -0
  21. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -0
  22. package/dist/odspDelayLoadedDeltaStream.js +259 -0
  23. package/dist/odspDelayLoadedDeltaStream.js.map +1 -0
  24. package/dist/odspDocumentDeltaConnection.d.ts +1 -3
  25. package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
  26. package/dist/odspDocumentDeltaConnection.js +6 -6
  27. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  28. package/dist/odspDocumentService.d.ts +10 -25
  29. package/dist/odspDocumentService.d.ts.map +1 -1
  30. package/dist/odspDocumentService.js +71 -204
  31. package/dist/odspDocumentService.js.map +1 -1
  32. package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
  33. package/dist/odspDocumentServiceFactory.js +1 -2
  34. package/dist/odspDocumentServiceFactory.js.map +1 -1
  35. package/dist/odspDocumentServiceFactoryCore.d.ts +1 -3
  36. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  37. package/dist/odspDocumentServiceFactoryCore.js +3 -3
  38. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  39. package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
  40. package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
  41. package/dist/odspDocumentServiceFactoryWithCodeSplit.js +5 -20
  42. package/dist/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
  43. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  44. package/dist/odspDocumentStorageManager.js +0 -2
  45. package/dist/odspDocumentStorageManager.js.map +1 -1
  46. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
  47. package/dist/odspDocumentStorageServiceBase.js +0 -2
  48. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  49. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  50. package/dist/odspSummaryUploadManager.js +0 -2
  51. package/dist/odspSummaryUploadManager.js.map +1 -1
  52. package/dist/odspUtils.d.ts +10 -1
  53. package/dist/odspUtils.d.ts.map +1 -1
  54. package/dist/odspUtils.js +5 -1
  55. package/dist/odspUtils.js.map +1 -1
  56. package/dist/packageVersion.d.ts +1 -1
  57. package/dist/packageVersion.js +1 -1
  58. package/dist/packageVersion.js.map +1 -1
  59. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  60. package/dist/zipItDataRepresentationUtils.js +0 -1
  61. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  62. package/lib/WriteBufferUtils.d.ts.map +1 -1
  63. package/lib/WriteBufferUtils.js +0 -1
  64. package/lib/WriteBufferUtils.js.map +1 -1
  65. package/lib/contractsPublic.d.ts.map +1 -1
  66. package/lib/contractsPublic.js.map +1 -1
  67. package/lib/createFile.d.ts +2 -6
  68. package/lib/createFile.d.ts.map +1 -1
  69. package/lib/createFile.js +15 -126
  70. package/lib/createFile.js.map +1 -1
  71. package/lib/createNewUtils.d.ts +16 -0
  72. package/lib/createNewUtils.d.ts.map +1 -1
  73. package/lib/createNewUtils.js +129 -1
  74. package/lib/createNewUtils.js.map +1 -1
  75. package/lib/fetchSnapshot.js +1 -1
  76. package/lib/fetchSnapshot.js.map +1 -1
  77. package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
  78. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
  79. package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
  80. package/lib/odspDelayLoadedDeltaStream.d.ts +75 -0
  81. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -0
  82. package/lib/odspDelayLoadedDeltaStream.js +255 -0
  83. package/lib/odspDelayLoadedDeltaStream.js.map +1 -0
  84. package/lib/odspDocumentDeltaConnection.d.ts +1 -3
  85. package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
  86. package/lib/odspDocumentDeltaConnection.js +6 -6
  87. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  88. package/lib/odspDocumentService.d.ts +10 -25
  89. package/lib/odspDocumentService.d.ts.map +1 -1
  90. package/lib/odspDocumentService.js +56 -207
  91. package/lib/odspDocumentService.js.map +1 -1
  92. package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
  93. package/lib/odspDocumentServiceFactory.js +1 -2
  94. package/lib/odspDocumentServiceFactory.js.map +1 -1
  95. package/lib/odspDocumentServiceFactoryCore.d.ts +1 -3
  96. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  97. package/lib/odspDocumentServiceFactoryCore.js +3 -3
  98. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  99. package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
  100. package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
  101. package/lib/odspDocumentServiceFactoryWithCodeSplit.js +5 -1
  102. package/lib/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
  103. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  104. package/lib/odspDocumentStorageManager.js +0 -2
  105. package/lib/odspDocumentStorageManager.js.map +1 -1
  106. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
  107. package/lib/odspDocumentStorageServiceBase.js +0 -2
  108. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  109. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  110. package/lib/odspSummaryUploadManager.js +0 -2
  111. package/lib/odspSummaryUploadManager.js.map +1 -1
  112. package/lib/odspUtils.d.ts +10 -1
  113. package/lib/odspUtils.d.ts.map +1 -1
  114. package/lib/odspUtils.js +3 -0
  115. package/lib/odspUtils.js.map +1 -1
  116. package/lib/packageVersion.d.ts +1 -1
  117. package/lib/packageVersion.js +1 -1
  118. package/lib/packageVersion.js.map +1 -1
  119. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  120. package/lib/zipItDataRepresentationUtils.js +0 -1
  121. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  122. package/package.json +13 -12
  123. package/src/WriteBufferUtils.ts +0 -1
  124. package/src/contractsPublic.ts +0 -1
  125. package/src/createFile.ts +23 -168
  126. package/src/createNewUtils.ts +188 -2
  127. package/src/fetchSnapshot.ts +1 -1
  128. package/src/localOdspDriver/localOdspDocumentServiceFactory.ts +0 -1
  129. package/src/odspDelayLoadedDeltaStream.ts +359 -0
  130. package/src/odspDocumentDeltaConnection.ts +4 -7
  131. package/src/odspDocumentService.ts +63 -283
  132. package/src/odspDocumentServiceFactory.ts +0 -2
  133. package/src/odspDocumentServiceFactoryCore.ts +1 -3
  134. package/src/odspDocumentServiceFactoryWithCodeSplit.ts +4 -1
  135. package/src/odspDocumentStorageManager.ts +0 -4
  136. package/src/odspDocumentStorageServiceBase.ts +0 -3
  137. package/src/odspSummaryUploadManager.ts +0 -4
  138. package/src/odspUtils.ts +16 -1
  139. package/src/packageVersion.ts +1 -1
  140. package/src/zipItDataRepresentationUtils.ts +0 -1
  141. package/dist/getSocketIo.d.ts +0 -11
  142. package/dist/getSocketIo.d.ts.map +0 -1
  143. package/dist/getSocketIo.js +0 -20
  144. package/dist/getSocketIo.js.map +0 -1
  145. package/lib/getSocketIo.d.ts +0 -11
  146. package/lib/getSocketIo.d.ts.map +0 -1
  147. package/lib/getSocketIo.js +0 -13
  148. package/lib/getSocketIo.js.map +0 -1
  149. package/src/getSocketIo.ts +0 -14
@@ -4,13 +4,11 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert, performance } from "@fluidframework/common-utils";
7
+ import { assert } from "@fluidframework/common-utils";
8
8
  import {
9
9
  ChildLogger,
10
- IFluidErrorBase,
11
10
  loggerToMonitoringContext,
12
11
  MonitoringContext,
13
- normalizeError,
14
12
  } from "@fluidframework/telemetry-utils";
15
13
  import {
16
14
  IDocumentDeltaConnection,
@@ -19,9 +17,7 @@ import {
19
17
  IResolvedUrl,
20
18
  IDocumentStorageService,
21
19
  IDocumentServicePolicies,
22
- DriverErrorType,
23
20
  } from "@fluidframework/driver-definitions";
24
- import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
25
21
  import {
26
22
  IClient,
27
23
  ISequencedDocumentMessage,
@@ -32,31 +28,32 @@ import {
32
28
  IEntry,
33
29
  HostStoragePolicy,
34
30
  InstrumentedStorageTokenFetcher,
35
- OdspErrorType,
36
31
  } from "@fluidframework/odsp-driver-definitions";
37
- import { hasFacetCodes } from "@fluidframework/odsp-doclib-utils";
38
- import type { io as SocketIOClientStatic } from "socket.io-client";
39
- import { HostStoragePolicyInternal, ISocketStorageDiscovery } from "./contracts";
32
+ import { HostStoragePolicyInternal } from "./contracts";
40
33
  import { IOdspCache } from "./odspCache";
41
34
  import { OdspDeltaStorageService, OdspDeltaStorageWithCache } from "./odspDeltaStorageService";
42
- import { OdspDocumentDeltaConnection } from "./odspDocumentDeltaConnection";
43
35
  import { OdspDocumentStorageService } from "./odspDocumentStorageManager";
44
- import { getWithRetryForTokenRefresh, getOdspResolvedUrl, TokenFetchOptionsEx } from "./odspUtils";
45
- import { fetchJoinSession } from "./vroom";
36
+ import { getOdspResolvedUrl } from "./odspUtils";
46
37
  import { isOdcOrigin } from "./odspUrlHelper";
47
38
  import { EpochTracker } from "./epochTracker";
48
39
  import { OpsCache } from "./opsCaching";
49
40
  import { RetryErrorsStorageAdapter } from "./retryErrorsStorageAdapter";
50
- import { pkgVersion as driverVersion } from "./packageVersion";
41
+ import type { OdspDelayLoadedDeltaStream } from "./odspDelayLoadedDeltaStream";
51
42
 
52
43
  /**
53
44
  * The DocumentService manages the Socket.IO connection and manages routing requests to connected
54
45
  * clients
55
46
  */
56
47
  export class OdspDocumentService implements IDocumentService {
57
- private _policies: IDocumentServicePolicies;
58
- // Timer which runs and executes the join session call after intervals.
59
- private joinSessionRefreshTimer: ReturnType<typeof setTimeout> | undefined;
48
+ private readonly _policies: IDocumentServicePolicies;
49
+
50
+ // Promise to load socket module only once.
51
+ private socketModuleP: Promise<OdspDelayLoadedDeltaStream> | undefined;
52
+
53
+ private odspDelayLoadedDeltaStream: OdspDelayLoadedDeltaStream | undefined;
54
+
55
+ private odspSocketModuleLoaded: boolean = false;
56
+
60
57
  /**
61
58
  * @param resolvedUrl - resolved url identifying document that will be managed by returned service instance.
62
59
  * @param getStorageToken - function that can provide the storage token. This is is also referred to as
@@ -65,7 +62,6 @@ export class OdspDocumentService implements IDocumentService {
65
62
  * to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
66
63
  * response payload.
67
64
  * @param logger - a logger that can capture performance and diagnostic information
68
- * @param socketIoClientFactory - A factory that returns a promise to the socket io library required by the driver
69
65
  * @param cache - This caches response for joinSession.
70
66
  * @param hostPolicy - This host constructed policy which customizes service behavior.
71
67
  * @param epochTracker - This helper class which adds epoch to backend calls made by returned service instance.
@@ -76,7 +72,6 @@ export class OdspDocumentService implements IDocumentService {
76
72
  getStorageToken: InstrumentedStorageTokenFetcher,
77
73
  getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
78
74
  logger: ITelemetryLogger,
79
- socketIoClientFactory: () => Promise<typeof SocketIOClientStatic>,
80
75
  cache: IOdspCache,
81
76
  hostPolicy: HostStoragePolicy,
82
77
  epochTracker: EpochTracker,
@@ -88,7 +83,6 @@ export class OdspDocumentService implements IDocumentService {
88
83
  getStorageToken,
89
84
  getWebsocketToken,
90
85
  logger,
91
- socketIoClientFactory,
92
86
  cache,
93
87
  hostPolicy,
94
88
  epochTracker,
@@ -101,16 +95,10 @@ export class OdspDocumentService implements IDocumentService {
101
95
 
102
96
  private readonly mc: MonitoringContext;
103
97
 
104
- private readonly joinSessionKey: string;
105
-
106
98
  private readonly hostPolicy: HostStoragePolicyInternal;
107
99
 
108
100
  private _opsCache?: OpsCache;
109
101
 
110
- private currentConnection?: OdspDocumentDeltaConnection;
111
-
112
- private relayServiceTenantAndSessionId: string | undefined;
113
-
114
102
  /**
115
103
  * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
116
104
  * @param getStorageToken - function that can provide the storage token. This is is also referred to as
@@ -130,7 +118,6 @@ export class OdspDocumentService implements IDocumentService {
130
118
  private readonly getStorageToken: InstrumentedStorageTokenFetcher,
131
119
  private readonly getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
132
120
  logger: ITelemetryLogger,
133
- private readonly socketIoClientFactory: () => Promise<typeof SocketIOClientStatic>,
134
121
  private readonly cache: IOdspCache,
135
122
  hostPolicy: HostStoragePolicy,
136
123
  private readonly epochTracker: EpochTracker,
@@ -142,7 +129,6 @@ export class OdspDocumentService implements IDocumentService {
142
129
  storageOnly: odspResolvedUrl.fileVersion !== undefined,
143
130
  };
144
131
 
145
- this.joinSessionKey = `${this.odspResolvedUrl.hashedDocumentId}/joinsession`;
146
132
  this.mc = loggerToMonitoringContext(
147
133
  ChildLogger.create(logger,
148
134
  undefined,
@@ -182,13 +168,14 @@ export class OdspDocumentService implements IDocumentService {
182
168
  this.epochTracker,
183
169
  // flushCallback
184
170
  async () => {
185
- if (this.currentConnection !== undefined && !this.currentConnection.disposed) {
186
- return this.currentConnection.flush();
171
+ const currentConnection = this.odspDelayLoadedDeltaStream?.currentDeltaConnection;
172
+ if (currentConnection !== undefined && !currentConnection.disposed) {
173
+ return currentConnection.flush();
187
174
  }
188
175
  throw new Error("Disconnected while uploading summary (attempt to perform flush())");
189
176
  },
190
177
  () => {
191
- return this.relayServiceTenantAndSessionId;
178
+ return this.odspDelayLoadedDeltaStream?.relayServiceTenantAndSessionId;
192
179
  },
193
180
  this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
194
181
  );
@@ -219,282 +206,76 @@ export class OdspDocumentService implements IDocumentService {
219
206
  this.mc.logger,
220
207
  batchSize,
221
208
  concurrency,
209
+ // Get Ops from storage callback.
222
210
  async (from, to, telemetryProps, fetchReason) => service.get(from, to, telemetryProps, fetchReason),
211
+ // Get cachedOps Callback.
223
212
  async (from, to) => {
224
213
  const res = await this.opsCache?.get(from, to);
225
214
  return res as ISequencedDocumentMessage[] ?? [];
226
215
  },
216
+ // Ops requestFromSocket Callback.
227
217
  (from, to) => {
228
- if (this.currentConnection !== undefined && !this.currentConnection.disposed) {
229
- this.currentConnection.requestOps(from, to);
218
+ const currentConnection = this.odspDelayLoadedDeltaStream?.currentDeltaConnection;
219
+ if (currentConnection !== undefined && !currentConnection.disposed) {
220
+ currentConnection.requestOps(from, to);
230
221
  }
231
222
  },
232
223
  (ops: ISequencedDocumentMessage[]) => this.opsReceived(ops),
233
224
  );
234
225
  }
235
226
 
236
- /** Annotate the given error indicating which connection step failed */
237
- private annotateConnectionError(
238
- error: any,
239
- failedConnectionStep: string,
240
- separateTokenRequest: boolean,
241
- ): IFluidErrorBase {
242
- return normalizeError(error, { props: {
243
- failedConnectionStep,
244
- separateTokenRequest,
245
- } });
246
- }
247
-
248
227
  /**
249
228
  * Connects to a delta stream endpoint for emitting ops.
250
229
  *
251
230
  * @returns returns the document delta stream service for onedrive/sharepoint driver.
252
231
  */
253
232
  public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {
254
- assert(this.currentConnection === undefined, 0x4ad /* Should not be called when connection is already present! */);
255
- // Attempt to connect twice, in case we used expired token.
256
- return getWithRetryForTokenRefresh<IDocumentDeltaConnection>(async (options) => {
257
- // Presence of getWebsocketToken callback dictates whether callback is used for fetching
258
- // websocket token or whether it is returned with joinSession response payload
259
- const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;
260
- const websocketTokenPromise = requestWebsocketTokenFromJoinSession
261
- ? Promise.resolve(null)
262
- : this.getWebsocketToken!(options);
263
-
264
- const annotateAndRethrowConnectionError = (step: string) => (error: any) => {
265
- throw this.annotateConnectionError(error, step, !requestWebsocketTokenFromJoinSession);
266
- };
267
-
268
- const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession, options);
269
- const [websocketEndpoint, websocketToken, io] =
270
- await Promise.all([
271
- joinSessionPromise.catch(annotateAndRethrowConnectionError("joinSession")),
272
- websocketTokenPromise.catch(annotateAndRethrowConnectionError("getWebsocketToken")),
273
- this.socketIoClientFactory().catch(annotateAndRethrowConnectionError("socketIoClientFactory")),
274
- ]);
275
-
276
- const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken ?? null);
277
- if (finalWebsocketToken === null) {
278
- throw this.annotateConnectionError(
279
- new NonRetryableError(
280
- "Websocket token is null",
281
- OdspErrorType.fetchTokenError,
282
- { driverVersion },
283
- ),
284
- "getWebsocketToken",
285
- !requestWebsocketTokenFromJoinSession);
286
- }
287
- try {
288
- const connection = await this.createDeltaConnection(
289
- websocketEndpoint.tenantId,
290
- websocketEndpoint.id,
291
- finalWebsocketToken,
292
- io,
293
- client,
294
- websocketEndpoint.deltaStreamSocketUrl);
295
- connection.on("op", (documentId, ops: ISequencedDocumentMessage[]) => {
296
- this.opsReceived(ops);
297
- });
298
- // On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again
299
- // get the auth error on reconnecting and face latency.
300
- connection.once("disconnect", (error: any) => {
301
- // Clear the join session refresh timer so that it can be restarted on reconnection.
302
- this.clearJoinSessionTimer();
303
- if (typeof error === "object" && error !== null
304
- && error.errorType === DriverErrorType.authorizationError) {
305
- this.cache.sessionJoinCache.remove(this.joinSessionKey);
306
- }
307
- // If we hit this assert, it means that "disconnect" event is emitted before the connection went through
308
- // dispose flow which is not correct and could lead to a bunch of erros.
309
- assert(connection.disposed, 0x4ae /* Connection should be disposed by now */);
310
- this.currentConnection = undefined;
311
- });
312
- this.currentConnection = connection;
313
- return connection;
314
- } catch (error) {
315
- this.cache.sessionJoinCache.remove(this.joinSessionKey);
316
-
317
- const normalizedError = this.annotateConnectionError(
318
- error,
319
- "createDeltaConnection",
320
- !requestWebsocketTokenFromJoinSession);
321
- if (typeof error === "object" && error !== null) {
322
- normalizedError.addTelemetryProperties({ socketDocumentId: websocketEndpoint.id });
323
- }
324
- throw normalizedError;
325
- }
326
- });
327
- }
328
-
329
- private clearJoinSessionTimer() {
330
- if (this.joinSessionRefreshTimer !== undefined) {
331
- clearTimeout(this.joinSessionRefreshTimer);
332
- this.joinSessionRefreshTimer = undefined;
233
+ if (this.socketModuleP === undefined) {
234
+ this.socketModuleP = this.getDelayLoadedDeltaStream();
333
235
  }
334
- }
335
-
336
- private async scheduleJoinSessionRefresh(delta: number) {
337
- await new Promise<void>((resolve, reject) => {
338
- this.joinSessionRefreshTimer = setTimeout(() => {
339
- getWithRetryForTokenRefresh(async (options) => {
340
- await this.joinSession(false, options);
341
- resolve();
342
- }).catch((error) => {
343
- reject(error);
344
- });
345
- }, delta);
346
- });
347
- }
348
-
349
- private async joinSession(
350
- requestSocketToken: boolean,
351
- options: TokenFetchOptionsEx,
352
- ) {
353
- const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {
354
- if (hasFacetCodes(e) && e.facetCodes !== undefined) {
355
- for (const code of e.facetCodes) {
356
- switch (code) {
357
- case "sessionForbiddenOnPreservedFiles":
358
- case "sessionForbiddenOnModerationEnabledLibrary":
359
- case "sessionForbiddenOnRequireCheckout":
360
- // This document can only be opened in storage-only mode.
361
- // DeltaManager will recognize this error
362
- // and load without a delta stream connection.
363
- this._policies = { ...this._policies, storageOnly: true };
364
- throw new DeltaStreamConnectionForbiddenError(code, { driverVersion });
365
- default:
366
- continue;
367
- }
368
- }
369
- }
370
- throw e;
371
- });
372
- this.relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;
373
- return response;
374
- }
375
-
376
- private async joinSessionCore(
377
- requestSocketToken: boolean,
378
- options: TokenFetchOptionsEx,
379
- ): Promise<ISocketStorageDiscovery> {
380
- const disableJoinSessionRefresh = this.mc.config.getBoolean("Fluid.Driver.Odsp.disableJoinSessionRefresh");
381
- const executeFetch = async () => {
382
- const joinSessionResponse = await fetchJoinSession(
383
- this.odspResolvedUrl,
384
- "opStream/joinSession",
385
- "POST",
386
- this.mc.logger,
387
- this.getStorageToken,
388
- this.epochTracker,
389
- requestSocketToken,
390
- options,
391
- disableJoinSessionRefresh,
392
- this.hostPolicy.sessionOptions?.unauthenticatedUserDisplayName,
393
- );
394
- return {
395
- entryTime: Date.now(),
396
- joinSessionResponse,
397
- };
398
- };
399
-
400
- const getResponseAndRefreshAfterDeltaMs = async () => {
401
- const _response = await this.cache.sessionJoinCache.addOrGet(this.joinSessionKey, executeFetch);
402
- // If the response does not contain refreshSessionDurationSeconds, then treat it as old flow and let the
403
- // cache entry to be treated as expired after 1 hour.
404
- _response.joinSessionResponse.refreshSessionDurationSeconds =
405
- _response.joinSessionResponse.refreshSessionDurationSeconds ?? 3600;
406
- return {
407
- ..._response,
408
- refreshAfterDeltaMs: this.calculateJoinSessionRefreshDelta(
409
- _response.entryTime, _response.joinSessionResponse.refreshSessionDurationSeconds),
410
- };
411
- };
412
- let response = await getResponseAndRefreshAfterDeltaMs();
413
- // This means that the cached entry has expired(This should not be possible if the response is fetched
414
- // from the network call). In this case we remove the cached entry and fetch the new response.
415
- if (response.refreshAfterDeltaMs <= 0) {
416
- this.cache.sessionJoinCache.remove(this.joinSessionKey);
417
- response = await getResponseAndRefreshAfterDeltaMs();
418
- }
419
- if (!disableJoinSessionRefresh) {
420
- const props = {
421
- entryTime: response.entryTime,
422
- refreshSessionDurationSeconds:
423
- response.joinSessionResponse.refreshSessionDurationSeconds,
424
- refreshAfterDeltaMs: response.refreshAfterDeltaMs,
425
- };
426
- if (response.refreshAfterDeltaMs > 0) {
427
- this.scheduleJoinSessionRefresh(response.refreshAfterDeltaMs)
428
- .catch((error) => {
429
- const canRetry = canRetryOnError(error);
430
- // Only record error event in case it is non retriable.
431
- if (!canRetry) {
432
- this.mc.logger.sendErrorEvent({
433
- eventName: "JoinSessionRefreshError",
434
- details: JSON.stringify(props),
435
- },
436
- error,
437
- );
438
- }
439
- });
440
- } else {
441
- // Logging just for informational purposes to help with debugging as this is a new feature.
442
- this.mc.logger.sendTelemetryEvent({
443
- eventName: "JoinSessionRefreshNotScheduled",
444
- details: JSON.stringify(props),
445
- });
446
- }
447
- }
448
- return response.joinSessionResponse;
449
- }
450
-
451
- private calculateJoinSessionRefreshDelta(responseFetchTime: number, refreshSessionDurationSeconds: number) {
452
- // 30 seconds is buffer time to refresh the session.
453
- return responseFetchTime + ((refreshSessionDurationSeconds * 1000) - 30000) - Date.now();
236
+ return this.socketModuleP
237
+ .then(async (m) => {
238
+ this.odspSocketModuleLoaded = true;
239
+ return m.connectToDeltaStream(client);
240
+ })
241
+ .catch((error) => {
242
+ // Setting undefined in case someone tries to recover from module failure by calling again.
243
+ this.socketModuleP = undefined;
244
+ this.odspSocketModuleLoaded = false;
245
+ throw error;
246
+ });
454
247
  }
455
248
 
456
249
  /**
457
- * Creats a connection to the given delta stream endpoint
458
- *
459
- * @param tenantId - the ID of the tenant
460
- * @param documentId - document ID
461
- * @param token - authorization token for delta service
462
- * @param io - websocket library
463
- * @param client - information about the client
464
- * @param webSocketUrl - websocket URL
250
+ * This dynamically imports the module for loading the delta connection. In many cases the delta stream, is not
251
+ * required during the critical load flow. So this way we don't have to bundle this in the initial bundle and can
252
+ * import this later on when required.
253
+ * @returns - delta stream object.
465
254
  */
466
- private async createDeltaConnection(
467
- tenantId: string,
468
- documentId: string,
469
- token: string | null,
470
- io: typeof SocketIOClientStatic,
471
- client: IClient,
472
- webSocketUrl: string,
473
- ): Promise<OdspDocumentDeltaConnection> {
474
- const startTime = performance.now();
475
- const connection = await OdspDocumentDeltaConnection.create(
476
- tenantId,
477
- documentId,
478
- token,
479
- io,
480
- client,
481
- webSocketUrl,
482
- this.mc.logger,
483
- 60000,
255
+ private async getDelayLoadedDeltaStream() {
256
+ assert(this.odspSocketModuleLoaded === false, 0x507 /* Should be loaded only once */);
257
+ const module = await import(/* webpackChunkName: "socketModule" */ "./odspDelayLoadedDeltaStream")
258
+ .then((m) => {
259
+ this.mc.logger.sendTelemetryEvent({ eventName: "SocketModuleLoaded" });
260
+ return m;
261
+ })
262
+ .catch((error) => {
263
+ this.mc.logger.sendErrorEvent( { eventName: "SocketModuleLoadFailed" }, error);
264
+ throw error;
265
+ });
266
+ this.odspDelayLoadedDeltaStream = new module.OdspDelayLoadedDeltaStream(
267
+ this.odspResolvedUrl,
268
+ this._policies,
269
+ this.getStorageToken,
270
+ this.getWebsocketToken,
271
+ this.mc,
272
+ this.cache,
273
+ this.hostPolicy,
484
274
  this.epochTracker,
275
+ (ops: ISequencedDocumentMessage[]) => this.opsReceived(ops),
485
276
  this.socketReferenceKeyPrefix,
486
277
  );
487
- const duration = performance.now() - startTime;
488
- // This event happens rather often, so it adds up to cost of telemetry.
489
- // Given that most reconnects result in reusing socket and happen very quickly,
490
- // report event only if it took longer than threshold.
491
- if (duration >= 2000) {
492
- this.mc.logger.sendPerformanceEvent({
493
- eventName: "ConnectionSuccess",
494
- duration,
495
- });
496
- }
497
- return connection;
278
+ return this.odspDelayLoadedDeltaStream;
498
279
  }
499
280
 
500
281
  public dispose(error?: any) {
@@ -508,9 +289,8 @@ export class OdspDocumentService implements IDocumentService {
508
289
  this._opsCache?.flushOps();
509
290
  }
510
291
  this._opsCache?.dispose();
511
- this.clearJoinSessionTimer();
512
- this.currentConnection?.dispose();
513
- this.currentConnection = undefined;
292
+ // Only need to dipose this, if it is already loaded.
293
+ this.odspDelayLoadedDeltaStream?.dispose();
514
294
  }
515
295
 
516
296
  protected get opsCache() {
@@ -11,7 +11,6 @@ import {
11
11
  HostStoragePolicy,
12
12
  } from "@fluidframework/odsp-driver-definitions";
13
13
  import { OdspDocumentServiceFactoryCore } from "./odspDocumentServiceFactoryCore";
14
- import { getSocketIo } from "./getSocketIo";
15
14
  // eslint-disable-next-line import/no-internal-modules
16
15
  import { LocalOdspDocumentServiceFactory } from "./localOdspDriver/localOdspDocumentServiceFactory";
17
16
 
@@ -29,7 +28,6 @@ export class OdspDocumentServiceFactory extends OdspDocumentServiceFactoryCore {
29
28
  super(
30
29
  getStorageToken,
31
30
  getWebsocketToken,
32
- async () => getSocketIo(),
33
31
  persistedCache,
34
32
  hostPolicy,
35
33
  );
@@ -31,7 +31,6 @@ import {
31
31
  ShareLinkTypes,
32
32
  ISharingLinkKind,
33
33
  } from "@fluidframework/odsp-driver-definitions";
34
- import type { io as SocketIOClientStatic } from "socket.io-client";
35
34
  import { v4 as uuid } from "uuid";
36
35
  import {
37
36
  LocalPersistentCache,
@@ -90,6 +89,7 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
90
89
 
91
90
  const createShareLinkParam = getSharingLinkParams(this.hostPolicy, searchParams);
92
91
  const newFileInfo: INewFileInfo = {
92
+ type: 'New',
93
93
  driveId: odspResolvedUrl.driveId,
94
94
  siteUrl: odspResolvedUrl.siteUrl,
95
95
  filePath,
@@ -158,7 +158,6 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
158
158
  constructor(
159
159
  private readonly getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,
160
160
  private readonly getWebsocketToken: TokenFetcher<OdspResourceTokenFetchOptions> | undefined,
161
- private readonly getSocketIOClient: () => Promise<typeof SocketIOClientStatic>,
162
161
  protected persistedCache: IPersistedCache = new LocalPersistentCache(),
163
162
  private readonly hostPolicy: HostStoragePolicy = {},
164
163
  ) {
@@ -222,7 +221,6 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
222
221
  storageTokenFetcher,
223
222
  webSocketTokenFetcher,
224
223
  odspLogger,
225
- this.getSocketIOClient,
226
224
  cacheAndTracker.cache,
227
225
  this.hostPolicy,
228
226
  cacheAndTracker.epochTracker,
@@ -12,6 +12,10 @@ import {
12
12
  } from "@fluidframework/odsp-driver-definitions";
13
13
  import { OdspDocumentServiceFactoryCore } from "./odspDocumentServiceFactoryCore";
14
14
 
15
+ /**
16
+ * @deprecated - This is deprecated in favour of OdspDocumentServiceFactory as the socket io is now loaded inside the
17
+ * other dynamically imported module.
18
+ */
15
19
  export class OdspDocumentServiceFactoryWithCodeSplit
16
20
  extends OdspDocumentServiceFactoryCore
17
21
  implements IDocumentServiceFactory {
@@ -24,7 +28,6 @@ export class OdspDocumentServiceFactoryWithCodeSplit
24
28
  super(
25
29
  getStorageToken,
26
30
  getWebsocketToken,
27
- async () => import("./getSocketIo").then((m) => m.getSocketIo()),
28
31
  persistedCache,
29
32
  hostPolicy,
30
33
  );
@@ -48,8 +48,6 @@ import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase
48
48
 
49
49
  export const defaultSummarizerCacheExpiryTimeout: number = 60 * 1000; // 60 seconds.
50
50
 
51
- /* eslint-disable max-len */
52
-
53
51
  // An implementation of Promise.race that gives you the winner of the promise race
54
52
  async function promiseRaceWithWinner<T>(promises: Promise<T>[]): Promise<{ index: number; value: T; }> {
55
53
  return new Promise((resolve, reject) => {
@@ -568,5 +566,3 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
568
566
  });
569
567
  }
570
568
  }
571
-
572
- /* eslint-enable max-len */
@@ -15,8 +15,6 @@ import * as api from "@fluidframework/protocol-definitions";
15
15
  import { IConfigProvider } from "@fluidframework/telemetry-utils";
16
16
  import { ISnapshotContents } from "./odspPublicUtils";
17
17
 
18
- /* eslint-disable max-len */
19
-
20
18
  const maximumCacheDurationMs: FiveDaysMs = 432000000; // 5 * 24 * 60 * 60 * 1000 = 5 days in ms
21
19
 
22
20
  class BlobCache {
@@ -288,4 +286,3 @@ export abstract class OdspDocumentStorageServiceBase implements IDocumentStorage
288
286
  }
289
287
  }
290
288
 
291
- /* eslint-enable max-len */
@@ -22,8 +22,6 @@ import { EpochTracker } from "./epochTracker";
22
22
  import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
23
23
  import { getWithRetryForTokenRefresh } from "./odspUtils";
24
24
 
25
- /* eslint-disable max-len */
26
-
27
25
  /**
28
26
  * This class manages a summary upload. When it receives a call to upload summary, it converts the summary tree into
29
27
  * a snapshot tree and then uploads that to the server.
@@ -239,5 +237,3 @@ export class OdspSummaryUploadManager {
239
237
  return { snapshotTree, blobs };
240
238
  }
241
239
  }
242
-
243
- /* eslint-enable max-len */
package/src/odspUtils.ts CHANGED
@@ -225,9 +225,15 @@ export async function fetchAndParseAsJSONHelper<T>(
225
225
  return res;
226
226
  }
227
227
 
228
- export interface INewFileInfo {
228
+
229
+ export interface IFileInfoBase {
230
+ type: 'New' | 'Existing';
229
231
  siteUrl: string;
230
232
  driveId: string;
233
+ }
234
+
235
+ export interface INewFileInfo extends IFileInfoBase {
236
+ type: 'New';
231
237
  filename: string;
232
238
  filePath: string;
233
239
  /**
@@ -240,6 +246,15 @@ export interface INewFileInfo {
240
246
  createLinkType?: ShareLinkTypes | ISharingLinkKind;
241
247
  }
242
248
 
249
+ export interface IExistingFileInfo extends IFileInfoBase {
250
+ type: 'Existing';
251
+ itemId: string;
252
+ }
253
+
254
+ export function isNewFileInfo(fileInfo: INewFileInfo | IExistingFileInfo): fileInfo is INewFileInfo {
255
+ return fileInfo.type === undefined || fileInfo.type === 'New';
256
+ }
257
+
243
258
  export function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {
244
259
  assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* "Not an ODSP resolved url" */);
245
260
  return resolvedUrl as IOdspResolvedUrl;
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.0.0-internal.2.2.1";
9
+ export const pkgVersion = "2.0.0-internal.2.3.0";
@@ -16,7 +16,6 @@ import { ReadBuffer } from "./ReadBufferUtils";
16
16
  import { pkgVersion as driverVersion } from "./packageVersion";
17
17
  import { measure } from "./odspUtils";
18
18
 
19
- // eslint-disable-next-line max-len
20
19
  // https://onedrive.visualstudio.com/SharePoint%20Online/_git/SPO?path=/cobalt/Base/Property/BinaryEncodedPropertyReader.cs&version=GBmaster&_a=contents
21
20
  /**
22
21
  * Control codes used by tree serialization / decentralization code. Same as on server. These can be found on
@@ -1,11 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import io from "socket.io-client";
6
- /**
7
- * This function only exists to create an ESM wrapper around the socket.io client module
8
- * for compatibility with ESM dynamic imports
9
- */
10
- export declare function getSocketIo(): typeof io;
11
- //# sourceMappingURL=getSocketIo.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getSocketIo.d.ts","sourceRoot":"","sources":["../src/getSocketIo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC;;;GAGG;AACH,wBAAgB,WAAW,IAAI,OAAO,EAAE,CAEvC"}