@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
@@ -8,11 +8,14 @@
8
8
  import BTree from 'sorted-btree';
9
9
  import {
10
10
  assert,
11
+ hasLength,
11
12
  assertNotUndefined,
12
13
  compareFiniteNumbers,
14
+ compareFiniteNumbersReversed,
13
15
  compareMaps,
14
16
  compareStrings,
15
17
  fail,
18
+ getOrCreate,
16
19
  Mutable,
17
20
  setPropertyIfDefined,
18
21
  } from '../Common';
@@ -24,7 +27,10 @@ import {
24
27
  OpSpaceCompressedId,
25
28
  SessionId,
26
29
  CompressedId,
30
+ UuidString,
31
+ AttributionId,
27
32
  } from '../Identifiers';
33
+ import { assertIsStableId, assertIsUuidString, isStableId } from '../UuidUtilities';
28
34
  import { AppendOnlyDoublySortedMap, AppendOnlySortedMap } from './AppendOnlySortedMap';
29
35
  import { getIds } from './IdRange';
30
36
  import {
@@ -34,12 +40,9 @@ import {
34
40
  numericUuidFromStableId,
35
41
  NumericUuid,
36
42
  stableIdFromNumericUuid,
37
- isStableId,
38
- assertIsStableId,
39
43
  ensureSessionUuid,
40
44
  } from './NumericUuid';
41
45
  import type {
42
- AttributionInfo,
43
46
  IdCreationRange,
44
47
  SerializedCluster,
45
48
  SerializedClusterOverrides,
@@ -114,9 +117,9 @@ interface Session {
114
117
  lastFinalizedLocalId: LocalCompressedId | undefined;
115
118
 
116
119
  /**
117
- * The attribution info of the session, if it exists.
120
+ * The attribution ID for the session
118
121
  */
119
- attributionInfo?: AttributionInfo;
122
+ readonly attributionId: AttributionId;
120
123
  }
121
124
 
122
125
  /**
@@ -309,7 +312,14 @@ export class IdCompressor {
309
312
  }
310
313
 
311
314
  /**
312
- * Session ID -> data about the session's current cluster.
315
+ * The UUID used for attribution of identities created by this compressor
316
+ */
317
+ public get attributionId(): AttributionId {
318
+ return this.localSession.attributionId;
319
+ }
320
+
321
+ /**
322
+ * Session ID -\> data about the session's current cluster.
313
323
  * Sessions are mutable, and thus should only be created via `createSession`.
314
324
  */
315
325
  private readonly sessions = new Map<SessionId, Session>();
@@ -319,11 +329,6 @@ export class IdCompressor {
319
329
  */
320
330
  private readonly localSession: Session;
321
331
 
322
- /**
323
- * Boolean to track whether attribution has been sent with an ID range yet. Prevents unnecessary bloat of ranges.
324
- */
325
- private sentAttributionInfo = false;
326
-
327
332
  /**
328
333
  * The base final ID of the next cluster to be created.
329
334
  */
@@ -348,18 +353,14 @@ export class IdCompressor {
348
353
 
349
354
  /**
350
355
  * Maps local IDs to the cluster they belong to (if any). This can be used to efficiently convert a local ID to a
351
- * final ID by finding an entry <= a given local ID (to find the cluster it is associated with) and checking
356
+ * final ID by finding an entry \<= a given local ID (to find the cluster it is associated with) and checking
352
357
  * it against `numFinalizedLocalIds`.
353
358
  */
354
359
  private readonly localIdToCluster: AppendOnlyDoublySortedMap<
355
360
  LocalCompressedId,
356
361
  [FinalCompressedId, IdCluster],
357
362
  FinalCompressedId
358
- > = new AppendOnlyDoublySortedMap(
359
- compareFiniteNumbersReversed,
360
- (value) => value[0],
361
- IdCompressor.overrideComparator
362
- );
363
+ > = new AppendOnlyDoublySortedMap(compareFiniteNumbersReversed, (value) => value[0], compareFiniteNumbers);
363
364
 
364
365
  /**
365
366
  * Contains entries for cluster base UUIDs and override strings (both local and final).
@@ -390,20 +391,26 @@ export class IdCompressor {
390
391
  }
391
392
 
392
393
  /**
393
- * @param localSessionId the `IdCompressor`'s current local session ID.
394
- * @param reservedIdCount the number of IDs that will be known by this compressor without relying on consensus. The reserved ID count
395
- * for a given session must be constant for any compressor that contains IDs from that session (i.e. any DDS that uses the ID
396
- * compressor must have the same reservedIdCount forever). Compressors with different reserved ID counts will fail to synchronize their
397
- * IDs.
398
- * @param attributionInfo information used by other clients to attribute IDs made by this client
394
+ * @param localSessionId - the `IdCompressor`'s current local session ID.
395
+ * @param reservedIdCount - the number of IDs that will be known by this compressor without relying on consensus.
396
+ * The reserved ID count for a given session must be constant for any compressor that contains IDs from that session
397
+ * (i.e. any DDS that uses the ID compressor must have the same reservedIdCount forever). Compressors with different
398
+ * reserved ID counts will fail to synchronize their IDs.
399
+ * @param attributionId - a UUID that identifies the user of this instance of the compressor. IDs created by this
400
+ * compressor will be associated with this UUID and can be queried later via `attributeID`. If no UUID is provided,
401
+ * this compressor will generate its own. An `AttributionId` is an `UuidString` which may be validated via
402
+ * {@link isUuidString} or generated via {@link generateStableId}.
399
403
  */
400
404
  public constructor(
401
405
  public readonly localSessionId: SessionId,
402
406
  public readonly reservedIdCount: number,
403
- attributionInfo?: AttributionInfo
407
+ attributionId?: AttributionId
404
408
  ) {
405
409
  assert(reservedIdCount >= 0, 'reservedIdCount must be non-negative');
406
- this.localSession = this.createSession(localSessionId, attributionInfo);
410
+ if (attributionId !== undefined) {
411
+ assertIsUuidString(attributionId);
412
+ }
413
+ this.localSession = this.createSession(localSessionId, attributionId);
407
414
  if (reservedIdCount > 0) {
408
415
  const clusterCapacity = this.clusterCapacity;
409
416
  this.clusterCapacity = reservedIdCount;
@@ -423,33 +430,29 @@ export class IdCompressor {
423
430
  /**
424
431
  * Creates a session object for the supplied ID.
425
432
  * Must only be called once per ID.
426
- * @param sessionId the ID for the session
433
+ * @param sessionId - the ID for the session
427
434
  * @returns the session object for the supplied ID
428
435
  */
429
- private createSession(sessionId: SessionId, attributionInfo: AttributionInfo | undefined): Session {
436
+ private createSession(sessionId: SessionId, attributionId: AttributionId | undefined): Session {
437
+ assert(!this.clustersAndOverridesInversion.has(sessionId));
430
438
  const existingSession = this.sessions.get(sessionId);
431
439
  if (existingSession !== undefined) {
432
440
  fail('createSession must only be called once for each session ID.');
433
441
  }
434
442
  const sessionUuid = numericUuidFromStableId(sessionId);
435
- assert(!this.clustersAndOverridesInversion.has(sessionId));
436
443
  const session: Session = {
437
444
  sessionUuid,
438
445
  currentClusterDetails: undefined,
439
446
  lastFinalizedLocalId: undefined,
447
+ attributionId: attributionId ?? sessionId,
440
448
  };
441
- setPropertyIfDefined(attributionInfo, session, 'attributionInfo');
442
449
  this.sessions.set(sessionId, session);
443
450
  return session;
444
451
  }
445
452
 
446
- private tryGetSession(sessionId: SessionId): Session | undefined {
447
- return this.sessions.get(sessionId);
448
- }
449
-
450
453
  /**
451
454
  * Return the nth reserved ID.
452
- * @param index the index of the ID to return
455
+ * @param index - the index of the ID to return
453
456
  */
454
457
  public getReservedId(index: number): SessionSpaceCompressedId & FinalCompressedId {
455
458
  if (index < 0 || index >= this.reservedIdCount) {
@@ -472,17 +475,17 @@ export class IdCompressor {
472
475
  }
473
476
 
474
477
  /**
475
- * Returns the attribution info associated with the compressor that created the ID, if it exists.
478
+ * Returns the attribution ID associated with the compressor that created the ID
476
479
  */
477
- public attributeId(id: SessionSpaceCompressedId): AttributionInfo | undefined {
480
+ public attributeId(id: SessionSpaceCompressedId): AttributionId {
478
481
  const opSpaceNormalizedId = this.normalizeToOpSpace(id);
479
482
  if (isLocalId(opSpaceNormalizedId)) {
480
- return this.localSession.attributionInfo;
483
+ return this.attributionId;
481
484
  }
482
485
  const [_, cluster] =
483
486
  this.getClusterForFinalId(opSpaceNormalizedId) ?? fail('Cluster does not exist for final ID');
484
487
 
485
- return cluster.session.attributionInfo;
488
+ return cluster.session.attributionId;
486
489
  }
487
490
 
488
491
  /**
@@ -505,7 +508,7 @@ export class IdCompressor {
505
508
  },
506
509
  };
507
510
  } else {
508
- const session = this.tryGetSession(sessionId) ?? fail('Unknown session, range may not be finalized.');
511
+ const session = this.sessions.get(sessionId) ?? fail('Unknown session, range may not be finalized.');
509
512
  const firstNumericUuid = incrementUuid(session.sessionUuid, -first - 1);
510
513
  const firstFinal =
511
514
  this.compressNumericUuid(firstNumericUuid) ??
@@ -560,18 +563,19 @@ export class IdCompressor {
560
563
  const lastTakenNormalized = this.lastTakenLocalId ?? 0;
561
564
  assert(lastLocalInRange <= lastTakenNormalized);
562
565
 
566
+ // The attribution ID is sent with each range, but it can be elided after the first IDs are allocated.
567
+ const sendAttributionId = this.lastTakenLocalId === undefined;
568
+
563
569
  let ids: IdCreationRange.Ids | undefined;
564
570
  if (lastLocalInRange !== lastTakenNormalized) {
565
571
  const firstLocalInRange = (lastTakenNormalized - 1) as UnackedLocalId;
566
- const localOverrides = [
572
+ const overrides = [
567
573
  ...this.localOverrides.getRange(
568
574
  (lastTakenNormalized - 1) as LocalCompressedId,
569
575
  lastLocalInRange as LocalCompressedId
570
576
  ),
571
- ];
572
- if (localOverrides.length > 0) {
573
- // Cast: typecript 4.4.4 doesn't infer that `localOverrides` has at least one element and is therefore an `Overrides`
574
- const overrides = localOverrides as unknown as IdCreationRange.Overrides;
577
+ ] as (readonly [UnackedLocalId, string])[];
578
+ if (hasLength(overrides, 1)) {
575
579
  assert(overrides[0][0] <= firstLocalInRange);
576
580
  assert(overrides[overrides.length - 1][0] >= lastLocalInRange);
577
581
  ids = {
@@ -591,9 +595,8 @@ export class IdCompressor {
591
595
  }
592
596
 
593
597
  const range: Mutable<IdCreationRange> = { sessionId: this.localSessionId };
594
- if (!this.sentAttributionInfo) {
595
- setPropertyIfDefined(this.localSession.attributionInfo, range, 'attributionInfo');
596
- this.sentAttributionInfo = true;
598
+ if (this.attributionId !== this.localSessionId && sendAttributionId) {
599
+ range.attributionId = this.attributionId;
597
600
  }
598
601
 
599
602
  if (ids === undefined) {
@@ -611,18 +614,17 @@ export class IdCompressor {
611
614
 
612
615
  /**
613
616
  * Finalizes the supplied range of IDs (which may be from either a remote or local session).
614
- * @param range the range of session-local IDs to finalize.
617
+ * @param range - the range of session-local IDs to finalize.
615
618
  */
616
619
  public finalizeCreationRange(range: IdCreationRange): void {
617
- const { sessionId, attributionInfo } = range;
620
+ const { sessionId, attributionId } = range;
618
621
 
619
622
  const isLocal = sessionId === this.localSessionId;
620
- let session = this.tryGetSession(sessionId);
623
+ const session = this.sessions.get(sessionId) ?? this.createSession(sessionId, attributionId);
621
624
  assert(
622
- range.attributionInfo === undefined || session === undefined || isLocal,
623
- 'Attribution info can only be supplied on initial range for a session, and never modified.'
625
+ range.attributionId === undefined || range.attributionId === session.attributionId,
626
+ "A session's attribution ID may never be modified."
624
627
  );
625
- session ??= this.createSession(sessionId, attributionInfo);
626
628
 
627
629
  const ids = getIds(range);
628
630
  if (ids === undefined) {
@@ -765,11 +767,7 @@ export class IdCompressor {
765
767
  cluster.overrides ??= new Map();
766
768
 
767
769
  const inversionKey = IdCompressor.createInversionKey(override);
768
- // TODO: This cast can be removed on typescript 4.6
769
- const existingIds = this.getExistingIdsForNewOverride(inversionKey, true) as [
770
- LocalCompressedId,
771
- FinalCompressedId
772
- ];
770
+ const existingIds = this.getExistingIdsForNewOverride(inversionKey, true);
773
771
  let overrideForCluster: string | FinalCompressedId;
774
772
  let associatedLocal: LocalCompressedId | undefined;
775
773
  if (existingIds !== undefined) {
@@ -870,8 +868,7 @@ export class IdCompressor {
870
868
  }
871
869
 
872
870
  private static createInversionKey(inversionKey: string): InversionKey {
873
- // TODO: This cast can be removed on typescript 4.6
874
- return isStableId(inversionKey) ? inversionKey : (`${nonStableOverridePrefix}${inversionKey}` as InversionKey);
871
+ return isStableId(inversionKey) ? inversionKey : `${nonStableOverridePrefix}${inversionKey}`;
875
872
  }
876
873
 
877
874
  private static isStableInversionKey(inversionKey: InversionKey): inversionKey is StableId {
@@ -940,7 +937,7 @@ export class IdCompressor {
940
937
 
941
938
  /**
942
939
  * Check if `a` might be within `range` of `b`, where both are treated as hex numbers.
943
- * @param range an integer
940
+ * @param range - an integer
944
941
  */
945
942
  private static uuidsMightCollide(a: StableId, b: StableId, range: number): boolean {
946
943
  // Check if any of the UUIDs in the cluster collide (i.e. any in [base, base + capacity)).
@@ -972,7 +969,7 @@ export class IdCompressor {
972
969
  /**
973
970
  * Generates a new compressed ID or returns an existing one.
974
971
  * This should ONLY be called to generate IDs for local operations.
975
- * @param override Specifies a specific string to be associated with the returned compressed ID.
972
+ * @param override - Specifies a specific string to be associated with the returned compressed ID.
976
973
  * Performance note: assigning override strings incurs a performance overhead.
977
974
  * @returns an existing ID if one already exists for `override`, and a new local ID otherwise. The returned ID is in session space.
978
975
  */
@@ -999,7 +996,7 @@ export class IdCompressor {
999
996
  /**
1000
997
  * Generates a range of compressed IDs.
1001
998
  * This should ONLY be called to generate IDs for local operations.
1002
- * @param count the number of IDs to generate, must be > 0.
999
+ * @param count - the number of IDs to generate, must be \> 0.
1003
1000
  * @returns a persistable descriptor of the ID range.
1004
1001
  */
1005
1002
  public generateCompressedIdRange(count: number): IdRangeDescriptor<LocalCompressedId> {
@@ -1019,7 +1016,7 @@ export class IdCompressor {
1019
1016
 
1020
1017
  /**
1021
1018
  * Decompresses a previously compressed ID into a UUID or override string.
1022
- * @param id the compressed ID to be decompressed.
1019
+ * @param id - the compressed ID to be decompressed.
1023
1020
  * @returns the UUID or override string associated with the compressed ID. Fails if the ID was not generated by this compressor.
1024
1021
  */
1025
1022
  public decompress(id: SessionSpaceCompressedId | FinalCompressedId): StableId | string {
@@ -1028,7 +1025,7 @@ export class IdCompressor {
1028
1025
 
1029
1026
  /**
1030
1027
  * Attempts to decompress a previously compressed ID into a UUID or override string.
1031
- * @param id the compressed ID to be decompressed.
1028
+ * @param id - the compressed ID to be decompressed.
1032
1029
  * @returns the UUID or override string associated with the compressed ID, or undefined if the ID was not generated by this compressor.
1033
1030
  */
1034
1031
  public tryDecompress(id: SessionSpaceCompressedId | FinalCompressedId): StableId | string | undefined {
@@ -1067,7 +1064,7 @@ export class IdCompressor {
1067
1064
 
1068
1065
  /**
1069
1066
  * Recompresses a decompressed ID, which could be a UUID or an override string.
1070
- * @param uncompressed the UUID or override string to recompress.
1067
+ * @param uncompressed - the UUID or override string to recompress.
1071
1068
  * @returns the `CompressedId` associated with `uncompressed`. Fails if it has not been previously compressed by this compressor.
1072
1069
  */
1073
1070
  public recompress(uncompressed: string): SessionSpaceCompressedId {
@@ -1076,7 +1073,7 @@ export class IdCompressor {
1076
1073
 
1077
1074
  /**
1078
1075
  * Attempts to recompresses a decompressed ID, which could be a UUID or an override string.
1079
- * @param uncompressed the UUID or override string to recompress,
1076
+ * @param uncompressed - the UUID or override string to recompress,
1080
1077
  * @returns the `CompressedId` associated with `uncompressed` or undefined if it has not been previously compressed by this compressor.
1081
1078
  */
1082
1079
  public tryRecompress(uncompressed: string): SessionSpaceCompressedId | undefined {
@@ -1092,8 +1089,7 @@ export class IdCompressor {
1092
1089
  uncompressedUuidNumeric?: NumericUuid
1093
1090
  ): SessionSpaceCompressedId | undefined {
1094
1091
  let numericUuid = uncompressedUuidNumeric;
1095
- // TODO: This cast can be removed on typescript 4.6, and should give improved typesafety.
1096
- const inversionKey = IdCompressor.createInversionKey(uncompressed) as StableId;
1092
+ const inversionKey = IdCompressor.createInversionKey(uncompressed);
1097
1093
  const isStable = IdCompressor.isStableInversionKey(inversionKey);
1098
1094
  const closestMatch = this.clustersAndOverridesInversion.getPairOrNextLower(inversionKey, reusedArray);
1099
1095
  if (closestMatch !== undefined) {
@@ -1140,7 +1136,6 @@ export class IdCompressor {
1140
1136
 
1141
1137
  if (isStable) {
1142
1138
  // May have already computed the numeric UUID, so avoid recomputing if possible
1143
- // TODO: This cast can be removed on typescript 4.6
1144
1139
  const localId = this.getLocalIdForStableId(numericUuid ?? inversionKey);
1145
1140
  if (localId !== undefined) {
1146
1141
  return localId;
@@ -1151,7 +1146,7 @@ export class IdCompressor {
1151
1146
 
1152
1147
  /**
1153
1148
  * Normalizes a session space ID into op space.
1154
- * @param id the local ID to normalize.
1149
+ * @param id - the local ID to normalize.
1155
1150
  * @returns the ID in op space.
1156
1151
  */
1157
1152
  public normalizeToOpSpace(id: SessionSpaceCompressedId): OpSpaceCompressedId {
@@ -1196,9 +1191,9 @@ export class IdCompressor {
1196
1191
 
1197
1192
  /**
1198
1193
  * Normalizes an ID into session space.
1199
- * @param id the ID to normalize. If it is a local ID, it is assumed to have been created by the session corresponding
1194
+ * @param id - the ID to normalize. If it is a local ID, it is assumed to have been created by the session corresponding
1200
1195
  * to `sessionId`.
1201
- * @param originSessionId the session from which `id` originated
1196
+ * @param originSessionId - the session from which `id` originated
1202
1197
  * @returns the session-space ID corresponding to `id`, which might not have been a final ID if the client that created it had not yet
1203
1198
  * finalized it. This can occur when a client references an ID during the window of time in which it is waiting to receive the ordered
1204
1199
  * range that contained it from the server.
@@ -1207,7 +1202,7 @@ export class IdCompressor {
1207
1202
 
1208
1203
  /**
1209
1204
  * Normalizes a final ID into session space.
1210
- * @param id the final ID to normalize.
1205
+ * @param id - the final ID to normalize.
1211
1206
  * @returns the session-space ID corresponding to `id`.
1212
1207
  */
1213
1208
  public normalizeToSessionSpace(id: FinalCompressedId): SessionSpaceCompressedId;
@@ -1222,7 +1217,7 @@ export class IdCompressor {
1222
1217
  }
1223
1218
  return id;
1224
1219
  } else {
1225
- const session = this.tryGetSession(originSessionId ?? fail());
1220
+ const session = this.sessions.get(originSessionId ?? fail());
1226
1221
  if (session === undefined) {
1227
1222
  fail('No IDs have ever been finalized by the supplied session.');
1228
1223
  }
@@ -1282,10 +1277,7 @@ export class IdCompressor {
1282
1277
  }
1283
1278
 
1284
1279
  private getLocalIdForStableId(stableId: StableId | NumericUuid): LocalCompressedId | undefined {
1285
- // TODO: This cast can be removed on typescript 4.6
1286
- const numericUuid = (
1287
- typeof stableId === 'string' ? numericUuidFromStableId(stableId) : stableId
1288
- ) as NumericUuid;
1280
+ const numericUuid = typeof stableId === 'string' ? numericUuidFromStableId(stableId) : stableId;
1289
1281
  const offset = getPositiveDelta(numericUuid, this.localSession.sessionUuid, this.localIdCount - 1);
1290
1282
  if (offset === undefined) {
1291
1283
  return undefined;
@@ -1309,8 +1301,8 @@ export class IdCompressor {
1309
1301
  }
1310
1302
 
1311
1303
  /**
1312
- * @returns if `other` is equal to this `IdCompressor`. The equality check includes local session state.
1313
- * @testOnly
1304
+ * @returns if `other` is equal to this `IdCompressor`. The equality check includes local session state only if specified.
1305
+ * \@testOnly
1314
1306
  */
1315
1307
  public equals(other: IdCompressor, compareLocalState: boolean): boolean {
1316
1308
  if (compareLocalState) {
@@ -1318,7 +1310,7 @@ export class IdCompressor {
1318
1310
  this.localIdCount !== other.localIdCount ||
1319
1311
  this.localSessionId !== other.localSessionId ||
1320
1312
  this.lastTakenLocalId !== other.lastTakenLocalId ||
1321
- this.sentAttributionInfo !== other.sentAttributionInfo
1313
+ this.attributionId !== other.attributionId
1322
1314
  ) {
1323
1315
  return false;
1324
1316
  }
@@ -1422,7 +1414,7 @@ export class IdCompressor {
1422
1414
 
1423
1415
  private static sessionDataEqual(a: Session, b: Session, checkCluster = true, compareLocalState = true): boolean {
1424
1416
  if (
1425
- a.attributionInfo !== b.attributionInfo ||
1417
+ a.attributionId !== b.attributionId ||
1426
1418
  !numericUuidEquals(a.sessionUuid, b.sessionUuid) ||
1427
1419
  a.lastFinalizedLocalId !== b.lastFinalizedLocalId
1428
1420
  ) {
@@ -1510,6 +1502,8 @@ export class IdCompressor {
1510
1502
  public serialize(withSession: boolean): SerializedIdCompressor {
1511
1503
  const serializedSessions: SerializedSessionData[] = [];
1512
1504
  const sessionIdToSessionIndex = new Map<SessionId, number>();
1505
+ const attributionIdToAttributionIndex = new Map<AttributionId, number>();
1506
+ let serializedAttributionIds: UuidString[] | undefined;
1513
1507
 
1514
1508
  for (const [sessionId, session] of this.sessions) {
1515
1509
  const isLocalSession = sessionId === this.localSessionId;
@@ -1520,8 +1514,16 @@ export class IdCompressor {
1520
1514
 
1521
1515
  if (includeSession) {
1522
1516
  const sessionData: Mutable<SerializedSessionData> = [sessionId];
1523
- if (session.attributionInfo !== undefined) {
1524
- sessionData.push(session.attributionInfo);
1517
+ if (session.attributionId !== sessionId) {
1518
+ // As an optimization, don't include the attributionId if it is its default (the sessionId)
1519
+ // 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.
1520
+ sessionData.push(
1521
+ getOrCreate(
1522
+ attributionIdToAttributionIndex,
1523
+ session.attributionId,
1524
+ (id) => (serializedAttributionIds ??= []).push(id) - 1
1525
+ )
1526
+ );
1525
1527
  }
1526
1528
  sessionIdToSessionIndex.set(sessionId, serializedSessions.length);
1527
1529
  serializedSessions.push(sessionData);
@@ -1567,13 +1569,14 @@ export class IdCompressor {
1567
1569
  // Reserved session not serialized, and local session is present but may not make IDs
1568
1570
  assert(serializedSessions.length - this.sessions.size <= 2, 'session not serialized');
1569
1571
 
1570
- const serializedIdCompressor = {
1572
+ const serializedIdCompressor: Omit<SerializedIdCompressor, '_versionedSerializedIdCompressor'> = {
1571
1573
  version: currentWrittenVersion,
1572
1574
  reservedIdCount: this.reservedIdCount,
1573
1575
  clusterCapacity: this.clusterCapacity,
1574
1576
  sessions: serializedSessions,
1575
1577
  clusters: serializedClusters,
1576
- } as unknown as SerializedIdCompressor;
1578
+ };
1579
+ setPropertyIfDefined(serializedAttributionIds, serializedIdCompressor, 'attributionIds');
1577
1580
 
1578
1581
  if (withSession) {
1579
1582
  const serializedWithSession = serializedIdCompressor as Mutable<SerializedIdCompressorWithOngoingSession>;
@@ -1585,14 +1588,13 @@ export class IdCompressor {
1585
1588
  localIdCount: this.localIdCount,
1586
1589
  overrides: [...this.localOverrides.entries()].map((entry) => [...entry]),
1587
1590
  lastTakenLocalId: this.lastTakenLocalId,
1588
- sentAttributionInfo: this.sentAttributionInfo,
1589
1591
  };
1590
1592
  }
1591
1593
 
1592
1594
  return serializedWithSession;
1593
1595
  }
1594
1596
 
1595
- return serializedIdCompressor;
1597
+ return serializedIdCompressor as SerializedIdCompressor;
1596
1598
  }
1597
1599
 
1598
1600
  /**
@@ -1601,48 +1603,60 @@ export class IdCompressor {
1601
1603
  public static deserialize(serialized: SerializedIdCompressorWithOngoingSession): IdCompressor;
1602
1604
 
1603
1605
  /**
1604
- * Deserialize an serialized IdCompressor with a new session. The provided serialized compressor
1605
- * must have an ongoing session.
1606
- * @param serialized the serialized compressor state
1607
- * @param newSessionId the session ID for the new compressor.
1608
- * @param attributionInfo information used by other clients to attribute IDs made by this client
1606
+ * Deserialize a serialized IdCompressor with a new session.
1607
+ * @param serialized - the serialized compressor state
1608
+ * @param newSessionId - the session ID for the new compressor.
1609
+ * @param attributionId - information used by other clients to attribute IDs made by this client
1609
1610
  */
1610
1611
  public static deserialize(
1611
1612
  serialized: SerializedIdCompressorWithNoSession,
1612
1613
  newSessionId: SessionId,
1613
- attributionInfo?: AttributionInfo
1614
+ attributionId?: AttributionId
1614
1615
  ): IdCompressor;
1615
1616
 
1616
1617
  public static deserialize(
1617
- serialized: SerializedIdCompressorWithNoSession | SerializedIdCompressorWithOngoingSession,
1618
- newSessionIdMaybe?: SessionId,
1619
- attributionInfoMaybe?: AttributionInfo
1618
+ ...args:
1619
+ | [
1620
+ serialized: SerializedIdCompressorWithNoSession,
1621
+ newSessionIdMaybe: SessionId,
1622
+ attributionIdMaybe?: AttributionId
1623
+ ]
1624
+ | [
1625
+ serialized: SerializedIdCompressorWithOngoingSession,
1626
+ newSessionIdMaybe?: undefined,
1627
+ attributionIdMaybe?: undefined
1628
+ ]
1620
1629
  ): IdCompressor {
1621
- const hasSession = hasOngoingSession(serialized);
1622
- let localSessionId: SessionId;
1623
- let attributionInfo: AttributionInfo | undefined;
1624
- let serializedLocalState: SerializedLocalState | undefined;
1625
- if (hasSession) {
1626
- assert(newSessionIdMaybe === undefined && attributionInfoMaybe === undefined);
1627
- // TODO: This cast can be removed on typescript 4.6
1628
- [localSessionId, attributionInfo] =
1629
- serialized.sessions[(serialized as SerializedIdCompressorWithOngoingSession).localSessionIndex];
1630
- // TODO: This cast can be removed on typescript 4.6
1631
- serializedLocalState = (serialized as SerializedIdCompressorWithOngoingSession).localState;
1632
- } else {
1633
- assert(newSessionIdMaybe !== undefined);
1634
- localSessionId = newSessionIdMaybe;
1635
- attributionInfo = attributionInfoMaybe;
1636
- }
1630
+ const [serialized, newSessionIdMaybe, attributionIdMaybe] = args;
1637
1631
 
1638
1632
  const {
1639
1633
  clusterCapacity,
1640
1634
  reservedIdCount,
1641
1635
  sessions: serializedSessions,
1642
1636
  clusters: serializedClusters,
1637
+ attributionIds: serializedAttributionIds,
1643
1638
  } = serialized;
1644
1639
 
1645
- const compressor = new IdCompressor(localSessionId, reservedIdCount, attributionInfo);
1640
+ let localSessionId: SessionId;
1641
+ let attributionId: AttributionId | undefined;
1642
+ let serializedLocalState: SerializedLocalState | undefined;
1643
+ if (newSessionIdMaybe === undefined) {
1644
+ // Alias of serialized, but known to be a SerializedIdCompressorWithOngoingSession
1645
+ const [serializedWithSession] = args;
1646
+ const serializedSessionData = serializedSessions[serializedWithSession.localSessionIndex];
1647
+ localSessionId = serializedSessionData[0];
1648
+ const attributionIndex = serializedSessionData[1];
1649
+ if (attributionIndex !== undefined) {
1650
+ assert(serializedAttributionIds !== undefined && serializedAttributionIds.length > attributionIndex);
1651
+ attributionId = serializedAttributionIds[attributionIndex];
1652
+ }
1653
+ serializedLocalState = serializedWithSession.localState;
1654
+ } else {
1655
+ localSessionId = newSessionIdMaybe;
1656
+ attributionId = attributionIdMaybe;
1657
+ }
1658
+
1659
+ const compressor = new IdCompressor(localSessionId, reservedIdCount, attributionId);
1646
1660
  compressor.clusterCapacity = clusterCapacity;
1647
1661
 
1648
1662
  const localOverridesInverse = new Map<string, LocalCompressedId>();
@@ -1650,7 +1664,6 @@ export class IdCompressor {
1650
1664
  // Do this part of local rehydration first since the cluster map population needs to query to local overrides
1651
1665
  compressor.localIdCount = serializedLocalState.localIdCount;
1652
1666
  compressor.lastTakenLocalId = serializedLocalState.lastTakenLocalId;
1653
- compressor.sentAttributionInfo = serializedLocalState.sentAttributionInfo;
1654
1667
  if (serializedLocalState.overrides !== undefined) {
1655
1668
  for (const [localId, override] of serializedLocalState.overrides) {
1656
1669
  compressor.localOverrides.append(localId, override);
@@ -1668,12 +1681,20 @@ export class IdCompressor {
1668
1681
  readonly sessionId: SessionId;
1669
1682
  }[] = [];
1670
1683
  for (const serializedSession of serializedSessions) {
1671
- const [sessionId, attributionInfo] = serializedSession;
1684
+ const [sessionId, attributionIndex] = serializedSession;
1672
1685
  if (sessionId === localSessionId) {
1673
- assert(hasSession, 'Cannot resume existing session.');
1686
+ assert(hasOngoingSession(serialized), 'Cannot resume existing session.');
1674
1687
  sessionInfos.push({ session: compressor.localSession, sessionId });
1675
1688
  } else {
1676
- const session = compressor.createSession(sessionId, attributionInfo);
1689
+ let attributionId: AttributionId | undefined;
1690
+ if (attributionIndex !== undefined) {
1691
+ assert(
1692
+ serializedAttributionIds !== undefined && serializedAttributionIds.length > attributionIndex,
1693
+ 'AttributionId index out of bounds'
1694
+ );
1695
+ attributionId = serializedAttributionIds[attributionIndex];
1696
+ }
1697
+ const session = compressor.createSession(sessionId, attributionId);
1677
1698
  sessionInfos.push({ session, sessionId });
1678
1699
  }
1679
1700
  }
@@ -1766,7 +1787,7 @@ export class IdCompressor {
1766
1787
 
1767
1788
  /**
1768
1789
  * Converts the given serialized compressor to the current version.
1769
- * @param serializedCompressor the serialized compressor to convert. Must not have been serialized with an ongoing session.
1790
+ * @param serializedCompressor - the serialized compressor to convert. Must not have been serialized with an ongoing session.
1770
1791
  * @returns a serialized compressor with no ongoing session.
1771
1792
  */
1772
1793
  public static convertToCurrentVersion(
@@ -1776,7 +1797,7 @@ export class IdCompressor {
1776
1797
 
1777
1798
  /**
1778
1799
  * Converts the given serialized compressor to the current version.
1779
- * @param serializedCompressor the serialized compressor to convert. Must have been serialized with an ongoing session.
1800
+ * @param serializedCompressor - the serialized compressor to convert. Must have been serialized with an ongoing session.
1780
1801
  * @returns a serialized compressor with the same ongoing session.
1781
1802
  */
1782
1803
  public static convertToCurrentVersion(
@@ -1825,10 +1846,8 @@ function deserializeCluster(serializedCluster: SerializedCluster): {
1825
1846
  return {
1826
1847
  sessionIndex,
1827
1848
  capacity,
1828
- // TODO: This cast can be removed on typescript 4.6
1829
- count: (hasCount ? countOrOverrides : capacity) as number,
1830
- // TODO: This cast can be removed on typescript 4.6
1831
- overrides: (hasCount ? overrides : countOrOverrides) as SerializedClusterOverrides,
1849
+ count: hasCount ? countOrOverrides : capacity,
1850
+ overrides: hasCount ? overrides : countOrOverrides,
1832
1851
  };
1833
1852
  }
1834
1853
 
@@ -1839,10 +1858,3 @@ function deserializeCluster(serializedCluster: SerializedCluster): {
1839
1858
  * lookup results should be extracted from the tuple immediately after invocation.
1840
1859
  */
1841
1860
  const reusedArray: [any, any] = [] as unknown as [any, any];
1842
-
1843
- /**
1844
- * A numeric comparator used for sorting in descending order.
1845
- */
1846
- function compareFiniteNumbersReversed<T extends number>(a: T, b: T): number {
1847
- return b - a;
1848
- }