@fluidframework/container-loader 2.0.0-dev.6.4.0.192049 → 2.0.0-dev.7.2.0.203917
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/CHANGELOG.md +122 -0
- package/api-extractor.json +9 -1
- package/api-report/container-loader.api.md +153 -0
- package/dist/catchUpMonitor.d.ts +2 -2
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/connectionManager.d.ts +2 -15
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +110 -98
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.js +1 -1
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.js +9 -9
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container-loader-alpha.d.ts +333 -0
- package/dist/container-loader-beta.d.ts +333 -0
- package/dist/container-loader-public.d.ts +333 -0
- package/dist/container-loader-untrimmed.d.ts +333 -0
- package/dist/container.d.ts +22 -2
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +235 -222
- package/dist/container.js.map +1 -1
- package/dist/containerContext.js +16 -16
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +9 -11
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +5 -4
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.d.ts.map +1 -1
- package/dist/debugLogger.js +5 -4
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +92 -96
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +14 -14
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +6 -9
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +26 -91
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/index.d.ts +6 -0
- package/dist/location-redirection-utilities/index.d.ts.map +1 -0
- package/dist/location-redirection-utilities/index.js +11 -0
- package/dist/location-redirection-utilities/index.js.map +1 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts +22 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js +51 -0
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +1 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +3 -5
- package/dist/protocol.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +3 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +18 -11
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/utils.d.ts +23 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +11 -3
- package/dist/utils.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +2 -2
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/connectionManager.d.ts +2 -15
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +113 -99
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.js +9 -9
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +22 -2
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +236 -223
- package/lib/container.js.map +1 -1
- package/lib/containerContext.js +16 -16
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +9 -11
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +5 -4
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.d.ts.map +1 -1
- package/lib/debugLogger.js +5 -4
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +92 -96
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +14 -14
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +6 -9
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +27 -92
- package/lib/loader.js.map +1 -1
- package/lib/location-redirection-utilities/index.d.ts +6 -0
- package/lib/location-redirection-utilities/index.d.ts.map +1 -0
- package/lib/location-redirection-utilities/index.js +6 -0
- package/lib/location-redirection-utilities/index.js.map +1 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts +22 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.d.ts.map +1 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js +46 -0
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +1 -2
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +1 -3
- package/lib/protocol.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +3 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +18 -11
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +23 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +9 -1
- package/lib/utils.js.map +1 -1
- package/package.json +24 -26
- package/src/connectionManager.ts +58 -31
- package/src/container.ts +48 -30
- package/src/containerStorageAdapter.ts +3 -9
- package/src/contracts.ts +8 -4
- package/src/debugLogger.ts +5 -1
- package/src/deltaManager.ts +16 -31
- package/src/index.ts +5 -0
- package/src/loader.ts +31 -99
- package/src/location-redirection-utilities/index.ts +9 -0
- package/src/location-redirection-utilities/resolveWithLocationRedirection.ts +59 -0
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +2 -6
- package/src/retriableDocumentStorageService.ts +29 -15
- package/src/utils.ts +23 -1
package/src/connectionManager.ts
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
IDocumentService,
|
|
17
17
|
IDocumentDeltaConnection,
|
|
18
18
|
IDocumentDeltaConnectionEvents,
|
|
19
|
+
// eslint-disable-next-line import/no-deprecated
|
|
20
|
+
DriverErrorType,
|
|
19
21
|
} from "@fluidframework/driver-definitions";
|
|
20
22
|
import {
|
|
21
23
|
canRetryOnError,
|
|
@@ -45,6 +47,7 @@ import {
|
|
|
45
47
|
import {
|
|
46
48
|
formatTick,
|
|
47
49
|
GenericError,
|
|
50
|
+
isFluidError,
|
|
48
51
|
ITelemetryLoggerExt,
|
|
49
52
|
normalizeError,
|
|
50
53
|
UsageError,
|
|
@@ -112,7 +115,15 @@ class NoDeltaStream
|
|
|
112
115
|
blockSize: 0,
|
|
113
116
|
};
|
|
114
117
|
checkpointSequenceNumber?: number | undefined = undefined;
|
|
115
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Connection which is not connected to socket.
|
|
120
|
+
* @param storageOnlyReason - Reason on why the connection to delta stream is not allowed.
|
|
121
|
+
* @param readonlyConnectionReason - reason/error if any which lead to using NoDeltaStream.
|
|
122
|
+
*/
|
|
123
|
+
constructor(
|
|
124
|
+
public readonly storageOnlyReason?: string,
|
|
125
|
+
public readonly readonlyConnectionReason?: IConnectionStateChangeReason,
|
|
126
|
+
) {
|
|
116
127
|
super();
|
|
117
128
|
}
|
|
118
129
|
submit(messages: IDocumentMessage[]): void {
|
|
@@ -409,7 +420,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
409
420
|
// Notify everyone we are in read-only state.
|
|
410
421
|
// Useful for data stores in case we hit some critical error,
|
|
411
422
|
// to switch to a mode where user edits are not accepted
|
|
412
|
-
this.set_readonlyPermissions(true, oldReadonlyValue);
|
|
423
|
+
this.set_readonlyPermissions(true, oldReadonlyValue, disconnectReason);
|
|
413
424
|
}
|
|
414
425
|
}
|
|
415
426
|
|
|
@@ -432,21 +443,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
432
443
|
}
|
|
433
444
|
|
|
434
445
|
/**
|
|
435
|
-
*
|
|
436
|
-
* Hosts may have read only views, indicating to data stores that no edits are allowed.
|
|
437
|
-
* This is independent from this._readonlyPermissions (permissions) and this.connectionMode
|
|
438
|
-
* (server can return "write" mode even when asked for "read")
|
|
439
|
-
* Leveraging same "readonly" event as runtime & data stores should behave the same in such case
|
|
440
|
-
* as in read-only permissions.
|
|
441
|
-
* But this.active can be used by some DDSes to figure out if ops can be sent
|
|
442
|
-
* (for example, read-only view still participates in code proposals / upgrades decisions)
|
|
443
|
-
*
|
|
444
|
-
* Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
|
|
445
|
-
* the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
|
|
446
|
-
* there are pending in the outbound queue, it will stop sending until force readonly is
|
|
447
|
-
* cleared.
|
|
448
|
-
*
|
|
449
|
-
* @param readonly - set or clear force readonly.
|
|
446
|
+
* {@inheritDoc Container.forceReadonly}
|
|
450
447
|
*/
|
|
451
448
|
public forceReadonly(readonly: boolean) {
|
|
452
449
|
if (readonly !== this._forceReadonly) {
|
|
@@ -487,10 +484,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
487
484
|
private set_readonlyPermissions(
|
|
488
485
|
newReadonlyValue: boolean,
|
|
489
486
|
oldReadonlyValue: boolean | undefined,
|
|
487
|
+
readonlyConnectionReason?: IConnectionStateChangeReason,
|
|
490
488
|
) {
|
|
491
489
|
this._readonlyPermissions = newReadonlyValue;
|
|
492
490
|
if (oldReadonlyValue !== this.readonly) {
|
|
493
|
-
this.props.readonlyChangeHandler(this.readonly);
|
|
491
|
+
this.props.readonlyChangeHandler(this.readonly, readonlyConnectionReason);
|
|
494
492
|
}
|
|
495
493
|
}
|
|
496
494
|
|
|
@@ -589,7 +587,23 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
589
587
|
}
|
|
590
588
|
} catch (origError: any) {
|
|
591
589
|
if (isDeltaStreamConnectionForbiddenError(origError)) {
|
|
592
|
-
connection = new NoDeltaStream(origError.storageOnlyReason
|
|
590
|
+
connection = new NoDeltaStream(origError.storageOnlyReason, {
|
|
591
|
+
text: origError.message,
|
|
592
|
+
error: origError,
|
|
593
|
+
});
|
|
594
|
+
requestedMode = "read";
|
|
595
|
+
break;
|
|
596
|
+
} else if (
|
|
597
|
+
isFluidError(origError) &&
|
|
598
|
+
// eslint-disable-next-line import/no-deprecated
|
|
599
|
+
origError.errorType === DriverErrorType.outOfStorageError
|
|
600
|
+
) {
|
|
601
|
+
// If we get out of storage error from calling joinsession, then use the NoDeltaStream object so
|
|
602
|
+
// that user can at least load the container.
|
|
603
|
+
connection = new NoDeltaStream(undefined, {
|
|
604
|
+
text: origError.message,
|
|
605
|
+
error: origError,
|
|
606
|
+
});
|
|
593
607
|
requestedMode = "read";
|
|
594
608
|
break;
|
|
595
609
|
}
|
|
@@ -722,7 +736,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
722
736
|
|
|
723
737
|
// Remove listeners first so we don't try to retrigger this flow accidentally through reconnectOnError
|
|
724
738
|
connection.off("op", this.opHandler);
|
|
725
|
-
connection.off("signal", this.
|
|
739
|
+
connection.off("signal", this.signalHandler);
|
|
726
740
|
connection.off("nack", this.nackHandler);
|
|
727
741
|
connection.off("disconnect", this.disconnectHandlerInternal);
|
|
728
742
|
connection.off("error", this.errorHandler);
|
|
@@ -805,7 +819,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
805
819
|
0x0e8 /* "readonly perf with write connection" */,
|
|
806
820
|
);
|
|
807
821
|
|
|
808
|
-
this.set_readonlyPermissions(
|
|
822
|
+
this.set_readonlyPermissions(
|
|
823
|
+
readonly,
|
|
824
|
+
oldReadonlyValue,
|
|
825
|
+
isNoDeltaStreamConnection(connection) ? connection.readonlyConnectionReason : undefined,
|
|
826
|
+
);
|
|
809
827
|
|
|
810
828
|
if (this._disposed) {
|
|
811
829
|
// Raise proper events, Log telemetry event and close connection.
|
|
@@ -816,7 +834,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
816
834
|
this._outbound.resume();
|
|
817
835
|
|
|
818
836
|
connection.on("op", this.opHandler);
|
|
819
|
-
connection.on("signal", this.
|
|
837
|
+
connection.on("signal", this.signalHandler);
|
|
820
838
|
connection.on("nack", this.nackHandler);
|
|
821
839
|
connection.on("disconnect", this.disconnectHandlerInternal);
|
|
822
840
|
connection.on("error", this.errorHandler);
|
|
@@ -882,28 +900,32 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
882
900
|
type: SignalType.Clear,
|
|
883
901
|
}),
|
|
884
902
|
};
|
|
885
|
-
this.props.signalHandler(clearSignal);
|
|
886
903
|
|
|
887
|
-
|
|
888
|
-
|
|
904
|
+
// list of signals to process due to this new connection
|
|
905
|
+
let signalsToProcess: ISignalMessage[] = [clearSignal];
|
|
906
|
+
|
|
907
|
+
const clientJoinSignals: ISignalMessage[] = (connection.initialClients ?? []).map(
|
|
908
|
+
(priorClient) => ({
|
|
889
909
|
clientId: null, // system signal
|
|
890
910
|
content: JSON.stringify({
|
|
891
911
|
type: SignalType.ClientJoin,
|
|
892
912
|
content: priorClient, // ISignalClient
|
|
893
913
|
}),
|
|
894
|
-
}
|
|
895
|
-
|
|
914
|
+
}),
|
|
915
|
+
);
|
|
916
|
+
if (clientJoinSignals.length > 0) {
|
|
917
|
+
signalsToProcess = signalsToProcess.concat(clientJoinSignals);
|
|
896
918
|
}
|
|
897
919
|
|
|
898
920
|
// Unfortunately, there is no defined order between initialSignals (including join & leave signals)
|
|
899
921
|
// and connection.initialClients. In practice, connection.initialSignals quite often contains join signal
|
|
900
922
|
// for "self" and connection.initialClients does not contain "self", so we have to process them after
|
|
901
923
|
// "clear" signal above.
|
|
902
|
-
if (connection.initialSignals !== undefined) {
|
|
903
|
-
|
|
904
|
-
this.props.signalHandler(signal);
|
|
905
|
-
}
|
|
924
|
+
if (connection.initialSignals !== undefined && connection.initialSignals.length > 0) {
|
|
925
|
+
signalsToProcess = signalsToProcess.concat(connection.initialSignals);
|
|
906
926
|
}
|
|
927
|
+
|
|
928
|
+
this.props.signalHandler(signalsToProcess);
|
|
907
929
|
}
|
|
908
930
|
|
|
909
931
|
/**
|
|
@@ -1121,6 +1143,11 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
1121
1143
|
this.props.incomingOpHandler(messages, "opHandler");
|
|
1122
1144
|
};
|
|
1123
1145
|
|
|
1146
|
+
private readonly signalHandler = (signalsArg: ISignalMessage | ISignalMessage[]) => {
|
|
1147
|
+
const signals = Array.isArray(signalsArg) ? signalsArg : [signalsArg];
|
|
1148
|
+
this.props.signalHandler(signals);
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1124
1151
|
// Always connect in write mode after getting nacked.
|
|
1125
1152
|
private readonly nackHandler = (documentId: string, messages: INack[]) => {
|
|
1126
1153
|
const message = messages[0];
|
package/src/container.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
TelemetryEventCategory,
|
|
16
16
|
IRequest,
|
|
17
17
|
IResponse,
|
|
18
|
+
// eslint-disable-next-line import/no-deprecated
|
|
18
19
|
IFluidRouter,
|
|
19
20
|
FluidObject,
|
|
20
21
|
LogLevel,
|
|
@@ -121,7 +122,6 @@ import { ConnectionManager } from "./connectionManager";
|
|
|
121
122
|
import { ConnectionState } from "./connectionState";
|
|
122
123
|
import {
|
|
123
124
|
IProtocolHandler,
|
|
124
|
-
OnlyValidTermValue,
|
|
125
125
|
ProtocolHandler,
|
|
126
126
|
ProtocolHandlerBuilder,
|
|
127
127
|
protocolHandlerShouldProcessSignal,
|
|
@@ -134,6 +134,8 @@ const savedContainerEvent = "saved";
|
|
|
134
134
|
|
|
135
135
|
const packageNotFactoryError = "Code package does not implement IRuntimeFactory";
|
|
136
136
|
|
|
137
|
+
const hasBlobsSummaryTree = ".hasAttachmentBlobs";
|
|
138
|
+
|
|
137
139
|
/**
|
|
138
140
|
* @internal
|
|
139
141
|
*/
|
|
@@ -359,7 +361,6 @@ export interface IPendingContainerState {
|
|
|
359
361
|
*/
|
|
360
362
|
savedOps: ISequencedDocumentMessage[];
|
|
361
363
|
url: string;
|
|
362
|
-
term: number;
|
|
363
364
|
clientId?: string;
|
|
364
365
|
}
|
|
365
366
|
|
|
@@ -474,7 +475,11 @@ export class Container
|
|
|
474
475
|
container.mc.logger,
|
|
475
476
|
{ eventName: "RehydrateDetachedFromSnapshot" },
|
|
476
477
|
async (_event) => {
|
|
477
|
-
const deserializedSummary = JSON.parse(snapshot)
|
|
478
|
+
const deserializedSummary = JSON.parse(snapshot);
|
|
479
|
+
if (!isCombinedAppAndProtocolSummary(deserializedSummary, hasBlobsSummaryTree)) {
|
|
480
|
+
throw new UsageError("Cannot rehydrate detached container. Incorrect format");
|
|
481
|
+
}
|
|
482
|
+
|
|
478
483
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
479
484
|
return container;
|
|
480
485
|
},
|
|
@@ -581,6 +586,7 @@ export class Container
|
|
|
581
586
|
private readonly savedOps: ISequencedDocumentMessage[] = [];
|
|
582
587
|
private baseSnapshot?: ISnapshotTree;
|
|
583
588
|
private baseSnapshotBlobs?: ISerializableBlobContents;
|
|
589
|
+
private readonly _containerId: string;
|
|
584
590
|
|
|
585
591
|
private lastVisible: number | undefined;
|
|
586
592
|
private readonly visibilityEventHandler: (() => void) | undefined;
|
|
@@ -595,6 +601,10 @@ export class Container
|
|
|
595
601
|
return this._deltaManager.connectionManager.connectionMode;
|
|
596
602
|
}
|
|
597
603
|
|
|
604
|
+
/**
|
|
605
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
606
|
+
*/
|
|
607
|
+
// eslint-disable-next-line import/no-deprecated
|
|
598
608
|
public get IFluidRouter(): IFluidRouter {
|
|
599
609
|
return this;
|
|
600
610
|
}
|
|
@@ -619,7 +629,21 @@ export class Container
|
|
|
619
629
|
}
|
|
620
630
|
|
|
621
631
|
/**
|
|
622
|
-
*
|
|
632
|
+
* Sends signal to runtime (and data stores) to be read-only.
|
|
633
|
+
* Hosts may have read only views, indicating to data stores that no edits are allowed.
|
|
634
|
+
* This is independent from this._readonlyPermissions (permissions) and this.connectionMode
|
|
635
|
+
* (server can return "write" mode even when asked for "read")
|
|
636
|
+
* Leveraging same "readonly" event as runtime & data stores should behave the same in such case
|
|
637
|
+
* as in read-only permissions.
|
|
638
|
+
* But this.active can be used by some DDSes to figure out if ops can be sent
|
|
639
|
+
* (for example, read-only view still participates in code proposals / upgrades decisions)
|
|
640
|
+
*
|
|
641
|
+
* Forcing Readonly does not prevent DDS from generating ops. It is up to user code to honour
|
|
642
|
+
* the readonly flag. If ops are generated, they will accumulate locally and not be sent. If
|
|
643
|
+
* there are pending in the outbound queue, it will stop sending until force readonly is
|
|
644
|
+
* cleared.
|
|
645
|
+
*
|
|
646
|
+
* @param readonly - set or clear force readonly.
|
|
623
647
|
*/
|
|
624
648
|
public forceReadonly(readonly: boolean) {
|
|
625
649
|
this._deltaManager.connectionManager.forceReadonly(readonly);
|
|
@@ -798,6 +822,8 @@ export class Container
|
|
|
798
822
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${
|
|
799
823
|
type !== undefined && type !== "" ? `/${type}` : ""
|
|
800
824
|
}`;
|
|
825
|
+
|
|
826
|
+
this._containerId = uuid();
|
|
801
827
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
802
828
|
// We assign the id later so property getter is used.
|
|
803
829
|
this.subLogger = createChildLogger({
|
|
@@ -805,7 +831,7 @@ export class Container
|
|
|
805
831
|
properties: {
|
|
806
832
|
all: {
|
|
807
833
|
clientType, // Differentiating summarizer container from main container
|
|
808
|
-
containerId:
|
|
834
|
+
containerId: this._containerId,
|
|
809
835
|
docId: () => this.resolvedUrl?.id,
|
|
810
836
|
containerAttachState: () => this._attachState,
|
|
811
837
|
containerLifecycleState: () => this._lifecycleState,
|
|
@@ -1137,7 +1163,6 @@ export class Container
|
|
|
1137
1163
|
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1138
1164
|
savedOps: this.savedOps,
|
|
1139
1165
|
url: this.resolvedUrl.url,
|
|
1140
|
-
term: OnlyValidTermValue,
|
|
1141
1166
|
// no need to save this if there is no pending runtime state
|
|
1142
1167
|
clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
|
|
1143
1168
|
};
|
|
@@ -1162,7 +1187,7 @@ export class Container
|
|
|
1162
1187
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1163
1188
|
|
|
1164
1189
|
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
1165
|
-
combinedSummary.tree[
|
|
1190
|
+
combinedSummary.tree[hasBlobsSummaryTree] = {
|
|
1166
1191
|
type: SummaryType.Blob,
|
|
1167
1192
|
content: "true",
|
|
1168
1193
|
};
|
|
@@ -1248,7 +1273,7 @@ export class Container
|
|
|
1248
1273
|
}, // progress
|
|
1249
1274
|
);
|
|
1250
1275
|
}
|
|
1251
|
-
|
|
1276
|
+
this.storageAdapter.connectToService(this.service);
|
|
1252
1277
|
|
|
1253
1278
|
if (hasAttachmentBlobs) {
|
|
1254
1279
|
// upload blobs to storage
|
|
@@ -1319,6 +1344,9 @@ export class Container
|
|
|
1319
1344
|
);
|
|
1320
1345
|
}
|
|
1321
1346
|
|
|
1347
|
+
/**
|
|
1348
|
+
* @deprecated Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
1349
|
+
*/
|
|
1322
1350
|
public async request(path: IRequest): Promise<IResponse> {
|
|
1323
1351
|
return PerformanceEvent.timedExecAsync(
|
|
1324
1352
|
this.mc.logger,
|
|
@@ -1537,18 +1565,15 @@ export class Container
|
|
|
1537
1565
|
this.client.details.type === summarizerClientType,
|
|
1538
1566
|
);
|
|
1539
1567
|
|
|
1540
|
-
//
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
// connections to same file) in two ways:
|
|
1547
|
-
// A) creation flow breaks (as one of the clients "sees" file as existing, and hits #2 above)
|
|
1548
|
-
// B) Once file is created, transition from view-only connection to write does not work - some bugs to be fixed.
|
|
1568
|
+
// Except in cases where it has stashed ops or requested by feature gate, the container will connect in "read" mode
|
|
1569
|
+
const mode =
|
|
1570
|
+
this.mc.config.getBoolean("Fluid.Container.ForceWriteConnection") === true ||
|
|
1571
|
+
(pendingLocalState?.savedOps.length ?? 0) > 0
|
|
1572
|
+
? "write"
|
|
1573
|
+
: "read";
|
|
1549
1574
|
const connectionArgs: IConnectionArgs = {
|
|
1550
1575
|
reason: { text: "DocumentOpen" },
|
|
1551
|
-
mode
|
|
1576
|
+
mode,
|
|
1552
1577
|
fetchOpsFromStorage: false,
|
|
1553
1578
|
};
|
|
1554
1579
|
|
|
@@ -1558,14 +1583,7 @@ export class Container
|
|
|
1558
1583
|
this.connectToDeltaStream(connectionArgs);
|
|
1559
1584
|
}
|
|
1560
1585
|
|
|
1561
|
-
|
|
1562
|
-
await this.storageAdapter.connectToService(this.service);
|
|
1563
|
-
} else {
|
|
1564
|
-
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
1565
|
-
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
1566
|
-
this.close(error);
|
|
1567
|
-
});
|
|
1568
|
-
}
|
|
1586
|
+
this.storageAdapter.connectToService(this.service);
|
|
1569
1587
|
|
|
1570
1588
|
this._attachState = AttachState.Attached;
|
|
1571
1589
|
|
|
@@ -1777,7 +1795,6 @@ export class Container
|
|
|
1777
1795
|
private async createDetached(codeDetails: IFluidCodeDetails) {
|
|
1778
1796
|
const attributes: IDocumentAttributes = {
|
|
1779
1797
|
sequenceNumber: detachedContainerRefSeqNumber,
|
|
1780
|
-
term: OnlyValidTermValue,
|
|
1781
1798
|
minimumSequenceNumber: 0,
|
|
1782
1799
|
};
|
|
1783
1800
|
|
|
@@ -1800,12 +1817,13 @@ export class Container
|
|
|
1800
1817
|
}
|
|
1801
1818
|
|
|
1802
1819
|
private async rehydrateDetachedFromSnapshot(detachedContainerSnapshot: ISummaryTree) {
|
|
1803
|
-
if (detachedContainerSnapshot.tree[
|
|
1820
|
+
if (detachedContainerSnapshot.tree[hasBlobsSummaryTree] !== undefined) {
|
|
1804
1821
|
assert(
|
|
1805
1822
|
!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
1806
1823
|
0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */,
|
|
1807
1824
|
);
|
|
1808
|
-
delete
|
|
1825
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
1826
|
+
delete detachedContainerSnapshot.tree[hasBlobsSummaryTree];
|
|
1809
1827
|
}
|
|
1810
1828
|
|
|
1811
1829
|
const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
|
|
@@ -1843,7 +1861,6 @@ export class Container
|
|
|
1843
1861
|
return {
|
|
1844
1862
|
minimumSequenceNumber: 0,
|
|
1845
1863
|
sequenceNumber: 0,
|
|
1846
|
-
term: OnlyValidTermValue,
|
|
1847
1864
|
};
|
|
1848
1865
|
}
|
|
1849
1866
|
|
|
@@ -1993,6 +2010,7 @@ export class Container
|
|
|
1993
2010
|
client.details.environment = [
|
|
1994
2011
|
client.details.environment,
|
|
1995
2012
|
` loaderVersion:${pkgVersion}`,
|
|
2013
|
+
` containerId:${this._containerId}`,
|
|
1996
2014
|
].join(";");
|
|
1997
2015
|
return client;
|
|
1998
2016
|
}
|
|
@@ -79,14 +79,14 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
79
79
|
this.disposed = true;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
public
|
|
82
|
+
public connectToService(service: IDocumentService): void {
|
|
83
83
|
if (!(this._storageService instanceof BlobOnlyStorage)) {
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const
|
|
87
|
+
const storageServiceP = service.connectToStorage();
|
|
88
88
|
const retriableStorage = (this._storageService = new RetriableDocumentStorageService(
|
|
89
|
-
|
|
89
|
+
storageServiceP,
|
|
90
90
|
this.logger,
|
|
91
91
|
));
|
|
92
92
|
|
|
@@ -99,12 +99,6 @@ export class ContainerStorageAdapter implements IDocumentStorageService, IDispos
|
|
|
99
99
|
this.addProtocolSummaryIfMissing,
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
// ensure we did not lose that policy in the process of wrapping
|
|
104
|
-
assert(
|
|
105
|
-
storageService.policies?.minBlobSize === this._storageService.policies?.minBlobSize,
|
|
106
|
-
0x0e0 /* "lost minBlobSize policy" */,
|
|
107
|
-
);
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
public loadSnapshotForRehydratingContainer(snapshotTree: ISnapshotTreeWithBlobContents) {
|
package/src/contracts.ts
CHANGED
|
@@ -133,10 +133,10 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
133
133
|
readonly incomingOpHandler: (messages: ISequencedDocumentMessage[], reason: string) => void;
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* Called by connection manager for each incoming
|
|
137
|
-
*
|
|
136
|
+
* Called by connection manager for each incoming signal.
|
|
137
|
+
* May be called before connectHandler is called (due to initial signals on socket connection)
|
|
138
138
|
*/
|
|
139
|
-
readonly signalHandler: (
|
|
139
|
+
readonly signalHandler: (signals: ISignalMessage[]) => void;
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* Called when connection manager experiences delay in connecting to relay service.
|
|
@@ -179,8 +179,12 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
179
179
|
*
|
|
180
180
|
* @param readonly - Whether or not the container is now read-only.
|
|
181
181
|
* `undefined` indicates that user permissions are not yet known.
|
|
182
|
+
* @param readonlyConnectionReason - reason/error if any for the change
|
|
182
183
|
*/
|
|
183
|
-
readonly readonlyChangeHandler: (
|
|
184
|
+
readonly readonlyChangeHandler: (
|
|
185
|
+
readonly?: boolean,
|
|
186
|
+
readonlyConnectionReason?: IConnectionStateChangeReason,
|
|
187
|
+
) => void;
|
|
184
188
|
|
|
185
189
|
/**
|
|
186
190
|
* Called whenever we try to start establishing a new connection.
|
package/src/debugLogger.ts
CHANGED
|
@@ -60,7 +60,10 @@ export class DebugLogger implements ITelemetryBaseLogger {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
private constructor(
|
|
63
|
+
private constructor(
|
|
64
|
+
private readonly debug: IDebugger,
|
|
65
|
+
private readonly debugErr: IDebugger,
|
|
66
|
+
) {}
|
|
64
67
|
|
|
65
68
|
/**
|
|
66
69
|
* Send an event to debug loggers
|
|
@@ -108,6 +111,7 @@ export class DebugLogger implements ITelemetryBaseLogger {
|
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
// Print multi-line.
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
111
115
|
logger(`${name} ${payload} ${tick} ${stack}`);
|
|
112
116
|
}
|
|
113
117
|
}
|
package/src/deltaManager.ts
CHANGED
|
@@ -50,7 +50,6 @@ import {
|
|
|
50
50
|
IConnectionStateChangeReason,
|
|
51
51
|
} from "./contracts";
|
|
52
52
|
import { DeltaQueue } from "./deltaQueue";
|
|
53
|
-
import { OnlyValidTermValue } from "./protocol";
|
|
54
53
|
import { ThrottlingWarning } from "./error";
|
|
55
54
|
|
|
56
55
|
export interface IConnectionArgs {
|
|
@@ -115,17 +114,6 @@ function isClientMessage(message: ISequencedDocumentMessage | IDocumentMessage):
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
/**
|
|
119
|
-
* Type is used to cast AbortController to represent new version of DOM API and prevent build issues
|
|
120
|
-
* TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
121
|
-
*/
|
|
122
|
-
type AbortControllerReal = AbortController & { abort(reason?: any): void };
|
|
123
|
-
/**
|
|
124
|
-
* Type is used to cast AbortSignal to represent new version of DOM API and prevent build issues
|
|
125
|
-
* TODO: Remove when typescript version of the repo contains the AbortSignal.reason property (AB#5045)
|
|
126
|
-
*/
|
|
127
|
-
type AbortSignalReal = AbortSignal & { reason: any };
|
|
128
|
-
|
|
129
117
|
/**
|
|
130
118
|
* Manages the flow of both inbound and outbound messages. This class ensures that shared objects receive delta
|
|
131
119
|
* messages in order regardless of possible network conditions or timings causing out of order delivery.
|
|
@@ -368,7 +356,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
368
356
|
assert(this.connectionManager.connected, 0x238 /* "called only in connected state" */);
|
|
369
357
|
|
|
370
358
|
const pendingSorted = this.pending.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
|
|
371
|
-
this.logger.
|
|
359
|
+
this.logger.sendTelemetryEvent({
|
|
372
360
|
...event,
|
|
373
361
|
// This directly tells us if fetching ops is in flight, and thus likely the reason of
|
|
374
362
|
// stalled op processing
|
|
@@ -403,7 +391,11 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
403
391
|
this.close(normalizeError(error));
|
|
404
392
|
}
|
|
405
393
|
},
|
|
406
|
-
signalHandler: (
|
|
394
|
+
signalHandler: (signals: ISignalMessage[]) => {
|
|
395
|
+
for (const signal of signals) {
|
|
396
|
+
this._inboundSignal.push(signal);
|
|
397
|
+
}
|
|
398
|
+
},
|
|
407
399
|
reconnectionDelayHandler: (delayMs: number, error: unknown) =>
|
|
408
400
|
this.emitDelayInfo(this.deltaStreamDelayId, delayMs, error),
|
|
409
401
|
closeHandler: (error: any) => this.close(error),
|
|
@@ -412,8 +404,12 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
412
404
|
connectHandler: (connection: IConnectionDetailsInternal) =>
|
|
413
405
|
this.connectHandler(connection),
|
|
414
406
|
pongHandler: (latency: number) => this.emit("pong", latency),
|
|
415
|
-
readonlyChangeHandler: (
|
|
416
|
-
|
|
407
|
+
readonlyChangeHandler: (
|
|
408
|
+
readonly?: boolean,
|
|
409
|
+
readonlyConnectionReason?: IConnectionStateChangeReason,
|
|
410
|
+
) => {
|
|
411
|
+
safeRaiseEvent(this, this.logger, "readonly", readonly, readonlyConnectionReason);
|
|
412
|
+
},
|
|
417
413
|
establishConnectionHandler: (reason: IConnectionStateChangeReason) =>
|
|
418
414
|
this.establishingConnection(reason),
|
|
419
415
|
cancelConnectionHandler: (reason: IConnectionStateChangeReason) =>
|
|
@@ -660,8 +656,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
660
656
|
// This is useless for known ranges (to is defined) as it means request is over either way.
|
|
661
657
|
// And it will cancel unbound request too early, not allowing us to learn where the end of the file is.
|
|
662
658
|
if (!opsFromFetch && cancelFetch(op)) {
|
|
663
|
-
|
|
664
|
-
(controller as AbortControllerReal).abort("DeltaManager getDeltas fetch cancelled");
|
|
659
|
+
controller.abort("DeltaManager getDeltas fetch cancelled");
|
|
665
660
|
this._inbound.off("push", opListener);
|
|
666
661
|
}
|
|
667
662
|
};
|
|
@@ -670,10 +665,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
670
665
|
this._inbound.on("push", opListener);
|
|
671
666
|
assert(this.closeAbortController.signal.onabort === null, 0x1e8 /* "reentrancy" */);
|
|
672
667
|
this.closeAbortController.signal.onabort = () =>
|
|
673
|
-
|
|
674
|
-
(controller as AbortControllerReal).abort(
|
|
675
|
-
(this.closeAbortController.signal as AbortSignalReal).reason,
|
|
676
|
-
);
|
|
668
|
+
controller.abort(this.closeAbortController.signal.reason);
|
|
677
669
|
|
|
678
670
|
const stream = this.deltaStorage.fetchMessages(
|
|
679
671
|
from, // inclusive
|
|
@@ -701,8 +693,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
701
693
|
this.logger.sendTelemetryEvent({
|
|
702
694
|
eventName: "DeltaManager_GetDeltasAborted",
|
|
703
695
|
fetchReason,
|
|
704
|
-
|
|
705
|
-
reason: (controller.signal as AbortSignalReal).reason,
|
|
696
|
+
reason: controller.signal.reason,
|
|
706
697
|
});
|
|
707
698
|
}
|
|
708
699
|
this.closeAbortController.signal.onabort = null;
|
|
@@ -759,8 +750,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
759
750
|
}
|
|
760
751
|
|
|
761
752
|
private clearQueues() {
|
|
762
|
-
|
|
763
|
-
(this.closeAbortController as AbortControllerReal).abort("DeltaManager is closed");
|
|
753
|
+
this.closeAbortController.abort("DeltaManager is closed");
|
|
764
754
|
|
|
765
755
|
this._inbound.clear();
|
|
766
756
|
this._inboundSignal.clear();
|
|
@@ -1059,11 +1049,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
1059
1049
|
0x267 /* "lastObservedSeqNumber should be updated first" */,
|
|
1060
1050
|
);
|
|
1061
1051
|
|
|
1062
|
-
// Back-compat for older server with no term
|
|
1063
|
-
if (message.term === undefined) {
|
|
1064
|
-
message.term = OnlyValidTermValue;
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
1052
|
if (this.handler === undefined) {
|
|
1068
1053
|
throw new Error("Attempted to process an inbound message without a handler attached");
|
|
1069
1054
|
}
|
package/src/index.ts
CHANGED
|
@@ -15,4 +15,9 @@ export {
|
|
|
15
15
|
Loader,
|
|
16
16
|
requestResolvedObjectFromContainer,
|
|
17
17
|
} from "./loader";
|
|
18
|
+
export {
|
|
19
|
+
isLocationRedirectionError,
|
|
20
|
+
resolveWithLocationRedirectionHandling,
|
|
21
|
+
} from "./location-redirection-utilities";
|
|
18
22
|
export { IProtocolHandler, ProtocolHandlerBuilder } from "./protocol";
|
|
23
|
+
export { tryParseCompatibleResolvedUrl, IParsedUrl } from "./utils";
|