@fluid-experimental/tree 0.59.2001 → 0.59.3000

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 (258) hide show
  1. package/.eslintrc.js +2 -0
  2. package/.vscode/SharedTree.code-workspace +15 -0
  3. package/.vscode/settings.json +6 -0
  4. package/dist/ChangeCompression.js +9 -9
  5. package/dist/ChangeCompression.js.map +1 -1
  6. package/dist/ChangeTypes.d.ts +1 -6
  7. package/dist/ChangeTypes.d.ts.map +1 -1
  8. package/dist/ChangeTypes.js +5 -5
  9. package/dist/ChangeTypes.js.map +1 -1
  10. package/dist/Checkout.js +14 -14
  11. package/dist/Checkout.js.map +1 -1
  12. package/dist/Common.d.ts +21 -3
  13. package/dist/Common.d.ts.map +1 -1
  14. package/dist/Common.js +29 -4
  15. package/dist/Common.js.map +1 -1
  16. package/dist/EditLog.js +26 -25
  17. package/dist/EditLog.js.map +1 -1
  18. package/dist/EditUtilities.js +17 -17
  19. package/dist/EditUtilities.js.map +1 -1
  20. package/dist/Forest.js +31 -31
  21. package/dist/Forest.js.map +1 -1
  22. package/dist/HistoryEditFactory.js +9 -9
  23. package/dist/HistoryEditFactory.js.map +1 -1
  24. package/dist/IdConversion.js +9 -9
  25. package/dist/IdConversion.js.map +1 -1
  26. package/dist/Identifiers.d.ts +4 -0
  27. package/dist/Identifiers.d.ts.map +1 -1
  28. package/dist/Identifiers.js.map +1 -1
  29. package/dist/LogViewer.d.ts +1 -5
  30. package/dist/LogViewer.d.ts.map +1 -1
  31. package/dist/LogViewer.js +11 -19
  32. package/dist/LogViewer.js.map +1 -1
  33. package/dist/MergeHealth.js +2 -2
  34. package/dist/MergeHealth.js.map +1 -1
  35. package/dist/NodeIdUtilities.js +2 -2
  36. package/dist/NodeIdUtilities.js.map +1 -1
  37. package/dist/PayloadUtilities.js +1 -1
  38. package/dist/PayloadUtilities.js.map +1 -1
  39. package/dist/RevisionValueCache.d.ts +13 -10
  40. package/dist/RevisionValueCache.d.ts.map +1 -1
  41. package/dist/RevisionValueCache.js +14 -11
  42. package/dist/RevisionValueCache.js.map +1 -1
  43. package/dist/RevisionView.js +4 -4
  44. package/dist/RevisionView.js.map +1 -1
  45. package/dist/SerializationUtilities.js +4 -4
  46. package/dist/SerializationUtilities.js.map +1 -1
  47. package/dist/SharedTree.d.ts +93 -31
  48. package/dist/SharedTree.d.ts.map +1 -1
  49. package/dist/SharedTree.js +160 -131
  50. package/dist/SharedTree.js.map +1 -1
  51. package/dist/SharedTreeEncoder.d.ts +3 -3
  52. package/dist/SharedTreeEncoder.d.ts.map +1 -1
  53. package/dist/SharedTreeEncoder.js +36 -36
  54. package/dist/SharedTreeEncoder.js.map +1 -1
  55. package/dist/StringInterner.js +1 -1
  56. package/dist/StringInterner.js.map +1 -1
  57. package/dist/Summary.js +1 -1
  58. package/dist/Summary.js.map +1 -1
  59. package/dist/SummaryBackCompatibility.js +8 -8
  60. package/dist/SummaryBackCompatibility.js.map +1 -1
  61. package/dist/Transaction.js +1 -1
  62. package/dist/Transaction.js.map +1 -1
  63. package/dist/TransactionInternal.js +17 -17
  64. package/dist/TransactionInternal.js.map +1 -1
  65. package/dist/TreeCompressor.d.ts.map +1 -1
  66. package/dist/TreeCompressor.js +6 -8
  67. package/dist/TreeCompressor.js.map +1 -1
  68. package/dist/TreeNodeHandle.js +4 -4
  69. package/dist/TreeNodeHandle.js.map +1 -1
  70. package/dist/TreeView.js +7 -7
  71. package/dist/TreeView.js.map +1 -1
  72. package/dist/TreeViewUtilities.js +2 -2
  73. package/dist/TreeViewUtilities.js.map +1 -1
  74. package/dist/UndoRedoHandler.js +1 -1
  75. package/dist/UndoRedoHandler.js.map +1 -1
  76. package/dist/UuidUtilities.d.ts +30 -0
  77. package/dist/UuidUtilities.d.ts.map +1 -0
  78. package/dist/UuidUtilities.js +106 -0
  79. package/dist/UuidUtilities.js.map +1 -0
  80. package/dist/id-compressor/AppendOnlySortedMap.d.ts +52 -28
  81. package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  82. package/dist/id-compressor/AppendOnlySortedMap.js +167 -90
  83. package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
  84. package/dist/id-compressor/IdCompressor.d.ts +43 -42
  85. package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
  86. package/dist/id-compressor/IdCompressor.js +179 -177
  87. package/dist/id-compressor/IdCompressor.js.map +1 -1
  88. package/dist/id-compressor/IdRange.js +1 -1
  89. package/dist/id-compressor/IdRange.js.map +1 -1
  90. package/dist/id-compressor/NumericUuid.d.ts +6 -14
  91. package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
  92. package/dist/id-compressor/NumericUuid.js +15 -76
  93. package/dist/id-compressor/NumericUuid.js.map +1 -1
  94. package/dist/id-compressor/SessionIdNormalizer.d.ts +122 -0
  95. package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
  96. package/dist/id-compressor/SessionIdNormalizer.js +418 -0
  97. package/dist/id-compressor/SessionIdNormalizer.js.map +1 -0
  98. package/dist/id-compressor/persisted-types/0.0.1.d.ts +6 -13
  99. package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
  100. package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
  101. package/dist/index.d.ts +2 -2
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/persisted-types/0.1.1.d.ts +1 -6
  105. package/dist/persisted-types/0.1.1.d.ts.map +1 -1
  106. package/dist/persisted-types/0.1.1.js +3 -3
  107. package/dist/persisted-types/0.1.1.js.map +1 -1
  108. package/lib/ChangeTypes.d.ts +1 -6
  109. package/lib/ChangeTypes.d.ts.map +1 -1
  110. package/lib/Checkout.js.map +1 -1
  111. package/lib/Common.d.ts +21 -3
  112. package/lib/Common.d.ts.map +1 -1
  113. package/lib/Common.js +25 -3
  114. package/lib/Common.js.map +1 -1
  115. package/lib/EditLog.js +2 -1
  116. package/lib/EditLog.js.map +1 -1
  117. package/lib/EditUtilities.js.map +1 -1
  118. package/lib/Forest.js.map +1 -1
  119. package/lib/HistoryEditFactory.js.map +1 -1
  120. package/lib/Identifiers.d.ts +4 -0
  121. package/lib/Identifiers.d.ts.map +1 -1
  122. package/lib/Identifiers.js.map +1 -1
  123. package/lib/LogViewer.d.ts +1 -5
  124. package/lib/LogViewer.d.ts.map +1 -1
  125. package/lib/LogViewer.js +5 -13
  126. package/lib/LogViewer.js.map +1 -1
  127. package/lib/MergeHealth.js.map +1 -1
  128. package/lib/NodeIdUtilities.js.map +1 -1
  129. package/lib/RevisionValueCache.d.ts +13 -10
  130. package/lib/RevisionValueCache.d.ts.map +1 -1
  131. package/lib/RevisionValueCache.js +10 -7
  132. package/lib/RevisionValueCache.js.map +1 -1
  133. package/lib/RevisionView.js.map +1 -1
  134. package/lib/SharedTree.d.ts +93 -31
  135. package/lib/SharedTree.d.ts.map +1 -1
  136. package/lib/SharedTree.js +107 -78
  137. package/lib/SharedTree.js.map +1 -1
  138. package/lib/SharedTreeEncoder.d.ts +3 -3
  139. package/lib/SharedTreeEncoder.d.ts.map +1 -1
  140. package/lib/SharedTreeEncoder.js +4 -4
  141. package/lib/SharedTreeEncoder.js.map +1 -1
  142. package/lib/StringInterner.js.map +1 -1
  143. package/lib/Summary.js.map +1 -1
  144. package/lib/TreeCompressor.d.ts.map +1 -1
  145. package/lib/TreeCompressor.js +1 -3
  146. package/lib/TreeCompressor.js.map +1 -1
  147. package/lib/TreeNodeHandle.js.map +1 -1
  148. package/lib/TreeView.js.map +1 -1
  149. package/lib/TreeViewUtilities.js.map +1 -1
  150. package/lib/UuidUtilities.d.ts +30 -0
  151. package/lib/UuidUtilities.d.ts.map +1 -0
  152. package/lib/UuidUtilities.js +98 -0
  153. package/lib/UuidUtilities.js.map +1 -0
  154. package/lib/id-compressor/AppendOnlySortedMap.d.ts +52 -28
  155. package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
  156. package/lib/id-compressor/AppendOnlySortedMap.js +165 -88
  157. package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
  158. package/lib/id-compressor/IdCompressor.d.ts +43 -42
  159. package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
  160. package/lib/id-compressor/IdCompressor.js +97 -95
  161. package/lib/id-compressor/IdCompressor.js.map +1 -1
  162. package/lib/id-compressor/NumericUuid.d.ts +6 -14
  163. package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
  164. package/lib/id-compressor/NumericUuid.js +11 -70
  165. package/lib/id-compressor/NumericUuid.js.map +1 -1
  166. package/lib/id-compressor/SessionIdNormalizer.d.ts +122 -0
  167. package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
  168. package/lib/id-compressor/SessionIdNormalizer.js +414 -0
  169. package/lib/id-compressor/SessionIdNormalizer.js.map +1 -0
  170. package/lib/id-compressor/persisted-types/0.0.1.d.ts +6 -13
  171. package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
  172. package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
  173. package/lib/index.d.ts +2 -2
  174. package/lib/index.d.ts.map +1 -1
  175. package/lib/index.js.map +1 -1
  176. package/lib/persisted-types/0.1.1.d.ts +1 -6
  177. package/lib/persisted-types/0.1.1.d.ts.map +1 -1
  178. package/lib/persisted-types/0.1.1.js.map +1 -1
  179. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +6 -0
  180. package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +1 -0
  181. package/lib/test/AppendOnlySortedMap.perf.tests.js +49 -0
  182. package/lib/test/AppendOnlySortedMap.perf.tests.js.map +1 -0
  183. package/lib/test/AppendOnlySortedMap.tests.js +56 -14
  184. package/lib/test/AppendOnlySortedMap.tests.js.map +1 -1
  185. package/lib/test/Checkout.tests.js +2 -2
  186. package/lib/test/Checkout.tests.js.map +1 -1
  187. package/lib/test/Forest.tests.js.map +1 -1
  188. package/lib/test/IdCompressor.perf.tests.js +8 -2
  189. package/lib/test/IdCompressor.perf.tests.js.map +1 -1
  190. package/lib/test/IdCompressor.tests.js +75 -24
  191. package/lib/test/IdCompressor.tests.js.map +1 -1
  192. package/lib/test/LogViewer.tests.js +3 -5
  193. package/lib/test/LogViewer.tests.js.map +1 -1
  194. package/lib/test/NumericUuid.perf.tests.js +4 -4
  195. package/lib/test/NumericUuid.perf.tests.js.map +1 -1
  196. package/lib/test/NumericUuid.tests.js +5 -4
  197. package/lib/test/NumericUuid.tests.js.map +1 -1
  198. package/lib/test/RevisionValueCache.tests.js.map +1 -1
  199. package/lib/test/RevisionView.tests.js.map +1 -1
  200. package/lib/test/SessionIdNormalizer.tests.d.ts +6 -0
  201. package/lib/test/SessionIdNormalizer.tests.d.ts.map +1 -0
  202. package/lib/test/SessionIdNormalizer.tests.js +299 -0
  203. package/lib/test/SessionIdNormalizer.tests.js.map +1 -0
  204. package/lib/test/Summary.tests.js +1 -1
  205. package/lib/test/Summary.tests.js.map +1 -1
  206. package/lib/test/TreeCompression.tests.js +1 -1
  207. package/lib/test/TreeCompression.tests.js.map +1 -1
  208. package/lib/test/Virtualization.tests.js +1 -1
  209. package/lib/test/Virtualization.tests.js.map +1 -1
  210. package/lib/test/fuzz/Generators.d.ts +3 -14
  211. package/lib/test/fuzz/Generators.d.ts.map +1 -1
  212. package/lib/test/fuzz/Generators.js +60 -151
  213. package/lib/test/fuzz/Generators.js.map +1 -1
  214. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +10 -7
  215. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
  216. package/lib/test/fuzz/SharedTreeFuzzTests.js +94 -104
  217. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
  218. package/lib/test/fuzz/Types.d.ts +2 -9
  219. package/lib/test/fuzz/Types.d.ts.map +1 -1
  220. package/lib/test/fuzz/Types.js +1 -1
  221. package/lib/test/fuzz/Types.js.map +1 -1
  222. package/lib/test/utilities/IdCompressorTestUtilities.d.ts +57 -11
  223. package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
  224. package/lib/test/utilities/IdCompressorTestUtilities.js +112 -98
  225. package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
  226. package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
  227. package/lib/test/utilities/PendingLocalStateTests.js +2 -1
  228. package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
  229. package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
  230. package/lib/test/utilities/SharedTreeTests.js +30 -1
  231. package/lib/test/utilities/SharedTreeTests.js.map +1 -1
  232. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
  233. package/lib/test/utilities/SharedTreeVersioningTests.js +20 -0
  234. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
  235. package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -1
  236. package/lib/test/utilities/SummaryLoadPerfTests.js +6 -3
  237. package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
  238. package/lib/test/utilities/TestNode.js.map +1 -1
  239. package/lib/test/utilities/TestUtilities.d.ts +9 -1
  240. package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
  241. package/lib/test/utilities/TestUtilities.js +27 -13
  242. package/lib/test/utilities/TestUtilities.js.map +1 -1
  243. package/package.json +19 -17
  244. package/src/Common.ts +42 -4
  245. package/src/EditLog.ts +1 -1
  246. package/src/Identifiers.ts +5 -0
  247. package/src/LogViewer.ts +4 -20
  248. package/src/RevisionValueCache.ts +11 -8
  249. package/src/SharedTree.ts +222 -75
  250. package/src/SharedTreeEncoder.ts +17 -11
  251. package/src/TreeCompressor.ts +2 -4
  252. package/src/UuidUtilities.ts +123 -0
  253. package/src/id-compressor/AppendOnlySortedMap.ts +183 -94
  254. package/src/id-compressor/IdCompressor.ts +144 -132
  255. package/src/id-compressor/NumericUuid.ts +11 -80
  256. package/src/id-compressor/SessionIdNormalizer.ts +497 -0
  257. package/src/id-compressor/persisted-types/0.0.1.ts +12 -15
  258. package/src/index.ts +5 -0
@@ -4,10 +4,11 @@
4
4
  */
5
5
  /* eslint-disable @typescript-eslint/restrict-plus-operands */
6
6
  import BTree from 'sorted-btree';
7
- import { assert, assertNotUndefined, compareFiniteNumbers, compareMaps, compareStrings, fail, setPropertyIfDefined, } from '../Common';
7
+ import { assert, hasLength, assertNotUndefined, compareFiniteNumbers, compareFiniteNumbersReversed, compareMaps, compareStrings, fail, getOrCreate, setPropertyIfDefined, } from '../Common';
8
+ import { assertIsStableId, assertIsUuidString, isStableId } from '../UuidUtilities';
8
9
  import { AppendOnlyDoublySortedMap, AppendOnlySortedMap } from './AppendOnlySortedMap';
9
10
  import { getIds } from './IdRange';
10
- import { numericUuidEquals, getPositiveDelta, incrementUuid, numericUuidFromStableId, stableIdFromNumericUuid, isStableId, assertIsStableId, ensureSessionUuid, } from './NumericUuid';
11
+ import { numericUuidEquals, getPositiveDelta, incrementUuid, numericUuidFromStableId, stableIdFromNumericUuid, ensureSessionUuid, } from './NumericUuid';
11
12
  /**
12
13
  * Roughly equates to a minimum of 1M sessions before we start allocating 64 bit IDs.
13
14
  * This value must *NOT* change without careful consideration to compatibility.
@@ -98,14 +99,17 @@ const nonStableOverridePrefix = '\ue15e'; // A character in the Private Use Area
98
99
  */
99
100
  export class IdCompressor {
100
101
  /**
101
- * @param localSessionId the `IdCompressor`'s current local session ID.
102
- * @param reservedIdCount the number of IDs that will be known by this compressor without relying on consensus. The reserved ID count
103
- * for a given session must be constant for any compressor that contains IDs from that session (i.e. any DDS that uses the ID
104
- * compressor must have the same reservedIdCount forever). Compressors with different reserved ID counts will fail to synchronize their
105
- * IDs.
106
- * @param attributionInfo information used by other clients to attribute IDs made by this client
102
+ * @param localSessionId - the `IdCompressor`'s current local session ID.
103
+ * @param reservedIdCount - the number of IDs that will be known by this compressor without relying on consensus.
104
+ * The reserved ID count for a given session must be constant for any compressor that contains IDs from that session
105
+ * (i.e. any DDS that uses the ID compressor must have the same reservedIdCount forever). Compressors with different
106
+ * reserved ID counts will fail to synchronize their IDs.
107
+ * @param attributionId - a UUID that identifies the user of this instance of the compressor. IDs created by this
108
+ * compressor will be associated with this UUID and can be queried later via `attributeID`. If no UUID is provided,
109
+ * this compressor will generate its own. An `AttributionId` is an `UuidString` which may be validated via
110
+ * {@link isUuidString} or generated via {@link generateStableId}.
107
111
  */
108
- constructor(localSessionId, reservedIdCount, attributionInfo) {
112
+ constructor(localSessionId, reservedIdCount, attributionId) {
109
113
  this.localSessionId = localSessionId;
110
114
  this.reservedIdCount = reservedIdCount;
111
115
  /**
@@ -114,14 +118,10 @@ export class IdCompressor {
114
118
  */
115
119
  this.newClusterCapacity = defaultClusterCapacity;
116
120
  /**
117
- * Session ID -> data about the session's current cluster.
121
+ * Session ID -\> data about the session's current cluster.
118
122
  * Sessions are mutable, and thus should only be created via `createSession`.
119
123
  */
120
124
  this.sessions = new Map();
121
- /**
122
- * Boolean to track whether attribution has been sent with an ID range yet. Prevents unnecessary bloat of ranges.
123
- */
124
- this.sentAttributionInfo = false;
125
125
  /**
126
126
  * The base final ID of the next cluster to be created.
127
127
  */
@@ -137,10 +137,10 @@ export class IdCompressor {
137
137
  this.localOverrides = new AppendOnlySortedMap(compareFiniteNumbersReversed);
138
138
  /**
139
139
  * Maps local IDs to the cluster they belong to (if any). This can be used to efficiently convert a local ID to a
140
- * final ID by finding an entry <= a given local ID (to find the cluster it is associated with) and checking
140
+ * final ID by finding an entry \<= a given local ID (to find the cluster it is associated with) and checking
141
141
  * it against `numFinalizedLocalIds`.
142
142
  */
143
- this.localIdToCluster = new AppendOnlyDoublySortedMap(compareFiniteNumbersReversed, (value) => value[0], IdCompressor.overrideComparator);
143
+ this.localIdToCluster = new AppendOnlyDoublySortedMap(compareFiniteNumbersReversed, (value) => value[0], compareFiniteNumbers);
144
144
  /**
145
145
  * Contains entries for cluster base UUIDs and override strings (both local and final).
146
146
  * As a performance optimization, entries for finalized strings also include the containing cluster object.
@@ -156,7 +156,10 @@ export class IdCompressor {
156
156
  */
157
157
  this.finalIdToCluster = new AppendOnlySortedMap(compareFiniteNumbers);
158
158
  assert(reservedIdCount >= 0, 'reservedIdCount must be non-negative');
159
- this.localSession = this.createSession(localSessionId, attributionInfo);
159
+ if (attributionId !== undefined) {
160
+ assertIsUuidString(attributionId);
161
+ }
162
+ this.localSession = this.createSession(localSessionId, attributionId);
160
163
  if (reservedIdCount > 0) {
161
164
  const clusterCapacity = this.clusterCapacity;
162
165
  this.clusterCapacity = reservedIdCount;
@@ -164,7 +167,7 @@ export class IdCompressor {
164
167
  sessionId: reservedSessionId,
165
168
  ids: {
166
169
  last: -reservedIdCount,
167
- overrides: [[-1, legacySharedTreeInitialTreeId]],
170
+ overrides: [[-1, legacySharedTreeInitialTreeId]], // Kludge: see `initialTreeId`
168
171
  },
169
172
  };
170
173
  // Reserved final IDs are implicitly finalized and no one locally created them, so finalizing immediately is safe.
@@ -187,6 +190,12 @@ export class IdCompressor {
187
190
  assert(value <= IdCompressor.maxClusterSize, 'Clusters must not exceed max cluster size');
188
191
  this.newClusterCapacity = value;
189
192
  }
193
+ /**
194
+ * The UUID used for attribution of identities created by this compressor
195
+ */
196
+ get attributionId() {
197
+ return this.localSession.attributionId;
198
+ }
190
199
  /**
191
200
  * Helper comparator for searching append-only sorted maps.
192
201
  */
@@ -196,31 +205,28 @@ export class IdCompressor {
196
205
  /**
197
206
  * Creates a session object for the supplied ID.
198
207
  * Must only be called once per ID.
199
- * @param sessionId the ID for the session
208
+ * @param sessionId - the ID for the session
200
209
  * @returns the session object for the supplied ID
201
210
  */
202
- createSession(sessionId, attributionInfo) {
211
+ createSession(sessionId, attributionId) {
212
+ assert(!this.clustersAndOverridesInversion.has(sessionId));
203
213
  const existingSession = this.sessions.get(sessionId);
204
214
  if (existingSession !== undefined) {
205
215
  fail('createSession must only be called once for each session ID.');
206
216
  }
207
217
  const sessionUuid = numericUuidFromStableId(sessionId);
208
- assert(!this.clustersAndOverridesInversion.has(sessionId));
209
218
  const session = {
210
219
  sessionUuid,
211
220
  currentClusterDetails: undefined,
212
221
  lastFinalizedLocalId: undefined,
222
+ attributionId: attributionId !== null && attributionId !== void 0 ? attributionId : sessionId,
213
223
  };
214
- setPropertyIfDefined(attributionInfo, session, 'attributionInfo');
215
224
  this.sessions.set(sessionId, session);
216
225
  return session;
217
226
  }
218
- tryGetSession(sessionId) {
219
- return this.sessions.get(sessionId);
220
- }
221
227
  /**
222
228
  * Return the nth reserved ID.
223
- * @param index the index of the ID to return
229
+ * @param index - the index of the ID to return
224
230
  */
225
231
  getReservedId(index) {
226
232
  if (index < 0 || index >= this.reservedIdCount) {
@@ -240,16 +246,16 @@ export class IdCompressor {
240
246
  }
241
247
  }
242
248
  /**
243
- * Returns the attribution info associated with the compressor that created the ID, if it exists.
249
+ * Returns the attribution ID associated with the compressor that created the ID
244
250
  */
245
251
  attributeId(id) {
246
252
  var _a;
247
253
  const opSpaceNormalizedId = this.normalizeToOpSpace(id);
248
254
  if (isLocalId(opSpaceNormalizedId)) {
249
- return this.localSession.attributionInfo;
255
+ return this.attributionId;
250
256
  }
251
257
  const [_, cluster] = (_a = this.getClusterForFinalId(opSpaceNormalizedId)) !== null && _a !== void 0 ? _a : fail('Cluster does not exist for final ID');
252
- return cluster.session.attributionInfo;
258
+ return cluster.session.attributionId;
253
259
  }
254
260
  /**
255
261
  * Provides the session-space IDs corresponding to a range of IDs.
@@ -270,7 +276,7 @@ export class IdCompressor {
270
276
  };
271
277
  }
272
278
  else {
273
- const session = (_a = this.tryGetSession(sessionId)) !== null && _a !== void 0 ? _a : fail('Unknown session, range may not be finalized.');
279
+ const session = (_a = this.sessions.get(sessionId)) !== null && _a !== void 0 ? _a : fail('Unknown session, range may not be finalized.');
274
280
  const firstNumericUuid = incrementUuid(session.sessionUuid, -first - 1);
275
281
  const firstFinal = (_b = this.compressNumericUuid(firstNumericUuid)) !== null && _b !== void 0 ? _b : fail('Remote range must be finalized before getting IDs.');
276
282
  assert(isFinalId(firstFinal), 'ID from a remote session ID must have final form, as overrides are impossible by definition.');
@@ -316,15 +322,15 @@ export class IdCompressor {
316
322
  const lastLocalInRange = -this.localIdCount;
317
323
  const lastTakenNormalized = (_a = this.lastTakenLocalId) !== null && _a !== void 0 ? _a : 0;
318
324
  assert(lastLocalInRange <= lastTakenNormalized);
325
+ // The attribution ID is sent with each range, but it can be elided after the first IDs are allocated.
326
+ const sendAttributionId = this.lastTakenLocalId === undefined;
319
327
  let ids;
320
328
  if (lastLocalInRange !== lastTakenNormalized) {
321
329
  const firstLocalInRange = (lastTakenNormalized - 1);
322
- const localOverrides = [
330
+ const overrides = [
323
331
  ...this.localOverrides.getRange((lastTakenNormalized - 1), lastLocalInRange),
324
332
  ];
325
- if (localOverrides.length > 0) {
326
- // Cast: typecript 4.4.4 doesn't infer that `localOverrides` has at least one element and is therefore an `Overrides`
327
- const overrides = localOverrides;
333
+ if (hasLength(overrides, 1)) {
328
334
  assert(overrides[0][0] <= firstLocalInRange);
329
335
  assert(overrides[overrides.length - 1][0] >= lastLocalInRange);
330
336
  ids = {
@@ -344,9 +350,8 @@ export class IdCompressor {
344
350
  this.lastTakenLocalId = lastLocalInRange;
345
351
  }
346
352
  const range = { sessionId: this.localSessionId };
347
- if (!this.sentAttributionInfo) {
348
- setPropertyIfDefined(this.localSession.attributionInfo, range, 'attributionInfo');
349
- this.sentAttributionInfo = true;
353
+ if (this.attributionId !== this.localSessionId && sendAttributionId) {
354
+ range.attributionId = this.attributionId;
350
355
  }
351
356
  if (ids === undefined) {
352
357
  return range;
@@ -357,15 +362,14 @@ export class IdCompressor {
357
362
  }
358
363
  /**
359
364
  * Finalizes the supplied range of IDs (which may be from either a remote or local session).
360
- * @param range the range of session-local IDs to finalize.
365
+ * @param range - the range of session-local IDs to finalize.
361
366
  */
362
367
  finalizeCreationRange(range) {
363
- var _a, _b;
364
- const { sessionId, attributionInfo } = range;
368
+ var _a, _b, _c;
369
+ const { sessionId, attributionId } = range;
365
370
  const isLocal = sessionId === this.localSessionId;
366
- let session = this.tryGetSession(sessionId);
367
- assert(range.attributionInfo === undefined || session === undefined || isLocal, 'Attribution info can only be supplied on initial range for a session, and never modified.');
368
- session !== null && session !== void 0 ? session : (session = this.createSession(sessionId, attributionInfo));
371
+ const session = (_a = this.sessions.get(sessionId)) !== null && _a !== void 0 ? _a : this.createSession(sessionId, attributionId);
372
+ assert(range.attributionId === undefined || range.attributionId === session.attributionId, "A session's attribution ID may never be modified.");
369
373
  const ids = getIds(range);
370
374
  if (ids === undefined) {
371
375
  return;
@@ -375,7 +379,7 @@ export class IdCompressor {
375
379
  cluster: undefined,
376
380
  clusterBase: undefined,
377
381
  };
378
- const normalizedLastFinalized = (_a = session.lastFinalizedLocalId) !== null && _a !== void 0 ? _a : 0;
382
+ const normalizedLastFinalized = (_b = session.lastFinalizedLocalId) !== null && _b !== void 0 ? _b : 0;
379
383
  const { first: newFirstFinalizedLocalId, last: newLastFinalizedLocalId } = ids;
380
384
  assert(newFirstFinalizedLocalId === normalizedLastFinalized - 1, 'Ranges finalized out of order.');
381
385
  // The total number of session-local IDs to finalize
@@ -481,9 +485,8 @@ export class IdCompressor {
481
485
  (normalizedLastFinalized - overriddenLocal) -
482
486
  1);
483
487
  }
484
- (_b = cluster.overrides) !== null && _b !== void 0 ? _b : (cluster.overrides = new Map());
488
+ (_c = cluster.overrides) !== null && _c !== void 0 ? _c : (cluster.overrides = new Map());
485
489
  const inversionKey = IdCompressor.createInversionKey(override);
486
- // TODO: This cast can be removed on typescript 4.6
487
490
  const existingIds = this.getExistingIdsForNewOverride(inversionKey, true);
488
491
  let overrideForCluster;
489
492
  let associatedLocal;
@@ -578,7 +581,6 @@ export class IdCompressor {
578
581
  return typeof compressionMapping === 'number';
579
582
  }
580
583
  static createInversionKey(inversionKey) {
581
- // TODO: This cast can be removed on typescript 4.6
582
584
  return isStableId(inversionKey) ? inversionKey : `${nonStableOverridePrefix}${inversionKey}`;
583
585
  }
584
586
  static isStableInversionKey(inversionKey) {
@@ -638,7 +640,7 @@ export class IdCompressor {
638
640
  }
639
641
  /**
640
642
  * Check if `a` might be within `range` of `b`, where both are treated as hex numbers.
641
- * @param range an integer
643
+ * @param range - an integer
642
644
  */
643
645
  static uuidsMightCollide(a, b, range) {
644
646
  // Check if any of the UUIDs in the cluster collide (i.e. any in [base, base + capacity)).
@@ -668,7 +670,7 @@ export class IdCompressor {
668
670
  /**
669
671
  * Generates a new compressed ID or returns an existing one.
670
672
  * This should ONLY be called to generate IDs for local operations.
671
- * @param override Specifies a specific string to be associated with the returned compressed ID.
673
+ * @param override - Specifies a specific string to be associated with the returned compressed ID.
672
674
  * Performance note: assigning override strings incurs a performance overhead.
673
675
  * @returns an existing ID if one already exists for `override`, and a new local ID otherwise. The returned ID is in session space.
674
676
  */
@@ -696,7 +698,7 @@ export class IdCompressor {
696
698
  /**
697
699
  * Generates a range of compressed IDs.
698
700
  * This should ONLY be called to generate IDs for local operations.
699
- * @param count the number of IDs to generate, must be > 0.
701
+ * @param count - the number of IDs to generate, must be \> 0.
700
702
  * @returns a persistable descriptor of the ID range.
701
703
  */
702
704
  generateCompressedIdRange(count) {
@@ -711,7 +713,7 @@ export class IdCompressor {
711
713
  }
712
714
  /**
713
715
  * Decompresses a previously compressed ID into a UUID or override string.
714
- * @param id the compressed ID to be decompressed.
716
+ * @param id - the compressed ID to be decompressed.
715
717
  * @returns the UUID or override string associated with the compressed ID. Fails if the ID was not generated by this compressor.
716
718
  */
717
719
  decompress(id) {
@@ -720,7 +722,7 @@ export class IdCompressor {
720
722
  }
721
723
  /**
722
724
  * Attempts to decompress a previously compressed ID into a UUID or override string.
723
- * @param id the compressed ID to be decompressed.
725
+ * @param id - the compressed ID to be decompressed.
724
726
  * @returns the UUID or override string associated with the compressed ID, or undefined if the ID was not generated by this compressor.
725
727
  */
726
728
  tryDecompress(id) {
@@ -762,7 +764,7 @@ export class IdCompressor {
762
764
  }
763
765
  /**
764
766
  * Recompresses a decompressed ID, which could be a UUID or an override string.
765
- * @param uncompressed the UUID or override string to recompress.
767
+ * @param uncompressed - the UUID or override string to recompress.
766
768
  * @returns the `CompressedId` associated with `uncompressed`. Fails if it has not been previously compressed by this compressor.
767
769
  */
768
770
  recompress(uncompressed) {
@@ -771,7 +773,7 @@ export class IdCompressor {
771
773
  }
772
774
  /**
773
775
  * Attempts to recompresses a decompressed ID, which could be a UUID or an override string.
774
- * @param uncompressed the UUID or override string to recompress,
776
+ * @param uncompressed - the UUID or override string to recompress,
775
777
  * @returns the `CompressedId` associated with `uncompressed` or undefined if it has not been previously compressed by this compressor.
776
778
  */
777
779
  tryRecompress(uncompressed) {
@@ -784,7 +786,6 @@ export class IdCompressor {
784
786
  recompressInternal(uncompressed, uncompressedUuidNumeric) {
785
787
  var _a, _b;
786
788
  let numericUuid = uncompressedUuidNumeric;
787
- // TODO: This cast can be removed on typescript 4.6, and should give improved typesafety.
788
789
  const inversionKey = IdCompressor.createInversionKey(uncompressed);
789
790
  const isStable = IdCompressor.isStableInversionKey(inversionKey);
790
791
  const closestMatch = this.clustersAndOverridesInversion.getPairOrNextLower(inversionKey, reusedArray);
@@ -827,7 +828,6 @@ export class IdCompressor {
827
828
  }
828
829
  if (isStable) {
829
830
  // May have already computed the numeric UUID, so avoid recomputing if possible
830
- // TODO: This cast can be removed on typescript 4.6
831
831
  const localId = this.getLocalIdForStableId(numericUuid !== null && numericUuid !== void 0 ? numericUuid : inversionKey);
832
832
  if (localId !== undefined) {
833
833
  return localId;
@@ -837,7 +837,7 @@ export class IdCompressor {
837
837
  }
838
838
  /**
839
839
  * Normalizes a session space ID into op space.
840
- * @param id the local ID to normalize.
840
+ * @param id - the local ID to normalize.
841
841
  * @returns the ID in op space.
842
842
  */
843
843
  normalizeToOpSpace(id) {
@@ -887,7 +887,7 @@ export class IdCompressor {
887
887
  return id;
888
888
  }
889
889
  else {
890
- const session = this.tryGetSession(originSessionId !== null && originSessionId !== void 0 ? originSessionId : fail());
890
+ const session = this.sessions.get(originSessionId !== null && originSessionId !== void 0 ? originSessionId : fail());
891
891
  if (session === undefined) {
892
892
  fail('No IDs have ever been finalized by the supplied session.');
893
893
  }
@@ -942,8 +942,7 @@ export class IdCompressor {
942
942
  return sessionSpaceId;
943
943
  }
944
944
  getLocalIdForStableId(stableId) {
945
- // TODO: This cast can be removed on typescript 4.6
946
- const numericUuid = (typeof stableId === 'string' ? numericUuidFromStableId(stableId) : stableId);
945
+ const numericUuid = typeof stableId === 'string' ? numericUuidFromStableId(stableId) : stableId;
947
946
  const offset = getPositiveDelta(numericUuid, this.localSession.sessionUuid, this.localIdCount - 1);
948
947
  if (offset === undefined) {
949
948
  return undefined;
@@ -962,15 +961,15 @@ export class IdCompressor {
962
961
  return possibleCluster;
963
962
  }
964
963
  /**
965
- * @returns if `other` is equal to this `IdCompressor`. The equality check includes local session state.
966
- * @testOnly
964
+ * @returns if `other` is equal to this `IdCompressor`. The equality check includes local session state only if specified.
965
+ * \@testOnly
967
966
  */
968
967
  equals(other, compareLocalState) {
969
968
  if (compareLocalState) {
970
969
  if (this.localIdCount !== other.localIdCount ||
971
970
  this.localSessionId !== other.localSessionId ||
972
971
  this.lastTakenLocalId !== other.lastTakenLocalId ||
973
- this.sentAttributionInfo !== other.sentAttributionInfo) {
972
+ this.attributionId !== other.attributionId) {
974
973
  return false;
975
974
  }
976
975
  if (!this.localOverrides.equals(other.localOverrides, (a, b) => a === b)) {
@@ -1052,7 +1051,7 @@ export class IdCompressor {
1052
1051
  return diff === undefined;
1053
1052
  }
1054
1053
  static sessionDataEqual(a, b, checkCluster = true, compareLocalState = true) {
1055
- if (a.attributionInfo !== b.attributionInfo ||
1054
+ if (a.attributionId !== b.attributionId ||
1056
1055
  !numericUuidEquals(a.sessionUuid, b.sessionUuid) ||
1057
1056
  a.lastFinalizedLocalId !== b.lastFinalizedLocalId) {
1058
1057
  return false;
@@ -1101,6 +1100,8 @@ export class IdCompressor {
1101
1100
  var _a;
1102
1101
  const serializedSessions = [];
1103
1102
  const sessionIdToSessionIndex = new Map();
1103
+ const attributionIdToAttributionIndex = new Map();
1104
+ let serializedAttributionIds;
1104
1105
  for (const [sessionId, session] of this.sessions) {
1105
1106
  const isLocalSession = sessionId === this.localSessionId;
1106
1107
  const includeSession = sessionId !== reservedSessionId && // Ignore reserved clusters, but
@@ -1108,8 +1109,10 @@ export class IdCompressor {
1108
1109
  (isLocalSession && withSession)); // include the un-acked local session if requested
1109
1110
  if (includeSession) {
1110
1111
  const sessionData = [sessionId];
1111
- if (session.attributionInfo !== undefined) {
1112
- sessionData.push(session.attributionInfo);
1112
+ if (session.attributionId !== sessionId) {
1113
+ // As an optimization, don't include the attributionId if it is its default (the sessionId)
1114
+ // Get the index into the array for the given attribution ID. If it doesn't exist, push it onto the array and update the map.
1115
+ sessionData.push(getOrCreate(attributionIdToAttributionIndex, session.attributionId, (id) => (serializedAttributionIds !== null && serializedAttributionIds !== void 0 ? serializedAttributionIds : (serializedAttributionIds = [])).push(id) - 1));
1113
1116
  }
1114
1117
  sessionIdToSessionIndex.set(sessionId, serializedSessions.length);
1115
1118
  serializedSessions.push(sessionData);
@@ -1156,6 +1159,7 @@ export class IdCompressor {
1156
1159
  sessions: serializedSessions,
1157
1160
  clusters: serializedClusters,
1158
1161
  };
1162
+ setPropertyIfDefined(serializedAttributionIds, serializedIdCompressor, 'attributionIds');
1159
1163
  if (withSession) {
1160
1164
  const serializedWithSession = serializedIdCompressor;
1161
1165
  serializedWithSession.localSessionIndex = serializedWithSession.sessions.findIndex(([sessionId]) => sessionId === this.localSessionId);
@@ -1164,40 +1168,41 @@ export class IdCompressor {
1164
1168
  localIdCount: this.localIdCount,
1165
1169
  overrides: [...this.localOverrides.entries()].map((entry) => [...entry]),
1166
1170
  lastTakenLocalId: this.lastTakenLocalId,
1167
- sentAttributionInfo: this.sentAttributionInfo,
1168
1171
  };
1169
1172
  }
1170
1173
  return serializedWithSession;
1171
1174
  }
1172
1175
  return serializedIdCompressor;
1173
1176
  }
1174
- static deserialize(serialized, newSessionIdMaybe, attributionInfoMaybe) {
1175
- const hasSession = hasOngoingSession(serialized);
1177
+ static deserialize(...args) {
1178
+ const [serialized, newSessionIdMaybe, attributionIdMaybe] = args;
1179
+ const { clusterCapacity, reservedIdCount, sessions: serializedSessions, clusters: serializedClusters, attributionIds: serializedAttributionIds, } = serialized;
1176
1180
  let localSessionId;
1177
- let attributionInfo;
1181
+ let attributionId;
1178
1182
  let serializedLocalState;
1179
- if (hasSession) {
1180
- assert(newSessionIdMaybe === undefined && attributionInfoMaybe === undefined);
1181
- // TODO: This cast can be removed on typescript 4.6
1182
- [localSessionId, attributionInfo] =
1183
- serialized.sessions[serialized.localSessionIndex];
1184
- // TODO: This cast can be removed on typescript 4.6
1185
- serializedLocalState = serialized.localState;
1183
+ if (newSessionIdMaybe === undefined) {
1184
+ // Alias of serialized, but known to be a SerializedIdCompressorWithOngoingSession
1185
+ const [serializedWithSession] = args;
1186
+ const serializedSessionData = serializedSessions[serializedWithSession.localSessionIndex];
1187
+ localSessionId = serializedSessionData[0];
1188
+ const attributionIndex = serializedSessionData[1];
1189
+ if (attributionIndex !== undefined) {
1190
+ assert(serializedAttributionIds !== undefined && serializedAttributionIds.length > attributionIndex);
1191
+ attributionId = serializedAttributionIds[attributionIndex];
1192
+ }
1193
+ serializedLocalState = serializedWithSession.localState;
1186
1194
  }
1187
1195
  else {
1188
- assert(newSessionIdMaybe !== undefined);
1189
1196
  localSessionId = newSessionIdMaybe;
1190
- attributionInfo = attributionInfoMaybe;
1197
+ attributionId = attributionIdMaybe;
1191
1198
  }
1192
- const { clusterCapacity, reservedIdCount, sessions: serializedSessions, clusters: serializedClusters, } = serialized;
1193
- const compressor = new IdCompressor(localSessionId, reservedIdCount, attributionInfo);
1199
+ const compressor = new IdCompressor(localSessionId, reservedIdCount, attributionId);
1194
1200
  compressor.clusterCapacity = clusterCapacity;
1195
1201
  const localOverridesInverse = new Map();
1196
1202
  if (serializedLocalState !== undefined) {
1197
1203
  // Do this part of local rehydration first since the cluster map population needs to query to local overrides
1198
1204
  compressor.localIdCount = serializedLocalState.localIdCount;
1199
1205
  compressor.lastTakenLocalId = serializedLocalState.lastTakenLocalId;
1200
- compressor.sentAttributionInfo = serializedLocalState.sentAttributionInfo;
1201
1206
  if (serializedLocalState.overrides !== undefined) {
1202
1207
  for (const [localId, override] of serializedLocalState.overrides) {
1203
1208
  compressor.localOverrides.append(localId, override);
@@ -1208,13 +1213,18 @@ export class IdCompressor {
1208
1213
  }
1209
1214
  const sessionInfos = [];
1210
1215
  for (const serializedSession of serializedSessions) {
1211
- const [sessionId, attributionInfo] = serializedSession;
1216
+ const [sessionId, attributionIndex] = serializedSession;
1212
1217
  if (sessionId === localSessionId) {
1213
- assert(hasSession, 'Cannot resume existing session.');
1218
+ assert(hasOngoingSession(serialized), 'Cannot resume existing session.');
1214
1219
  sessionInfos.push({ session: compressor.localSession, sessionId });
1215
1220
  }
1216
1221
  else {
1217
- const session = compressor.createSession(sessionId, attributionInfo);
1222
+ let attributionId;
1223
+ if (attributionIndex !== undefined) {
1224
+ assert(serializedAttributionIds !== undefined && serializedAttributionIds.length > attributionIndex, 'AttributionId index out of bounds');
1225
+ attributionId = serializedAttributionIds[attributionIndex];
1226
+ }
1227
+ const session = compressor.createSession(sessionId, attributionId);
1218
1228
  sessionInfos.push({ session, sessionId });
1219
1229
  }
1220
1230
  }
@@ -1321,10 +1331,8 @@ function deserializeCluster(serializedCluster) {
1321
1331
  return {
1322
1332
  sessionIndex,
1323
1333
  capacity,
1324
- // TODO: This cast can be removed on typescript 4.6
1325
- count: (hasCount ? countOrOverrides : capacity),
1326
- // TODO: This cast can be removed on typescript 4.6
1327
- overrides: (hasCount ? overrides : countOrOverrides),
1334
+ count: hasCount ? countOrOverrides : capacity,
1335
+ overrides: hasCount ? overrides : countOrOverrides,
1328
1336
  };
1329
1337
  }
1330
1338
  /**
@@ -1334,10 +1342,4 @@ function deserializeCluster(serializedCluster) {
1334
1342
  * lookup results should be extracted from the tuple immediately after invocation.
1335
1343
  */
1336
1344
  const reusedArray = [];
1337
- /**
1338
- * A numeric comparator used for sorting in descending order.
1339
- */
1340
- function compareFiniteNumbersReversed(a, b) {
1341
- return b - a;
1342
- }
1343
1345
  //# sourceMappingURL=IdCompressor.js.map