@fluidframework/container-loader 2.0.0-rc.4.0.6 → 2.0.0-rc.5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/api-extractor/api-extractor-lint-bundle.json +5 -0
  3. package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
  4. package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
  5. package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
  6. package/api-extractor/api-extractor-lint-public.esm.json +5 -0
  7. package/api-extractor.json +1 -1
  8. package/api-report/{container-loader.api.md → container-loader.alpha.api.md} +60 -17
  9. package/api-report/container-loader.beta.api.md +44 -0
  10. package/api-report/container-loader.public.api.md +44 -0
  11. package/biome.jsonc +4 -0
  12. package/dist/attachment.d.ts +1 -1
  13. package/dist/attachment.d.ts.map +1 -1
  14. package/dist/attachment.js.map +1 -1
  15. package/dist/audience.d.ts +3 -2
  16. package/dist/audience.d.ts.map +1 -1
  17. package/dist/audience.js.map +1 -1
  18. package/dist/catchUpMonitor.d.ts.map +1 -1
  19. package/dist/catchUpMonitor.js.map +1 -1
  20. package/dist/connectionManager.d.ts +4 -3
  21. package/dist/connectionManager.d.ts.map +1 -1
  22. package/dist/connectionManager.js +7 -8
  23. package/dist/connectionManager.js.map +1 -1
  24. package/dist/connectionStateHandler.d.ts +1 -1
  25. package/dist/connectionStateHandler.d.ts.map +1 -1
  26. package/dist/connectionStateHandler.js +8 -4
  27. package/dist/connectionStateHandler.js.map +1 -1
  28. package/dist/container.d.ts +3 -2
  29. package/dist/container.d.ts.map +1 -1
  30. package/dist/container.js +78 -80
  31. package/dist/container.js.map +1 -1
  32. package/dist/containerContext.d.ts +2 -2
  33. package/dist/containerContext.d.ts.map +1 -1
  34. package/dist/containerContext.js.map +1 -1
  35. package/dist/containerStorageAdapter.d.ts +5 -4
  36. package/dist/containerStorageAdapter.d.ts.map +1 -1
  37. package/dist/containerStorageAdapter.js +24 -8
  38. package/dist/containerStorageAdapter.js.map +1 -1
  39. package/dist/contracts.d.ts +2 -2
  40. package/dist/contracts.d.ts.map +1 -1
  41. package/dist/contracts.js.map +1 -1
  42. package/dist/debugLogger.d.ts.map +1 -1
  43. package/dist/debugLogger.js.map +1 -1
  44. package/dist/deltaManager.d.ts +6 -5
  45. package/dist/deltaManager.d.ts.map +1 -1
  46. package/dist/deltaManager.js +15 -16
  47. package/dist/deltaManager.js.map +1 -1
  48. package/dist/deltaQueue.d.ts.map +1 -1
  49. package/dist/deltaQueue.js.map +1 -1
  50. package/dist/disposal.js.map +1 -1
  51. package/dist/error.d.ts.map +1 -1
  52. package/dist/error.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/legacy.d.ts +5 -0
  57. package/dist/loadPaused.js.map +1 -1
  58. package/dist/loader.d.ts +1 -1
  59. package/dist/loader.d.ts.map +1 -1
  60. package/dist/loader.js.map +1 -1
  61. package/dist/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  62. package/dist/memoryBlobStorage.d.ts.map +1 -1
  63. package/dist/memoryBlobStorage.js +4 -3
  64. package/dist/memoryBlobStorage.js.map +1 -1
  65. package/dist/noopHeuristic.d.ts +1 -1
  66. package/dist/noopHeuristic.d.ts.map +1 -1
  67. package/dist/noopHeuristic.js.map +1 -1
  68. package/dist/packageVersion.d.ts +1 -1
  69. package/dist/packageVersion.js +1 -1
  70. package/dist/packageVersion.js.map +1 -1
  71. package/dist/protocol/index.d.ts +7 -0
  72. package/dist/protocol/index.d.ts.map +1 -0
  73. package/dist/protocol/index.js +12 -0
  74. package/dist/protocol/index.js.map +1 -0
  75. package/dist/protocol/protocol.d.ts +52 -0
  76. package/dist/protocol/protocol.d.ts.map +1 -0
  77. package/dist/protocol/protocol.js +112 -0
  78. package/dist/protocol/protocol.js.map +1 -0
  79. package/dist/protocol/quorum.d.ts +185 -0
  80. package/dist/protocol/quorum.d.ts.map +1 -0
  81. package/dist/protocol/quorum.js +419 -0
  82. package/dist/protocol/quorum.js.map +1 -0
  83. package/dist/protocol.d.ts +4 -4
  84. package/dist/protocol.d.ts.map +1 -1
  85. package/dist/protocol.js +6 -6
  86. package/dist/protocol.js.map +1 -1
  87. package/dist/protocolTreeDocumentStorageService.d.ts +16 -9
  88. package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
  89. package/dist/protocolTreeDocumentStorageService.js +18 -9
  90. package/dist/protocolTreeDocumentStorageService.js.map +1 -1
  91. package/dist/quorum.d.ts +1 -1
  92. package/dist/quorum.d.ts.map +1 -1
  93. package/dist/quorum.js.map +1 -1
  94. package/dist/retriableDocumentStorageService.d.ts +2 -2
  95. package/dist/retriableDocumentStorageService.d.ts.map +1 -1
  96. package/dist/retriableDocumentStorageService.js +3 -1
  97. package/dist/retriableDocumentStorageService.js.map +1 -1
  98. package/dist/serializedStateManager.d.ts +5 -5
  99. package/dist/serializedStateManager.d.ts.map +1 -1
  100. package/dist/serializedStateManager.js +16 -12
  101. package/dist/serializedStateManager.js.map +1 -1
  102. package/dist/utils.d.ts +2 -1
  103. package/dist/utils.d.ts.map +1 -1
  104. package/dist/utils.js +7 -7
  105. package/dist/utils.js.map +1 -1
  106. package/lib/attachment.d.ts +1 -1
  107. package/lib/attachment.d.ts.map +1 -1
  108. package/lib/attachment.js.map +1 -1
  109. package/lib/audience.d.ts +3 -2
  110. package/lib/audience.d.ts.map +1 -1
  111. package/lib/audience.js.map +1 -1
  112. package/lib/catchUpMonitor.d.ts.map +1 -1
  113. package/lib/catchUpMonitor.js.map +1 -1
  114. package/lib/connectionManager.d.ts +4 -3
  115. package/lib/connectionManager.d.ts.map +1 -1
  116. package/lib/connectionManager.js +6 -7
  117. package/lib/connectionManager.js.map +1 -1
  118. package/lib/connectionStateHandler.d.ts +1 -1
  119. package/lib/connectionStateHandler.d.ts.map +1 -1
  120. package/lib/connectionStateHandler.js +8 -4
  121. package/lib/connectionStateHandler.js.map +1 -1
  122. package/lib/container.d.ts +3 -2
  123. package/lib/container.d.ts.map +1 -1
  124. package/lib/container.js +20 -22
  125. package/lib/container.js.map +1 -1
  126. package/lib/containerContext.d.ts +2 -2
  127. package/lib/containerContext.d.ts.map +1 -1
  128. package/lib/containerContext.js.map +1 -1
  129. package/lib/containerStorageAdapter.d.ts +5 -4
  130. package/lib/containerStorageAdapter.d.ts.map +1 -1
  131. package/lib/containerStorageAdapter.js +24 -8
  132. package/lib/containerStorageAdapter.js.map +1 -1
  133. package/lib/contracts.d.ts +2 -2
  134. package/lib/contracts.d.ts.map +1 -1
  135. package/lib/contracts.js.map +1 -1
  136. package/lib/debugLogger.d.ts.map +1 -1
  137. package/lib/debugLogger.js.map +1 -1
  138. package/lib/deltaManager.d.ts +6 -5
  139. package/lib/deltaManager.d.ts.map +1 -1
  140. package/lib/deltaManager.js +8 -9
  141. package/lib/deltaManager.js.map +1 -1
  142. package/lib/deltaQueue.d.ts.map +1 -1
  143. package/lib/deltaQueue.js.map +1 -1
  144. package/lib/disposal.js.map +1 -1
  145. package/lib/error.d.ts.map +1 -1
  146. package/lib/error.js.map +1 -1
  147. package/lib/index.d.ts +1 -0
  148. package/lib/index.d.ts.map +1 -1
  149. package/lib/index.js.map +1 -1
  150. package/lib/legacy.d.ts +5 -0
  151. package/lib/loadPaused.js.map +1 -1
  152. package/lib/loader.d.ts +1 -1
  153. package/lib/loader.d.ts.map +1 -1
  154. package/lib/loader.js.map +1 -1
  155. package/lib/location-redirection-utilities/resolveWithLocationRedirection.js.map +1 -1
  156. package/lib/memoryBlobStorage.d.ts.map +1 -1
  157. package/lib/memoryBlobStorage.js +4 -3
  158. package/lib/memoryBlobStorage.js.map +1 -1
  159. package/lib/noopHeuristic.d.ts +1 -1
  160. package/lib/noopHeuristic.d.ts.map +1 -1
  161. package/lib/noopHeuristic.js.map +1 -1
  162. package/lib/packageVersion.d.ts +1 -1
  163. package/lib/packageVersion.js +1 -1
  164. package/lib/packageVersion.js.map +1 -1
  165. package/lib/protocol/index.d.ts +7 -0
  166. package/lib/protocol/index.d.ts.map +1 -0
  167. package/lib/protocol/index.js +7 -0
  168. package/lib/protocol/index.js.map +1 -0
  169. package/lib/protocol/protocol.d.ts +52 -0
  170. package/lib/protocol/protocol.d.ts.map +1 -0
  171. package/lib/protocol/protocol.js +108 -0
  172. package/lib/protocol/protocol.js.map +1 -0
  173. package/lib/protocol/quorum.d.ts +185 -0
  174. package/lib/protocol/quorum.d.ts.map +1 -0
  175. package/lib/protocol/quorum.js +410 -0
  176. package/lib/protocol/quorum.js.map +1 -0
  177. package/lib/protocol.d.ts +4 -4
  178. package/lib/protocol.d.ts.map +1 -1
  179. package/lib/protocol.js +2 -2
  180. package/lib/protocol.js.map +1 -1
  181. package/lib/protocolTreeDocumentStorageService.d.ts +16 -9
  182. package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
  183. package/lib/protocolTreeDocumentStorageService.js +18 -9
  184. package/lib/protocolTreeDocumentStorageService.js.map +1 -1
  185. package/lib/quorum.d.ts +1 -1
  186. package/lib/quorum.d.ts.map +1 -1
  187. package/lib/quorum.js.map +1 -1
  188. package/lib/retriableDocumentStorageService.d.ts +2 -2
  189. package/lib/retriableDocumentStorageService.d.ts.map +1 -1
  190. package/lib/retriableDocumentStorageService.js +3 -1
  191. package/lib/retriableDocumentStorageService.js.map +1 -1
  192. package/lib/serializedStateManager.d.ts +5 -5
  193. package/lib/serializedStateManager.d.ts.map +1 -1
  194. package/lib/serializedStateManager.js +17 -13
  195. package/lib/serializedStateManager.js.map +1 -1
  196. package/lib/tsdoc-metadata.json +1 -1
  197. package/lib/utils.d.ts +2 -1
  198. package/lib/utils.d.ts.map +1 -1
  199. package/lib/utils.js +3 -3
  200. package/lib/utils.js.map +1 -1
  201. package/package.json +32 -31
  202. package/src/attachment.ts +8 -5
  203. package/src/audience.ts +4 -7
  204. package/src/catchUpMonitor.ts +4 -2
  205. package/src/connectionManager.ts +27 -24
  206. package/src/connectionStateHandler.ts +11 -10
  207. package/src/container.ts +64 -72
  208. package/src/containerContext.ts +5 -5
  209. package/src/containerStorageAdapter.ts +37 -22
  210. package/src/contracts.ts +4 -5
  211. package/src/debugLogger.ts +2 -3
  212. package/src/deltaManager.ts +13 -18
  213. package/src/deltaQueue.ts +4 -1
  214. package/src/error.ts +4 -1
  215. package/src/index.ts +7 -0
  216. package/src/loadPaused.ts +1 -1
  217. package/src/loader.ts +1 -1
  218. package/src/memoryBlobStorage.ts +6 -4
  219. package/src/noopHeuristic.ts +1 -1
  220. package/src/packageVersion.ts +1 -1
  221. package/src/protocol/README.md +10 -0
  222. package/src/protocol/index.ts +16 -0
  223. package/src/protocol/protocol.ts +185 -0
  224. package/src/protocol/quorum.ts +577 -0
  225. package/src/protocol.ts +6 -9
  226. package/src/protocolTreeDocumentStorageService.ts +30 -13
  227. package/src/quorum.ts +1 -1
  228. package/src/retriableDocumentStorageService.ts +6 -7
  229. package/src/serializedStateManager.ts +33 -34
  230. package/src/utils.ts +16 -10
  231. package/tsconfig.json +2 -0
  232. 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,419 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.Quorum = exports.QuorumProposals = exports.QuorumClients = void 0;
11
+ const client_utils_1 = require("@fluid-internal/client-utils");
12
+ const internal_1 = require("@fluidframework/core-utils/internal");
13
+ const events_pkg_1 = __importDefault(require("events_pkg"));
14
+ const { EventEmitter } = events_pkg_1.default;
15
+ /**
16
+ * Structure for tracking proposals that have been sequenced but not approved yet.
17
+ */
18
+ class PendingProposal {
19
+ constructor(sequenceNumber, key, value, local) {
20
+ this.sequenceNumber = sequenceNumber;
21
+ this.key = key;
22
+ this.value = value;
23
+ this.local = local;
24
+ }
25
+ }
26
+ /**
27
+ * The QuorumClients is used to track members joining and leaving the collaboration session.
28
+ * @internal
29
+ */
30
+ class QuorumClients extends client_utils_1.TypedEventEmitter {
31
+ get disposed() {
32
+ return this.isDisposed;
33
+ }
34
+ constructor(snapshot) {
35
+ super();
36
+ this.isDisposed = false;
37
+ this.members = new Map(snapshot);
38
+ this.snapshotCache = snapshot;
39
+ }
40
+ /**
41
+ * Snapshots the current state of the QuorumClients
42
+ * @returns a snapshot of the clients in the quorum
43
+ */
44
+ snapshot() {
45
+ this.snapshotCache ?? (this.snapshotCache = Array.from(this.members));
46
+ return this.snapshotCache;
47
+ }
48
+ /**
49
+ * Adds a new client to the quorum
50
+ */
51
+ addMember(clientId, details) {
52
+ (0, internal_1.assert)(!!clientId, 0x9a0 /* clientId has to be non-empty string */);
53
+ (0, internal_1.assert)(!this.members.has(clientId), 0x9a1 /* clientId not found */);
54
+ this.members.set(clientId, details);
55
+ this.emit("addMember", clientId, details);
56
+ // clear the cache
57
+ this.snapshotCache = undefined;
58
+ }
59
+ /**
60
+ * Removes a client from the quorum
61
+ */
62
+ removeMember(clientId) {
63
+ (0, internal_1.assert)(!!clientId, 0x9a2 /* clientId has to be non-empty string */);
64
+ (0, internal_1.assert)(this.members.has(clientId), 0x9a3 /* clientId not found */);
65
+ this.members.delete(clientId);
66
+ this.emit("removeMember", clientId);
67
+ // clear the cache
68
+ this.snapshotCache = undefined;
69
+ }
70
+ /**
71
+ * Retrieves all the members in the quorum
72
+ */
73
+ getMembers() {
74
+ return new Map(this.members);
75
+ }
76
+ /**
77
+ * Retrieves a specific member of the quorum
78
+ */
79
+ getMember(clientId) {
80
+ return this.members.get(clientId);
81
+ }
82
+ dispose() {
83
+ this.isDisposed = true;
84
+ }
85
+ }
86
+ exports.QuorumClients = QuorumClients;
87
+ /**
88
+ * The QuorumProposals holds a key/value store. Proposed values become finalized in the store once all connected
89
+ * clients have seen the proposal.
90
+ * @internal
91
+ */
92
+ class QuorumProposals extends client_utils_1.TypedEventEmitter {
93
+ get disposed() {
94
+ return this.isDisposed;
95
+ }
96
+ constructor(snapshot, sendProposal) {
97
+ super();
98
+ this.sendProposal = sendProposal;
99
+ this.isDisposed = false;
100
+ // Event emitter for changes to the environment that affect pending proposal promises.
101
+ this.stateEvents = new EventEmitter();
102
+ this.proposals = new Map(snapshot.proposals.map(([, proposal]) => {
103
+ return [
104
+ proposal.sequenceNumber,
105
+ new PendingProposal(proposal.sequenceNumber, proposal.key, proposal.value, false),
106
+ ];
107
+ }));
108
+ this.values = new Map(snapshot.values);
109
+ this.proposalsSnapshotCache = snapshot.proposals;
110
+ this.valuesSnapshotCache = snapshot.values;
111
+ }
112
+ /**
113
+ * Snapshots the current state of the QuorumProposals
114
+ * @returns arrays of proposals and values
115
+ */
116
+ snapshot() {
117
+ this.proposalsSnapshotCache ?? (this.proposalsSnapshotCache = Array.from(this.proposals).map(([sequenceNumber, proposal]) => [
118
+ sequenceNumber,
119
+ { sequenceNumber, key: proposal.key, value: proposal.value },
120
+ [], // rejections, which has been removed
121
+ ]));
122
+ this.valuesSnapshotCache ?? (this.valuesSnapshotCache = Array.from(this.values));
123
+ return {
124
+ proposals: this.proposalsSnapshotCache,
125
+ values: this.valuesSnapshotCache,
126
+ };
127
+ }
128
+ /**
129
+ * Returns whether the quorum has achieved a consensus for the given key.
130
+ */
131
+ has(key) {
132
+ return this.values.has(key);
133
+ }
134
+ /**
135
+ * Returns the consensus value for the given key
136
+ */
137
+ get(key) {
138
+ return this.values.get(key)?.value;
139
+ }
140
+ /**
141
+ * Returns additional data about the approved consensus value
142
+ * @deprecated Removed in recent protocol-definitions. Use get() instead.
143
+ */
144
+ getApprovalData(key) {
145
+ return this.values.get(key);
146
+ }
147
+ /**
148
+ * Proposes a new value. Returns a promise that will either:
149
+ * - Resolve when the proposal is accepted
150
+ * - Reject if the proposal fails to send or if the QuorumProposals is disposed
151
+ */
152
+ async propose(key, value) {
153
+ const clientSequenceNumber = this.sendProposal(key, value);
154
+ if (clientSequenceNumber < 0) {
155
+ this.emit("error", { eventName: "ProposalInDisconnectedState", key });
156
+ throw new Error("Can't propose in disconnected state");
157
+ }
158
+ return new Promise((resolve, reject) => {
159
+ // The sequence number that our proposal was assigned and went pending.
160
+ // If undefined, then it's not sequenced yet.
161
+ let thisProposalSequenceNumber;
162
+ // A proposal goes through two phases before this promise resolves:
163
+ // 1. Sequencing - waiting for the proposal to be ack'd by the server.
164
+ // 2. Approval - waiting for the proposal to be approved by connected clients.
165
+ const localProposalSequencedHandler = (sequencedCSN, sequenceNumber) => {
166
+ if (sequencedCSN === clientSequenceNumber) {
167
+ thisProposalSequenceNumber = sequenceNumber;
168
+ this.stateEvents.off("localProposalSequenced", localProposalSequencedHandler);
169
+ this.stateEvents.off("disconnected", disconnectedHandler);
170
+ this.stateEvents.on("localProposalApproved", localProposalApprovedHandler);
171
+ }
172
+ };
173
+ const localProposalApprovedHandler = (sequenceNumber) => {
174
+ // Proposals can be uniquely identified by the sequenceNumber they were assigned.
175
+ if (sequenceNumber === thisProposalSequenceNumber) {
176
+ resolve();
177
+ removeListeners();
178
+ }
179
+ };
180
+ // There are two error flows we consider: disconnect and disposal.
181
+ // If we get disconnected before the proposal is sequenced, it has one of two possible futures:
182
+ // 1. We reconnect and see the proposal was sequenced in the meantime.
183
+ // -> The promise can still resolve, once it is approved.
184
+ // 2. We reconnect and see the proposal was not sequenced in the meantime, so it will never sequence.
185
+ // -> The promise rejects.
186
+ const disconnectedHandler = () => {
187
+ // If we haven't seen the ack by the time we disconnect, we hope to see it by the time we reconnect.
188
+ if (thisProposalSequenceNumber === undefined) {
189
+ this.stateEvents.once("connected", () => {
190
+ // If we don't see the ack by the time reconnection finishes, it failed to send.
191
+ if (thisProposalSequenceNumber === undefined) {
192
+ reject(new Error("Client disconnected without successfully sending proposal"));
193
+ removeListeners();
194
+ }
195
+ });
196
+ }
197
+ };
198
+ // If the QuorumProposals is disposed of, we assume something catastrophic has happened
199
+ // All outstanding proposals are considered rejected.
200
+ const disposedHandler = () => {
201
+ reject(new Error("QuorumProposals was disposed"));
202
+ removeListeners();
203
+ };
204
+ // Convenience function to clean up our listeners.
205
+ const removeListeners = () => {
206
+ this.stateEvents.off("localProposalSequenced", localProposalSequencedHandler);
207
+ this.stateEvents.off("localProposalApproved", localProposalApprovedHandler);
208
+ this.stateEvents.off("disconnected", disconnectedHandler);
209
+ this.stateEvents.off("disposed", disposedHandler);
210
+ };
211
+ this.stateEvents.on("localProposalSequenced", localProposalSequencedHandler);
212
+ this.stateEvents.on("disconnected", disconnectedHandler);
213
+ this.stateEvents.on("disposed", disposedHandler);
214
+ });
215
+ }
216
+ /**
217
+ * Begins tracking a new proposal
218
+ */
219
+ addProposal(key, value, sequenceNumber, local, clientSequenceNumber) {
220
+ (0, internal_1.assert)(!this.proposals.has(sequenceNumber), 0x9a4 /* sequenceNumber not found */);
221
+ const proposal = new PendingProposal(sequenceNumber, key, value, local);
222
+ this.proposals.set(sequenceNumber, proposal);
223
+ // Legacy event, from rejection support. May still have some use for clients to learn that a proposal is
224
+ // likely to be approved soon.
225
+ this.emit("addProposal", proposal);
226
+ if (local) {
227
+ this.stateEvents.emit("localProposalSequenced", clientSequenceNumber, sequenceNumber);
228
+ }
229
+ // clear the proposal cache
230
+ this.proposalsSnapshotCache = undefined;
231
+ }
232
+ /**
233
+ * Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
234
+ * becomes an approved value.
235
+ */
236
+ updateMinimumSequenceNumber(message) {
237
+ const msn = message.minimumSequenceNumber;
238
+ // Accept proposals proposals whose sequenceNumber is <= the minimumSequenceNumber
239
+ // Return a sorted list of approved proposals. We sort so that we apply them in their sequence number order
240
+ // TODO this can be optimized if necessary to avoid the linear search+sort
241
+ const completed = [];
242
+ for (const [sequenceNumber, proposal] of this.proposals) {
243
+ if (sequenceNumber <= msn) {
244
+ completed.push(proposal);
245
+ }
246
+ }
247
+ completed.sort((a, b) => a.sequenceNumber - b.sequenceNumber);
248
+ for (const proposal of completed) {
249
+ const committedProposal = {
250
+ approvalSequenceNumber: message.sequenceNumber,
251
+ // No longer used. We still stamp a -1 for compat with older versions of the quorum.
252
+ // Can be removed after 0.1035 and higher is ubiquitous.
253
+ commitSequenceNumber: -1,
254
+ key: proposal.key,
255
+ sequenceNumber: proposal.sequenceNumber,
256
+ value: proposal.value,
257
+ };
258
+ this.values.set(committedProposal.key, committedProposal);
259
+ // clear the values cache
260
+ this.valuesSnapshotCache = undefined;
261
+ // check if there are multiple proposals with matching keys
262
+ let proposalSettled = false;
263
+ let proposalKeySeen = false;
264
+ for (const [, p] of this.proposals) {
265
+ if (p.key === committedProposal.key) {
266
+ if (!proposalKeySeen) {
267
+ // set proposalSettled to true if the proposal key match is unique thus far
268
+ proposalSettled = true;
269
+ }
270
+ else {
271
+ // set proposalSettled to false if matching proposal key is not unique
272
+ proposalSettled = false;
273
+ break;
274
+ }
275
+ proposalKeySeen = true;
276
+ }
277
+ }
278
+ this.emit("approveProposal", committedProposal.sequenceNumber, committedProposal.key, committedProposal.value, committedProposal.approvalSequenceNumber);
279
+ // emit approveProposalComplete when all pending proposals are processed
280
+ if (proposalSettled) {
281
+ this.emit("approveProposalComplete", committedProposal.sequenceNumber, committedProposal.key, committedProposal.value, committedProposal.approvalSequenceNumber);
282
+ }
283
+ this.proposals.delete(proposal.sequenceNumber);
284
+ // clear the proposals cache
285
+ this.proposalsSnapshotCache = undefined;
286
+ if (proposal.local) {
287
+ this.stateEvents.emit("localProposalApproved", proposal.sequenceNumber);
288
+ }
289
+ }
290
+ }
291
+ setConnectionState(connected) {
292
+ if (connected) {
293
+ this.stateEvents.emit("connected");
294
+ }
295
+ else {
296
+ this.stateEvents.emit("disconnected");
297
+ }
298
+ }
299
+ dispose() {
300
+ this.isDisposed = true;
301
+ this.stateEvents.emit("disposed");
302
+ }
303
+ }
304
+ exports.QuorumProposals = QuorumProposals;
305
+ /**
306
+ * A quorum represents all clients currently within the collaboration window. As well as the values
307
+ * they have agreed upon and any pending proposals.
308
+ * @internal
309
+ */
310
+ class Quorum extends client_utils_1.TypedEventEmitter {
311
+ get disposed() {
312
+ return this.isDisposed;
313
+ }
314
+ constructor(members, proposals, values, sendProposal) {
315
+ super();
316
+ this.isDisposed = false;
317
+ this.quorumClients = new QuorumClients(members);
318
+ this.quorumClients.on("addMember", (clientId, details) => {
319
+ this.emit("addMember", clientId, details);
320
+ });
321
+ this.quorumClients.on("removeMember", (clientId) => {
322
+ this.emit("removeMember", clientId);
323
+ });
324
+ this.quorumProposals = new QuorumProposals({ proposals, values }, sendProposal);
325
+ this.quorumProposals.on("addProposal", (proposal) => {
326
+ this.emit("addProposal", proposal);
327
+ });
328
+ this.quorumProposals.on("approveProposal", (sequenceNumber, key, value, approvalSequenceNumber) => {
329
+ this.emit("approveProposal", sequenceNumber, key, value, approvalSequenceNumber);
330
+ });
331
+ }
332
+ close() {
333
+ this.removeAllListeners();
334
+ }
335
+ /**
336
+ * Snapshots the entire quorum
337
+ * @returns a quorum snapshot
338
+ */
339
+ snapshot() {
340
+ const members = this.quorumClients.snapshot();
341
+ const { proposals, values } = this.quorumProposals.snapshot();
342
+ return {
343
+ members,
344
+ proposals,
345
+ values,
346
+ };
347
+ }
348
+ /**
349
+ * Returns whether the quorum has achieved a consensus for the given key.
350
+ */
351
+ has(key) {
352
+ return this.quorumProposals.has(key);
353
+ }
354
+ /**
355
+ * Returns the consensus value for the given key
356
+ */
357
+ get(key) {
358
+ return this.quorumProposals.get(key);
359
+ }
360
+ /**
361
+ * Returns additional data about the approved consensus value
362
+ * @deprecated Removed in recent protocol-definitions. Use get() instead.
363
+ */
364
+ getApprovalData(key) {
365
+ return this.quorumProposals.getApprovalData(key);
366
+ }
367
+ /**
368
+ * Adds a new client to the quorum
369
+ */
370
+ addMember(clientId, details) {
371
+ this.quorumClients.addMember(clientId, details);
372
+ }
373
+ /**
374
+ * Removes a client from the quorum
375
+ */
376
+ removeMember(clientId) {
377
+ this.quorumClients.removeMember(clientId);
378
+ }
379
+ /**
380
+ * Retrieves all the members in the quorum
381
+ */
382
+ getMembers() {
383
+ return this.quorumClients.getMembers();
384
+ }
385
+ /**
386
+ * Retrieves a specific member of the quorum
387
+ */
388
+ getMember(clientId) {
389
+ return this.quorumClients.getMember(clientId);
390
+ }
391
+ /**
392
+ * Proposes a new value. Returns a promise that will resolve when the proposal is either accepted, or reject if
393
+ * the proposal fails to send.
394
+ */
395
+ async propose(key, value) {
396
+ return this.quorumProposals.propose(key, value);
397
+ }
398
+ /**
399
+ * Begins tracking a new proposal
400
+ */
401
+ addProposal(key, value, sequenceNumber, local, clientSequenceNumber) {
402
+ return this.quorumProposals.addProposal(key, value, sequenceNumber, local, clientSequenceNumber);
403
+ }
404
+ /**
405
+ * Updates the minimum sequence number. If the MSN advances past the sequence number for any proposal then it
406
+ * becomes an approved value.
407
+ */
408
+ updateMinimumSequenceNumber(message) {
409
+ this.quorumProposals.updateMinimumSequenceNumber(message);
410
+ }
411
+ setConnectionState(connected, clientId) {
412
+ this.quorumProposals.setConnectionState(connected);
413
+ }
414
+ dispose() {
415
+ throw new Error("Not implemented.");
416
+ }
417
+ }
418
+ exports.Quorum = Quorum;
419
+ //# sourceMappingURL=quorum.js.map