@fluidframework/id-compressor 2.0.0-dev-rc.1.0.0.232845 → 2.0.0-dev-rc.2.0.0.245554

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 (263) hide show
  1. package/.eslintrc.cjs +5 -5
  2. package/{.mocharc.js → .mocharc.cjs} +1 -1
  3. package/CHANGELOG.md +8 -0
  4. package/{api-extractor-esm.json → api-extractor-cjs.json} +5 -1
  5. package/api-extractor-lint.json +1 -1
  6. package/api-extractor.json +1 -1
  7. package/api-report/id-compressor.api.md +2 -2
  8. package/dist/finalSpace.d.ts +2 -2
  9. package/dist/finalSpace.d.ts.map +1 -1
  10. package/dist/finalSpace.js +4 -4
  11. package/dist/finalSpace.js.map +1 -1
  12. package/dist/id-compressor-alpha.d.ts +114 -47
  13. package/dist/id-compressor-beta.d.ts +83 -75
  14. package/dist/id-compressor-public.d.ts +83 -75
  15. package/dist/id-compressor-untrimmed.d.ts +114 -47
  16. package/dist/idCompressor.d.ts +9 -3
  17. package/dist/idCompressor.d.ts.map +1 -1
  18. package/dist/idCompressor.js +76 -64
  19. package/dist/idCompressor.js.map +1 -1
  20. package/dist/identifiers.d.ts +1 -1
  21. package/dist/identifiers.d.ts.map +1 -1
  22. package/dist/identifiers.js.map +1 -1
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +8 -8
  26. package/dist/index.js.map +1 -1
  27. package/dist/package.json +3 -0
  28. package/dist/packageVersion.d.ts +1 -1
  29. package/dist/packageVersion.js +1 -1
  30. package/dist/packageVersion.js.map +1 -1
  31. package/dist/persistanceUtilities.d.ts +1 -1
  32. package/dist/persistanceUtilities.d.ts.map +1 -1
  33. package/dist/persistanceUtilities.js.map +1 -1
  34. package/dist/sessionSpaceNormalizer.d.ts +2 -2
  35. package/dist/sessionSpaceNormalizer.d.ts.map +1 -1
  36. package/dist/sessionSpaceNormalizer.js +4 -4
  37. package/dist/sessionSpaceNormalizer.js.map +1 -1
  38. package/dist/sessions.d.ts +2 -2
  39. package/dist/sessions.d.ts.map +1 -1
  40. package/dist/sessions.js +12 -12
  41. package/dist/sessions.js.map +1 -1
  42. package/dist/test/appendOnlySortedMap.perf.spec.d.ts +6 -0
  43. package/dist/test/appendOnlySortedMap.perf.spec.d.ts.map +1 -0
  44. package/dist/test/appendOnlySortedMap.perf.spec.js.map +1 -0
  45. package/dist/test/appendOnlySortedMap.spec.d.ts +6 -0
  46. package/dist/test/appendOnlySortedMap.spec.d.ts.map +1 -0
  47. package/dist/test/appendOnlySortedMap.spec.js.map +1 -0
  48. package/dist/test/idCompressor.perf.spec.d.ts +6 -0
  49. package/dist/test/idCompressor.perf.spec.d.ts.map +1 -0
  50. package/dist/test/idCompressor.perf.spec.js.map +1 -0
  51. package/dist/test/idCompressor.spec.d.ts +6 -0
  52. package/dist/test/idCompressor.spec.d.ts.map +1 -0
  53. package/dist/test/idCompressor.spec.js.map +1 -0
  54. package/dist/test/idCompressorTestUtilities.d.ts +245 -0
  55. package/dist/test/idCompressorTestUtilities.d.ts.map +1 -0
  56. package/dist/test/idCompressorTestUtilities.js +589 -0
  57. package/dist/test/idCompressorTestUtilities.js.map +1 -0
  58. package/dist/test/index.d.ts +9 -0
  59. package/dist/test/index.d.ts.map +1 -0
  60. package/dist/test/index.js +13 -0
  61. package/dist/test/index.js.map +1 -0
  62. package/dist/test/numericUuid.spec.d.ts +6 -0
  63. package/dist/test/numericUuid.spec.d.ts.map +1 -0
  64. package/dist/test/numericUuid.spec.js.map +1 -0
  65. package/dist/test/sessionSpaceNormalizer.spec.d.ts +6 -0
  66. package/dist/test/sessionSpaceNormalizer.spec.d.ts.map +1 -0
  67. package/dist/test/sessionSpaceNormalizer.spec.js.map +1 -0
  68. package/dist/test/snapshots/dirname.cjs +16 -0
  69. package/dist/test/snapshots/dirname.cjs.map +1 -0
  70. package/dist/test/snapshots/dirname.d.cts +6 -0
  71. package/dist/test/snapshots/dirname.d.cts.map +1 -0
  72. package/{lib/types/idCompressor.mjs → dist/test/snapshots/summary.spec.d.ts} +1 -1
  73. package/dist/test/snapshots/summary.spec.d.ts.map +1 -0
  74. package/dist/test/snapshots/summary.spec.js.map +1 -0
  75. package/dist/test/testCommon.d.ts +90 -0
  76. package/dist/test/testCommon.d.ts.map +1 -0
  77. package/dist/test/testCommon.js +90 -0
  78. package/dist/test/testCommon.js.map +1 -0
  79. package/dist/tsdoc-metadata.json +1 -1
  80. package/dist/types/idCompressor.d.ts +134 -67
  81. package/dist/types/idCompressor.d.ts.map +1 -1
  82. package/dist/types/idCompressor.js.map +1 -1
  83. package/dist/types/index.d.ts +3 -3
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/index.js.map +1 -1
  86. package/dist/types/persisted-types/0.0.1.d.ts +1 -1
  87. package/dist/types/persisted-types/0.0.1.d.ts.map +1 -1
  88. package/dist/types/persisted-types/0.0.1.js.map +1 -1
  89. package/dist/types/persisted-types/index.d.ts +1 -1
  90. package/dist/types/persisted-types/index.d.ts.map +1 -1
  91. package/dist/types/persisted-types/index.js.map +1 -1
  92. package/dist/utilities.d.ts +2 -2
  93. package/dist/utilities.d.ts.map +1 -1
  94. package/dist/utilities.js.map +1 -1
  95. package/lib/{appendOnlySortedMap.d.mts → appendOnlySortedMap.d.ts} +1 -1
  96. package/lib/appendOnlySortedMap.d.ts.map +1 -0
  97. package/lib/{appendOnlySortedMap.mjs → appendOnlySortedMap.js} +3 -1
  98. package/lib/appendOnlySortedMap.js.map +1 -0
  99. package/lib/{finalSpace.d.mts → finalSpace.d.ts} +3 -3
  100. package/lib/finalSpace.d.ts.map +1 -0
  101. package/lib/{finalSpace.mjs → finalSpace.js} +2 -2
  102. package/lib/finalSpace.js.map +1 -0
  103. package/lib/{id-compressor-alpha.d.mts → id-compressor-alpha.d.ts} +114 -47
  104. package/lib/id-compressor-beta.d.ts +179 -0
  105. package/lib/id-compressor-public.d.ts +179 -0
  106. package/lib/{id-compressor-untrimmed.d.mts → id-compressor-untrimmed.d.ts} +114 -47
  107. package/lib/{idCompressor.d.mts → idCompressor.d.ts} +10 -4
  108. package/lib/idCompressor.d.ts.map +1 -0
  109. package/lib/{idCompressor.mjs → idCompressor.js} +20 -8
  110. package/lib/idCompressor.js.map +1 -0
  111. package/lib/{identifiers.d.mts → identifiers.d.ts} +2 -2
  112. package/lib/identifiers.d.ts.map +1 -0
  113. package/lib/{identifiers.mjs → identifiers.js} +1 -1
  114. package/lib/identifiers.js.map +1 -0
  115. package/lib/{index.d.mts → index.d.ts} +7 -4
  116. package/lib/index.d.ts.map +1 -0
  117. package/lib/{index.mjs → index.js} +6 -3
  118. package/lib/index.js.map +1 -0
  119. package/lib/{packageVersion.d.mts → packageVersion.d.ts} +2 -2
  120. package/lib/packageVersion.d.ts.map +1 -0
  121. package/lib/{packageVersion.mjs → packageVersion.js} +2 -2
  122. package/lib/packageVersion.js.map +1 -0
  123. package/lib/{persistanceUtilities.d.mts → persistanceUtilities.d.ts} +2 -2
  124. package/lib/persistanceUtilities.d.ts.map +1 -0
  125. package/lib/{persistanceUtilities.mjs → persistanceUtilities.js} +1 -1
  126. package/lib/persistanceUtilities.js.map +1 -0
  127. package/lib/{sessionSpaceNormalizer.d.mts → sessionSpaceNormalizer.d.ts} +3 -3
  128. package/lib/sessionSpaceNormalizer.d.ts.map +1 -0
  129. package/lib/{sessionSpaceNormalizer.mjs → sessionSpaceNormalizer.js} +3 -3
  130. package/lib/sessionSpaceNormalizer.js.map +1 -0
  131. package/lib/{sessions.d.mts → sessions.d.ts} +3 -3
  132. package/lib/sessions.d.ts.map +1 -0
  133. package/lib/{sessions.mjs → sessions.js} +2 -2
  134. package/lib/sessions.js.map +1 -0
  135. package/lib/test/appendOnlySortedMap.perf.spec.d.ts +6 -0
  136. package/lib/test/appendOnlySortedMap.perf.spec.d.ts.map +1 -0
  137. package/lib/test/appendOnlySortedMap.perf.spec.js +49 -0
  138. package/lib/test/appendOnlySortedMap.perf.spec.js.map +1 -0
  139. package/lib/test/appendOnlySortedMap.spec.d.ts +6 -0
  140. package/lib/test/appendOnlySortedMap.spec.d.ts.map +1 -0
  141. package/lib/test/appendOnlySortedMap.spec.js +166 -0
  142. package/lib/test/appendOnlySortedMap.spec.js.map +1 -0
  143. package/lib/test/idCompressor.perf.spec.d.ts +6 -0
  144. package/lib/test/idCompressor.perf.spec.d.ts.map +1 -0
  145. package/lib/test/idCompressor.perf.spec.js +281 -0
  146. package/lib/test/idCompressor.perf.spec.js.map +1 -0
  147. package/lib/test/idCompressor.spec.d.ts +6 -0
  148. package/lib/test/idCompressor.spec.d.ts.map +1 -0
  149. package/lib/test/idCompressor.spec.js +953 -0
  150. package/lib/test/idCompressor.spec.js.map +1 -0
  151. package/lib/test/idCompressorTestUtilities.d.ts +245 -0
  152. package/lib/test/idCompressorTestUtilities.d.ts.map +1 -0
  153. package/lib/test/idCompressorTestUtilities.js +576 -0
  154. package/lib/test/idCompressorTestUtilities.js.map +1 -0
  155. package/lib/test/index.d.ts +9 -0
  156. package/lib/test/index.d.ts.map +1 -0
  157. package/lib/test/index.js +9 -0
  158. package/lib/test/index.js.map +1 -0
  159. package/lib/test/numericUuid.spec.d.ts +6 -0
  160. package/lib/test/numericUuid.spec.d.ts.map +1 -0
  161. package/lib/test/numericUuid.spec.js +74 -0
  162. package/lib/test/numericUuid.spec.js.map +1 -0
  163. package/lib/test/sessionSpaceNormalizer.spec.d.ts +6 -0
  164. package/lib/test/sessionSpaceNormalizer.spec.d.ts.map +1 -0
  165. package/lib/test/sessionSpaceNormalizer.spec.js +43 -0
  166. package/lib/test/sessionSpaceNormalizer.spec.js.map +1 -0
  167. package/lib/test/snapshots/dirname.cjs +16 -0
  168. package/lib/test/snapshots/dirname.cjs.map +1 -0
  169. package/lib/test/snapshots/dirname.d.cts +6 -0
  170. package/lib/test/snapshots/dirname.d.cts.map +1 -0
  171. package/lib/test/snapshots/summary.spec.d.ts +6 -0
  172. package/lib/test/snapshots/summary.spec.d.ts.map +1 -0
  173. package/lib/test/snapshots/summary.spec.js +127 -0
  174. package/lib/test/snapshots/summary.spec.js.map +1 -0
  175. package/lib/test/testCommon.d.ts +90 -0
  176. package/lib/test/testCommon.d.ts.map +1 -0
  177. package/lib/test/testCommon.js +80 -0
  178. package/lib/test/testCommon.js.map +1 -0
  179. package/lib/types/{idCompressor.d.mts → idCompressor.d.ts} +135 -68
  180. package/lib/types/idCompressor.d.ts.map +1 -0
  181. package/lib/types/{identifiers.mjs → idCompressor.js} +1 -1
  182. package/lib/types/idCompressor.js.map +1 -0
  183. package/lib/types/{identifiers.d.mts → identifiers.d.ts} +1 -1
  184. package/lib/types/identifiers.d.ts.map +1 -0
  185. package/lib/types/identifiers.js +6 -0
  186. package/lib/types/identifiers.js.map +1 -0
  187. package/lib/types/{index.d.mts → index.d.ts} +4 -4
  188. package/lib/types/index.d.ts.map +1 -0
  189. package/lib/types/{persisted-types/0.0.1.mjs → index.js} +1 -1
  190. package/lib/types/index.js.map +1 -0
  191. package/lib/types/persisted-types/{0.0.1.d.mts → 0.0.1.d.ts} +2 -2
  192. package/lib/types/persisted-types/0.0.1.d.ts.map +1 -0
  193. package/lib/types/persisted-types/{index.mjs → 0.0.1.js} +1 -1
  194. package/lib/types/persisted-types/0.0.1.js.map +1 -0
  195. package/lib/types/persisted-types/{index.d.mts → index.d.ts} +2 -2
  196. package/lib/types/persisted-types/index.d.ts.map +1 -0
  197. package/lib/types/{index.mjs → persisted-types/index.js} +1 -1
  198. package/lib/types/persisted-types/index.js.map +1 -0
  199. package/lib/{utilities.d.mts → utilities.d.ts} +3 -3
  200. package/lib/utilities.d.ts.map +1 -0
  201. package/lib/{utilities.mjs → utilities.js} +2 -1
  202. package/lib/utilities.js.map +1 -0
  203. package/package.json +88 -22
  204. package/src/finalSpace.ts +2 -2
  205. package/src/idCompressor.ts +22 -8
  206. package/src/identifiers.ts +1 -1
  207. package/src/index.ts +3 -3
  208. package/src/packageVersion.ts +1 -1
  209. package/src/persistanceUtilities.ts +1 -1
  210. package/src/sessionSpaceNormalizer.ts +3 -3
  211. package/src/sessions.ts +3 -3
  212. package/src/test/idCompressorTestUtilities.ts +921 -0
  213. package/src/test/index.ts +10 -0
  214. package/src/test/snapshots/dirname.cts +13 -0
  215. package/src/test/snapshots/files/compressor-with-finalized-range-from-one-client +1 -0
  216. package/src/test/snapshots/files/compressor-with-finalized-range-from-one-client-client2 +1 -0
  217. package/src/test/snapshots/files/compressors-with-finalized-ranges-from-two-clients +1 -0
  218. package/src/test/snapshots/files/compressors-with-finalized-ranges-from-two-clients-client2 +1 -0
  219. package/src/test/snapshots/files/empty-compressor-summary +1 -0
  220. package/src/test/snapshots/files/expansion-semantics +1 -0
  221. package/src/test/snapshots/files/expansion-semantics-client2 +1 -0
  222. package/src/test/testCommon.ts +143 -0
  223. package/src/test/tsconfig.cjs.json +12 -0
  224. package/src/test/tsconfig.json +20 -0
  225. package/src/types/idCompressor.ts +147 -72
  226. package/src/types/index.ts +8 -3
  227. package/src/types/persisted-types/0.0.1.ts +1 -1
  228. package/src/types/persisted-types/index.ts +1 -1
  229. package/src/utilities.ts +2 -2
  230. package/tsconfig.cjs.json +7 -0
  231. package/tsconfig.json +2 -5
  232. package/lib/appendOnlySortedMap.d.mts.map +0 -1
  233. package/lib/appendOnlySortedMap.mjs.map +0 -1
  234. package/lib/finalSpace.d.mts.map +0 -1
  235. package/lib/finalSpace.mjs.map +0 -1
  236. package/lib/id-compressor-beta.d.mts +0 -171
  237. package/lib/id-compressor-public.d.mts +0 -171
  238. package/lib/idCompressor.d.mts.map +0 -1
  239. package/lib/idCompressor.mjs.map +0 -1
  240. package/lib/identifiers.d.mts.map +0 -1
  241. package/lib/identifiers.mjs.map +0 -1
  242. package/lib/index.d.mts.map +0 -1
  243. package/lib/index.mjs.map +0 -1
  244. package/lib/packageVersion.d.mts.map +0 -1
  245. package/lib/packageVersion.mjs.map +0 -1
  246. package/lib/persistanceUtilities.d.mts.map +0 -1
  247. package/lib/persistanceUtilities.mjs.map +0 -1
  248. package/lib/sessionSpaceNormalizer.d.mts.map +0 -1
  249. package/lib/sessionSpaceNormalizer.mjs.map +0 -1
  250. package/lib/sessions.d.mts.map +0 -1
  251. package/lib/sessions.mjs.map +0 -1
  252. package/lib/types/idCompressor.d.mts.map +0 -1
  253. package/lib/types/idCompressor.mjs.map +0 -1
  254. package/lib/types/identifiers.d.mts.map +0 -1
  255. package/lib/types/identifiers.mjs.map +0 -1
  256. package/lib/types/index.d.mts.map +0 -1
  257. package/lib/types/index.mjs.map +0 -1
  258. package/lib/types/persisted-types/0.0.1.d.mts.map +0 -1
  259. package/lib/types/persisted-types/0.0.1.mjs.map +0 -1
  260. package/lib/types/persisted-types/index.d.mts.map +0 -1
  261. package/lib/types/persisted-types/index.mjs.map +0 -1
  262. package/lib/utilities.d.mts.map +0 -1
  263. package/lib/utilities.mjs.map +0 -1
@@ -0,0 +1,589 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createAlwaysFinalizedIdCompressor = exports.generateCompressedIds = exports.performFuzzActions = exports.makeOpGenerator = exports.mergeArrayMaps = exports.expectSerializes = exports.roundtrip = exports.IdCompressorTestNetwork = exports.sessionIds = exports.buildHugeCompressor = exports.CompressorFactory = exports.DestinationClient = exports.OriginatingClient = exports.OutsideClient = exports.MetaClient = exports.Client = void 0;
8
+ const assert_1 = require("assert");
9
+ const stochastic_test_utils_1 = require("@fluid-private/stochastic-test-utils");
10
+ const index_js_1 = require("..//index.js");
11
+ const idCompressor_js_1 = require("../idCompressor.js");
12
+ const utilities_js_1 = require("../utilities.js");
13
+ const testCommon_js_1 = require("./testCommon.js");
14
+ /** Identifies a compressor in a network */
15
+ var Client;
16
+ (function (Client) {
17
+ Client["Client1"] = "Client1";
18
+ Client["Client2"] = "Client2";
19
+ Client["Client3"] = "Client3";
20
+ })(Client || (exports.Client = Client = {}));
21
+ /** Identifies categories of compressors */
22
+ var MetaClient;
23
+ (function (MetaClient) {
24
+ MetaClient["All"] = "All";
25
+ })(MetaClient || (exports.MetaClient = MetaClient = {}));
26
+ /** Identifies a compressor inside the network but outside the three specially tracked clients. */
27
+ var OutsideClient;
28
+ (function (OutsideClient) {
29
+ OutsideClient["Remote"] = "Remote";
30
+ })(OutsideClient || (exports.OutsideClient = OutsideClient = {}));
31
+ exports.OriginatingClient = { ...Client, ...OutsideClient };
32
+ exports.DestinationClient = { ...Client, ...MetaClient };
33
+ // eslint-disable-next-line @typescript-eslint/no-extraneous-class
34
+ class CompressorFactory {
35
+ /**
36
+ * Creates a new compressor with the supplied cluster capacity.
37
+ */
38
+ static createCompressor(client, clusterCapacity = 5, logger) {
39
+ return CompressorFactory.createCompressorWithSession(exports.sessionIds.get(client), clusterCapacity, logger);
40
+ }
41
+ /**
42
+ * Creates a new compressor with the supplied cluster capacity.
43
+ */
44
+ static createCompressorWithSession(sessionId, clusterCapacity = 5, logger) {
45
+ const compressor = (0, index_js_1.createIdCompressor)(sessionId, logger);
46
+ // eslint-disable-next-line @typescript-eslint/dot-notation
47
+ compressor["nextRequestedClusterSize"] = clusterCapacity;
48
+ return compressor;
49
+ }
50
+ }
51
+ exports.CompressorFactory = CompressorFactory;
52
+ /**
53
+ * Utility for building a huge compressor.
54
+ * Build via the compressor factory.
55
+ */
56
+ function buildHugeCompressor(numSessions = 10000, capacity = 10, numClustersPerSession = 3) {
57
+ const compressor = CompressorFactory.createCompressorWithSession((0, utilities_js_1.createSessionId)(), capacity);
58
+ const sessions = [];
59
+ for (let i = 0; i < numSessions; i++) {
60
+ sessions.push((0, utilities_js_1.createSessionId)());
61
+ }
62
+ for (let i = 0; i < numSessions * numClustersPerSession; i++) {
63
+ const sessionId = sessions[i % numSessions];
64
+ if (Math.random() > 0.1) {
65
+ for (let j = 0; j < Math.round(capacity / 2); j++) {
66
+ compressor.generateCompressedId();
67
+ }
68
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
69
+ }
70
+ compressor.finalizeCreationRange({
71
+ sessionId,
72
+ ids: {
73
+ firstGenCount: Math.floor(i / numSessions) * capacity + 1,
74
+ count: capacity,
75
+ requestedClusterSize: capacity,
76
+ },
77
+ });
78
+ }
79
+ return compressor;
80
+ }
81
+ exports.buildHugeCompressor = buildHugeCompressor;
82
+ function makeSessionIds() {
83
+ const stableIds = new Map();
84
+ const clients = Object.values(Client);
85
+ for (let i = 0; i < clients.length; i++) {
86
+ // Place session uuids roughly in the middle of uuid space to increase odds of encountering interesting
87
+ // orderings in sorted collections
88
+ const sessionId = (0, utilities_js_1.assertIsSessionId)(`88888888-8888-4888-b${i}88-888888888888`);
89
+ stableIds.set(clients[i], sessionId);
90
+ }
91
+ return stableIds;
92
+ }
93
+ /**
94
+ * An array of session ID strings corresponding to all non-local `Client` entries.
95
+ */
96
+ exports.sessionIds = makeSessionIds();
97
+ /**
98
+ * Simulates a network of ID compressors.
99
+ * Not suitable for performance testing.
100
+ */
101
+ class IdCompressorTestNetwork {
102
+ constructor(initialClusterSize = 5) {
103
+ this.initialClusterSize = initialClusterSize;
104
+ /** The log of operations seen by the server so far. Append-only. */
105
+ this.serverOperations = [];
106
+ const compressors = new Map();
107
+ const clientProgress = new Map();
108
+ const clientIds = new Map();
109
+ const clientSequencedIds = new Map();
110
+ for (const client of Object.values(Client)) {
111
+ const compressor = CompressorFactory.createCompressor(client, initialClusterSize);
112
+ compressors.set(client, compressor);
113
+ clientProgress.set(client, 0);
114
+ clientIds.set(client, []);
115
+ clientSequencedIds.set(client, []);
116
+ }
117
+ this.compressors = compressors;
118
+ this.clientProgress = clientProgress;
119
+ this.idLogs = clientIds;
120
+ this.sequencedIdLogs = clientSequencedIds;
121
+ }
122
+ /**
123
+ * Returns the number of undelivered operations for the given client that are in flight in the network.
124
+ */
125
+ getPendingOperations(destination) {
126
+ return this.serverOperations.length - this.clientProgress.get(destination);
127
+ }
128
+ /**
129
+ * Returns an immutable handle to a compressor in the network.
130
+ */
131
+ getCompressor(client) {
132
+ const compressors = this.compressors;
133
+ const handler = {
134
+ get(_, property) {
135
+ return compressors.get(client)[property];
136
+ },
137
+ set(_, property, value) {
138
+ compressors.get(client)[property] = value;
139
+ return true;
140
+ },
141
+ };
142
+ return new Proxy({}, handler);
143
+ }
144
+ /**
145
+ * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and
146
+ * should only be used if the network will not be used again.
147
+ */
148
+ getCompressorUnsafe(client) {
149
+ return this.getCompressor(client);
150
+ }
151
+ /**
152
+ * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and
153
+ * should only be used if the network will not be used again. Additionally, the returned compressor will be invalidated/unusable
154
+ * if any network operations cause it to be regenerated (serialization/deserialization, etc.).
155
+ */
156
+ getCompressorUnsafeNoProxy(client) {
157
+ return this.compressors.get(client);
158
+ }
159
+ /**
160
+ * Returns data for all IDs created and received by this client, including ack's of their own (i.e. their own IDs will appear twice)
161
+ */
162
+ getIdLog(client) {
163
+ return this.idLogs.get(client);
164
+ }
165
+ /**
166
+ * Returns data for all IDs received by this client, including ack's of their own.
167
+ */
168
+ getSequencedIdLog(client) {
169
+ return this.sequencedIdLogs.get(client);
170
+ }
171
+ /**
172
+ * Get all compressors for the given destination
173
+ */
174
+ getTargetCompressors(clientTo) {
175
+ return clientTo === MetaClient.All
176
+ ? [...this.compressors.entries()]
177
+ : [[clientTo, this.getCompressor(clientTo)]];
178
+ }
179
+ /**
180
+ * Changes the capacity request amount for a client. It will take effect immediately.
181
+ */
182
+ changeCapacity(client, newClusterCapacity) {
183
+ // eslint-disable-next-line @typescript-eslint/dot-notation
184
+ this.compressors.get(client)["nextRequestedClusterSize"] = newClusterCapacity;
185
+ }
186
+ addNewId(client, id, originatingClient, sessionIdFrom, isSequenced) {
187
+ const idData = {
188
+ id,
189
+ originatingClient,
190
+ sessionId: sessionIdFrom,
191
+ isSequenced,
192
+ };
193
+ const clientIds = this.idLogs.get(client);
194
+ clientIds.push(idData);
195
+ if (isSequenced) {
196
+ const sequencedIds = this.sequencedIdLogs.get(client);
197
+ sequencedIds.push(idData);
198
+ }
199
+ }
200
+ /**
201
+ * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action.
202
+ * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called.
203
+ */
204
+ allocateAndSendIds(clientFrom, numIds) {
205
+ return this.allocateAndSendIdsFromRemoteClient(clientFrom, exports.sessionIds.get(clientFrom), numIds);
206
+ }
207
+ /**
208
+ * Same contract as `allocateAndSendIds`, but the originating client will be a client with the supplied sessionId.
209
+ */
210
+ allocateAndSendIdsFromRemoteClient(clientFrom, sessionIdFrom, numIds) {
211
+ (0, assert_1.strict)(numIds > 0, "Must allocate a non-zero number of IDs");
212
+ if (clientFrom === exports.OriginatingClient.Remote) {
213
+ const range = {
214
+ sessionId: sessionIdFrom,
215
+ ids: {
216
+ firstGenCount: 1,
217
+ count: numIds,
218
+ requestedClusterSize: this.getCompressor(Client.Client1)[
219
+ // eslint-disable-next-line @typescript-eslint/dot-notation
220
+ "nextRequestedClusterSize"],
221
+ },
222
+ };
223
+ const opSpaceIds = [];
224
+ for (let i = 0; i < numIds; i++) {
225
+ opSpaceIds.push(-(i + 1));
226
+ }
227
+ this.serverOperations.push([range, opSpaceIds, clientFrom, sessionIdFrom]);
228
+ return opSpaceIds;
229
+ }
230
+ else {
231
+ (0, assert_1.strict)(sessionIdFrom === exports.sessionIds.get(clientFrom));
232
+ const compressor = this.compressors.get(clientFrom);
233
+ const sessionSpaceIds = generateCompressedIds(compressor, numIds);
234
+ for (let i = 0; i < numIds; i++) {
235
+ this.addNewId(clientFrom, sessionSpaceIds[i], clientFrom, sessionIdFrom, false);
236
+ }
237
+ const opSpaceIds = sessionSpaceIds.map((id) => compressor.normalizeToOpSpace(id));
238
+ const creationRange = compressor.takeNextCreationRange();
239
+ this.serverOperations.push([creationRange, opSpaceIds, clientFrom, sessionIdFrom]);
240
+ return opSpaceIds;
241
+ }
242
+ }
243
+ /**
244
+ * Delivers all undelivered ID ranges from the server to the target clients.
245
+ */
246
+ deliverOperations(clientTakingDelivery, opsToDeliver) {
247
+ let opIndexBound;
248
+ if (clientTakingDelivery === exports.DestinationClient.All) {
249
+ (0, assert_1.strict)(opsToDeliver === undefined);
250
+ opIndexBound = this.serverOperations.length;
251
+ }
252
+ else {
253
+ opIndexBound =
254
+ opsToDeliver !== undefined
255
+ ? this.clientProgress.get(clientTakingDelivery) + opsToDeliver
256
+ : this.serverOperations.length;
257
+ }
258
+ for (const [clientTo, compressorTo] of this.getTargetCompressors(clientTakingDelivery)) {
259
+ for (let i = this.clientProgress.get(clientTo); i < opIndexBound; i++) {
260
+ const [range, opSpaceIds, clientFrom, sessionIdFrom] = this.serverOperations[i];
261
+ compressorTo.finalizeCreationRange(range);
262
+ const ids = range.ids;
263
+ if (ids !== undefined) {
264
+ for (const id of opSpaceIds) {
265
+ const sessionSpaceId = compressorTo.normalizeToSessionSpace(id, range.sessionId);
266
+ this.addNewId(clientTo, sessionSpaceId, clientFrom, sessionIdFrom, true);
267
+ }
268
+ }
269
+ }
270
+ this.clientProgress.set(clientTo, opIndexBound);
271
+ }
272
+ }
273
+ /**
274
+ * Simulate a client disconnecting (and serializing), then reconnecting (and deserializing)
275
+ */
276
+ goOfflineThenResume(client) {
277
+ const compressor = this.compressors.get(client);
278
+ const [_, resumedCompressor] = roundtrip(compressor, true);
279
+ this.compressors.set(client, resumedCompressor);
280
+ }
281
+ /**
282
+ * Ensure general validity of the network state. Useful for calling periodically or at the end of test scenarios.
283
+ */
284
+ assertNetworkState() {
285
+ const sequencedLogs = Object.values(Client).map((client) => [this.compressors.get(client), this.getSequencedIdLog(client)]);
286
+ // First, ensure all clients each generated a unique ID for each of their own calls to generate.
287
+ for (const [compressor, ids] of sequencedLogs) {
288
+ const allUuids = new Set();
289
+ for (const idData of ids) {
290
+ const uuid = compressor.decompress(idData.id);
291
+ assert_1.strict.strictEqual(!allUuids.has(uuid), true, "Duplicate UUID generated.");
292
+ allUuids.add(uuid);
293
+ }
294
+ }
295
+ const maxLogLength = sequencedLogs
296
+ .map(([_, data]) => data.length)
297
+ .reduce((p, n) => Math.max(p, n));
298
+ function getNextLogWithEntryAt(logsIndex, entryIndex) {
299
+ for (let i = logsIndex; i < sequencedLogs.length; i++) {
300
+ const log = sequencedLogs[i];
301
+ if (log[1].length > entryIndex) {
302
+ return i;
303
+ }
304
+ }
305
+ return undefined;
306
+ }
307
+ const uuids = new Set();
308
+ const finalIds = new Set();
309
+ const idIndicesAggregator = new Map();
310
+ function* getIdLogEntries(columnIndex) {
311
+ let current = getNextLogWithEntryAt(0, columnIndex);
312
+ while (current !== undefined) {
313
+ const next = getNextLogWithEntryAt(current + 1, columnIndex);
314
+ const [compressor, log] = sequencedLogs[current];
315
+ if (next === undefined) {
316
+ yield [[compressor, log[columnIndex]]];
317
+ }
318
+ else {
319
+ const [compressorNext, logNext] = sequencedLogs[next];
320
+ yield [
321
+ [compressor, log[columnIndex]],
322
+ [compressorNext, logNext[columnIndex]],
323
+ ];
324
+ }
325
+ current = next;
326
+ }
327
+ }
328
+ for (let i = 0; i < maxLogLength; i++) {
329
+ let idCreatorCount = 0;
330
+ let originatingSession;
331
+ for (const [current, next] of getIdLogEntries(i)) {
332
+ const [compressorA, idDataA] = current;
333
+ const sessionSpaceIdA = idDataA.id;
334
+ const idIndex = (0, testCommon_js_1.getOrCreate)(idIndicesAggregator, idDataA.sessionId, () => 0);
335
+ originatingSession ?? (originatingSession = idDataA.sessionId);
336
+ (0, assert_1.strict)(idDataA.sessionId === originatingSession, "Test infra gave wrong originating client to TestIdData");
337
+ // Only one client should have this ID as local in its session space, as only one client could have created this ID
338
+ if ((0, testCommon_js_1.isLocalId)(sessionSpaceIdA)) {
339
+ if (originatingSession !== exports.OriginatingClient.Remote) {
340
+ assert_1.strict.strictEqual(idDataA.sessionId, this.compressors.get(idDataA.originatingClient)
341
+ .localSessionId);
342
+ }
343
+ idCreatorCount++;
344
+ }
345
+ const uuidASessionSpace = compressorA.decompress(sessionSpaceIdA);
346
+ assert_1.strict.strictEqual(uuidASessionSpace, (0, testCommon_js_1.incrementStableId)(idDataA.sessionId, idIndex));
347
+ assert_1.strict.strictEqual(compressorA.recompress(uuidASessionSpace), sessionSpaceIdA);
348
+ uuids.add(uuidASessionSpace);
349
+ const opSpaceIdA = compressorA.normalizeToOpSpace(sessionSpaceIdA);
350
+ if (!(0, testCommon_js_1.isFinalId)(opSpaceIdA)) {
351
+ (0, testCommon_js_1.fail)("IDs should have been finalized.");
352
+ }
353
+ const reNormalizedIdA = compressorA.normalizeToSessionSpace(opSpaceIdA, compressorA.localSessionId);
354
+ assert_1.strict.strictEqual(reNormalizedIdA, sessionSpaceIdA);
355
+ finalIds.add(opSpaceIdA);
356
+ const uuidAOpSpace = compressorA.decompress(reNormalizedIdA);
357
+ assert_1.strict.strictEqual(uuidASessionSpace, uuidAOpSpace);
358
+ if (next !== undefined) {
359
+ const [compressorB, idDataB] = next;
360
+ const sessionSpaceIdB = idDataB.id;
361
+ const uuidBSessionSpace = compressorB.decompress(sessionSpaceIdB);
362
+ assert_1.strict.strictEqual(uuidASessionSpace, uuidBSessionSpace);
363
+ const opSpaceIdB = compressorB.normalizeToOpSpace(sessionSpaceIdB);
364
+ if (opSpaceIdA !== opSpaceIdB) {
365
+ compressorB.normalizeToOpSpace(sessionSpaceIdB);
366
+ compressorA.normalizeToOpSpace(sessionSpaceIdA);
367
+ }
368
+ assert_1.strict.strictEqual(opSpaceIdA, opSpaceIdB);
369
+ if (!(0, testCommon_js_1.isFinalId)(opSpaceIdB)) {
370
+ (0, testCommon_js_1.fail)("IDs should have been finalized.");
371
+ }
372
+ const uuidBOpSpace = compressorB.decompress(opSpaceIdB);
373
+ assert_1.strict.strictEqual(uuidAOpSpace, uuidBOpSpace);
374
+ }
375
+ }
376
+ (0, assert_1.strict)(idCreatorCount <= 1, "Only one client can create an ID.");
377
+ assert_1.strict.strictEqual(uuids.size, finalIds.size);
378
+ (0, assert_1.strict)(originatingSession !== undefined, "Expected originating client to be defined");
379
+ idIndicesAggregator.set(originatingSession,
380
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
381
+ (idIndicesAggregator.get(originatingSession) ??
382
+ (0, testCommon_js_1.fail)("Expected pre-existing index for originating client")) + 1);
383
+ }
384
+ for (const [compressor] of sequencedLogs) {
385
+ expectSerializes(compressor);
386
+ }
387
+ }
388
+ }
389
+ exports.IdCompressorTestNetwork = IdCompressorTestNetwork;
390
+ function roundtrip(compressor, withSession) {
391
+ if (withSession) {
392
+ const serialized = compressor.serialize(withSession);
393
+ return [serialized, idCompressor_js_1.IdCompressor.deserialize(serialized)];
394
+ }
395
+ const nonLocalSerialized = compressor.serialize(withSession);
396
+ return [nonLocalSerialized, idCompressor_js_1.IdCompressor.deserialize(nonLocalSerialized, (0, utilities_js_1.createSessionId)())];
397
+ }
398
+ exports.roundtrip = roundtrip;
399
+ /**
400
+ * Asserts that the supplied compressor correctly roundtrips through serialization/deserialization.
401
+ */
402
+ function expectSerializes(compressor) {
403
+ function expectSerializesWithSession(withSession) {
404
+ let serialized;
405
+ let deserialized;
406
+ if (withSession) {
407
+ [serialized, deserialized] = roundtrip(compressor, true);
408
+ }
409
+ else {
410
+ [serialized, deserialized] = roundtrip(compressor, false);
411
+ }
412
+ assert_1.strict.strictEqual(compressor.equals(deserialized, withSession), true);
413
+ return serialized;
414
+ }
415
+ return [
416
+ expectSerializesWithSession(false),
417
+ expectSerializesWithSession(true),
418
+ ];
419
+ }
420
+ exports.expectSerializes = expectSerializes;
421
+ /**
422
+ * Merges 'from' into 'to', and returns 'to'.
423
+ */
424
+ function mergeArrayMaps(to, from) {
425
+ for (const [key, value] of from.entries()) {
426
+ const entry = to.get(key);
427
+ if (entry !== undefined) {
428
+ entry.push(...value);
429
+ }
430
+ else {
431
+ to.set(key, [...value]);
432
+ }
433
+ }
434
+ return to;
435
+ }
436
+ exports.mergeArrayMaps = mergeArrayMaps;
437
+ const defaultOptions = {
438
+ maxClusterSize: 25,
439
+ validateInterval: 200,
440
+ outsideAllocationFraction: 0.1,
441
+ };
442
+ function makeOpGenerator(options) {
443
+ const { maxClusterSize, validateInterval, outsideAllocationFraction } = {
444
+ ...defaultOptions,
445
+ ...options,
446
+ };
447
+ (0, assert_1.strict)(outsideAllocationFraction >= 0 && outsideAllocationFraction <= 1);
448
+ function allocateIdsGenerator({ activeClients, clusterSize, random, }) {
449
+ const client = random.pick(activeClients);
450
+ const maxIdsPerUsage = clusterSize * 2;
451
+ const numIds = Math.floor(random.real(0, 1) ** 3 * maxIdsPerUsage) + 1;
452
+ return {
453
+ type: "allocateIds",
454
+ client,
455
+ numIds,
456
+ };
457
+ }
458
+ function allocateOutsideIdsGenerator({ clusterSize, random, }) {
459
+ const maxIdsPerUsage = clusterSize * 2;
460
+ const numIds = Math.floor(random.real(0, 1) ** 3 * maxIdsPerUsage) + 1;
461
+ return {
462
+ type: "allocateOutsideIds",
463
+ sessionId: (0, utilities_js_1.createSessionId)(),
464
+ numIds,
465
+ };
466
+ }
467
+ function changeCapacityGenerator({ random, activeClients }) {
468
+ return {
469
+ type: "changeCapacity",
470
+ client: random.pick(activeClients),
471
+ newSize: Math.min(Math.floor(random.real(0, 1) ** 2 * maxClusterSize) + 1, maxClusterSize),
472
+ };
473
+ }
474
+ function deliverAllOperationsGenerator() {
475
+ return {
476
+ type: "deliverAllOperations",
477
+ };
478
+ }
479
+ function deliverSomeOperationsGenerator({ random, selectableClients, network, }) {
480
+ const pendingClients = selectableClients.filter((c) => network.getPendingOperations(c) > 0);
481
+ if (pendingClients.length === 0) {
482
+ return {
483
+ type: "deliverSomeOperations",
484
+ client: random.pick(selectableClients),
485
+ count: 0,
486
+ };
487
+ }
488
+ const client = random.pick(pendingClients);
489
+ return {
490
+ type: "deliverSomeOperations",
491
+ client,
492
+ count: random.integer(1, network.getPendingOperations(client)),
493
+ };
494
+ }
495
+ function reconnectGenerator({ activeClients, random }) {
496
+ return { type: "reconnect", client: random.pick(activeClients) };
497
+ }
498
+ const allocationWeight = 16;
499
+ return (0, stochastic_test_utils_1.interleave)((0, stochastic_test_utils_1.createWeightedGenerator)([
500
+ [changeCapacityGenerator, 1],
501
+ [allocateIdsGenerator, Math.round(allocationWeight * (1 - outsideAllocationFraction))],
502
+ [allocateOutsideIdsGenerator, Math.round(allocationWeight * outsideAllocationFraction)],
503
+ [deliverAllOperationsGenerator, 2],
504
+ [deliverSomeOperationsGenerator, 6],
505
+ [reconnectGenerator, 1],
506
+ ]), (0, stochastic_test_utils_1.take)(1, (0, stochastic_test_utils_1.repeat)({ type: "validate" })), validateInterval);
507
+ }
508
+ exports.makeOpGenerator = makeOpGenerator;
509
+ /**
510
+ * Performs random actions on a test network.
511
+ * @param generator - the generator used to provide operations
512
+ * @param network - the test network to test
513
+ * @param seed - the seed for the random generation of the fuzz actions
514
+ * @param observerClient - if provided, this client will never generate local ids
515
+ * @param synchronizeAtEnd - if provided, all client will have all operations delivered from the server at the end of the test
516
+ * @param validator - if provided, this callback will be invoked periodically during the fuzz test.
517
+ */
518
+ function performFuzzActions(generator, network, seed, observerClient, synchronizeAtEnd = true, validator, saveInfo) {
519
+ const random = (0, stochastic_test_utils_1.makeRandom)(seed);
520
+ const selectableClients = network
521
+ .getTargetCompressors(MetaClient.All)
522
+ .map(([client]) => client);
523
+ const initialState = {
524
+ random,
525
+ network,
526
+ activeClients: selectableClients.filter((c) => c !== observerClient),
527
+ selectableClients,
528
+ clusterSize: network.initialClusterSize,
529
+ };
530
+ (0, stochastic_test_utils_1.performFuzzActions)(generator, {
531
+ allocateIds: (state, { client, numIds }) => {
532
+ network.allocateAndSendIdsFromRemoteClient(client, exports.sessionIds.get(client), numIds);
533
+ return state;
534
+ },
535
+ allocateOutsideIds: (state, { sessionId, numIds }) => {
536
+ network.allocateAndSendIdsFromRemoteClient(exports.OriginatingClient.Remote, sessionId, numIds);
537
+ return state;
538
+ },
539
+ changeCapacity: (state, op) => {
540
+ network.changeCapacity(op.client, op.newSize);
541
+ return { ...state, clusterSize: op.newSize };
542
+ },
543
+ deliverSomeOperations: (state, op) => {
544
+ network.deliverOperations(op.client, op.count);
545
+ return state;
546
+ },
547
+ deliverAllOperations: (state) => {
548
+ network.deliverOperations(exports.DestinationClient.All);
549
+ return state;
550
+ },
551
+ reconnect: (state, { client }) => {
552
+ network.goOfflineThenResume(client);
553
+ return state;
554
+ },
555
+ validate: (state) => {
556
+ network.deliverOperations(exports.DestinationClient.All);
557
+ validator?.(network);
558
+ return state;
559
+ },
560
+ }, initialState, saveInfo);
561
+ if (synchronizeAtEnd) {
562
+ network.deliverOperations(exports.DestinationClient.All);
563
+ validator?.(network);
564
+ }
565
+ }
566
+ exports.performFuzzActions = performFuzzActions;
567
+ /**
568
+ * Helper to generate a fixed number of IDs.
569
+ */
570
+ function generateCompressedIds(compressor, count) {
571
+ const ids = [];
572
+ for (let i = 0; i < count; i++) {
573
+ ids.push(compressor.generateCompressedId());
574
+ }
575
+ return ids;
576
+ }
577
+ exports.generateCompressedIds = generateCompressedIds;
578
+ function createAlwaysFinalizedIdCompressor(sessionIdOrLogger, loggerOrUndefined) {
579
+ const compressor = sessionIdOrLogger === undefined
580
+ ? (0, index_js_1.createIdCompressor)()
581
+ : typeof sessionIdOrLogger === "string"
582
+ ? (0, index_js_1.createIdCompressor)(sessionIdOrLogger, loggerOrUndefined)
583
+ : (0, index_js_1.createIdCompressor)(sessionIdOrLogger);
584
+ // Permanently put the compressor in a ghost session
585
+ compressor.startGhostSession((0, utilities_js_1.createSessionId)());
586
+ return compressor;
587
+ }
588
+ exports.createAlwaysFinalizedIdCompressor = createAlwaysFinalizedIdCompressor;
589
+ //# sourceMappingURL=idCompressorTestUtilities.js.map