@fluidframework/container-loader 2.0.0-dev-rc.5.0.0.268409 → 2.0.0-dev-rc.5.0.0.270987
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.
- package/api-report/container-loader.alpha.api.md +59 -4
- package/api-report/container-loader.beta.api.md +7 -3
- package/api-report/container-loader.public.api.md +7 -3
- package/biome.jsonc +4 -0
- package/dist/audience.js +2 -1
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.js +11 -8
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +2 -2
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +91 -63
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.js +35 -11
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +3 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +162 -126
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +34 -8
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.js +17 -9
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +2 -0
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +2 -2
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +48 -35
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +14 -7
- package/dist/deltaQueue.js.map +1 -1
- package/dist/error.js +5 -4
- package/dist/error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +5 -0
- package/dist/loader.js +5 -1
- package/dist/loader.js.map +1 -1
- package/dist/noopHeuristic.d.ts +1 -1
- package/dist/noopHeuristic.d.ts.map +1 -1
- package/dist/noopHeuristic.js +3 -1
- package/dist/noopHeuristic.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol/index.d.ts +7 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +12 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/protocol.d.ts +52 -0
- package/dist/protocol/protocol.d.ts.map +1 -0
- package/dist/protocol/protocol.js +115 -0
- package/dist/protocol/protocol.js.map +1 -0
- package/dist/protocol/quorum.d.ts +185 -0
- package/dist/protocol/quorum.d.ts.map +1 -0
- package/dist/protocol/quorum.js +440 -0
- package/dist/protocol/quorum.js.map +1 -0
- package/dist/protocol.d.ts +2 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -2
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +7 -7
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +16 -7
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +5 -3
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +26 -5
- package/dist/serializedStateManager.js.map +1 -1
- package/lib/audience.js +2 -1
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.js +11 -8
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +2 -2
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +91 -63
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.js +35 -11
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +3 -2
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +162 -126
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +34 -8
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.js +17 -9
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +2 -0
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +2 -2
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +48 -35
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +14 -7
- package/lib/deltaQueue.js.map +1 -1
- package/lib/error.js +5 -4
- package/lib/error.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +5 -0
- package/lib/loader.js +5 -1
- package/lib/loader.js.map +1 -1
- package/lib/noopHeuristic.d.ts +1 -1
- package/lib/noopHeuristic.d.ts.map +1 -1
- package/lib/noopHeuristic.js +3 -1
- package/lib/noopHeuristic.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol/index.d.ts +7 -0
- package/lib/protocol/index.d.ts.map +1 -0
- package/lib/protocol/index.js +7 -0
- package/lib/protocol/index.js.map +1 -0
- package/lib/protocol/protocol.d.ts +52 -0
- package/lib/protocol/protocol.d.ts.map +1 -0
- package/lib/protocol/protocol.js +111 -0
- package/lib/protocol/protocol.js.map +1 -0
- package/lib/protocol/quorum.d.ts +185 -0
- package/lib/protocol/quorum.d.ts.map +1 -0
- package/lib/protocol/quorum.js +431 -0
- package/lib/protocol/quorum.js.map +1 -0
- package/lib/protocol.d.ts +2 -3
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +3 -1
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +7 -7
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +16 -7
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/retriableDocumentStorageService.js +4 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +5 -3
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +26 -5
- package/lib/serializedStateManager.js.map +1 -1
- package/package.json +19 -14
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +3 -7
- package/src/container.ts +16 -9
- package/src/containerContext.ts +2 -5
- package/src/contracts.ts +3 -6
- package/src/deltaManager.ts +3 -5
- package/src/index.ts +7 -0
- package/src/noopHeuristic.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/protocol/README.md +10 -0
- package/src/protocol/index.ts +16 -0
- package/src/protocol/protocol.ts +185 -0
- package/src/protocol/quorum.ts +584 -0
- package/src/protocol.ts +4 -6
- package/src/protocolTreeDocumentStorageService.ts +16 -8
- package/src/serializedStateManager.ts +25 -4
- package/tsconfig.json +2 -0
package/lib/container.js
CHANGED
|
@@ -189,6 +189,41 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
189
189
|
return container;
|
|
190
190
|
}, { start: true, end: true, cancel: "generic" });
|
|
191
191
|
}
|
|
192
|
+
// Tells if container can reconnect on losing fist connection
|
|
193
|
+
// If false, container gets closed on loss of connection.
|
|
194
|
+
_canReconnect;
|
|
195
|
+
clientDetailsOverride;
|
|
196
|
+
urlResolver;
|
|
197
|
+
serviceFactory;
|
|
198
|
+
codeLoader;
|
|
199
|
+
options;
|
|
200
|
+
scope;
|
|
201
|
+
subLogger;
|
|
202
|
+
// eslint-disable-next-line import/no-deprecated
|
|
203
|
+
detachedBlobStorage;
|
|
204
|
+
protocolHandlerBuilder;
|
|
205
|
+
client;
|
|
206
|
+
mc;
|
|
207
|
+
/**
|
|
208
|
+
* Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
|
|
209
|
+
*/
|
|
210
|
+
clone;
|
|
211
|
+
/**
|
|
212
|
+
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
213
|
+
*
|
|
214
|
+
* States are allowed to progress to further states:
|
|
215
|
+
* "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
|
|
216
|
+
*
|
|
217
|
+
* For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
|
|
218
|
+
*
|
|
219
|
+
* loading: Container has been created, but is not yet in normal/loaded state
|
|
220
|
+
* loaded: Container is in normal/loaded state
|
|
221
|
+
* closing: Container has started closing process (for re-entrancy prevention)
|
|
222
|
+
* disposing: Container has started disposing process (for re-entrancy prevention)
|
|
223
|
+
* closed: Container has closed
|
|
224
|
+
* disposed: Container has been disposed
|
|
225
|
+
*/
|
|
226
|
+
_lifecycleState = "loading";
|
|
192
227
|
setLoaded() {
|
|
193
228
|
// It's conceivable the container could be closed when this is called
|
|
194
229
|
// Only transition states if currently loading
|
|
@@ -231,18 +266,39 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
231
266
|
get disposed() {
|
|
232
267
|
return this._lifecycleState === "disposing" || this._lifecycleState === "disposed";
|
|
233
268
|
}
|
|
269
|
+
storageAdapter;
|
|
270
|
+
_deltaManager;
|
|
271
|
+
service;
|
|
272
|
+
_runtime;
|
|
234
273
|
get runtime() {
|
|
235
274
|
if (this._runtime === undefined) {
|
|
236
275
|
throw new Error("Attempted to access runtime before it was defined");
|
|
237
276
|
}
|
|
238
277
|
return this._runtime;
|
|
239
278
|
}
|
|
279
|
+
_protocolHandler;
|
|
240
280
|
get protocolHandler() {
|
|
241
281
|
if (this._protocolHandler === undefined) {
|
|
242
282
|
throw new Error("Attempted to access protocolHandler before it was defined");
|
|
243
283
|
}
|
|
244
284
|
return this._protocolHandler;
|
|
245
285
|
}
|
|
286
|
+
/** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
|
|
287
|
+
inboundQueuePausedFromInit = true;
|
|
288
|
+
firstConnection = true;
|
|
289
|
+
connectionTransitionTimes = [];
|
|
290
|
+
_loadedFromVersion;
|
|
291
|
+
_dirtyContainer = false;
|
|
292
|
+
attachmentData = { state: AttachState.Detached };
|
|
293
|
+
serializedStateManager;
|
|
294
|
+
_containerId;
|
|
295
|
+
lastVisible;
|
|
296
|
+
visibilityEventHandler;
|
|
297
|
+
connectionStateHandler;
|
|
298
|
+
clientsWhoShouldHaveLeft = new Set();
|
|
299
|
+
_containerMetadata = {};
|
|
300
|
+
setAutoReconnectTime = performance.now();
|
|
301
|
+
noopHeuristic;
|
|
246
302
|
get connectionMode() {
|
|
247
303
|
return this._deltaManager.connectionManager.connectionMode;
|
|
248
304
|
}
|
|
@@ -306,6 +362,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
306
362
|
get isInteractiveClient() {
|
|
307
363
|
return this.deltaManager.clientDetails.capabilities.interactive;
|
|
308
364
|
}
|
|
365
|
+
get supportGetSnapshotApi() {
|
|
366
|
+
const supportGetSnapshotApi = this.mc.config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch2") ===
|
|
367
|
+
true && this.service?.policies?.supportGetSnapshotApi === true;
|
|
368
|
+
return supportGetSnapshotApi;
|
|
369
|
+
}
|
|
309
370
|
/**
|
|
310
371
|
* Get the code details that are currently specified for the container.
|
|
311
372
|
* @returns The current code details if any are specified, undefined if none are specified.
|
|
@@ -313,6 +374,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
313
374
|
getSpecifiedCodeDetails() {
|
|
314
375
|
return this.getCodeDetailsFromQuorum();
|
|
315
376
|
}
|
|
377
|
+
_loadedCodeDetails;
|
|
316
378
|
/**
|
|
317
379
|
* Get the code details that were used to load the container.
|
|
318
380
|
* @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
|
|
@@ -321,6 +383,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
321
383
|
getLoadedCodeDetails() {
|
|
322
384
|
return this._loadedCodeDetails;
|
|
323
385
|
}
|
|
386
|
+
_loadedModule;
|
|
324
387
|
/**
|
|
325
388
|
* Retrieves the audience associated with the document
|
|
326
389
|
*/
|
|
@@ -359,6 +422,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
359
422
|
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
360
423
|
});
|
|
361
424
|
}
|
|
425
|
+
_lifecycleEvents = new TypedEventEmitter();
|
|
362
426
|
constructor(createProps, loadProps) {
|
|
363
427
|
super((name, error) => {
|
|
364
428
|
this.mc.logger.sendErrorEvent({
|
|
@@ -367,128 +431,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
367
431
|
}, error);
|
|
368
432
|
this.close(normalizeError(error));
|
|
369
433
|
});
|
|
370
|
-
/**
|
|
371
|
-
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
372
|
-
*
|
|
373
|
-
* States are allowed to progress to further states:
|
|
374
|
-
* "loading" - "loaded" - "closing" - "disposing" - "closed" - "disposed"
|
|
375
|
-
*
|
|
376
|
-
* For example, moving from "closed" to "disposing" is not allowed since it is an earlier state.
|
|
377
|
-
*
|
|
378
|
-
* loading: Container has been created, but is not yet in normal/loaded state
|
|
379
|
-
* loaded: Container is in normal/loaded state
|
|
380
|
-
* closing: Container has started closing process (for re-entrancy prevention)
|
|
381
|
-
* disposing: Container has started disposing process (for re-entrancy prevention)
|
|
382
|
-
* closed: Container has closed
|
|
383
|
-
* disposed: Container has been disposed
|
|
384
|
-
*/
|
|
385
|
-
this._lifecycleState = "loading";
|
|
386
|
-
/** During initialization we pause the inbound queues. We track this state to ensure we only call resume once */
|
|
387
|
-
this.inboundQueuePausedFromInit = true;
|
|
388
|
-
this.firstConnection = true;
|
|
389
|
-
this.connectionTransitionTimes = [];
|
|
390
|
-
this._dirtyContainer = false;
|
|
391
|
-
this.attachmentData = { state: AttachState.Detached };
|
|
392
|
-
this.clientsWhoShouldHaveLeft = new Set();
|
|
393
|
-
this._containerMetadata = {};
|
|
394
|
-
this.setAutoReconnectTime = performance.now();
|
|
395
|
-
this._lifecycleEvents = new TypedEventEmitter();
|
|
396
|
-
this._disposed = false;
|
|
397
|
-
this.attach = runSingle(async (request, attachProps) => {
|
|
398
|
-
await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
399
|
-
if (this._lifecycleState !== "loaded" ||
|
|
400
|
-
this.attachmentData.state === AttachState.Attached) {
|
|
401
|
-
// pre-0.58 error message: containerNotValidForAttach
|
|
402
|
-
throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
|
|
403
|
-
}
|
|
404
|
-
const normalizeErrorAndClose = (error) => {
|
|
405
|
-
const newError = normalizeError(error);
|
|
406
|
-
this.close(newError);
|
|
407
|
-
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
408
|
-
newError.addTelemetryProperties({
|
|
409
|
-
resolvedUrl: this.service?.resolvedUrl?.url,
|
|
410
|
-
});
|
|
411
|
-
return newError;
|
|
412
|
-
};
|
|
413
|
-
const setAttachmentData = (attachmentData) => {
|
|
414
|
-
const previousState = this.attachmentData.state;
|
|
415
|
-
this.attachmentData = attachmentData;
|
|
416
|
-
const state = this.attachmentData.state;
|
|
417
|
-
if (state !== previousState && state !== AttachState.Detached) {
|
|
418
|
-
try {
|
|
419
|
-
this.runtime.setAttachState(state);
|
|
420
|
-
this.emit(state.toLocaleLowerCase());
|
|
421
|
-
}
|
|
422
|
-
catch (error) {
|
|
423
|
-
throw normalizeErrorAndClose(error);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
const createAttachmentSummary = (redirectTable) => {
|
|
428
|
-
try {
|
|
429
|
-
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
430
|
-
return combineAppAndProtocolSummary(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
|
|
431
|
-
}
|
|
432
|
-
catch (error) {
|
|
433
|
-
throw normalizeErrorAndClose(error);
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
const createOrGetStorageService = async (summary) => {
|
|
437
|
-
// Actually go and create the resolved document
|
|
438
|
-
if (this.service === undefined) {
|
|
439
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
440
|
-
assert(this.client.details.type !== summarizerClientType &&
|
|
441
|
-
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
442
|
-
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
443
|
-
cancel: this._deltaManager.closeAbortController.signal,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
this.storageAdapter.connectToService(this.service);
|
|
447
|
-
return this.storageAdapter;
|
|
448
|
-
};
|
|
449
|
-
let attachP = runRetriableAttachProcess({
|
|
450
|
-
initialAttachmentData: this.attachmentData,
|
|
451
|
-
offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
|
|
452
|
-
detachedBlobStorage: this.detachedBlobStorage,
|
|
453
|
-
setAttachmentData,
|
|
454
|
-
createAttachmentSummary,
|
|
455
|
-
createOrGetStorageService,
|
|
456
|
-
});
|
|
457
|
-
// only enable the new behavior if the config is set
|
|
458
|
-
if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
|
|
459
|
-
attachP = attachP.catch((error) => {
|
|
460
|
-
throw normalizeErrorAndClose(error);
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
// If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
|
|
464
|
-
const snapshotWithBlobs = await attachP;
|
|
465
|
-
this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
|
|
466
|
-
if (!this.closed) {
|
|
467
|
-
this.detachedBlobStorage.dispose?.();
|
|
468
|
-
this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
|
|
469
|
-
fetchOpsFromStorage: false,
|
|
470
|
-
reason: { text: "createDetached" },
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}, { start: true, end: true, cancel: "generic" });
|
|
474
|
-
});
|
|
475
|
-
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
476
|
-
if (this.resolvedUrl === undefined) {
|
|
477
|
-
return undefined;
|
|
478
|
-
}
|
|
479
|
-
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
|
|
480
|
-
};
|
|
481
|
-
this.metadataUpdateHandler = (metadata) => {
|
|
482
|
-
this._containerMetadata = { ...this._containerMetadata, ...metadata };
|
|
483
|
-
this.emit("metadataUpdate", metadata);
|
|
484
|
-
};
|
|
485
|
-
this.updateDirtyContainerState = (dirty) => {
|
|
486
|
-
if (this._dirtyContainer === dirty) {
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
this._dirtyContainer = dirty;
|
|
490
|
-
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
491
|
-
};
|
|
492
434
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
493
435
|
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
494
436
|
const pendingLocalState = loadProps?.pendingLocalState;
|
|
@@ -633,7 +575,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
633
575
|
const offlineLoadEnabled = (this.isInteractiveClient &&
|
|
634
576
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
|
|
635
577
|
options.enableOfflineLoad === true;
|
|
636
|
-
this.serializedStateManager = new SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this.
|
|
578
|
+
this.serializedStateManager = new SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this._deltaManager.connectionManager.shouldJoinWrite());
|
|
637
579
|
const isDomAvailable = typeof document === "object" &&
|
|
638
580
|
document !== null &&
|
|
639
581
|
typeof document.addEventListener === "function" &&
|
|
@@ -716,6 +658,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
716
658
|
}
|
|
717
659
|
}
|
|
718
660
|
}
|
|
661
|
+
_disposed = false;
|
|
719
662
|
disposeCore(error) {
|
|
720
663
|
assert(!this._disposed, 0x54c /* Container already disposed */);
|
|
721
664
|
this._disposed = true;
|
|
@@ -814,6 +757,84 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
814
757
|
};
|
|
815
758
|
return JSON.stringify(detachedContainerState);
|
|
816
759
|
}
|
|
760
|
+
attach = runSingle(async (request, attachProps) => {
|
|
761
|
+
await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
762
|
+
if (this._lifecycleState !== "loaded" ||
|
|
763
|
+
this.attachmentData.state === AttachState.Attached) {
|
|
764
|
+
// pre-0.58 error message: containerNotValidForAttach
|
|
765
|
+
throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
|
|
766
|
+
}
|
|
767
|
+
const normalizeErrorAndClose = (error) => {
|
|
768
|
+
const newError = normalizeError(error);
|
|
769
|
+
this.close(newError);
|
|
770
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
771
|
+
newError.addTelemetryProperties({
|
|
772
|
+
resolvedUrl: this.service?.resolvedUrl?.url,
|
|
773
|
+
});
|
|
774
|
+
return newError;
|
|
775
|
+
};
|
|
776
|
+
const setAttachmentData = (attachmentData) => {
|
|
777
|
+
const previousState = this.attachmentData.state;
|
|
778
|
+
this.attachmentData = attachmentData;
|
|
779
|
+
const state = this.attachmentData.state;
|
|
780
|
+
if (state !== previousState && state !== AttachState.Detached) {
|
|
781
|
+
try {
|
|
782
|
+
this.runtime.setAttachState(state);
|
|
783
|
+
this.emit(state.toLocaleLowerCase());
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
throw normalizeErrorAndClose(error);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
const createAttachmentSummary = (redirectTable) => {
|
|
791
|
+
try {
|
|
792
|
+
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
793
|
+
return combineAppAndProtocolSummary(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
throw normalizeErrorAndClose(error);
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
const createOrGetStorageService = async (summary) => {
|
|
800
|
+
// Actually go and create the resolved document
|
|
801
|
+
if (this.service === undefined) {
|
|
802
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
803
|
+
assert(this.client.details.type !== summarizerClientType &&
|
|
804
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
805
|
+
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
806
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
this.storageAdapter.connectToService(this.service);
|
|
810
|
+
return this.storageAdapter;
|
|
811
|
+
};
|
|
812
|
+
let attachP = runRetriableAttachProcess({
|
|
813
|
+
initialAttachmentData: this.attachmentData,
|
|
814
|
+
offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
|
|
815
|
+
detachedBlobStorage: this.detachedBlobStorage,
|
|
816
|
+
setAttachmentData,
|
|
817
|
+
createAttachmentSummary,
|
|
818
|
+
createOrGetStorageService,
|
|
819
|
+
});
|
|
820
|
+
// only enable the new behavior if the config is set
|
|
821
|
+
if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
|
|
822
|
+
attachP = attachP.catch((error) => {
|
|
823
|
+
throw normalizeErrorAndClose(error);
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
// If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
|
|
827
|
+
const snapshotWithBlobs = await attachP;
|
|
828
|
+
this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs, this.supportGetSnapshotApi);
|
|
829
|
+
if (!this.closed) {
|
|
830
|
+
this.detachedBlobStorage.dispose?.();
|
|
831
|
+
this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
|
|
832
|
+
fetchOpsFromStorage: false,
|
|
833
|
+
reason: { text: "createDetached" },
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
837
|
+
});
|
|
817
838
|
setAutoReconnectInternal(mode, reason) {
|
|
818
839
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
819
840
|
if (currentMode === mode) {
|
|
@@ -886,6 +907,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
886
907
|
// Ensure connection to web socket
|
|
887
908
|
this.connectToDeltaStream(args);
|
|
888
909
|
}
|
|
910
|
+
getAbsoluteUrl = async (relativeUrl) => {
|
|
911
|
+
if (this.resolvedUrl === undefined) {
|
|
912
|
+
return undefined;
|
|
913
|
+
}
|
|
914
|
+
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
|
|
915
|
+
};
|
|
889
916
|
async proposeCodeDetails(codeDetails) {
|
|
890
917
|
if (!isFluidCodeDetails(codeDetails)) {
|
|
891
918
|
throw new Error("Provided codeDetails are not IFluidCodeDetails");
|
|
@@ -954,6 +981,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
954
981
|
}
|
|
955
982
|
this._deltaManager.connect(args);
|
|
956
983
|
}
|
|
984
|
+
metadataUpdateHandler = (metadata) => {
|
|
985
|
+
this._containerMetadata = { ...this._containerMetadata, ...metadata };
|
|
986
|
+
this.emit("metadataUpdate", metadata);
|
|
987
|
+
};
|
|
957
988
|
async createDocumentService(serviceProvider) {
|
|
958
989
|
const service = await serviceProvider();
|
|
959
990
|
// Back-compat for Old driver
|
|
@@ -990,10 +1021,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
990
1021
|
state: AttachState.Attached,
|
|
991
1022
|
};
|
|
992
1023
|
timings.phase2 = performance.now();
|
|
993
|
-
const supportGetSnapshotApi = this.mc.config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch") ===
|
|
994
|
-
true && this.service?.policies?.supportGetSnapshotApi === true;
|
|
995
1024
|
// Fetch specified snapshot.
|
|
996
|
-
const { baseSnapshot, version } = await this.serializedStateManager.fetchSnapshot(specifiedVersion, supportGetSnapshotApi);
|
|
1025
|
+
const { baseSnapshot, version } = await this.serializedStateManager.fetchSnapshot(specifiedVersion, this.supportGetSnapshotApi);
|
|
997
1026
|
const baseSnapshotTree = getSnapshotTree(baseSnapshot);
|
|
998
1027
|
this._loadedFromVersion = version;
|
|
999
1028
|
const attributes = await getDocumentAttributes(this.storageAdapter, baseSnapshotTree);
|
|
@@ -1499,6 +1528,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1499
1528
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1500
1529
|
this._loadedCodeDetails = codeDetails;
|
|
1501
1530
|
}
|
|
1531
|
+
updateDirtyContainerState = (dirty) => {
|
|
1532
|
+
if (this._dirtyContainer === dirty) {
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
this._dirtyContainer = dirty;
|
|
1536
|
+
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
1537
|
+
};
|
|
1502
1538
|
/**
|
|
1503
1539
|
* Set the connected state of the ContainerContext
|
|
1504
1540
|
* This controls the "connected" state of the ContainerRuntime as well
|