@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
@@ -5,9 +5,9 @@
5
5
 
6
6
  /* eslint-disable no-bitwise */
7
7
 
8
- import { v4 } from 'uuid';
9
8
  import { assert, fail } from '../Common';
10
9
  import { SessionId, StableId } from '../Identifiers';
10
+ import { generateStableId } from '../UuidUtilities';
11
11
 
12
12
  /**
13
13
  * A UUID (128 bit identifier) optimized for use as a 128 bit unsigned integer with fast addition and toString operations.
@@ -30,9 +30,9 @@ const fiftyThirdBit = 2 ** 52;
30
30
 
31
31
  /**
32
32
  * Calculates the numeric delta between a and b (i.e. a - b).
33
- * @param a an uuid
34
- * @param b an other uuid
35
- * @param maxDelta the maximum integer delta (inclusive) to tolerate.
33
+ * @param a - an uuid
34
+ * @param b - an other uuid
35
+ * @param maxDelta - the maximum integer delta (inclusive) to tolerate.
36
36
  * @returns undefined if the delta is negative or greater than `maxDelta`
37
37
  */
38
38
  export function getPositiveDelta(a: NumericUuid, b: NumericUuid, maxDelta: number): number | undefined {
@@ -97,7 +97,7 @@ function padToLengthWithZeros(str: string, count: number): string {
97
97
  }
98
98
 
99
99
  /**
100
- * @param offset an optional offset to increment the returned StableId
100
+ * @param offset - an optional offset to increment the returned StableId
101
101
  * @returns the string representation of a `NumericUuid`.
102
102
  */
103
103
  export function stableIdFromNumericUuid(uuid: NumericUuid, offset = 0): StableId {
@@ -111,7 +111,7 @@ export function stableIdFromNumericUuid(uuid: NumericUuid, offset = 0): StableId
111
111
  }
112
112
 
113
113
  /**
114
- * @param stableId a minimal uuid string
114
+ * @param stableId - a minimal uuid string
115
115
  * @returns a numeric representation of `stableId`.
116
116
  */
117
117
  export function numericUuidFromStableId(stableId: StableId): NumericUuid {
@@ -121,81 +121,12 @@ export function numericUuidFromStableId(stableId: StableId): NumericUuid {
121
121
  return uuid as readonly (number | string)[] as NumericUuid;
122
122
  }
123
123
 
124
- /**
125
- * Asserts that the supplied uuid is a stable ID.
126
- */
127
- export function assertIsStableId(uuid: string): StableId {
128
- assert(isStableId(uuid), `${uuid} is not a StableId.`);
129
- return uuid;
130
- }
131
-
132
- const charCode0 = '0'.charCodeAt(0);
133
- const charCode9 = '9'.charCodeAt(0);
134
- const charCodea = 'a'.charCodeAt(0);
135
- const charCodef = 'f'.charCodeAt(0);
136
- const charCodeA = 'A'.charCodeAt(0);
137
- const charCodeF = 'F'.charCodeAt(0);
138
-
139
- /**
140
- * Returns whether the supplied string is a v4 variant 2 uuid.
141
- */
142
- export function isStableId(str: string): str is StableId {
143
- if (str.length !== 36) {
144
- return false;
145
- }
146
-
147
- for (let i = 0; i < str.length; i++) {
148
- switch (i) {
149
- case 8:
150
- case 13:
151
- case 18:
152
- case 23: {
153
- if (str.charAt(i) !== '-') {
154
- return false;
155
- }
156
- break;
157
- }
158
-
159
- case 14: {
160
- if (str.charAt(i) !== '4') {
161
- return false;
162
- }
163
- break;
164
- }
165
-
166
- case 19: {
167
- const c = str.charAt(i);
168
- if (c !== '8' && c !== '9' && c !== 'a' && c !== 'b') {
169
- return false;
170
- }
171
- break;
172
- }
173
-
174
- default: {
175
- const code = str.charCodeAt(i);
176
- const isUuidChar =
177
- (code >= charCode0 && code <= charCode9) ||
178
- (code >= charCodea && code <= charCodef) ||
179
- (code >= charCodeA && code <= charCodeF);
180
-
181
- if (!isUuidChar) {
182
- return false;
183
- }
184
- break;
185
- }
186
- }
187
- }
188
-
189
- return true;
190
- }
191
-
192
124
  /**
193
125
  * Creates a session base ID.
194
126
  * This method (rather than standard uuid generation methods) should be used to generate session IDs.
195
127
  */
196
128
  export function createSessionId(): SessionId {
197
- const uuid = assertIsStableId(v4());
198
- return ensureSessionUuid(uuid);
129
+ return ensureSessionUuid(generateStableId());
199
130
  }
200
131
 
201
132
  /**
@@ -213,7 +144,7 @@ const maxUpperNumber = 2 ** 48 - 1;
213
144
 
214
145
  /**
215
146
  * Increments the uuid. `amount` must be a positive integer.
216
- * @returns the result of incrementing the uuid by `amount`.`
147
+ * @returns the result of incrementing the uuid by `amount`.
217
148
  */
218
149
  export function incrementUuid(uuid: NumericUuid, amount: number): NumericUuid {
219
150
  /*
@@ -335,9 +266,9 @@ namespace ChunkMath {
335
266
  }
336
267
 
337
268
  /**
338
- * Returns the number representation of the given bits corresponding to the variant chunk. The value is derived by parsing all bits
339
- * except for reserved bits (i.e. the variant bits).
340
- * @param variantChunkBits
269
+ * Returns the number representation of the given bits corresponding to the variant chunk. The value is derived by
270
+ * parsing all bits except for reserved bits (i.e. the variant bits).
271
+ * @param variantChunk - the variantChunk
341
272
  */
342
273
  export function getNumericValue(variantChunk: string): number {
343
274
  const variantChunkBits = Number.parseInt(variantChunk, 16);
@@ -0,0 +1,497 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /* eslint-disable @typescript-eslint/restrict-plus-operands */
7
+
8
+ import { assert, compareFiniteNumbers, compareFiniteNumbersReversed, fail, Mutable } from '../Common';
9
+ import { FinalCompressedId, LocalCompressedId, SessionSpaceCompressedId } from '../Identifiers';
10
+ import { AppendOnlyDoublySortedMap } from './AppendOnlySortedMap';
11
+
12
+ /**
13
+ * Maps IDs created by a session between their local and final forms (i.e. normalization). These IDs are in a contiguous range.
14
+ * The local and final forms of IDs made by a session can be thought of as two equal-length sparse arrays, aligned such
15
+ * that normalizeLocalToFinal(locals[i]) === finals[i] and vice versa.
16
+ * Below is an example to illustrate how various mappings can arise:
17
+ *
18
+ * +- Creation Index
19
+ * / +- Locals
20
+ * / / +- Finals
21
+ * / / /
22
+ * ---+-----+----
23
+ * 0 | -1 | 0 -|___ Two IDs are allocated as locals since no cluster exists. A new cluster is created when acked.
24
+ * 1 | -2 | 1 -|
25
+ * 2 | | 2 -|
26
+ * 3 | | 3 --|-- Three more IDs are allocated as finals eagerly since a cluster exists with available capacity.
27
+ * 4 | | 4 -|
28
+ * 5 | -6 | 10 ----- One ID is allocated as a local (it overflows the existing cluster) and a new cluster is created after ack.
29
+ * 6 | | 11 ----- One ID is allocated as a final eagerly into the existing cluster.
30
+ * 7 | -8 | 12 ----- A local ID with an override is allocated. The override forces it to be a local ID.
31
+ * 8 | | 13
32
+ * 9 | | 14
33
+ * 10 | -11 | ----- A local ID is allocated. It has no corresponding final ID since it has not been acked.
34
+ *
35
+ * Note that in this example, some IDs (those at indices 2, 3, 4, 6, 8, and 9) have no local form. The ID at index 10 has no final form.
36
+ * These kinds of "gaps" occur due to the timing of allocation calls on the client and how they relate to finalization/cluster creation,
37
+ * which depends on receiving an ack/sequence number from the server. Given this context, "session space" can be thought of as:
38
+ *
39
+ * for each index in the range of IDs created by a session:
40
+ * the local form if it exists, otherwise the final form
41
+ *
42
+ * This class is designed to efficiently build and query these mappings by leveraging the facts that much of the range (in both local and
43
+ * final space) is uninterrupted by "gaps" and can be compactly represented by a (first, last) pair and is easily binary searched for
44
+ * normalization between local and final space.
45
+ */
46
+ export class SessionIdNormalizer<TRangeObject> {
47
+ private nextLocalId: LocalCompressedId = -1 as LocalCompressedId;
48
+ private readonly idRanges: AppendOnlyDoublySortedMap<
49
+ LocalCompressedId,
50
+ [lastLocal: LocalCompressedId, finalRanges: FinalRanges<TRangeObject> | undefined],
51
+ FinalCompressedId
52
+ > = new AppendOnlyDoublySortedMap(
53
+ compareFiniteNumbersReversed,
54
+ ([_, finalRanges]) => {
55
+ if (finalRanges !== undefined) {
56
+ const first = getFirstRange(finalRanges);
57
+ return extractFirstFinalFromRange(first);
58
+ }
59
+ return Number.POSITIVE_INFINITY as FinalCompressedId;
60
+ },
61
+ compareFiniteNumbers
62
+ );
63
+
64
+ public constructor(private readonly expensiveAsserts = false) {}
65
+
66
+ /**
67
+ * Converts the final ID to its session-space representation.
68
+ * This will be the corresponding local if a local form exists, and `finalId` otherwise.
69
+ */
70
+ public getSessionSpaceId(finalId: FinalCompressedId): SessionSpaceCompressedId | undefined {
71
+ const localRange = this.idRanges.getPairOrNextLowerByValue(finalId);
72
+ if (localRange !== undefined) {
73
+ const [firstLocal, [lastLocal, finalRanges]] = localRange;
74
+ const finalRange = getPairOrNextLowerByValue(firstLocal, finalRanges, finalId);
75
+ if (finalRange !== undefined) {
76
+ const [alignedLocal, [firstFinal, lastFinal]] = finalRange;
77
+ if (finalId <= lastFinal) {
78
+ const localRangeDelta = alignedLocal - lastLocal;
79
+ const finalDelta = finalId - firstFinal;
80
+ if (finalDelta <= localRangeDelta) {
81
+ return (alignedLocal - finalDelta) as LocalCompressedId;
82
+ }
83
+ // `finalId` was an eagerly generated final ID
84
+ return finalId as SessionSpaceCompressedId;
85
+ }
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+
91
+ /**
92
+ * Converts the local ID to its corresponding final ID, if one exists.
93
+ */
94
+ public getFinalId(localId: LocalCompressedId): [FinalCompressedId, TRangeObject] | undefined {
95
+ const localRange =
96
+ this.idRanges.getPairOrNextLower(localId) ?? fail('Local ID was never recorded with this normalizer.');
97
+ const [firstLocal, [lastLocal, finalRanges]] = localRange;
98
+ if (localId < lastLocal) {
99
+ fail('Local ID was never recorded with this normalizer.');
100
+ }
101
+ const finalRange = getPairOrNextLower(firstLocal, finalRanges, localId);
102
+ if (finalRange !== undefined) {
103
+ const [alignedLocal, [firstFinal, lastFinal, rangeObject]] = finalRange;
104
+ const rangeDelta = lastFinal - firstFinal;
105
+ const localDelta = alignedLocal - localId;
106
+ if (localDelta <= rangeDelta) {
107
+ // Local is within a range segment that has a corresponding final
108
+ return [(firstFinal + localDelta) as FinalCompressedId, rangeObject];
109
+ }
110
+ }
111
+ return undefined;
112
+ }
113
+
114
+ /**
115
+ * Returns the index of the supplied session-space ID in the total range of IDs created by the session, if the ID was created
116
+ * by the session for this `SessionIdNormalizer`.
117
+ */
118
+ public getCreationIndex(finalId: FinalCompressedId): number | undefined {
119
+ const localRange = this.idRanges.getPairOrNextLowerByValue(finalId);
120
+ if (localRange !== undefined) {
121
+ const [firstLocal, [_, finalRanges]] = localRange;
122
+ const finalRange = getPairOrNextLowerByValue(firstLocal, finalRanges, finalId);
123
+ if (finalRange !== undefined) {
124
+ const [alignedLocal, [firstFinal, lastFinal]] = finalRange;
125
+ if (finalId <= lastFinal) {
126
+ const finalDelta = finalId - firstFinal;
127
+ return -alignedLocal - 1 + finalDelta;
128
+ }
129
+ }
130
+ }
131
+ return undefined;
132
+ }
133
+
134
+ /**
135
+ * Returns the session-space ID at the supplied creation index, if one exists.
136
+ */
137
+ public getIdByCreationIndex(index: number): SessionSpaceCompressedId | undefined {
138
+ const localByIndex = -(index + 1) as LocalCompressedId;
139
+ const localRange = this.idRanges.getPairOrNextLower(localByIndex);
140
+ if (localRange === undefined) {
141
+ return undefined;
142
+ }
143
+ const [firstLocal, [lastLocal, finalRanges]] = localRange;
144
+ if (localByIndex >= lastLocal) {
145
+ return localByIndex;
146
+ }
147
+ const finalRange =
148
+ getPairOrNextLower(firstLocal, finalRanges, localByIndex) ??
149
+ fail('Final ranges not aligned with owning local range.');
150
+
151
+ const [alignedLocal, [firstFinal, lastFinal]] = finalRange;
152
+ const localDelta = alignedLocal - localByIndex;
153
+ const finalId = firstFinal + localDelta;
154
+ if (finalId <= lastFinal) {
155
+ // `finalId` was an eagerly generated final ID
156
+ return finalId as SessionSpaceCompressedId;
157
+ }
158
+ return undefined;
159
+ }
160
+
161
+ private static makeFinalRangesMap<TRangeObject>(): FinalRangesMap<TRangeObject> {
162
+ return new AppendOnlyDoublySortedMap(
163
+ compareFiniteNumbersReversed,
164
+ extractFirstFinalFromRange,
165
+ compareFiniteNumbers
166
+ );
167
+ }
168
+
169
+ /**
170
+ * Returns the last final ID known to this normalizer.
171
+ */
172
+ public getLastFinalId(): FinalCompressedId | undefined {
173
+ const lastIndex = this.idRanges.size - 1;
174
+ const secondToLast = Math.max(0, lastIndex - 1);
175
+ for (let i = lastIndex; i >= secondToLast; i--) {
176
+ const localRange = this.idRanges.getAtIndex(i);
177
+ if (localRange !== undefined) {
178
+ const finalRanges = localRange[1][1];
179
+ if (finalRanges !== undefined) {
180
+ return getLastRange(finalRanges)[1];
181
+ }
182
+ }
183
+ }
184
+ return undefined;
185
+ }
186
+
187
+ /**
188
+ * Registers and returns the next ID in local space with this normalizer. An example:
189
+ *
190
+ * Locals: [-1, -2, X, X]
191
+ * Finals: [ 0, 1, 2, 3]
192
+ * In this scenario, a call to this method would generate and return -5.
193
+ */
194
+ public addLocalId(): LocalCompressedId {
195
+ const localId = this.nextLocalId-- as LocalCompressedId;
196
+ const lastLocalRange = this.idRanges.last();
197
+ if (lastLocalRange !== undefined) {
198
+ const lastLocal = lastLocalRange[1][0];
199
+ if (localId === lastLocal - 1) {
200
+ // New local simply expands the last local range tracked
201
+ lastLocalRange[1][0] = localId;
202
+ return localId;
203
+ }
204
+ }
205
+
206
+ if (this.expensiveAsserts) {
207
+ if (lastLocalRange === undefined) {
208
+ assert(localId === -1, 'Local ID space must start at -1.');
209
+ } else {
210
+ const [firstLocal, [_, finalRanges]] = lastLocalRange;
211
+ let finalDelta = 0;
212
+ for (const [_, [firstFinal, lastFinal]] of entries(firstLocal, finalRanges)) {
213
+ finalDelta += lastFinal - firstFinal + 1;
214
+ }
215
+ assert(localId === firstLocal - finalDelta, 'Local ID space must be contiguous.');
216
+ }
217
+ }
218
+
219
+ this.idRanges.append(localId, [localId, undefined]);
220
+ return localId;
221
+ }
222
+
223
+ /**
224
+ * Registers a final ID with this normalizer.
225
+ * If there are any local IDs at the tip of session-space that do not have a corresponding final, it will be registered (aligned) with
226
+ * the first of those. Otherwise, will be registered as the next ID in session space in creation order. An example:
227
+ *
228
+ * Locals: [-1, -2, X, -4]
229
+ * Finals: [ 0, 1, 2, X]
230
+ * Calling `addFinalIds` with first === last === 5 results in the following:
231
+ * Locals: [-1, -2, X, -4]
232
+ * Finals: [ 0, 1, 2, 5]
233
+ * Calling `addFinalIds` with first === last === 6 results in the following:
234
+ * Locals: [-1, -2, X, -4, X]
235
+ * Finals: [ 0, 1, 2, 5, 6]
236
+ *
237
+ * Non-contiguous final IDs (i.e. the first final after a "gap" in final space) must always correspond to a local ID. For example,
238
+ * in the final call to `addFinalIds` above would fail if first === last === 9, because the resulting state would have a
239
+ * non-contiguous final ID without a local form:
240
+ * Locals: [-1, -2, X, -4, X]
241
+ * Finals: [ 0, 1, 2, 5, 9]
242
+ * ^final ID 9 is not contiguous and does not have a corresponding local ID
243
+ */
244
+ public addFinalIds(firstFinal: FinalCompressedId, lastFinal: FinalCompressedId, rangeObject: TRangeObject): void {
245
+ assert(lastFinal >= firstFinal, 'Malformed normalization range.');
246
+ const [firstLocal, finalRangesObj] =
247
+ this.idRanges.last() ?? fail('Final IDs must be added to an existing local range.');
248
+ const [lastLocal, finalRanges] = finalRangesObj;
249
+ let nextLocal: LocalCompressedId;
250
+ if (finalRanges === undefined) {
251
+ finalRangesObj[1] = [firstFinal, lastFinal, rangeObject];
252
+ nextLocal = Math.min(this.nextLocalId, firstLocal - (lastFinal - firstFinal) - 1) as LocalCompressedId;
253
+ } else {
254
+ const isSingle = isSingleRange(finalRanges);
255
+ let lastFinalRange: FinalRange<TRangeObject>;
256
+ let firstAlignedLocal: LocalCompressedId;
257
+ if (isSingle) {
258
+ firstAlignedLocal = firstLocal;
259
+ lastFinalRange = finalRanges;
260
+ } else {
261
+ [firstAlignedLocal, lastFinalRange] = finalRanges.last() ?? fail('Map should be non-empty.');
262
+ }
263
+
264
+ const [firstAlignedFinal, lastAlignedFinal] = lastFinalRange;
265
+ const lastAlignedLocal = firstAlignedLocal - (lastAlignedFinal - firstAlignedFinal);
266
+ nextLocal = Math.min(
267
+ this.nextLocalId,
268
+ lastAlignedLocal - (lastFinal - firstFinal) - 2
269
+ ) as LocalCompressedId;
270
+ if (firstFinal === lastAlignedFinal + 1) {
271
+ lastFinalRange[1] = lastFinal;
272
+ } else {
273
+ const alignedLocal = (lastAlignedLocal - 1) as LocalCompressedId;
274
+ let rangeMap: FinalRangesMap<TRangeObject>;
275
+ if (isSingle) {
276
+ // Convert the single range to a range collection
277
+ rangeMap = SessionIdNormalizer.makeFinalRangesMap();
278
+ rangeMap.append(firstAlignedLocal, lastFinalRange);
279
+ finalRangesObj[1] = rangeMap;
280
+ } else {
281
+ rangeMap = finalRanges;
282
+ }
283
+ rangeMap.append(alignedLocal, [firstFinal, lastFinal, rangeObject]);
284
+ assert(alignedLocal >= lastLocal, 'Gaps in final space must align to a local.');
285
+ }
286
+ if (this.expensiveAsserts) {
287
+ this.idRanges.assertValid();
288
+ }
289
+ }
290
+
291
+ this.nextLocalId = nextLocal;
292
+ }
293
+
294
+ /**
295
+ * Returns an enumerable of all session-space IDs known to this normalizer, in creation order.
296
+ */
297
+ public *[Symbol.iterator](): IterableIterator<SessionSpaceCompressedId> {
298
+ for (const [firstLocal, [lastLocal, finalRanges]] of this.idRanges.entries()) {
299
+ for (let i = firstLocal; i >= lastLocal; i--) {
300
+ yield i;
301
+ }
302
+ if (finalRanges !== undefined) {
303
+ // Safe to get only the last final range, as all others must have corresponding locals (see `addFinalIds` docs)
304
+ let lastFinalRange: FinalRange<TRangeObject>;
305
+ let alignedLocal: LocalCompressedId;
306
+ if (isSingleRange(finalRanges)) {
307
+ alignedLocal = firstLocal;
308
+ lastFinalRange = finalRanges;
309
+ } else {
310
+ [alignedLocal, lastFinalRange] = finalRanges.last() ?? fail('Map should be non-empty.');
311
+ }
312
+ const [firstFinal, lastFinal] = lastFinalRange;
313
+ const localRangeDelta = alignedLocal - lastLocal;
314
+ for (let i = firstFinal + localRangeDelta + 1; i <= lastFinal; i++) {
315
+ yield i as SessionSpaceCompressedId;
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ public serialize(): SerializedSessionIdNormalizer {
322
+ const serialized: Mutable<SerializedSessionIdNormalizer> = { localRanges: [] };
323
+ const localRanges = serialized.localRanges as Mutable<typeof serialized.localRanges>;
324
+ for (const [firstLocal, finalRanges] of this.idRanges.entries()) {
325
+ const [lastLocal, finalRangesTable] = finalRanges;
326
+ if (finalRangesTable !== undefined) {
327
+ const serializedFinalRanges: [LocalCompressedId, FinalCompressedId, FinalCompressedId][] = [];
328
+ for (const [alignedLocal, [firstFinal, lastFinal]] of entries(firstLocal, finalRangesTable)) {
329
+ serializedFinalRanges.push([alignedLocal, firstFinal, lastFinal]);
330
+ }
331
+ localRanges.push([firstLocal, lastLocal, serializedFinalRanges]);
332
+ } else {
333
+ localRanges.push([firstLocal, lastLocal]);
334
+ }
335
+ }
336
+ return serialized;
337
+ }
338
+
339
+ public static deserialize<TRangeObject>(
340
+ serialized: SerializedSessionIdNormalizer,
341
+ getRangeObject: (finalId: FinalCompressedId) => TRangeObject
342
+ ): SessionIdNormalizer<TRangeObject> {
343
+ const normalizer = new SessionIdNormalizer<TRangeObject>();
344
+ const { idRanges } = normalizer;
345
+ for (const [firstLocal, lastLocal, serializedFinalRanges] of serialized.localRanges) {
346
+ let finalRanges: FinalRanges<TRangeObject> | undefined;
347
+ if (serializedFinalRanges !== undefined) {
348
+ assert(serializedFinalRanges.length !== 0, 'Empty range should not be reified.');
349
+ if (serializedFinalRanges.length === 1) {
350
+ const [_, firstFinal, lastFinal] = serializedFinalRanges[0];
351
+ finalRanges = [firstFinal, lastFinal, getRangeObject(firstFinal)];
352
+ } else {
353
+ finalRanges = SessionIdNormalizer.makeFinalRangesMap<TRangeObject>();
354
+ for (const [alignedLocal, firstFinal, lastFinal] of serializedFinalRanges) {
355
+ finalRanges.append(alignedLocal, [firstFinal, lastFinal, getRangeObject(firstFinal)]);
356
+ }
357
+ }
358
+ }
359
+ idRanges.append(firstLocal, [lastLocal, finalRanges]);
360
+ }
361
+ return normalizer;
362
+ }
363
+
364
+ public equals(
365
+ other: SessionIdNormalizer<TRangeObject>,
366
+ compareRangeObjects: (a: TRangeObject, b: TRangeObject) => boolean = (a, b) => a === b
367
+ ): boolean {
368
+ return this.idRanges.equals(other.idRanges, (localRangeA, localRangeB) => {
369
+ const [lastLocalA, finalRangesA] = localRangeA;
370
+ const [lastLocalB, finalRangesB] = localRangeB;
371
+ if (finalRangesA === undefined || finalRangesB === undefined) {
372
+ return finalRangesA === finalRangesB;
373
+ }
374
+
375
+ const rangeEquals = (finalRangeA: FinalRange<TRangeObject>, finalRangeB: FinalRange<TRangeObject>) => {
376
+ const [firstFinalA, lastFinalA, rangeObjectA] = finalRangeA;
377
+ const [firstFinalB, lastFinalB, rangeObjectB] = finalRangeB;
378
+ return (
379
+ firstFinalA === firstFinalB &&
380
+ lastFinalA === lastFinalB &&
381
+ compareRangeObjects(rangeObjectA, rangeObjectB)
382
+ );
383
+ };
384
+
385
+ if (isSingleRange(finalRangesA) || isSingleRange(finalRangesB)) {
386
+ if (!isSingleRange(finalRangesA) || !isSingleRange(finalRangesB)) {
387
+ return false;
388
+ }
389
+ return rangeEquals(finalRangesA, finalRangesB);
390
+ }
391
+
392
+ return lastLocalA === lastLocalB && finalRangesA.equals(finalRangesB, rangeEquals);
393
+ });
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Serialized table for normalizing IDs made by the local session.
399
+ *
400
+ * TODO: Move this into ID compressor persisted types when integrated.
401
+ */
402
+ export interface SerializedSessionIdNormalizer {
403
+ readonly localRanges: readonly (readonly [
404
+ firstLocal: LocalCompressedId,
405
+ lastLocal: LocalCompressedId,
406
+ finalRanges?: readonly (readonly [
407
+ alignedLocal: LocalCompressedId,
408
+ firstFinal: FinalCompressedId,
409
+ lastFinal: FinalCompressedId
410
+ ])[]
411
+ ])[];
412
+ }
413
+
414
+ type FinalRange<TRangeObject> = [
415
+ firstFinal: FinalCompressedId,
416
+ lastFinal: FinalCompressedId,
417
+ rangeObject: TRangeObject
418
+ ];
419
+
420
+ type FinalRangesMap<TRangeObject> = AppendOnlyDoublySortedMap<
421
+ LocalCompressedId,
422
+ FinalRange<TRangeObject>,
423
+ FinalCompressedId
424
+ >;
425
+
426
+ type FinalRanges<TRangeObject> = FinalRange<TRangeObject> | FinalRangesMap<TRangeObject>;
427
+
428
+ function isSingleRange<TRangeObject>(ranges: FinalRanges<TRangeObject>): ranges is FinalRange<TRangeObject> {
429
+ return Array.isArray(ranges);
430
+ }
431
+
432
+ function getLastRange<TRangeObject>(finalRanges: FinalRanges<TRangeObject>): FinalRange<TRangeObject> {
433
+ if (isSingleRange(finalRanges)) {
434
+ return finalRanges;
435
+ }
436
+ return (finalRanges.last() ?? fail('Map must be non-empty'))[1];
437
+ }
438
+
439
+ function getFirstRange<TRangeObject>(finalRanges: FinalRanges<TRangeObject>): FinalRange<TRangeObject> {
440
+ if (isSingleRange(finalRanges)) {
441
+ return finalRanges;
442
+ }
443
+ return (finalRanges.first() ?? fail('Map must be non-empty'))[1];
444
+ }
445
+
446
+ function extractFirstFinalFromRange<TRangeObject>(finalRange: FinalRange<TRangeObject>): FinalCompressedId {
447
+ return finalRange[0];
448
+ }
449
+
450
+ function getPairOrNextLowerByValue<TRangeObject>(
451
+ firstLocal: LocalCompressedId,
452
+ finalRanges: FinalRanges<TRangeObject> | undefined,
453
+ finalId: FinalCompressedId
454
+ ): readonly [LocalCompressedId, FinalRange<TRangeObject>] | undefined {
455
+ if (finalRanges === undefined) {
456
+ return undefined;
457
+ }
458
+ if (isSingleRange(finalRanges)) {
459
+ if (finalId < finalRanges[0]) {
460
+ return undefined;
461
+ }
462
+ return [firstLocal, finalRanges];
463
+ }
464
+ return finalRanges.getPairOrNextLowerByValue(finalId);
465
+ }
466
+
467
+ function getPairOrNextLower<TRangeObject>(
468
+ firstLocal: LocalCompressedId,
469
+ finalRanges: FinalRanges<TRangeObject> | undefined,
470
+ localId: LocalCompressedId
471
+ ): readonly [LocalCompressedId, FinalRange<TRangeObject>] | undefined {
472
+ if (finalRanges === undefined) {
473
+ return undefined;
474
+ }
475
+ if (isSingleRange(finalRanges)) {
476
+ if (localId > firstLocal) {
477
+ return undefined;
478
+ }
479
+ return [firstLocal, finalRanges];
480
+ }
481
+ return finalRanges.getPairOrNextLower(localId);
482
+ }
483
+
484
+ function* entries<TRangeObject>(
485
+ firstLocal: LocalCompressedId,
486
+ finalRanges: FinalRanges<TRangeObject> | undefined
487
+ ): IterableIterator<readonly [LocalCompressedId, FinalRange<TRangeObject>]> {
488
+ if (finalRanges !== undefined) {
489
+ if (isSingleRange(finalRanges)) {
490
+ yield [firstLocal, finalRanges];
491
+ } else {
492
+ for (const range of finalRanges.entries()) {
493
+ yield range;
494
+ }
495
+ }
496
+ }
497
+ }
@@ -3,13 +3,13 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import type { Serializable } from '@fluidframework/datastore-definitions';
7
- import type { FinalCompressedId, LocalCompressedId, OpSpaceCompressedId, SessionId } from '../../Identifiers';
8
-
9
- /**
10
- * Extensible attribution info associated with a session.
11
- */
12
- export type AttributionInfo = Serializable;
6
+ import type {
7
+ AttributionId,
8
+ FinalCompressedId,
9
+ LocalCompressedId,
10
+ OpSpaceCompressedId,
11
+ SessionId,
12
+ } from '../../Identifiers';
13
13
 
14
14
  /**
15
15
  * A serialized ID allocation session for an `IdCompressor`.
@@ -21,9 +21,9 @@ export type SerializedSessionData = readonly [
21
21
  sessionId: SessionId,
22
22
 
23
23
  /**
24
- * The attribution info provided for the session
24
+ * Index into the serialized AttributionIDs array; points to the attribution ID provided for this session
25
25
  */
26
- attributionInfo?: AttributionInfo
26
+ attributionId?: number
27
27
  ];
28
28
 
29
29
  export type SerializedClusterOverrides = readonly [
@@ -75,11 +75,6 @@ export interface SerializedLocalState {
75
75
  */
76
76
  overrides?: SerializedLocalOverrides;
77
77
 
78
- /**
79
- * Boolean to track whether attribution has been sent with an ID range yet.
80
- */
81
- sentAttributionInfo: boolean;
82
-
83
78
  /**
84
79
  * The most recent local ID in a range returned by `takeNextCreationRange`.
85
80
  */
@@ -106,6 +101,8 @@ export interface SerializedIdCompressor extends VersionedSerializedIdCompressor
106
101
  readonly sessions: readonly SerializedSessionData[];
107
102
  /** All clusters in the compressor in the order they were created. */
108
103
  readonly clusters: readonly SerializedCluster[];
104
+ /** All attribution IDs for all sessions */
105
+ readonly attributionIds?: readonly AttributionId[];
109
106
  }
110
107
 
111
108
  /**
@@ -154,7 +151,7 @@ export interface SerializedIdCompressorWithOngoingSession extends SerializedIdCo
154
151
  export interface IdCreationRange {
155
152
  readonly sessionId: SessionId;
156
153
  readonly ids?: IdCreationRange.Ids;
157
- readonly attributionInfo?: AttributionInfo;
154
+ readonly attributionId?: AttributionId;
158
155
  }
159
156
 
160
157
  export type UnackedLocalId = LocalCompressedId & OpSpaceCompressedId;