@fluidframework/container-loader 2.0.0-rc.4.0.6 → 2.0.0-rc.5.0.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.
- package/CHANGELOG.md +21 -0
- package/api-extractor/api-extractor-lint-bundle.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
- package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-public.esm.json +5 -0
- package/api-extractor.json +1 -1
- package/api-report/{container-loader.api.md → container-loader.alpha.api.md} +60 -17
- package/api-report/container-loader.beta.api.md +44 -0
- package/api-report/container-loader.public.api.md +44 -0
- package/biome.jsonc +4 -0
- package/dist/attachment.d.ts +1 -1
- package/dist/attachment.d.ts.map +1 -1
- package/dist/attachment.js.map +1 -1
- package/dist/audience.d.ts +3 -2
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts.map +1 -1
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +4 -3
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +7 -8
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +1 -1
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +8 -4
- 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 +78 -80
- 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.map +1 -1
- package/dist/containerStorageAdapter.d.ts +5 -4
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +24 -8
- 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.d.ts.map +1 -1
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +6 -5
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +15 -16
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js.map +1 -1
- package/dist/disposal.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- 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/loadPaused.js.map +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js.map +1 -1
- package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/dist/memoryBlobStorage.d.ts.map +1 -1
- package/dist/memoryBlobStorage.js +4 -3
- package/dist/memoryBlobStorage.js.map +1 -1
- package/dist/noopHeuristic.d.ts +1 -1
- package/dist/noopHeuristic.d.ts.map +1 -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 +112 -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 +419 -0
- package/dist/protocol/quorum.js.map +1 -0
- package/dist/protocol.d.ts +4 -4
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +6 -6
- package/dist/protocol.js.map +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts +16 -9
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/protocolTreeDocumentStorageService.js +18 -9
- package/dist/protocolTreeDocumentStorageService.js.map +1 -1
- package/dist/quorum.d.ts +1 -1
- package/dist/quorum.d.ts.map +1 -1
- package/dist/quorum.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +3 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/serializedStateManager.d.ts +5 -5
- package/dist/serializedStateManager.d.ts.map +1 -1
- package/dist/serializedStateManager.js +16 -12
- package/dist/serializedStateManager.js.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +7 -7
- package/dist/utils.js.map +1 -1
- package/lib/attachment.d.ts +1 -1
- package/lib/attachment.d.ts.map +1 -1
- package/lib/attachment.js.map +1 -1
- package/lib/audience.d.ts +3 -2
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts.map +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +4 -3
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +6 -7
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +1 -1
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +8 -4
- 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 +20 -22
- 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.map +1 -1
- package/lib/containerStorageAdapter.d.ts +5 -4
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +24 -8
- 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.d.ts.map +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +6 -5
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +8 -9
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js.map +1 -1
- package/lib/disposal.js.map +1 -1
- package/lib/error.d.ts.map +1 -1
- 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/loadPaused.js.map +1 -1
- package/lib/loader.d.ts +1 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js.map +1 -1
- package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
- package/lib/memoryBlobStorage.d.ts.map +1 -1
- package/lib/memoryBlobStorage.js +4 -3
- package/lib/memoryBlobStorage.js.map +1 -1
- package/lib/noopHeuristic.d.ts +1 -1
- package/lib/noopHeuristic.d.ts.map +1 -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 +108 -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 +410 -0
- package/lib/protocol/quorum.js.map +1 -0
- package/lib/protocol.d.ts +4 -4
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -2
- package/lib/protocol.js.map +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts +16 -9
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/protocolTreeDocumentStorageService.js +18 -9
- package/lib/protocolTreeDocumentStorageService.js.map +1 -1
- package/lib/quorum.d.ts +1 -1
- package/lib/quorum.d.ts.map +1 -1
- package/lib/quorum.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +2 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +3 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/serializedStateManager.d.ts +5 -5
- package/lib/serializedStateManager.d.ts.map +1 -1
- package/lib/serializedStateManager.js +17 -13
- package/lib/serializedStateManager.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/utils.d.ts +2 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +3 -3
- package/lib/utils.js.map +1 -1
- package/package.json +32 -31
- package/src/attachment.ts +8 -5
- package/src/audience.ts +4 -7
- package/src/catchUpMonitor.ts +4 -2
- package/src/connectionManager.ts +27 -24
- package/src/connectionStateHandler.ts +11 -10
- package/src/container.ts +64 -72
- package/src/containerContext.ts +5 -5
- package/src/containerStorageAdapter.ts +37 -22
- package/src/contracts.ts +4 -5
- package/src/debugLogger.ts +2 -3
- package/src/deltaManager.ts +13 -18
- package/src/deltaQueue.ts +4 -1
- package/src/error.ts +4 -1
- package/src/index.ts +7 -0
- package/src/loadPaused.ts +1 -1
- package/src/loader.ts +1 -1
- package/src/memoryBlobStorage.ts +6 -4
- 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 +577 -0
- package/src/protocol.ts +6 -9
- package/src/protocolTreeDocumentStorageService.ts +30 -13
- package/src/quorum.ts +1 -1
- package/src/retriableDocumentStorageService.ts +6 -7
- package/src/serializedStateManager.ts +33 -34
- package/src/utils.ts +16 -10
- package/tsconfig.json +2 -0
- package/tsdoc.json +4 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
6
|
+
import { IQuorumClients, ISequencedClient } from "@fluidframework/driver-definitions";
|
|
7
|
+
import { ISequencedDocumentMessage, ICommittedProposal, IQuorum, IQuorumProposals, ISequencedProposal } from "@fluidframework/driver-definitions/internal";
|
|
8
|
+
/**
|
|
9
|
+
* Snapshot format for a QuorumClients
|
|
10
|
+
* @alpha
|
|
11
|
+
*/
|
|
12
|
+
export type QuorumClientsSnapshot = [string, ISequencedClient][];
|
|
13
|
+
/**
|
|
14
|
+
* Snapshot format for a QuorumProposals
|
|
15
|
+
* @alpha
|
|
16
|
+
*/
|
|
17
|
+
export type QuorumProposalsSnapshot = {
|
|
18
|
+
proposals: [number, ISequencedProposal, string[]][];
|
|
19
|
+
values: [string, ICommittedProposal][];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Snapshot format for a Quorum
|
|
23
|
+
* @alpha
|
|
24
|
+
*/
|
|
25
|
+
export interface IQuorumSnapshot {
|
|
26
|
+
members: QuorumClientsSnapshot;
|
|
27
|
+
proposals: QuorumProposalsSnapshot["proposals"];
|
|
28
|
+
values: QuorumProposalsSnapshot["values"];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The QuorumClients is used to track members joining and leaving the collaboration session.
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare class QuorumClients extends TypedEventEmitter<IQuorumClients["on"]> implements IQuorumClients {
|
|
35
|
+
private readonly members;
|
|
36
|
+
private isDisposed;
|
|
37
|
+
get disposed(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Cached snapshot state, to avoid unnecessary deep clones on repeated snapshot calls.
|
|
40
|
+
* Cleared immediately (set to undefined) when the cache becomes invalid.
|
|
41
|
+
*/
|
|
42
|
+
private snapshotCache;
|
|
43
|
+
constructor(snapshot: QuorumClientsSnapshot);
|
|
44
|
+
/**
|
|
45
|
+
* Snapshots the current state of the QuorumClients
|
|
46
|
+
* @returns a snapshot of the clients in the quorum
|
|
47
|
+
*/
|
|
48
|
+
snapshot(): QuorumClientsSnapshot;
|
|
49
|
+
/**
|
|
50
|
+
* Adds a new client to the quorum
|
|
51
|
+
*/
|
|
52
|
+
addMember(clientId: string, details: ISequencedClient): void;
|
|
53
|
+
/**
|
|
54
|
+
* Removes a client from the quorum
|
|
55
|
+
*/
|
|
56
|
+
removeMember(clientId: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves all the members in the quorum
|
|
59
|
+
*/
|
|
60
|
+
getMembers(): Map<string, ISequencedClient>;
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves a specific member of the quorum
|
|
63
|
+
*/
|
|
64
|
+
getMember(clientId: string): ISequencedClient | undefined;
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The QuorumProposals holds a key/value store. Proposed values become finalized in the store once all connected
|
|
69
|
+
* clients have seen the proposal.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export declare class QuorumProposals extends TypedEventEmitter<IQuorumProposals["on"]> implements IQuorumProposals {
|
|
73
|
+
private readonly sendProposal;
|
|
74
|
+
private readonly proposals;
|
|
75
|
+
private readonly values;
|
|
76
|
+
private isDisposed;
|
|
77
|
+
get disposed(): boolean;
|
|
78
|
+
private readonly stateEvents;
|
|
79
|
+
/**
|
|
80
|
+
* Cached snapshot state, to avoid unnecessary deep clones on repeated snapshot calls.
|
|
81
|
+
* Cleared immediately (set to undefined) when the cache becomes invalid.
|
|
82
|
+
*/
|
|
83
|
+
private proposalsSnapshotCache;
|
|
84
|
+
private valuesSnapshotCache;
|
|
85
|
+
constructor(snapshot: QuorumProposalsSnapshot, sendProposal: (key: string, value: any) => number);
|
|
86
|
+
/**
|
|
87
|
+
* Snapshots the current state of the QuorumProposals
|
|
88
|
+
* @returns arrays of proposals and values
|
|
89
|
+
*/
|
|
90
|
+
snapshot(): QuorumProposalsSnapshot;
|
|
91
|
+
/**
|
|
92
|
+
* Returns whether the quorum has achieved a consensus for the given key.
|
|
93
|
+
*/
|
|
94
|
+
has(key: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the consensus value for the given key
|
|
97
|
+
*/
|
|
98
|
+
get(key: string): any;
|
|
99
|
+
/**
|
|
100
|
+
* Returns additional data about the approved consensus value
|
|
101
|
+
* @deprecated Removed in recent protocol-definitions. Use get() instead.
|
|
102
|
+
*/
|
|
103
|
+
getApprovalData(key: string): ICommittedProposal | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Proposes a new value. Returns a promise that will either:
|
|
106
|
+
* - Resolve when the proposal is accepted
|
|
107
|
+
* - Reject if the proposal fails to send or if the QuorumProposals is disposed
|
|
108
|
+
*/
|
|
109
|
+
propose(key: string, value: any): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Begins tracking a new proposal
|
|
112
|
+
*/
|
|
113
|
+
addProposal(key: string, value: any, sequenceNumber: number, local: boolean, clientSequenceNumber: number): void;
|
|
114
|
+
/**
|
|
115
|
+
* Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
|
|
116
|
+
* becomes an approved value.
|
|
117
|
+
*/
|
|
118
|
+
updateMinimumSequenceNumber(message: ISequencedDocumentMessage): void;
|
|
119
|
+
setConnectionState(connected: boolean): void;
|
|
120
|
+
dispose(): void;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A quorum represents all clients currently within the collaboration window. As well as the values
|
|
124
|
+
* they have agreed upon and any pending proposals.
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
export declare class Quorum extends TypedEventEmitter<IQuorum["on"]> implements IQuorum {
|
|
128
|
+
private readonly quorumClients;
|
|
129
|
+
private readonly quorumProposals;
|
|
130
|
+
private readonly isDisposed;
|
|
131
|
+
get disposed(): boolean;
|
|
132
|
+
constructor(members: QuorumClientsSnapshot, proposals: QuorumProposalsSnapshot["proposals"], values: QuorumProposalsSnapshot["values"], sendProposal: (key: string, value: any) => number);
|
|
133
|
+
close(): void;
|
|
134
|
+
/**
|
|
135
|
+
* Snapshots the entire quorum
|
|
136
|
+
* @returns a quorum snapshot
|
|
137
|
+
*/
|
|
138
|
+
snapshot(): IQuorumSnapshot;
|
|
139
|
+
/**
|
|
140
|
+
* Returns whether the quorum has achieved a consensus for the given key.
|
|
141
|
+
*/
|
|
142
|
+
has(key: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Returns the consensus value for the given key
|
|
145
|
+
*/
|
|
146
|
+
get(key: string): any;
|
|
147
|
+
/**
|
|
148
|
+
* Returns additional data about the approved consensus value
|
|
149
|
+
* @deprecated Removed in recent protocol-definitions. Use get() instead.
|
|
150
|
+
*/
|
|
151
|
+
getApprovalData(key: string): ICommittedProposal | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Adds a new client to the quorum
|
|
154
|
+
*/
|
|
155
|
+
addMember(clientId: string, details: ISequencedClient): void;
|
|
156
|
+
/**
|
|
157
|
+
* Removes a client from the quorum
|
|
158
|
+
*/
|
|
159
|
+
removeMember(clientId: string): void;
|
|
160
|
+
/**
|
|
161
|
+
* Retrieves all the members in the quorum
|
|
162
|
+
*/
|
|
163
|
+
getMembers(): Map<string, ISequencedClient>;
|
|
164
|
+
/**
|
|
165
|
+
* Retrieves a specific member of the quorum
|
|
166
|
+
*/
|
|
167
|
+
getMember(clientId: string): ISequencedClient | undefined;
|
|
168
|
+
/**
|
|
169
|
+
* Proposes a new value. Returns a promise that will resolve when the proposal is either accepted, or reject if
|
|
170
|
+
* the proposal fails to send.
|
|
171
|
+
*/
|
|
172
|
+
propose(key: string, value: any): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Begins tracking a new proposal
|
|
175
|
+
*/
|
|
176
|
+
addProposal(key: string, value: any, sequenceNumber: number, local: boolean, clientSequenceNumber: number): void;
|
|
177
|
+
/**
|
|
178
|
+
* Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
|
|
179
|
+
* becomes an approved value.
|
|
180
|
+
*/
|
|
181
|
+
updateMinimumSequenceNumber(message: ISequencedDocumentMessage): void;
|
|
182
|
+
setConnectionState(connected: boolean, clientId?: string): void;
|
|
183
|
+
dispose(): void;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=quorum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quorum.d.ts","sourceRoot":"","sources":["../../src/protocol/quorum.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EACN,yBAAyB,EACzB,kBAAkB,EAClB,OAAO,EACP,gBAAgB,EAChB,kBAAkB,EAClB,MAAM,6CAA6C,CAAC;AAgBrD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAEjE;;;GAGG;AAEH,MAAM,MAAM,uBAAuB,GAAG;IACrC,SAAS,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACpD,MAAM,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,qBAAqB,CAAC;IAC/B,SAAS,EAAE,uBAAuB,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,qBAAa,aACZ,SAAQ,iBAAiB,CAAC,cAAc,CAAC,IAAI,CAAC,CAC9C,YAAW,cAAc;IAEzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAkB;IACpC,IAAW,QAAQ,YAElB;IAED;;;OAGG;IACH,OAAO,CAAC,aAAa,CAAoC;gBAE7C,QAAQ,EAAE,qBAAqB;IAO3C;;;OAGG;IACI,QAAQ,IAAI,qBAAqB;IAMxC;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB;IAU5D;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM;IAUpC;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIlD;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIzD,OAAO,IAAI,IAAI;CAGtB;AAED;;;;GAIG;AACH,qBAAa,eACZ,SAAQ,iBAAiB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAChD,YAAW,gBAAgB;IAqB1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAnB9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,UAAU,CAAkB;IACpC,IAAW,QAAQ,YAElB;IAGD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAElD;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAmD;IACjF,OAAO,CAAC,mBAAmB,CAAgD;gBAG1E,QAAQ,EAAE,uBAAuB,EAChB,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM;IAsBnE;;;OAGG;IACI,QAAQ,IAAI,uBAAuB;IAgB1C;;OAEG;IACI,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACI,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IAI5B;;;OAGG;IACI,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAInE;;;;OAIG;IACU,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAoE5D;;OAEG;IACI,WAAW,CACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,GAAG,EACV,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM;IAmB7B;;;OAGG;IACI,2BAA2B,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI;IA6ErE,kBAAkB,CAAC,SAAS,EAAE,OAAO;IAQrC,OAAO,IAAI,IAAI;CAItB;AAED;;;;GAIG;AACH,qBAAa,MAAO,SAAQ,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAE,YAAW,OAAO;IAC9E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,IAAW,QAAQ,YAElB;gBAGA,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,uBAAuB,CAAC,WAAW,CAAC,EAC/C,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,EACzC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM;IAwB3C,KAAK;IAIZ;;;OAGG;IACI,QAAQ,IAAI,eAAe;IAUlC;;OAEG;IACI,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACI,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IAI5B;;;OAGG;IACI,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAInE;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB;IAI5D;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM;IAIpC;;OAEG;IACI,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIlD;;OAEG;IACI,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIhE;;;OAGG;IACU,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D;;OAEG;IACI,WAAW,CACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,GAAG,EACV,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,OAAO,EACd,oBAAoB,EAAE,MAAM;IAW7B;;;OAGG;IACI,2BAA2B,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI;IAIrE,kBAAkB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM;IAIxD,OAAO,IAAI,IAAI;CAGtB"}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
6
|
+
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
+
import events_pkg from "events_pkg";
|
|
8
|
+
const { EventEmitter } = events_pkg;
|
|
9
|
+
/**
|
|
10
|
+
* Structure for tracking proposals that have been sequenced but not approved yet.
|
|
11
|
+
*/
|
|
12
|
+
class PendingProposal {
|
|
13
|
+
constructor(sequenceNumber, key, value, local) {
|
|
14
|
+
this.sequenceNumber = sequenceNumber;
|
|
15
|
+
this.key = key;
|
|
16
|
+
this.value = value;
|
|
17
|
+
this.local = local;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The QuorumClients is used to track members joining and leaving the collaboration session.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export class QuorumClients extends TypedEventEmitter {
|
|
25
|
+
get disposed() {
|
|
26
|
+
return this.isDisposed;
|
|
27
|
+
}
|
|
28
|
+
constructor(snapshot) {
|
|
29
|
+
super();
|
|
30
|
+
this.isDisposed = false;
|
|
31
|
+
this.members = new Map(snapshot);
|
|
32
|
+
this.snapshotCache = snapshot;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Snapshots the current state of the QuorumClients
|
|
36
|
+
* @returns a snapshot of the clients in the quorum
|
|
37
|
+
*/
|
|
38
|
+
snapshot() {
|
|
39
|
+
this.snapshotCache ?? (this.snapshotCache = Array.from(this.members));
|
|
40
|
+
return this.snapshotCache;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Adds a new client to the quorum
|
|
44
|
+
*/
|
|
45
|
+
addMember(clientId, details) {
|
|
46
|
+
assert(!!clientId, 0x9a0 /* clientId has to be non-empty string */);
|
|
47
|
+
assert(!this.members.has(clientId), 0x9a1 /* clientId not found */);
|
|
48
|
+
this.members.set(clientId, details);
|
|
49
|
+
this.emit("addMember", clientId, details);
|
|
50
|
+
// clear the cache
|
|
51
|
+
this.snapshotCache = undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Removes a client from the quorum
|
|
55
|
+
*/
|
|
56
|
+
removeMember(clientId) {
|
|
57
|
+
assert(!!clientId, 0x9a2 /* clientId has to be non-empty string */);
|
|
58
|
+
assert(this.members.has(clientId), 0x9a3 /* clientId not found */);
|
|
59
|
+
this.members.delete(clientId);
|
|
60
|
+
this.emit("removeMember", clientId);
|
|
61
|
+
// clear the cache
|
|
62
|
+
this.snapshotCache = undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves all the members in the quorum
|
|
66
|
+
*/
|
|
67
|
+
getMembers() {
|
|
68
|
+
return new Map(this.members);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Retrieves a specific member of the quorum
|
|
72
|
+
*/
|
|
73
|
+
getMember(clientId) {
|
|
74
|
+
return this.members.get(clientId);
|
|
75
|
+
}
|
|
76
|
+
dispose() {
|
|
77
|
+
this.isDisposed = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The QuorumProposals holds a key/value store. Proposed values become finalized in the store once all connected
|
|
82
|
+
* clients have seen the proposal.
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
export class QuorumProposals extends TypedEventEmitter {
|
|
86
|
+
get disposed() {
|
|
87
|
+
return this.isDisposed;
|
|
88
|
+
}
|
|
89
|
+
constructor(snapshot, sendProposal) {
|
|
90
|
+
super();
|
|
91
|
+
this.sendProposal = sendProposal;
|
|
92
|
+
this.isDisposed = false;
|
|
93
|
+
// Event emitter for changes to the environment that affect pending proposal promises.
|
|
94
|
+
this.stateEvents = new EventEmitter();
|
|
95
|
+
this.proposals = new Map(snapshot.proposals.map(([, proposal]) => {
|
|
96
|
+
return [
|
|
97
|
+
proposal.sequenceNumber,
|
|
98
|
+
new PendingProposal(proposal.sequenceNumber, proposal.key, proposal.value, false),
|
|
99
|
+
];
|
|
100
|
+
}));
|
|
101
|
+
this.values = new Map(snapshot.values);
|
|
102
|
+
this.proposalsSnapshotCache = snapshot.proposals;
|
|
103
|
+
this.valuesSnapshotCache = snapshot.values;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Snapshots the current state of the QuorumProposals
|
|
107
|
+
* @returns arrays of proposals and values
|
|
108
|
+
*/
|
|
109
|
+
snapshot() {
|
|
110
|
+
this.proposalsSnapshotCache ?? (this.proposalsSnapshotCache = Array.from(this.proposals).map(([sequenceNumber, proposal]) => [
|
|
111
|
+
sequenceNumber,
|
|
112
|
+
{ sequenceNumber, key: proposal.key, value: proposal.value },
|
|
113
|
+
[], // rejections, which has been removed
|
|
114
|
+
]));
|
|
115
|
+
this.valuesSnapshotCache ?? (this.valuesSnapshotCache = Array.from(this.values));
|
|
116
|
+
return {
|
|
117
|
+
proposals: this.proposalsSnapshotCache,
|
|
118
|
+
values: this.valuesSnapshotCache,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns whether the quorum has achieved a consensus for the given key.
|
|
123
|
+
*/
|
|
124
|
+
has(key) {
|
|
125
|
+
return this.values.has(key);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns the consensus value for the given key
|
|
129
|
+
*/
|
|
130
|
+
get(key) {
|
|
131
|
+
return this.values.get(key)?.value;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns additional data about the approved consensus value
|
|
135
|
+
* @deprecated Removed in recent protocol-definitions. Use get() instead.
|
|
136
|
+
*/
|
|
137
|
+
getApprovalData(key) {
|
|
138
|
+
return this.values.get(key);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Proposes a new value. Returns a promise that will either:
|
|
142
|
+
* - Resolve when the proposal is accepted
|
|
143
|
+
* - Reject if the proposal fails to send or if the QuorumProposals is disposed
|
|
144
|
+
*/
|
|
145
|
+
async propose(key, value) {
|
|
146
|
+
const clientSequenceNumber = this.sendProposal(key, value);
|
|
147
|
+
if (clientSequenceNumber < 0) {
|
|
148
|
+
this.emit("error", { eventName: "ProposalInDisconnectedState", key });
|
|
149
|
+
throw new Error("Can't propose in disconnected state");
|
|
150
|
+
}
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
// The sequence number that our proposal was assigned and went pending.
|
|
153
|
+
// If undefined, then it's not sequenced yet.
|
|
154
|
+
let thisProposalSequenceNumber;
|
|
155
|
+
// A proposal goes through two phases before this promise resolves:
|
|
156
|
+
// 1. Sequencing - waiting for the proposal to be ack'd by the server.
|
|
157
|
+
// 2. Approval - waiting for the proposal to be approved by connected clients.
|
|
158
|
+
const localProposalSequencedHandler = (sequencedCSN, sequenceNumber) => {
|
|
159
|
+
if (sequencedCSN === clientSequenceNumber) {
|
|
160
|
+
thisProposalSequenceNumber = sequenceNumber;
|
|
161
|
+
this.stateEvents.off("localProposalSequenced", localProposalSequencedHandler);
|
|
162
|
+
this.stateEvents.off("disconnected", disconnectedHandler);
|
|
163
|
+
this.stateEvents.on("localProposalApproved", localProposalApprovedHandler);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const localProposalApprovedHandler = (sequenceNumber) => {
|
|
167
|
+
// Proposals can be uniquely identified by the sequenceNumber they were assigned.
|
|
168
|
+
if (sequenceNumber === thisProposalSequenceNumber) {
|
|
169
|
+
resolve();
|
|
170
|
+
removeListeners();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
// There are two error flows we consider: disconnect and disposal.
|
|
174
|
+
// If we get disconnected before the proposal is sequenced, it has one of two possible futures:
|
|
175
|
+
// 1. We reconnect and see the proposal was sequenced in the meantime.
|
|
176
|
+
// -> The promise can still resolve, once it is approved.
|
|
177
|
+
// 2. We reconnect and see the proposal was not sequenced in the meantime, so it will never sequence.
|
|
178
|
+
// -> The promise rejects.
|
|
179
|
+
const disconnectedHandler = () => {
|
|
180
|
+
// If we haven't seen the ack by the time we disconnect, we hope to see it by the time we reconnect.
|
|
181
|
+
if (thisProposalSequenceNumber === undefined) {
|
|
182
|
+
this.stateEvents.once("connected", () => {
|
|
183
|
+
// If we don't see the ack by the time reconnection finishes, it failed to send.
|
|
184
|
+
if (thisProposalSequenceNumber === undefined) {
|
|
185
|
+
reject(new Error("Client disconnected without successfully sending proposal"));
|
|
186
|
+
removeListeners();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
// If the QuorumProposals is disposed of, we assume something catastrophic has happened
|
|
192
|
+
// All outstanding proposals are considered rejected.
|
|
193
|
+
const disposedHandler = () => {
|
|
194
|
+
reject(new Error("QuorumProposals was disposed"));
|
|
195
|
+
removeListeners();
|
|
196
|
+
};
|
|
197
|
+
// Convenience function to clean up our listeners.
|
|
198
|
+
const removeListeners = () => {
|
|
199
|
+
this.stateEvents.off("localProposalSequenced", localProposalSequencedHandler);
|
|
200
|
+
this.stateEvents.off("localProposalApproved", localProposalApprovedHandler);
|
|
201
|
+
this.stateEvents.off("disconnected", disconnectedHandler);
|
|
202
|
+
this.stateEvents.off("disposed", disposedHandler);
|
|
203
|
+
};
|
|
204
|
+
this.stateEvents.on("localProposalSequenced", localProposalSequencedHandler);
|
|
205
|
+
this.stateEvents.on("disconnected", disconnectedHandler);
|
|
206
|
+
this.stateEvents.on("disposed", disposedHandler);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Begins tracking a new proposal
|
|
211
|
+
*/
|
|
212
|
+
addProposal(key, value, sequenceNumber, local, clientSequenceNumber) {
|
|
213
|
+
assert(!this.proposals.has(sequenceNumber), 0x9a4 /* sequenceNumber not found */);
|
|
214
|
+
const proposal = new PendingProposal(sequenceNumber, key, value, local);
|
|
215
|
+
this.proposals.set(sequenceNumber, proposal);
|
|
216
|
+
// Legacy event, from rejection support. May still have some use for clients to learn that a proposal is
|
|
217
|
+
// likely to be approved soon.
|
|
218
|
+
this.emit("addProposal", proposal);
|
|
219
|
+
if (local) {
|
|
220
|
+
this.stateEvents.emit("localProposalSequenced", clientSequenceNumber, sequenceNumber);
|
|
221
|
+
}
|
|
222
|
+
// clear the proposal cache
|
|
223
|
+
this.proposalsSnapshotCache = undefined;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
|
|
227
|
+
* becomes an approved value.
|
|
228
|
+
*/
|
|
229
|
+
updateMinimumSequenceNumber(message) {
|
|
230
|
+
const msn = message.minimumSequenceNumber;
|
|
231
|
+
// Accept proposals proposals whose sequenceNumber is <= the minimumSequenceNumber
|
|
232
|
+
// Return a sorted list of approved proposals. We sort so that we apply them in their sequence number order
|
|
233
|
+
// TODO this can be optimized if necessary to avoid the linear search+sort
|
|
234
|
+
const completed = [];
|
|
235
|
+
for (const [sequenceNumber, proposal] of this.proposals) {
|
|
236
|
+
if (sequenceNumber <= msn) {
|
|
237
|
+
completed.push(proposal);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
completed.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
|
|
241
|
+
for (const proposal of completed) {
|
|
242
|
+
const committedProposal = {
|
|
243
|
+
approvalSequenceNumber: message.sequenceNumber,
|
|
244
|
+
// No longer used. We still stamp a -1 for compat with older versions of the quorum.
|
|
245
|
+
// Can be removed after 0.1035 and higher is ubiquitous.
|
|
246
|
+
commitSequenceNumber: -1,
|
|
247
|
+
key: proposal.key,
|
|
248
|
+
sequenceNumber: proposal.sequenceNumber,
|
|
249
|
+
value: proposal.value,
|
|
250
|
+
};
|
|
251
|
+
this.values.set(committedProposal.key, committedProposal);
|
|
252
|
+
// clear the values cache
|
|
253
|
+
this.valuesSnapshotCache = undefined;
|
|
254
|
+
// check if there are multiple proposals with matching keys
|
|
255
|
+
let proposalSettled = false;
|
|
256
|
+
let proposalKeySeen = false;
|
|
257
|
+
for (const [, p] of this.proposals) {
|
|
258
|
+
if (p.key === committedProposal.key) {
|
|
259
|
+
if (!proposalKeySeen) {
|
|
260
|
+
// set proposalSettled to true if the proposal key match is unique thus far
|
|
261
|
+
proposalSettled = true;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// set proposalSettled to false if matching proposal key is not unique
|
|
265
|
+
proposalSettled = false;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
proposalKeySeen = true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.emit("approveProposal", committedProposal.sequenceNumber, committedProposal.key, committedProposal.value, committedProposal.approvalSequenceNumber);
|
|
272
|
+
// emit approveProposalComplete when all pending proposals are processed
|
|
273
|
+
if (proposalSettled) {
|
|
274
|
+
this.emit("approveProposalComplete", committedProposal.sequenceNumber, committedProposal.key, committedProposal.value, committedProposal.approvalSequenceNumber);
|
|
275
|
+
}
|
|
276
|
+
this.proposals.delete(proposal.sequenceNumber);
|
|
277
|
+
// clear the proposals cache
|
|
278
|
+
this.proposalsSnapshotCache = undefined;
|
|
279
|
+
if (proposal.local) {
|
|
280
|
+
this.stateEvents.emit("localProposalApproved", proposal.sequenceNumber);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
setConnectionState(connected) {
|
|
285
|
+
if (connected) {
|
|
286
|
+
this.stateEvents.emit("connected");
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.stateEvents.emit("disconnected");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
dispose() {
|
|
293
|
+
this.isDisposed = true;
|
|
294
|
+
this.stateEvents.emit("disposed");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* A quorum represents all clients currently within the collaboration window. As well as the values
|
|
299
|
+
* they have agreed upon and any pending proposals.
|
|
300
|
+
* @internal
|
|
301
|
+
*/
|
|
302
|
+
export class Quorum extends TypedEventEmitter {
|
|
303
|
+
get disposed() {
|
|
304
|
+
return this.isDisposed;
|
|
305
|
+
}
|
|
306
|
+
constructor(members, proposals, values, sendProposal) {
|
|
307
|
+
super();
|
|
308
|
+
this.isDisposed = false;
|
|
309
|
+
this.quorumClients = new QuorumClients(members);
|
|
310
|
+
this.quorumClients.on("addMember", (clientId, details) => {
|
|
311
|
+
this.emit("addMember", clientId, details);
|
|
312
|
+
});
|
|
313
|
+
this.quorumClients.on("removeMember", (clientId) => {
|
|
314
|
+
this.emit("removeMember", clientId);
|
|
315
|
+
});
|
|
316
|
+
this.quorumProposals = new QuorumProposals({ proposals, values }, sendProposal);
|
|
317
|
+
this.quorumProposals.on("addProposal", (proposal) => {
|
|
318
|
+
this.emit("addProposal", proposal);
|
|
319
|
+
});
|
|
320
|
+
this.quorumProposals.on("approveProposal", (sequenceNumber, key, value, approvalSequenceNumber) => {
|
|
321
|
+
this.emit("approveProposal", sequenceNumber, key, value, approvalSequenceNumber);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
close() {
|
|
325
|
+
this.removeAllListeners();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Snapshots the entire quorum
|
|
329
|
+
* @returns a quorum snapshot
|
|
330
|
+
*/
|
|
331
|
+
snapshot() {
|
|
332
|
+
const members = this.quorumClients.snapshot();
|
|
333
|
+
const { proposals, values } = this.quorumProposals.snapshot();
|
|
334
|
+
return {
|
|
335
|
+
members,
|
|
336
|
+
proposals,
|
|
337
|
+
values,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Returns whether the quorum has achieved a consensus for the given key.
|
|
342
|
+
*/
|
|
343
|
+
has(key) {
|
|
344
|
+
return this.quorumProposals.has(key);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Returns the consensus value for the given key
|
|
348
|
+
*/
|
|
349
|
+
get(key) {
|
|
350
|
+
return this.quorumProposals.get(key);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Returns additional data about the approved consensus value
|
|
354
|
+
* @deprecated Removed in recent protocol-definitions. Use get() instead.
|
|
355
|
+
*/
|
|
356
|
+
getApprovalData(key) {
|
|
357
|
+
return this.quorumProposals.getApprovalData(key);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Adds a new client to the quorum
|
|
361
|
+
*/
|
|
362
|
+
addMember(clientId, details) {
|
|
363
|
+
this.quorumClients.addMember(clientId, details);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Removes a client from the quorum
|
|
367
|
+
*/
|
|
368
|
+
removeMember(clientId) {
|
|
369
|
+
this.quorumClients.removeMember(clientId);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Retrieves all the members in the quorum
|
|
373
|
+
*/
|
|
374
|
+
getMembers() {
|
|
375
|
+
return this.quorumClients.getMembers();
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Retrieves a specific member of the quorum
|
|
379
|
+
*/
|
|
380
|
+
getMember(clientId) {
|
|
381
|
+
return this.quorumClients.getMember(clientId);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Proposes a new value. Returns a promise that will resolve when the proposal is either accepted, or reject if
|
|
385
|
+
* the proposal fails to send.
|
|
386
|
+
*/
|
|
387
|
+
async propose(key, value) {
|
|
388
|
+
return this.quorumProposals.propose(key, value);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Begins tracking a new proposal
|
|
392
|
+
*/
|
|
393
|
+
addProposal(key, value, sequenceNumber, local, clientSequenceNumber) {
|
|
394
|
+
return this.quorumProposals.addProposal(key, value, sequenceNumber, local, clientSequenceNumber);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
|
|
398
|
+
* becomes an approved value.
|
|
399
|
+
*/
|
|
400
|
+
updateMinimumSequenceNumber(message) {
|
|
401
|
+
this.quorumProposals.updateMinimumSequenceNumber(message);
|
|
402
|
+
}
|
|
403
|
+
setConnectionState(connected, clientId) {
|
|
404
|
+
this.quorumProposals.setConnectionState(connected);
|
|
405
|
+
}
|
|
406
|
+
dispose() {
|
|
407
|
+
throw new Error("Not implemented.");
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=quorum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quorum.js","sourceRoot":"","sources":["../../src/protocol/quorum.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { IQuorumClients, ISequencedClient } from \"@fluidframework/driver-definitions\";\nimport {\n\tISequencedDocumentMessage,\n\tICommittedProposal,\n\tIQuorum,\n\tIQuorumProposals,\n\tISequencedProposal,\n} from \"@fluidframework/driver-definitions/internal\";\nimport events_pkg from \"events_pkg\";\nconst { EventEmitter } = events_pkg;\n\n/**\n * Structure for tracking proposals that have been sequenced but not approved yet.\n */\nclass PendingProposal implements ISequencedProposal {\n\tconstructor(\n\t\tpublic readonly sequenceNumber: number,\n\t\tpublic readonly key: string,\n\t\tpublic readonly value: any,\n\t\tpublic readonly local: boolean,\n\t) {}\n}\n\n/**\n * Snapshot format for a QuorumClients\n * @alpha\n */\nexport type QuorumClientsSnapshot = [string, ISequencedClient][];\n\n/**\n * Snapshot format for a QuorumProposals\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport type QuorumProposalsSnapshot = {\n\tproposals: [number, ISequencedProposal, string[]][];\n\tvalues: [string, ICommittedProposal][];\n};\n\n/**\n * Snapshot format for a Quorum\n * @alpha\n */\nexport interface IQuorumSnapshot {\n\tmembers: QuorumClientsSnapshot;\n\tproposals: QuorumProposalsSnapshot[\"proposals\"];\n\tvalues: QuorumProposalsSnapshot[\"values\"];\n}\n\n/**\n * The QuorumClients is used to track members joining and leaving the collaboration session.\n * @internal\n */\nexport class QuorumClients\n\textends TypedEventEmitter<IQuorumClients[\"on\"]>\n\timplements IQuorumClients\n{\n\tprivate readonly members: Map<string, ISequencedClient>;\n\tprivate isDisposed: boolean = false;\n\tpublic get disposed() {\n\t\treturn this.isDisposed;\n\t}\n\n\t/**\n\t * Cached snapshot state, to avoid unnecessary deep clones on repeated snapshot calls.\n\t * Cleared immediately (set to undefined) when the cache becomes invalid.\n\t */\n\tprivate snapshotCache: QuorumClientsSnapshot | undefined;\n\n\tconstructor(snapshot: QuorumClientsSnapshot) {\n\t\tsuper();\n\n\t\tthis.members = new Map(snapshot);\n\t\tthis.snapshotCache = snapshot;\n\t}\n\n\t/**\n\t * Snapshots the current state of the QuorumClients\n\t * @returns a snapshot of the clients in the quorum\n\t */\n\tpublic snapshot(): QuorumClientsSnapshot {\n\t\tthis.snapshotCache ??= Array.from(this.members);\n\n\t\treturn this.snapshotCache;\n\t}\n\n\t/**\n\t * Adds a new client to the quorum\n\t */\n\tpublic addMember(clientId: string, details: ISequencedClient) {\n\t\tassert(!!clientId, 0x9a0 /* clientId has to be non-empty string */);\n\t\tassert(!this.members.has(clientId), 0x9a1 /* clientId not found */);\n\t\tthis.members.set(clientId, details);\n\t\tthis.emit(\"addMember\", clientId, details);\n\n\t\t// clear the cache\n\t\tthis.snapshotCache = undefined;\n\t}\n\n\t/**\n\t * Removes a client from the quorum\n\t */\n\tpublic removeMember(clientId: string) {\n\t\tassert(!!clientId, 0x9a2 /* clientId has to be non-empty string */);\n\t\tassert(this.members.has(clientId), 0x9a3 /* clientId not found */);\n\t\tthis.members.delete(clientId);\n\t\tthis.emit(\"removeMember\", clientId);\n\n\t\t// clear the cache\n\t\tthis.snapshotCache = undefined;\n\t}\n\n\t/**\n\t * Retrieves all the members in the quorum\n\t */\n\tpublic getMembers(): Map<string, ISequencedClient> {\n\t\treturn new Map(this.members);\n\t}\n\n\t/**\n\t * Retrieves a specific member of the quorum\n\t */\n\tpublic getMember(clientId: string): ISequencedClient | undefined {\n\t\treturn this.members.get(clientId);\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.isDisposed = true;\n\t}\n}\n\n/**\n * The QuorumProposals holds a key/value store. Proposed values become finalized in the store once all connected\n * clients have seen the proposal.\n * @internal\n */\nexport class QuorumProposals\n\textends TypedEventEmitter<IQuorumProposals[\"on\"]>\n\timplements IQuorumProposals\n{\n\tprivate readonly proposals: Map<number, PendingProposal>;\n\tprivate readonly values: Map<string, ICommittedProposal>;\n\tprivate isDisposed: boolean = false;\n\tpublic get disposed() {\n\t\treturn this.isDisposed;\n\t}\n\n\t// Event emitter for changes to the environment that affect pending proposal promises.\n\tprivate readonly stateEvents = new EventEmitter();\n\n\t/**\n\t * Cached snapshot state, to avoid unnecessary deep clones on repeated snapshot calls.\n\t * Cleared immediately (set to undefined) when the cache becomes invalid.\n\t */\n\tprivate proposalsSnapshotCache: QuorumProposalsSnapshot[\"proposals\"] | undefined;\n\tprivate valuesSnapshotCache: QuorumProposalsSnapshot[\"values\"] | undefined;\n\n\tconstructor(\n\t\tsnapshot: QuorumProposalsSnapshot,\n\t\tprivate readonly sendProposal: (key: string, value: any) => number,\n\t) {\n\t\tsuper();\n\n\t\tthis.proposals = new Map(\n\t\t\tsnapshot.proposals.map(([, proposal]) => {\n\t\t\t\treturn [\n\t\t\t\t\tproposal.sequenceNumber,\n\t\t\t\t\tnew PendingProposal(\n\t\t\t\t\t\tproposal.sequenceNumber,\n\t\t\t\t\t\tproposal.key,\n\t\t\t\t\t\tproposal.value,\n\t\t\t\t\t\tfalse, // local\n\t\t\t\t\t),\n\t\t\t\t] as [number, PendingProposal];\n\t\t\t}),\n\t\t);\n\t\tthis.values = new Map(snapshot.values);\n\t\tthis.proposalsSnapshotCache = snapshot.proposals;\n\t\tthis.valuesSnapshotCache = snapshot.values;\n\t}\n\n\t/**\n\t * Snapshots the current state of the QuorumProposals\n\t * @returns arrays of proposals and values\n\t */\n\tpublic snapshot(): QuorumProposalsSnapshot {\n\t\tthis.proposalsSnapshotCache ??= Array.from(this.proposals).map(\n\t\t\t([sequenceNumber, proposal]) => [\n\t\t\t\tsequenceNumber,\n\t\t\t\t{ sequenceNumber, key: proposal.key, value: proposal.value },\n\t\t\t\t[], // rejections, which has been removed\n\t\t\t],\n\t\t);\n\t\tthis.valuesSnapshotCache ??= Array.from(this.values);\n\n\t\treturn {\n\t\t\tproposals: this.proposalsSnapshotCache,\n\t\t\tvalues: this.valuesSnapshotCache,\n\t\t};\n\t}\n\n\t/**\n\t * Returns whether the quorum has achieved a consensus for the given key.\n\t */\n\tpublic has(key: string): boolean {\n\t\treturn this.values.has(key);\n\t}\n\n\t/**\n\t * Returns the consensus value for the given key\n\t */\n\tpublic get(key: string): any {\n\t\treturn this.values.get(key)?.value;\n\t}\n\n\t/**\n\t * Returns additional data about the approved consensus value\n\t * @deprecated Removed in recent protocol-definitions. Use get() instead.\n\t */\n\tpublic getApprovalData(key: string): ICommittedProposal | undefined {\n\t\treturn this.values.get(key);\n\t}\n\n\t/**\n\t * Proposes a new value. Returns a promise that will either:\n\t * - Resolve when the proposal is accepted\n\t * - Reject if the proposal fails to send or if the QuorumProposals is disposed\n\t */\n\tpublic async propose(key: string, value: any): Promise<void> {\n\t\tconst clientSequenceNumber = this.sendProposal(key, value);\n\t\tif (clientSequenceNumber < 0) {\n\t\t\tthis.emit(\"error\", { eventName: \"ProposalInDisconnectedState\", key });\n\t\t\tthrow new Error(\"Can't propose in disconnected state\");\n\t\t}\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\t// The sequence number that our proposal was assigned and went pending.\n\t\t\t// If undefined, then it's not sequenced yet.\n\t\t\tlet thisProposalSequenceNumber: number | undefined;\n\n\t\t\t// A proposal goes through two phases before this promise resolves:\n\t\t\t// 1. Sequencing - waiting for the proposal to be ack'd by the server.\n\t\t\t// 2. Approval - waiting for the proposal to be approved by connected clients.\n\t\t\tconst localProposalSequencedHandler = (sequencedCSN: number, sequenceNumber: number) => {\n\t\t\t\tif (sequencedCSN === clientSequenceNumber) {\n\t\t\t\t\tthisProposalSequenceNumber = sequenceNumber;\n\t\t\t\t\tthis.stateEvents.off(\"localProposalSequenced\", localProposalSequencedHandler);\n\t\t\t\t\tthis.stateEvents.off(\"disconnected\", disconnectedHandler);\n\t\t\t\t\tthis.stateEvents.on(\"localProposalApproved\", localProposalApprovedHandler);\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst localProposalApprovedHandler = (sequenceNumber: number) => {\n\t\t\t\t// Proposals can be uniquely identified by the sequenceNumber they were assigned.\n\t\t\t\tif (sequenceNumber === thisProposalSequenceNumber) {\n\t\t\t\t\tresolve();\n\t\t\t\t\tremoveListeners();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// There are two error flows we consider: disconnect and disposal.\n\t\t\t// If we get disconnected before the proposal is sequenced, it has one of two possible futures:\n\t\t\t// 1. We reconnect and see the proposal was sequenced in the meantime.\n\t\t\t// -> The promise can still resolve, once it is approved.\n\t\t\t// 2. We reconnect and see the proposal was not sequenced in the meantime, so it will never sequence.\n\t\t\t// -> The promise rejects.\n\t\t\tconst disconnectedHandler = () => {\n\t\t\t\t// If we haven't seen the ack by the time we disconnect, we hope to see it by the time we reconnect.\n\t\t\t\tif (thisProposalSequenceNumber === undefined) {\n\t\t\t\t\tthis.stateEvents.once(\"connected\", () => {\n\t\t\t\t\t\t// If we don't see the ack by the time reconnection finishes, it failed to send.\n\t\t\t\t\t\tif (thisProposalSequenceNumber === undefined) {\n\t\t\t\t\t\t\treject(new Error(\"Client disconnected without successfully sending proposal\"));\n\t\t\t\t\t\t\tremoveListeners();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t\t// If the QuorumProposals is disposed of, we assume something catastrophic has happened\n\t\t\t// All outstanding proposals are considered rejected.\n\t\t\tconst disposedHandler = () => {\n\t\t\t\treject(new Error(\"QuorumProposals was disposed\"));\n\t\t\t\tremoveListeners();\n\t\t\t};\n\t\t\t// Convenience function to clean up our listeners.\n\t\t\tconst removeListeners = () => {\n\t\t\t\tthis.stateEvents.off(\"localProposalSequenced\", localProposalSequencedHandler);\n\t\t\t\tthis.stateEvents.off(\"localProposalApproved\", localProposalApprovedHandler);\n\t\t\t\tthis.stateEvents.off(\"disconnected\", disconnectedHandler);\n\t\t\t\tthis.stateEvents.off(\"disposed\", disposedHandler);\n\t\t\t};\n\t\t\tthis.stateEvents.on(\"localProposalSequenced\", localProposalSequencedHandler);\n\t\t\tthis.stateEvents.on(\"disconnected\", disconnectedHandler);\n\t\t\tthis.stateEvents.on(\"disposed\", disposedHandler);\n\t\t});\n\t}\n\n\t/**\n\t * Begins tracking a new proposal\n\t */\n\tpublic addProposal(\n\t\tkey: string,\n\t\tvalue: any,\n\t\tsequenceNumber: number,\n\t\tlocal: boolean,\n\t\tclientSequenceNumber: number,\n\t) {\n\t\tassert(!this.proposals.has(sequenceNumber), 0x9a4 /* sequenceNumber not found */);\n\n\t\tconst proposal = new PendingProposal(sequenceNumber, key, value, local);\n\t\tthis.proposals.set(sequenceNumber, proposal);\n\n\t\t// Legacy event, from rejection support. May still have some use for clients to learn that a proposal is\n\t\t// likely to be approved soon.\n\t\tthis.emit(\"addProposal\", proposal);\n\n\t\tif (local) {\n\t\t\tthis.stateEvents.emit(\"localProposalSequenced\", clientSequenceNumber, sequenceNumber);\n\t\t}\n\n\t\t// clear the proposal cache\n\t\tthis.proposalsSnapshotCache = undefined;\n\t}\n\n\t/**\n\t * Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it\n\t * becomes an approved value.\n\t */\n\tpublic updateMinimumSequenceNumber(message: ISequencedDocumentMessage): void {\n\t\tconst msn = message.minimumSequenceNumber;\n\n\t\t// Accept proposals proposals whose sequenceNumber is <= the minimumSequenceNumber\n\n\t\t// Return a sorted list of approved proposals. We sort so that we apply them in their sequence number order\n\t\t// TODO this can be optimized if necessary to avoid the linear search+sort\n\t\tconst completed: PendingProposal[] = [];\n\t\tfor (const [sequenceNumber, proposal] of this.proposals) {\n\t\t\tif (sequenceNumber <= msn) {\n\t\t\t\tcompleted.push(proposal);\n\t\t\t}\n\t\t}\n\t\tcompleted.sort((a, b) => a.sequenceNumber - b.sequenceNumber);\n\n\t\tfor (const proposal of completed) {\n\t\t\tconst committedProposal: ICommittedProposal = {\n\t\t\t\tapprovalSequenceNumber: message.sequenceNumber,\n\t\t\t\t// No longer used. We still stamp a -1 for compat with older versions of the quorum.\n\t\t\t\t// Can be removed after 0.1035 and higher is ubiquitous.\n\t\t\t\tcommitSequenceNumber: -1,\n\t\t\t\tkey: proposal.key,\n\t\t\t\tsequenceNumber: proposal.sequenceNumber,\n\t\t\t\tvalue: proposal.value,\n\t\t\t};\n\n\t\t\tthis.values.set(committedProposal.key, committedProposal);\n\n\t\t\t// clear the values cache\n\t\t\tthis.valuesSnapshotCache = undefined;\n\n\t\t\t// check if there are multiple proposals with matching keys\n\t\t\tlet proposalSettled = false;\n\t\t\tlet proposalKeySeen = false;\n\t\t\tfor (const [, p] of this.proposals) {\n\t\t\t\tif (p.key === committedProposal.key) {\n\t\t\t\t\tif (!proposalKeySeen) {\n\t\t\t\t\t\t// set proposalSettled to true if the proposal key match is unique thus far\n\t\t\t\t\t\tproposalSettled = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// set proposalSettled to false if matching proposal key is not unique\n\t\t\t\t\t\tproposalSettled = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tproposalKeySeen = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.emit(\n\t\t\t\t\"approveProposal\",\n\t\t\t\tcommittedProposal.sequenceNumber,\n\t\t\t\tcommittedProposal.key,\n\t\t\t\tcommittedProposal.value,\n\t\t\t\tcommittedProposal.approvalSequenceNumber,\n\t\t\t);\n\n\t\t\t// emit approveProposalComplete when all pending proposals are processed\n\t\t\tif (proposalSettled) {\n\t\t\t\tthis.emit(\n\t\t\t\t\t\"approveProposalComplete\",\n\t\t\t\t\tcommittedProposal.sequenceNumber,\n\t\t\t\t\tcommittedProposal.key,\n\t\t\t\t\tcommittedProposal.value,\n\t\t\t\t\tcommittedProposal.approvalSequenceNumber,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.proposals.delete(proposal.sequenceNumber);\n\n\t\t\t// clear the proposals cache\n\t\t\tthis.proposalsSnapshotCache = undefined;\n\t\t\tif (proposal.local) {\n\t\t\t\tthis.stateEvents.emit(\"localProposalApproved\", proposal.sequenceNumber);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic setConnectionState(connected: boolean) {\n\t\tif (connected) {\n\t\t\tthis.stateEvents.emit(\"connected\");\n\t\t} else {\n\t\t\tthis.stateEvents.emit(\"disconnected\");\n\t\t}\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.isDisposed = true;\n\t\tthis.stateEvents.emit(\"disposed\");\n\t}\n}\n\n/**\n * A quorum represents all clients currently within the collaboration window. As well as the values\n * they have agreed upon and any pending proposals.\n * @internal\n */\nexport class Quorum extends TypedEventEmitter<IQuorum[\"on\"]> implements IQuorum {\n\tprivate readonly quorumClients: QuorumClients;\n\tprivate readonly quorumProposals: QuorumProposals;\n\tprivate readonly isDisposed: boolean = false;\n\tpublic get disposed() {\n\t\treturn this.isDisposed;\n\t}\n\n\tconstructor(\n\t\tmembers: QuorumClientsSnapshot,\n\t\tproposals: QuorumProposalsSnapshot[\"proposals\"],\n\t\tvalues: QuorumProposalsSnapshot[\"values\"],\n\t\tsendProposal: (key: string, value: any) => number,\n\t) {\n\t\tsuper();\n\n\t\tthis.quorumClients = new QuorumClients(members);\n\t\tthis.quorumClients.on(\"addMember\", (clientId: string, details: ISequencedClient) => {\n\t\t\tthis.emit(\"addMember\", clientId, details);\n\t\t});\n\t\tthis.quorumClients.on(\"removeMember\", (clientId: string) => {\n\t\t\tthis.emit(\"removeMember\", clientId);\n\t\t});\n\n\t\tthis.quorumProposals = new QuorumProposals({ proposals, values }, sendProposal);\n\t\tthis.quorumProposals.on(\"addProposal\", (proposal: ISequencedProposal) => {\n\t\t\tthis.emit(\"addProposal\", proposal);\n\t\t});\n\t\tthis.quorumProposals.on(\n\t\t\t\"approveProposal\",\n\t\t\t(sequenceNumber: number, key: string, value: any, approvalSequenceNumber: number) => {\n\t\t\t\tthis.emit(\"approveProposal\", sequenceNumber, key, value, approvalSequenceNumber);\n\t\t\t},\n\t\t);\n\t}\n\n\tpublic close() {\n\t\tthis.removeAllListeners();\n\t}\n\n\t/**\n\t * Snapshots the entire quorum\n\t * @returns a quorum snapshot\n\t */\n\tpublic snapshot(): IQuorumSnapshot {\n\t\tconst members = this.quorumClients.snapshot();\n\t\tconst { proposals, values } = this.quorumProposals.snapshot();\n\t\treturn {\n\t\t\tmembers,\n\t\t\tproposals,\n\t\t\tvalues,\n\t\t};\n\t}\n\n\t/**\n\t * Returns whether the quorum has achieved a consensus for the given key.\n\t */\n\tpublic has(key: string): boolean {\n\t\treturn this.quorumProposals.has(key);\n\t}\n\n\t/**\n\t * Returns the consensus value for the given key\n\t */\n\tpublic get(key: string): any {\n\t\treturn this.quorumProposals.get(key);\n\t}\n\n\t/**\n\t * Returns additional data about the approved consensus value\n\t * @deprecated Removed in recent protocol-definitions. Use get() instead.\n\t */\n\tpublic getApprovalData(key: string): ICommittedProposal | undefined {\n\t\treturn this.quorumProposals.getApprovalData(key);\n\t}\n\n\t/**\n\t * Adds a new client to the quorum\n\t */\n\tpublic addMember(clientId: string, details: ISequencedClient) {\n\t\tthis.quorumClients.addMember(clientId, details);\n\t}\n\n\t/**\n\t * Removes a client from the quorum\n\t */\n\tpublic removeMember(clientId: string) {\n\t\tthis.quorumClients.removeMember(clientId);\n\t}\n\n\t/**\n\t * Retrieves all the members in the quorum\n\t */\n\tpublic getMembers(): Map<string, ISequencedClient> {\n\t\treturn this.quorumClients.getMembers();\n\t}\n\n\t/**\n\t * Retrieves a specific member of the quorum\n\t */\n\tpublic getMember(clientId: string): ISequencedClient | undefined {\n\t\treturn this.quorumClients.getMember(clientId);\n\t}\n\n\t/**\n\t * Proposes a new value. Returns a promise that will resolve when the proposal is either accepted, or reject if\n\t * the proposal fails to send.\n\t */\n\tpublic async propose(key: string, value: any): Promise<void> {\n\t\treturn this.quorumProposals.propose(key, value);\n\t}\n\n\t/**\n\t * Begins tracking a new proposal\n\t */\n\tpublic addProposal(\n\t\tkey: string,\n\t\tvalue: any,\n\t\tsequenceNumber: number,\n\t\tlocal: boolean,\n\t\tclientSequenceNumber: number,\n\t) {\n\t\treturn this.quorumProposals.addProposal(\n\t\t\tkey,\n\t\t\tvalue,\n\t\t\tsequenceNumber,\n\t\t\tlocal,\n\t\t\tclientSequenceNumber,\n\t\t);\n\t}\n\n\t/**\n\t * Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it\n\t * becomes an approved value.\n\t */\n\tpublic updateMinimumSequenceNumber(message: ISequencedDocumentMessage): void {\n\t\tthis.quorumProposals.updateMinimumSequenceNumber(message);\n\t}\n\n\tpublic setConnectionState(connected: boolean, clientId?: string) {\n\t\tthis.quorumProposals.setConnectionState(connected);\n\t}\n\n\tpublic dispose(): void {\n\t\tthrow new Error(\"Not implemented.\");\n\t}\n}\n"]}
|