@fluidframework/id-compressor 2.32.0 → 2.33.0

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 (40) hide show
  1. package/.eslintrc.cjs +5 -2
  2. package/CHANGELOG.md +4 -0
  3. package/api-report/id-compressor.legacy.alpha.api.md +10 -10
  4. package/dist/packageVersion.d.ts +1 -1
  5. package/dist/packageVersion.js +1 -1
  6. package/dist/packageVersion.js.map +1 -1
  7. package/dist/test/appendOnlySortedMap.spec.js.map +1 -1
  8. package/dist/test/idCompressor.spec.js.map +1 -1
  9. package/dist/test/idCompressorTestUtilities.d.ts +14 -0
  10. package/dist/test/idCompressorTestUtilities.d.ts.map +1 -1
  11. package/dist/test/idCompressorTestUtilities.js +49 -23
  12. package/dist/test/idCompressorTestUtilities.js.map +1 -1
  13. package/dist/test/index.d.ts +1 -1
  14. package/dist/test/index.d.ts.map +1 -1
  15. package/dist/test/index.js +2 -1
  16. package/dist/test/index.js.map +1 -1
  17. package/dist/test/numericUuid.spec.js.map +1 -1
  18. package/dist/test/sessionSpaceNormalizer.spec.js.map +1 -1
  19. package/dist/test/snapshots/summary.spec.js.map +1 -1
  20. package/lib/packageVersion.d.ts +1 -1
  21. package/lib/packageVersion.js +1 -1
  22. package/lib/packageVersion.js.map +1 -1
  23. package/lib/test/appendOnlySortedMap.spec.js.map +1 -1
  24. package/lib/test/idCompressor.spec.js.map +1 -1
  25. package/lib/test/idCompressorTestUtilities.d.ts +14 -0
  26. package/lib/test/idCompressorTestUtilities.d.ts.map +1 -1
  27. package/lib/test/idCompressorTestUtilities.js +46 -22
  28. package/lib/test/idCompressorTestUtilities.js.map +1 -1
  29. package/lib/test/index.d.ts +1 -1
  30. package/lib/test/index.d.ts.map +1 -1
  31. package/lib/test/index.js +1 -1
  32. package/lib/test/index.js.map +1 -1
  33. package/lib/test/numericUuid.spec.js.map +1 -1
  34. package/lib/test/sessionSpaceNormalizer.spec.js.map +1 -1
  35. package/lib/test/snapshots/summary.spec.js.map +1 -1
  36. package/lib/tsdoc-metadata.json +1 -1
  37. package/package.json +9 -10
  38. package/src/packageVersion.ts +1 -1
  39. package/src/test/idCompressorTestUtilities.ts +52 -25
  40. package/src/test/index.ts +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"summary.spec.js","sourceRoot":"","sources":["../../../src/test/snapshots/summary.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/* eslint-disable import/no-nodejs-modules */\n\nimport { strict as assert } from \"node:assert\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { SessionId, createIdCompressor } from \"../../index.js\";\n\nimport { _dirname } from \"./dirname.cjs\";\n\nconst regenerateSnapshots = process.argv.includes(\"--snapshot\");\n\nlet currentTestName: string | undefined;\nlet currentTestFile: string | undefined;\n\n// Simple filter to avoid tests with a name that would accidentally be parsed as directory traversal or other confusing things.\nconst nameCheck = new RegExp(/^[^\"/\\\\]+$/);\n\nassert(_dirname.match(/(dist|lib)[/\\\\]test[/\\\\]snapshots$/));\nconst snapshotsFolder = path.join(_dirname, `../../../src/test/snapshots`);\nassert(existsSync(snapshotsFolder));\n\n/**\n * Delete the existing test file directory and recreate it.\n *\n * If the directory does not already exist, this will create it.\n *\n * @param dirPath - The path within the `snapshots` directory.\n */\nfunction useSnapshotDirectory(dirPath: string = \"files\"): void {\n\tconst normalizedDir = path.join(snapshotsFolder, dirPath);\n\t// Basic sanity check to avoid bugs like accidentally recursively deleting everything under `/` if something went wrong (like dirPath navigated up directories a lot).\n\tassert(normalizedDir.startsWith(snapshotsFolder));\n\n\tif (regenerateSnapshots) {\n\t\tif (existsSync(normalizedDir)) {\n\t\t\tconsole.log(`removing snapshot directory: ${normalizedDir}`);\n\t\t\trmSync(normalizedDir, { recursive: true, force: true });\n\t\t}\n\t\tmkdirSync(normalizedDir, { recursive: true });\n\t}\n\n\tbeforeEach(function (): void {\n\t\tcurrentTestName = this.currentTest?.title.replace(/ /g, \"-\") ?? assert.fail();\n\t\tcurrentTestFile = path.join(normalizedDir, currentTestName);\n\t});\n\n\tafterEach(() => {\n\t\tcurrentTestFile = undefined;\n\t\tcurrentTestName = undefined;\n\t});\n}\n\nfunction takeSnapshot(data: string, suffix: string = \"\"): void {\n\tassert(\n\t\tcurrentTestName !== undefined,\n\t\t\"use `useSnapshotDirectory` to configure the tests containing describe block to take snapshots\",\n\t);\n\tassert(currentTestFile !== undefined);\n\n\t// Ensure test name doesn't accidentally navigate up directories or things like that.\n\t// Done here instead of in beforeEach so errors surface better.\n\tif (nameCheck.test(currentTestName) === false) {\n\t\tassert.fail(`Expected test name to pass sanitization: \"${currentTestName}\"`);\n\t}\n\n\tconst fullFile = currentTestFile + suffix;\n\n\tconst exists = existsSync(fullFile);\n\tif (regenerateSnapshots) {\n\t\tassert(exists === false, \"snapshot should not already exist: possible name collision.\");\n\t\twriteFileSync(fullFile, data);\n\t} else {\n\t\tassert(exists, `test snapshot file does not exist: \"${fullFile}\"`);\n\t\tconst pastData = readFileSync(fullFile, \"utf8\");\n\t\tassert.equal(data, pastData, `snapshot different for \"${currentTestName}\"`);\n\t}\n}\n\ndescribe(\"snapshot tests\", () => {\n\tconst client1 = \"403f1e16-5265-4074-8507-417d99a05ee9\" as SessionId;\n\tconst client2 = \"4b02675a-5a81-428d-b7b7-d8c33a16bfde\" as SessionId;\n\n\tuseSnapshotDirectory();\n\n\tit(\"empty compressor summary\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst summary = compressor.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t});\n\n\tit(\"compressor with finalized range from one client\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t}\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n\n\tit(\"compressors with finalized ranges from two clients\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t\tcompressor2.generateCompressedId();\n\t\t}\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tconst idRange2 = compressor2.takeNextCreationRange();\n\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tcompressor2.finalizeCreationRange(idRange);\n\t\tcompressor.finalizeCreationRange(idRange2);\n\t\tcompressor2.finalizeCreationRange(idRange2);\n\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n\n\tit(\"expansion semantics\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\t\t// eslint-disable-next-line @typescript-eslint/dot-notation\n\t\tcompressor[\"nextRequestedClusterSize\"] = 2;\n\t\tcompressor.generateCompressedId();\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tcompressor2.finalizeCreationRange(idRange);\n\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t}\n\n\t\tconst expansionIdRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(expansionIdRange);\n\t\tcompressor2.finalizeCreationRange(expansionIdRange);\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n});\n"]}
1
+ {"version":3,"file":"summary.spec.js","sourceRoot":"","sources":["../../../src/test/snapshots/summary.spec.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { SessionId, createIdCompressor } from \"../../index.js\";\nimport { modifyClusterSize } from \"../idCompressorTestUtilities.js\";\n\nimport { _dirname } from \"./dirname.cjs\";\n\nconst regenerateSnapshots = process.argv.includes(\"--snapshot\");\n\nlet currentTestName: string | undefined;\nlet currentTestFile: string | undefined;\n\n// Simple filter to avoid tests with a name that would accidentally be parsed as directory traversal or other confusing things.\nconst nameCheck = new RegExp(/^[^\"/\\\\]+$/);\n\nassert(_dirname.match(/(dist|lib)[/\\\\]test[/\\\\]snapshots$/));\nconst snapshotsFolder = path.join(_dirname, `../../../src/test/snapshots`);\nassert(existsSync(snapshotsFolder));\n\n/**\n * Delete the existing test file directory and recreate it.\n *\n * If the directory does not already exist, this will create it.\n *\n * @param dirPath - The path within the `snapshots` directory.\n */\nfunction useSnapshotDirectory(dirPath: string = \"files\"): void {\n\tconst normalizedDir = path.join(snapshotsFolder, dirPath);\n\t// Basic sanity check to avoid bugs like accidentally recursively deleting everything under `/` if something went wrong (like dirPath navigated up directories a lot).\n\tassert(normalizedDir.startsWith(snapshotsFolder));\n\n\tif (regenerateSnapshots) {\n\t\tif (existsSync(normalizedDir)) {\n\t\t\tconsole.log(`removing snapshot directory: ${normalizedDir}`);\n\t\t\trmSync(normalizedDir, { recursive: true, force: true });\n\t\t}\n\t\tmkdirSync(normalizedDir, { recursive: true });\n\t}\n\n\tbeforeEach(function (): void {\n\t\tcurrentTestName = this.currentTest?.title.replace(/ /g, \"-\") ?? assert.fail();\n\t\tcurrentTestFile = path.join(normalizedDir, currentTestName);\n\t});\n\n\tafterEach(() => {\n\t\tcurrentTestFile = undefined;\n\t\tcurrentTestName = undefined;\n\t});\n}\n\nfunction takeSnapshot(data: string, suffix: string = \"\"): void {\n\tassert(\n\t\tcurrentTestName !== undefined,\n\t\t\"use `useSnapshotDirectory` to configure the tests containing describe block to take snapshots\",\n\t);\n\tassert(currentTestFile !== undefined);\n\n\t// Ensure test name doesn't accidentally navigate up directories or things like that.\n\t// Done here instead of in beforeEach so errors surface better.\n\tif (nameCheck.test(currentTestName) === false) {\n\t\tassert.fail(`Expected test name to pass sanitization: \"${currentTestName}\"`);\n\t}\n\n\tconst fullFile = currentTestFile + suffix;\n\n\tconst exists = existsSync(fullFile);\n\tif (regenerateSnapshots) {\n\t\tassert(exists === false, \"snapshot should not already exist: possible name collision.\");\n\t\twriteFileSync(fullFile, data);\n\t} else {\n\t\tassert(exists, `test snapshot file does not exist: \"${fullFile}\"`);\n\t\tconst pastData = readFileSync(fullFile, \"utf8\");\n\t\tassert.equal(data, pastData, `snapshot different for \"${currentTestName}\"`);\n\t}\n}\n\ndescribe(\"snapshot tests\", () => {\n\tconst client1 = \"403f1e16-5265-4074-8507-417d99a05ee9\" as SessionId;\n\tconst client2 = \"4b02675a-5a81-428d-b7b7-d8c33a16bfde\" as SessionId;\n\n\tuseSnapshotDirectory();\n\n\tit(\"empty compressor summary\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst summary = compressor.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t});\n\n\tit(\"compressor with finalized range from one client\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t}\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n\n\tit(\"compressors with finalized ranges from two clients\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\n\t\tfor (let i = 0; i < 10; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t\tcompressor2.generateCompressedId();\n\t\t}\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tconst idRange2 = compressor2.takeNextCreationRange();\n\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tcompressor2.finalizeCreationRange(idRange);\n\t\tcompressor.finalizeCreationRange(idRange2);\n\t\tcompressor2.finalizeCreationRange(idRange2);\n\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n\n\tit(\"expansion semantics\", () => {\n\t\tconst compressor = createIdCompressor(client1);\n\t\tconst compressor2 = createIdCompressor(client2);\n\t\tmodifyClusterSize(compressor, 2);\n\t\tcompressor.generateCompressedId();\n\t\tconst idRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(idRange);\n\t\tcompressor2.finalizeCreationRange(idRange);\n\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\tcompressor.generateCompressedId();\n\t\t}\n\n\t\tconst expansionIdRange = compressor.takeNextCreationRange();\n\t\tcompressor.finalizeCreationRange(expansionIdRange);\n\t\tcompressor2.finalizeCreationRange(expansionIdRange);\n\t\tconst summary = compressor.serialize(false);\n\t\tconst summary2 = compressor2.serialize(false);\n\n\t\ttakeSnapshot(summary);\n\t\ttakeSnapshot(summary2, \"-client2\");\n\t});\n});\n"]}
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.50.1"
8
+ "packageVersion": "7.52.5"
9
9
  }
10
10
  ]
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/id-compressor",
3
- "version": "2.32.0",
3
+ "version": "2.33.0",
4
4
  "description": "ID compressor",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -81,25 +81,25 @@
81
81
  "temp-directory": "nyc/.nyc_output"
82
82
  },
83
83
  "dependencies": {
84
- "@fluid-internal/client-utils": "~2.32.0",
85
- "@fluidframework/core-interfaces": "~2.32.0",
86
- "@fluidframework/core-utils": "~2.32.0",
87
- "@fluidframework/telemetry-utils": "~2.32.0",
84
+ "@fluid-internal/client-utils": "~2.33.0",
85
+ "@fluidframework/core-interfaces": "~2.33.0",
86
+ "@fluidframework/core-utils": "~2.33.0",
87
+ "@fluidframework/telemetry-utils": "~2.33.0",
88
88
  "@tylerbu/sorted-btree-es6": "^1.8.0",
89
89
  "uuid": "^9.0.0"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@arethetypeswrong/cli": "^0.17.1",
93
93
  "@biomejs/biome": "~1.9.3",
94
- "@fluid-internal/mocha-test-setup": "~2.32.0",
95
- "@fluid-private/stochastic-test-utils": "~2.32.0",
94
+ "@fluid-internal/mocha-test-setup": "~2.33.0",
95
+ "@fluid-private/stochastic-test-utils": "~2.33.0",
96
96
  "@fluid-tools/benchmark": "^0.50.0",
97
97
  "@fluid-tools/build-cli": "^0.55.0",
98
98
  "@fluidframework/build-common": "^2.0.3",
99
99
  "@fluidframework/build-tools": "^0.55.0",
100
100
  "@fluidframework/eslint-config-fluid": "^5.7.3",
101
- "@fluidframework/id-compressor-previous": "npm:@fluidframework/id-compressor@2.31.0",
102
- "@microsoft/api-extractor": "7.50.1",
101
+ "@fluidframework/id-compressor-previous": "npm:@fluidframework/id-compressor@2.32.0",
102
+ "@microsoft/api-extractor": "7.52.5",
103
103
  "@types/mocha": "^10.0.10",
104
104
  "@types/node": "^18.19.0",
105
105
  "@types/uuid": "^9.0.2",
@@ -110,7 +110,6 @@
110
110
  "eslint": "~8.55.0",
111
111
  "mocha": "^10.8.2",
112
112
  "mocha-multi-reporters": "^1.5.1",
113
- "moment": "^2.21.0",
114
113
  "rimraf": "^4.4.0",
115
114
  "typescript": "~5.4.5"
116
115
  },
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/id-compressor";
9
- export const pkgVersion = "2.32.0";
9
+ export const pkgVersion = "2.33.0";
@@ -3,7 +3,6 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- // eslint-disable-next-line import/no-nodejs-modules
7
6
  import { strict as assert } from "node:assert";
8
7
 
9
8
  import {
@@ -114,12 +113,46 @@ export class CompressorFactory {
114
113
  logger?: ITelemetryBaseLogger,
115
114
  ): IdCompressor {
116
115
  const compressor = createIdCompressor(sessionId, logger) as IdCompressor;
117
- // eslint-disable-next-line @typescript-eslint/dot-notation
118
- compressor["nextRequestedClusterSize"] = clusterCapacity;
116
+ modifyClusterSize(compressor, clusterCapacity);
119
117
  return compressor;
120
118
  }
121
119
  }
122
120
 
121
+ /**
122
+ * Modify the requested cluster size of the provided compressor.
123
+ * @remarks
124
+ * This is useful for testing purposes for a few reasons:
125
+ * - Id compressor bugs are often related to edge cases that occur on cluster boundaries
126
+ * - Smaller cluster sizes can enable writing tests without for loops generating "ids until a new cluster is created"
127
+ */
128
+ export function modifyClusterSize(compressor: IIdCompressor, newClusterSize: number): void {
129
+ verifyCompressorLike(compressor);
130
+ // eslint-disable-next-line @typescript-eslint/dot-notation
131
+ compressor["nextRequestedClusterSize"] = newClusterSize;
132
+ }
133
+
134
+ /**
135
+ * Returns the current cluster size of the compressor.
136
+ * @privateRemarks
137
+ * This is useful in writing tests to avoid having to hardcode the (currently constant) cluster size.
138
+ */
139
+ export function getClusterSize(compressor: ReadonlyIdCompressor): number {
140
+ verifyCompressorLike(compressor);
141
+ // eslint-disable-next-line @typescript-eslint/dot-notation
142
+ return compressor["nextRequestedClusterSize"] as number;
143
+ }
144
+
145
+ function verifyCompressorLike(compressor: ReadonlyIdCompressor | IIdCompressor): void {
146
+ assert(
147
+ // Some IdCompressor tests wrap underlying compressors with proxies--allow this for now.
148
+ // Because of id-compressor's dynamic import in container-runtime, instanceof checks for IdCompressor
149
+ // also won't necessarily work nicely. Get a small amount of validation that this function should work
150
+ // as intended by at least verifying the property name exists.
151
+ // eslint-disable-next-line @typescript-eslint/dot-notation
152
+ typeof compressor["nextRequestedClusterSize"] === "number",
153
+ );
154
+ }
155
+
123
156
  /**
124
157
  * Utility for building a huge compressor.
125
158
  * Build via the compressor factory.
@@ -311,7 +344,7 @@ export class IdCompressorTestNetwork {
311
344
  * Changes the capacity request amount for a client. It will take effect immediately.
312
345
  */
313
346
  public changeCapacity(client: Client, newClusterCapacity: number): void {
314
- changeCapacity(this.compressors.get(client), newClusterCapacity);
347
+ modifyClusterSize(this.compressors.get(client), newClusterCapacity);
315
348
  }
316
349
 
317
350
  private addNewId(
@@ -362,11 +395,7 @@ export class IdCompressorTestNetwork {
362
395
  ids: {
363
396
  firstGenCount: 1,
364
397
  count: numIds,
365
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
366
- requestedClusterSize: this.getCompressor(Client.Client1)[
367
- // eslint-disable-next-line @typescript-eslint/dot-notation
368
- "nextRequestedClusterSize"
369
- ],
398
+ requestedClusterSize: getClusterSize(this.getCompressor(Client.Client1)),
370
399
  localIdRanges: [], // remote session, can safely ignore in tests
371
400
  },
372
401
  };
@@ -673,11 +702,6 @@ export class IdCompressorTestNetwork {
673
702
  }
674
703
  }
675
704
 
676
- function changeCapacity(compressor: IdCompressor, newClusterCapacity: number): void {
677
- // eslint-disable-next-line @typescript-eslint/dot-notation
678
- compressor["nextRequestedClusterSize"] = newClusterCapacity;
679
- }
680
-
681
705
  /**
682
706
  * Roundtrips the supplied compressor through serialization and deserialization.
683
707
  */
@@ -703,17 +727,16 @@ export function roundtrip(
703
727
  ] {
704
728
  // preserve the capacity request as this property is normally private and resets
705
729
  // to a default on construction (deserialization)
706
- // eslint-disable-next-line @typescript-eslint/dot-notation, @typescript-eslint/no-unsafe-assignment
707
- const capacity: number = compressor["nextRequestedClusterSize"];
730
+ const capacity: number = getClusterSize(compressor);
708
731
  if (withSession) {
709
732
  const serialized = compressor.serialize(withSession);
710
733
  const roundtripped = IdCompressor.deserialize(serialized);
711
- changeCapacity(roundtripped, capacity);
734
+ modifyClusterSize(roundtripped, capacity);
712
735
  return [serialized, roundtripped];
713
736
  } else {
714
737
  const nonLocalSerialized = compressor.serialize(withSession);
715
738
  const roundtripped = IdCompressor.deserialize(nonLocalSerialized, createSessionId());
716
- changeCapacity(roundtripped, capacity);
739
+ modifyClusterSize(roundtripped, capacity);
717
740
  return [nonLocalSerialized, roundtripped];
718
741
  }
719
742
  }
@@ -1045,13 +1068,17 @@ export function createAlwaysFinalizedIdCompressor(
1045
1068
  sessionIdOrLogger?: SessionId | ITelemetryBaseLogger,
1046
1069
  loggerOrUndefined?: ITelemetryBaseLogger,
1047
1070
  ): IIdCompressor & IIdCompressorCore {
1048
- const compressor =
1049
- sessionIdOrLogger === undefined
1050
- ? createIdCompressor()
1051
- : typeof sessionIdOrLogger === "string"
1052
- ? createIdCompressor(sessionIdOrLogger, loggerOrUndefined)
1053
- : createIdCompressor(sessionIdOrLogger);
1071
+ const sessionId =
1072
+ typeof sessionIdOrLogger === "string" ? sessionIdOrLogger : createSessionId();
1073
+ const logger =
1074
+ (loggerOrUndefined ?? typeof sessionIdOrLogger === "object")
1075
+ ? (sessionIdOrLogger as ITelemetryBaseLogger)
1076
+ : undefined;
1077
+ // This local session is unused, but it needs to not collide with the GhostSession, so allocate a random one.
1078
+ // This causes the compressor to serialize non-deterministically even when provided an explicit SessionId.
1079
+ // This can be fixed in the future if needed.
1080
+ const compressor = createIdCompressor(createSessionId(), logger);
1054
1081
  // Permanently put the compressor in a ghost session
1055
- (compressor as IdCompressor).startGhostSession(createSessionId());
1082
+ (compressor as IdCompressor).startGhostSession(sessionId);
1056
1083
  return compressor;
1057
1084
  }
package/src/test/index.ts CHANGED
@@ -7,4 +7,7 @@
7
7
  * Exports for test utilities for `id-compressor`
8
8
  */
9
9
 
10
- export { createAlwaysFinalizedIdCompressor } from "./idCompressorTestUtilities.js";
10
+ export {
11
+ createAlwaysFinalizedIdCompressor,
12
+ modifyClusterSize,
13
+ } from "./idCompressorTestUtilities.js";