@fluidframework/container-loader 2.0.0-dev-rc.5.0.0.268409 → 2.0.0-dev-rc.5.0.0.270401
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/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 +2 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +156 -123
- 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 +1 -2
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +10 -2
- 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 +2 -2
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +156 -123
- 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 +1 -2
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +10 -2
- package/lib/serializedStateManager.js.map +1 -1
- package/package.json +13 -11
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +3 -7
- package/src/container.ts +4 -4
- 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 +1 -1
- 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
|
}
|
|
@@ -313,6 +369,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
313
369
|
getSpecifiedCodeDetails() {
|
|
314
370
|
return this.getCodeDetailsFromQuorum();
|
|
315
371
|
}
|
|
372
|
+
_loadedCodeDetails;
|
|
316
373
|
/**
|
|
317
374
|
* Get the code details that were used to load the container.
|
|
318
375
|
* @returns The code details that were used to load the container if it is loaded, undefined if it is not yet
|
|
@@ -321,6 +378,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
321
378
|
getLoadedCodeDetails() {
|
|
322
379
|
return this._loadedCodeDetails;
|
|
323
380
|
}
|
|
381
|
+
_loadedModule;
|
|
324
382
|
/**
|
|
325
383
|
* Retrieves the audience associated with the document
|
|
326
384
|
*/
|
|
@@ -359,6 +417,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
359
417
|
this._lifecycleEvents.once("disposed", disposedHandler);
|
|
360
418
|
});
|
|
361
419
|
}
|
|
420
|
+
_lifecycleEvents = new TypedEventEmitter();
|
|
362
421
|
constructor(createProps, loadProps) {
|
|
363
422
|
super((name, error) => {
|
|
364
423
|
this.mc.logger.sendErrorEvent({
|
|
@@ -367,128 +426,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
367
426
|
}, error);
|
|
368
427
|
this.close(normalizeError(error));
|
|
369
428
|
});
|
|
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
429
|
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
493
430
|
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
494
431
|
const pendingLocalState = loadProps?.pendingLocalState;
|
|
@@ -633,7 +570,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
633
570
|
const offlineLoadEnabled = (this.isInteractiveClient &&
|
|
634
571
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
|
|
635
572
|
options.enableOfflineLoad === true;
|
|
636
|
-
this.serializedStateManager = new SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this.
|
|
573
|
+
this.serializedStateManager = new SerializedStateManager(pendingLocalState, this.subLogger, this.storageAdapter, offlineLoadEnabled, this, () => this._deltaManager.connectionManager.shouldJoinWrite());
|
|
637
574
|
const isDomAvailable = typeof document === "object" &&
|
|
638
575
|
document !== null &&
|
|
639
576
|
typeof document.addEventListener === "function" &&
|
|
@@ -716,6 +653,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
716
653
|
}
|
|
717
654
|
}
|
|
718
655
|
}
|
|
656
|
+
_disposed = false;
|
|
719
657
|
disposeCore(error) {
|
|
720
658
|
assert(!this._disposed, 0x54c /* Container already disposed */);
|
|
721
659
|
this._disposed = true;
|
|
@@ -814,6 +752,84 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
814
752
|
};
|
|
815
753
|
return JSON.stringify(detachedContainerState);
|
|
816
754
|
}
|
|
755
|
+
attach = runSingle(async (request, attachProps) => {
|
|
756
|
+
await PerformanceEvent.timedExecAsync(this.mc.logger, { eventName: "Attach" }, async () => {
|
|
757
|
+
if (this._lifecycleState !== "loaded" ||
|
|
758
|
+
this.attachmentData.state === AttachState.Attached) {
|
|
759
|
+
// pre-0.58 error message: containerNotValidForAttach
|
|
760
|
+
throw new UsageError(`The Container is not in a valid state for attach [${this._lifecycleState}] and [${this.attachState}]`);
|
|
761
|
+
}
|
|
762
|
+
const normalizeErrorAndClose = (error) => {
|
|
763
|
+
const newError = normalizeError(error);
|
|
764
|
+
this.close(newError);
|
|
765
|
+
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
766
|
+
newError.addTelemetryProperties({
|
|
767
|
+
resolvedUrl: this.service?.resolvedUrl?.url,
|
|
768
|
+
});
|
|
769
|
+
return newError;
|
|
770
|
+
};
|
|
771
|
+
const setAttachmentData = (attachmentData) => {
|
|
772
|
+
const previousState = this.attachmentData.state;
|
|
773
|
+
this.attachmentData = attachmentData;
|
|
774
|
+
const state = this.attachmentData.state;
|
|
775
|
+
if (state !== previousState && state !== AttachState.Detached) {
|
|
776
|
+
try {
|
|
777
|
+
this.runtime.setAttachState(state);
|
|
778
|
+
this.emit(state.toLocaleLowerCase());
|
|
779
|
+
}
|
|
780
|
+
catch (error) {
|
|
781
|
+
throw normalizeErrorAndClose(error);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
const createAttachmentSummary = (redirectTable) => {
|
|
786
|
+
try {
|
|
787
|
+
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
788
|
+
return combineAppAndProtocolSummary(this.runtime.createSummary(redirectTable), this.captureProtocolSummary());
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
throw normalizeErrorAndClose(error);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
const createOrGetStorageService = async (summary) => {
|
|
795
|
+
// Actually go and create the resolved document
|
|
796
|
+
if (this.service === undefined) {
|
|
797
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
798
|
+
assert(this.client.details.type !== summarizerClientType &&
|
|
799
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
800
|
+
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
801
|
+
cancel: this._deltaManager.closeAbortController.signal,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
this.storageAdapter.connectToService(this.service);
|
|
805
|
+
return this.storageAdapter;
|
|
806
|
+
};
|
|
807
|
+
let attachP = runRetriableAttachProcess({
|
|
808
|
+
initialAttachmentData: this.attachmentData,
|
|
809
|
+
offlineLoadEnabled: this.serializedStateManager.offlineLoadEnabled,
|
|
810
|
+
detachedBlobStorage: this.detachedBlobStorage,
|
|
811
|
+
setAttachmentData,
|
|
812
|
+
createAttachmentSummary,
|
|
813
|
+
createOrGetStorageService,
|
|
814
|
+
});
|
|
815
|
+
// only enable the new behavior if the config is set
|
|
816
|
+
if (this.mc.config.getBoolean("Fluid.Container.RetryOnAttachFailure") !== true) {
|
|
817
|
+
attachP = attachP.catch((error) => {
|
|
818
|
+
throw normalizeErrorAndClose(error);
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
// If offline load is enabled, attachP will return the attach summary (in Snapshot format) so we can initialize SerializedStateManager
|
|
822
|
+
const snapshotWithBlobs = await attachP;
|
|
823
|
+
this.serializedStateManager.setInitialSnapshot(snapshotWithBlobs);
|
|
824
|
+
if (!this.closed) {
|
|
825
|
+
this.detachedBlobStorage.dispose?.();
|
|
826
|
+
this.handleDeltaConnectionArg(attachProps?.deltaConnection, {
|
|
827
|
+
fetchOpsFromStorage: false,
|
|
828
|
+
reason: { text: "createDetached" },
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}, { start: true, end: true, cancel: "generic" });
|
|
832
|
+
});
|
|
817
833
|
setAutoReconnectInternal(mode, reason) {
|
|
818
834
|
const currentMode = this._deltaManager.connectionManager.reconnectMode;
|
|
819
835
|
if (currentMode === mode) {
|
|
@@ -886,6 +902,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
886
902
|
// Ensure connection to web socket
|
|
887
903
|
this.connectToDeltaStream(args);
|
|
888
904
|
}
|
|
905
|
+
getAbsoluteUrl = async (relativeUrl) => {
|
|
906
|
+
if (this.resolvedUrl === undefined) {
|
|
907
|
+
return undefined;
|
|
908
|
+
}
|
|
909
|
+
return this.urlResolver.getAbsoluteUrl(this.resolvedUrl, relativeUrl, getPackageName(this._loadedCodeDetails));
|
|
910
|
+
};
|
|
889
911
|
async proposeCodeDetails(codeDetails) {
|
|
890
912
|
if (!isFluidCodeDetails(codeDetails)) {
|
|
891
913
|
throw new Error("Provided codeDetails are not IFluidCodeDetails");
|
|
@@ -954,6 +976,10 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
954
976
|
}
|
|
955
977
|
this._deltaManager.connect(args);
|
|
956
978
|
}
|
|
979
|
+
metadataUpdateHandler = (metadata) => {
|
|
980
|
+
this._containerMetadata = { ...this._containerMetadata, ...metadata };
|
|
981
|
+
this.emit("metadataUpdate", metadata);
|
|
982
|
+
};
|
|
957
983
|
async createDocumentService(serviceProvider) {
|
|
958
984
|
const service = await serviceProvider();
|
|
959
985
|
// Back-compat for Old driver
|
|
@@ -1499,6 +1525,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1499
1525
|
this._lifecycleEvents.emit("runtimeInstantiated");
|
|
1500
1526
|
this._loadedCodeDetails = codeDetails;
|
|
1501
1527
|
}
|
|
1528
|
+
updateDirtyContainerState = (dirty) => {
|
|
1529
|
+
if (this._dirtyContainer === dirty) {
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
this._dirtyContainer = dirty;
|
|
1533
|
+
this.emit(dirty ? dirtyContainerEvent : savedContainerEvent);
|
|
1534
|
+
};
|
|
1502
1535
|
/**
|
|
1503
1536
|
* Set the connected state of the ContainerContext
|
|
1504
1537
|
* This controls the "connected" state of the ContainerRuntime as well
|