@fluidframework/id-compressor 3.2.0 → 3.4.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 (102) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/api-report/id-compressor.legacy.beta.api.md +0 -49
  3. package/dist/idCompressor.d.ts +65 -22
  4. package/dist/idCompressor.d.ts.map +1 -1
  5. package/dist/idCompressor.js +401 -60
  6. package/dist/idCompressor.js.map +1 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +3 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/legacy.d.ts +1 -12
  12. package/dist/packageVersion.d.ts +1 -1
  13. package/dist/packageVersion.js +1 -1
  14. package/dist/packageVersion.js.map +1 -1
  15. package/dist/test/idCompressor.perf.spec.js.map +1 -1
  16. package/dist/test/idCompressor.spec.js.map +1 -1
  17. package/dist/test/idCompressorTestUtilities.d.ts.map +1 -1
  18. package/dist/test/idCompressorTestUtilities.js +8 -3
  19. package/dist/test/idCompressorTestUtilities.js.map +1 -1
  20. package/dist/test/sharding.fuzz.spec.d.ts +6 -0
  21. package/dist/test/sharding.fuzz.spec.d.ts.map +1 -0
  22. package/dist/test/sharding.fuzz.spec.js.map +1 -0
  23. package/dist/test/sharding.spec.d.ts +6 -0
  24. package/dist/test/sharding.spec.d.ts.map +1 -0
  25. package/dist/test/sharding.spec.js.map +1 -0
  26. package/dist/test/snapshots/summary.spec.js.map +1 -1
  27. package/dist/test/types/validateIdCompressorPrevious.generated.js.map +1 -1
  28. package/dist/types/idCompressor.d.ts +205 -20
  29. package/dist/types/idCompressor.d.ts.map +1 -1
  30. package/dist/types/idCompressor.js +15 -0
  31. package/dist/types/idCompressor.js.map +1 -1
  32. package/dist/types/index.d.ts +2 -1
  33. package/dist/types/index.d.ts.map +1 -1
  34. package/dist/types/index.js +3 -0
  35. package/dist/types/index.js.map +1 -1
  36. package/dist/types/persisted-types/0.0.1.d.ts +4 -4
  37. package/dist/types/persisted-types/0.0.1.js.map +1 -1
  38. package/dist/utilities.d.ts +1 -1
  39. package/dist/utilities.js +1 -1
  40. package/dist/utilities.js.map +1 -1
  41. package/lib/idCompressor.d.ts +65 -22
  42. package/lib/idCompressor.d.ts.map +1 -1
  43. package/lib/idCompressor.js +402 -61
  44. package/lib/idCompressor.js.map +1 -1
  45. package/lib/index.d.ts +2 -1
  46. package/lib/index.d.ts.map +1 -1
  47. package/lib/index.js +1 -0
  48. package/lib/index.js.map +1 -1
  49. package/lib/legacy.d.ts +1 -12
  50. package/lib/packageVersion.d.ts +1 -1
  51. package/lib/packageVersion.js +1 -1
  52. package/lib/packageVersion.js.map +1 -1
  53. package/lib/test/idCompressor.perf.spec.js.map +1 -1
  54. package/lib/test/idCompressor.spec.js.map +1 -1
  55. package/lib/test/idCompressorTestUtilities.d.ts.map +1 -1
  56. package/lib/test/idCompressorTestUtilities.js +8 -3
  57. package/lib/test/idCompressorTestUtilities.js.map +1 -1
  58. package/lib/test/sharding.fuzz.spec.d.ts +6 -0
  59. package/lib/test/sharding.fuzz.spec.d.ts.map +1 -0
  60. package/lib/test/sharding.fuzz.spec.js.map +1 -0
  61. package/lib/test/sharding.spec.d.ts +6 -0
  62. package/lib/test/sharding.spec.d.ts.map +1 -0
  63. package/lib/test/sharding.spec.js.map +1 -0
  64. package/lib/test/snapshots/summary.spec.js.map +1 -1
  65. package/lib/test/types/validateIdCompressorPrevious.generated.js.map +1 -1
  66. package/lib/types/idCompressor.d.ts +205 -20
  67. package/lib/types/idCompressor.d.ts.map +1 -1
  68. package/lib/types/idCompressor.js +14 -1
  69. package/lib/types/idCompressor.js.map +1 -1
  70. package/lib/types/index.d.ts +2 -1
  71. package/lib/types/index.d.ts.map +1 -1
  72. package/lib/types/index.js +1 -1
  73. package/lib/types/index.js.map +1 -1
  74. package/lib/types/persisted-types/0.0.1.d.ts +4 -4
  75. package/lib/types/persisted-types/0.0.1.js.map +1 -1
  76. package/lib/utilities.d.ts +1 -1
  77. package/lib/utilities.js +1 -1
  78. package/lib/utilities.js.map +1 -1
  79. package/package.json +8 -8
  80. package/src/idCompressor.ts +550 -78
  81. package/src/index.ts +11 -5
  82. package/src/packageVersion.ts +1 -1
  83. package/src/test/idCompressorTestUtilities.ts +16 -3
  84. package/src/test/snapshots/v2/compressor-with-finalized-range-from-one-client +1 -0
  85. package/src/test/snapshots/v2/compressor-with-finalized-range-from-one-client-client2 +1 -0
  86. package/src/test/snapshots/v2/compressors-with-finalized-ranges-from-two-clients +1 -0
  87. package/src/test/snapshots/v2/compressors-with-finalized-ranges-from-two-clients-client2 +1 -0
  88. package/src/test/snapshots/v2/empty-compressor-summary +1 -0
  89. package/src/test/snapshots/v2/expansion-semantics +1 -0
  90. package/src/test/snapshots/v2/expansion-semantics-client2 +1 -0
  91. package/src/test/snapshots/v3/compressor-with-finalized-range-from-one-client +1 -0
  92. package/src/test/snapshots/v3/compressor-with-finalized-range-from-one-client-client2 +1 -0
  93. package/src/test/snapshots/v3/compressors-with-finalized-ranges-from-two-clients +1 -0
  94. package/src/test/snapshots/v3/compressors-with-finalized-ranges-from-two-clients-client2 +1 -0
  95. package/src/test/snapshots/v3/empty-compressor-summary +1 -0
  96. package/src/test/snapshots/v3/expansion-semantics +1 -0
  97. package/src/test/snapshots/v3/expansion-semantics-client2 +1 -0
  98. package/src/test/types/validateIdCompressorPrevious.generated.ts +1 -109
  99. package/src/types/idCompressor.ts +225 -20
  100. package/src/types/index.ts +13 -1
  101. package/src/types/persisted-types/0.0.1.ts +4 -4
  102. package/src/utilities.ts +1 -1
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export {
13
13
  serializeIdCompressor,
14
14
  toIdCompressorWithCore,
15
15
  } from "./idCompressor.js";
16
+ export { SerializationVersion } from "./types/index.js";
16
17
  export { type FinalCompressedId, isFinalId } from "./identifiers.js";
17
18
  export {
18
19
  createSessionId,
@@ -21,14 +22,19 @@ export {
21
22
  isStableId,
22
23
  } from "./utilities.js";
23
24
  export type {
24
- IIdCompressorCore,
25
+ IdCreationRange,
25
26
  IIdCompressor,
27
+ IIdCompressorCore,
28
+ IdCompressorEvents,
29
+ OpSpaceCompressedId,
30
+ ParentShardSynchronizationToken,
26
31
  SerializedIdCompressor,
27
- SerializedIdCompressorWithOngoingSession,
28
32
  SerializedIdCompressorWithNoSession,
29
- SessionSpaceCompressedId,
30
- OpSpaceCompressedId,
33
+ SerializedIdCompressorWithOngoingSession,
34
+ SerializedIdCompressorShard,
31
35
  SessionId,
36
+ SessionSpaceCompressedId,
37
+ ShardSynchronizationToken,
38
+ ShardToken,
32
39
  StableId,
33
- IdCreationRange,
34
40
  } from "./types/index.js";
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/id-compressor";
9
- export const pkgVersion = "3.2.0";
9
+ export const pkgVersion = "3.4.0";
@@ -33,6 +33,7 @@ import {
33
33
  toIdCompressorWithCore,
34
34
  } from "../index.js";
35
35
  import { SessionSpaceNormalizer } from "../sessionSpaceNormalizer.js";
36
+ import { SerializationVersion } from "../types/index.js";
36
37
  import { assertIsSessionId, createSessionId, localIdFromGenCount } from "../utilities.js";
37
38
 
38
39
  import {
@@ -113,7 +114,11 @@ export class CompressorFactory {
113
114
  clusterCapacity = 5,
114
115
  logger?: ITelemetryBaseLogger,
115
116
  ): IdCompressor {
116
- const compressor = createIdCompressor(sessionId, logger) as IdCompressor;
117
+ const compressor = createIdCompressor(
118
+ sessionId,
119
+ SerializationVersion.V3,
120
+ logger,
121
+ ) as IdCompressor;
117
122
  modifyClusterSize(compressor, clusterCapacity);
118
123
  return compressor;
119
124
  }
@@ -732,7 +737,10 @@ export function roundtrip(
732
737
  const capacity: number = getClusterSize(compressor);
733
738
  if (withSession) {
734
739
  const serialized = compressor.serialize(withSession);
735
- const roundtripped = IdCompressor.deserialize({ serialized });
740
+ const roundtripped = IdCompressor.deserialize({
741
+ serialized,
742
+ requestedWriteVersion: SerializationVersion.V3,
743
+ });
736
744
  modifyClusterSize(roundtripped, capacity);
737
745
  return [serialized, roundtripped];
738
746
  } else {
@@ -740,6 +748,7 @@ export function roundtrip(
740
748
  const roundtripped = IdCompressor.deserialize({
741
749
  serialized: nonLocalSerialized,
742
750
  newSessionId: createSessionId(),
751
+ requestedWriteVersion: SerializationVersion.V3,
743
752
  });
744
753
  modifyClusterSize(roundtripped, capacity);
745
754
  return [nonLocalSerialized, roundtripped];
@@ -1085,7 +1094,11 @@ export function createAlwaysFinalizedIdCompressor(
1085
1094
  // This local session is unused, but it needs to not collide with the GhostSession, so allocate a random one.
1086
1095
  // This causes the compressor to serialize non-deterministically even when provided an explicit SessionId.
1087
1096
  // This can be fixed in the future if needed.
1088
- const compressor = createIdCompressor(random.uuid4() as SessionId, logger);
1097
+ const compressor = createIdCompressor(
1098
+ random.uuid4() as SessionId,
1099
+ SerializationVersion.V3,
1100
+ logger,
1101
+ );
1089
1102
  // Permanently put the compressor in a ghost session
1090
1103
  (compressor as IdCompressor).startGhostSession(sessionId);
1091
1104
  return toIdCompressorWithCore(compressor);
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAFCAQAAAAAAAACRA
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEDpXqCZfUEHBR2USVl4/AAB3r8WOsPYt3ejBGppnQksAQAAAAAAAAAAAAAAAABQgEAAAAAAAAAkQAAAAAAAAPA/AAAAAABQgEAAAAAAAAAkQA==
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEDevxY6w9i3d6MEammdCSwB6V6gmX1BBwUdlElZePwAAQAAAAAAAPA/AAAAAABQgEAAAAAAAAAkQAAAAAAAAAAAAAAAAABQgEAAAAAAAAAkQA==
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAAAYQAAAAAAAABBA
@@ -0,0 +1 @@
1
+ AAAAAAAAAEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAAAYQAAAAAAAABBA
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAFCAQAAAAAAAACRA
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEDpXqCZfUEHBR2USVl4/AAB3r8WOsPYt3ejBGppnQksAQAAAAAAAAAAAAAAAABQgEAAAAAAAAAkQAAAAAAAAPA/AAAAAABQgEAAAAAAAAAkQA==
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEDevxY6w9i3d6MEammdCSwB6V6gmX1BBwUdlElZePwAAQAAAAAAAPA/AAAAAABQgEAAAAAAAAAkQAAAAAAAAAAAAAAAAABQgEAAAAAAAAAkQA==
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAAAYQAAAAAAAABBA
@@ -0,0 +1 @@
1
+ AAAAAAAACEAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D/pXqCZfUEHBR2USVl4/AABAAAAAAAAAAAAAAAAAAAYQAAAAAAAABBA
@@ -7,7 +7,7 @@
7
7
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
8
8
  * Generated by "flub generate typetests" from @fluid-tools/build-cli.
9
9
  *
10
- * Baseline (previous) version: 3.1.0
10
+ * Baseline (previous) version: 3.3.0
11
11
  */
12
12
 
13
13
  import type { TypeOnly, MinimalType, FullType, requireAssignableTo } from "@fluidframework/build-tools";
@@ -17,60 +17,6 @@ import type * as current from "@fluidframework/id-compressor/legacy";
17
17
 
18
18
  declare type MakeUnusedImportErrorsGoAway<T> = TypeOnly<T> | MinimalType<T> | FullType<T> | typeof old | typeof current | requireAssignableTo<true, true>;
19
19
 
20
- /*
21
- * Validate backward compatibility by using the current type in place of the old type.
22
- * If this test starts failing, it indicates a change that is not backward compatible.
23
- * To acknowledge the breaking change, add the following to package.json under
24
- * typeValidation.broken:
25
- * "Function_createIdCompressor": {"backCompat": false}
26
- */
27
- declare type current_as_old_for_Function_createIdCompressor = requireAssignableTo<TypeOnly<typeof current.createIdCompressor>, TypeOnly<typeof old.createIdCompressor>>
28
-
29
- /*
30
- * Validate backward compatibility by using the current type in place of the old type.
31
- * If this test starts failing, it indicates a change that is not backward compatible.
32
- * To acknowledge the breaking change, add the following to package.json under
33
- * typeValidation.broken:
34
- * "Function_createSessionId": {"backCompat": false}
35
- */
36
- declare type current_as_old_for_Function_createSessionId = requireAssignableTo<TypeOnly<typeof current.createSessionId>, TypeOnly<typeof old.createSessionId>>
37
-
38
- /*
39
- * Validate backward compatibility by using the current type in place of the old type.
40
- * If this test starts failing, it indicates a change that is not backward compatible.
41
- * To acknowledge the breaking change, add the following to package.json under
42
- * typeValidation.broken:
43
- * "Function_deserializeIdCompressor": {"backCompat": false}
44
- */
45
- declare type current_as_old_for_Function_deserializeIdCompressor = requireAssignableTo<TypeOnly<typeof current.deserializeIdCompressor>, TypeOnly<typeof old.deserializeIdCompressor>>
46
-
47
- /*
48
- * Validate backward compatibility by using the current type in place of the old type.
49
- * If this test starts failing, it indicates a change that is not backward compatible.
50
- * To acknowledge the breaking change, add the following to package.json under
51
- * typeValidation.broken:
52
- * "Function_serializeIdCompressor": {"backCompat": false}
53
- */
54
- declare type current_as_old_for_Function_serializeIdCompressor = requireAssignableTo<TypeOnly<typeof current.serializeIdCompressor>, TypeOnly<typeof old.serializeIdCompressor>>
55
-
56
- /*
57
- * Validate forward compatibility by using the old type in place of the current type.
58
- * If this test starts failing, it indicates a change that is not forward compatible.
59
- * To acknowledge the breaking change, add the following to package.json under
60
- * typeValidation.broken:
61
- * "Interface_IdCreationRange": {"forwardCompat": false}
62
- */
63
- declare type old_as_current_for_Interface_IdCreationRange = requireAssignableTo<TypeOnly<old.IdCreationRange>, TypeOnly<current.IdCreationRange>>
64
-
65
- /*
66
- * Validate backward compatibility by using the current type in place of the old type.
67
- * If this test starts failing, it indicates a change that is not backward compatible.
68
- * To acknowledge the breaking change, add the following to package.json under
69
- * typeValidation.broken:
70
- * "Interface_IdCreationRange": {"backCompat": false}
71
- */
72
- declare type current_as_old_for_Interface_IdCreationRange = requireAssignableTo<TypeOnly<current.IdCreationRange>, TypeOnly<old.IdCreationRange>>
73
-
74
20
  /*
75
21
  * Validate backward compatibility by using the current type in place of the old type.
76
22
  * If this test starts failing, it indicates a change that is not backward compatible.
@@ -98,60 +44,6 @@ declare type old_as_current_for_TypeAlias_OpSpaceCompressedId = requireAssignabl
98
44
  */
99
45
  declare type current_as_old_for_TypeAlias_OpSpaceCompressedId = requireAssignableTo<TypeOnly<current.OpSpaceCompressedId>, TypeOnly<old.OpSpaceCompressedId>>
100
46
 
101
- /*
102
- * Validate forward compatibility by using the old type in place of the current type.
103
- * If this test starts failing, it indicates a change that is not forward compatible.
104
- * To acknowledge the breaking change, add the following to package.json under
105
- * typeValidation.broken:
106
- * "TypeAlias_SerializedIdCompressor": {"forwardCompat": false}
107
- */
108
- declare type old_as_current_for_TypeAlias_SerializedIdCompressor = requireAssignableTo<TypeOnly<old.SerializedIdCompressor>, TypeOnly<current.SerializedIdCompressor>>
109
-
110
- /*
111
- * Validate backward compatibility by using the current type in place of the old type.
112
- * If this test starts failing, it indicates a change that is not backward compatible.
113
- * To acknowledge the breaking change, add the following to package.json under
114
- * typeValidation.broken:
115
- * "TypeAlias_SerializedIdCompressor": {"backCompat": false}
116
- */
117
- declare type current_as_old_for_TypeAlias_SerializedIdCompressor = requireAssignableTo<TypeOnly<current.SerializedIdCompressor>, TypeOnly<old.SerializedIdCompressor>>
118
-
119
- /*
120
- * Validate forward compatibility by using the old type in place of the current type.
121
- * If this test starts failing, it indicates a change that is not forward compatible.
122
- * To acknowledge the breaking change, add the following to package.json under
123
- * typeValidation.broken:
124
- * "TypeAlias_SerializedIdCompressorWithNoSession": {"forwardCompat": false}
125
- */
126
- declare type old_as_current_for_TypeAlias_SerializedIdCompressorWithNoSession = requireAssignableTo<TypeOnly<old.SerializedIdCompressorWithNoSession>, TypeOnly<current.SerializedIdCompressorWithNoSession>>
127
-
128
- /*
129
- * Validate backward compatibility by using the current type in place of the old type.
130
- * If this test starts failing, it indicates a change that is not backward compatible.
131
- * To acknowledge the breaking change, add the following to package.json under
132
- * typeValidation.broken:
133
- * "TypeAlias_SerializedIdCompressorWithNoSession": {"backCompat": false}
134
- */
135
- declare type current_as_old_for_TypeAlias_SerializedIdCompressorWithNoSession = requireAssignableTo<TypeOnly<current.SerializedIdCompressorWithNoSession>, TypeOnly<old.SerializedIdCompressorWithNoSession>>
136
-
137
- /*
138
- * Validate forward compatibility by using the old type in place of the current type.
139
- * If this test starts failing, it indicates a change that is not forward compatible.
140
- * To acknowledge the breaking change, add the following to package.json under
141
- * typeValidation.broken:
142
- * "TypeAlias_SerializedIdCompressorWithOngoingSession": {"forwardCompat": false}
143
- */
144
- declare type old_as_current_for_TypeAlias_SerializedIdCompressorWithOngoingSession = requireAssignableTo<TypeOnly<old.SerializedIdCompressorWithOngoingSession>, TypeOnly<current.SerializedIdCompressorWithOngoingSession>>
145
-
146
- /*
147
- * Validate backward compatibility by using the current type in place of the old type.
148
- * If this test starts failing, it indicates a change that is not backward compatible.
149
- * To acknowledge the breaking change, add the following to package.json under
150
- * typeValidation.broken:
151
- * "TypeAlias_SerializedIdCompressorWithOngoingSession": {"backCompat": false}
152
- */
153
- declare type current_as_old_for_TypeAlias_SerializedIdCompressorWithOngoingSession = requireAssignableTo<TypeOnly<current.SerializedIdCompressorWithOngoingSession>, TypeOnly<old.SerializedIdCompressorWithOngoingSession>>
154
-
155
47
  /*
156
48
  * Validate forward compatibility by using the old type in place of the current type.
157
49
  * If this test starts failing, it indicates a change that is not forward compatible.
@@ -9,12 +9,35 @@ import type {
9
9
  SessionSpaceCompressedId,
10
10
  StableId,
11
11
  } from "./identifiers.js";
12
+ import type { Listenable } from "@fluidframework/core-interfaces";
12
13
  import type {
13
14
  IdCreationRange,
14
15
  SerializedIdCompressorWithNoSession,
15
16
  SerializedIdCompressorWithOngoingSession,
16
17
  } from "./persisted-types/index.js";
17
18
 
19
+ /**
20
+ * Serialization format versions for IdCompressor.
21
+ * @internal
22
+ */
23
+ export const SerializationVersion = {
24
+ /**
25
+ * Base format without sharding support
26
+ */
27
+ V2: 2,
28
+ /**
29
+ * Adds optional sharding state
30
+ */
31
+ V3: 3,
32
+ } as const;
33
+
34
+ /**
35
+ * Type representing valid serialization version values.
36
+ * @internal
37
+ */
38
+ export type SerializationVersion =
39
+ (typeof SerializationVersion)[keyof typeof SerializationVersion];
40
+
18
41
  /**
19
42
  * A distributed UUID generator and compressor.
20
43
  *
@@ -79,12 +102,20 @@ import type {
79
102
  * @internal
80
103
  */
81
104
  export interface IIdCompressorCore {
105
+ /**
106
+ * Events emitted by the ID compressor.
107
+ */
108
+ readonly events: Listenable<IdCompressorEvents>;
109
+
82
110
  /**
83
111
  * Returns a range of IDs created by this session in a format for sending to the server for finalizing.
84
112
  * The range will include all IDs generated via calls to `generateCompressedId` since the last time a
85
113
  * range was taken (via this method or `takeUnfinalizedCreationRange`).
86
114
  * @returns the range of IDs, which may be empty. This range must be sent to the server for ordering before
87
115
  * it is finalized. Ranges must be sent to the server in the order that they are taken via calls to this method.
116
+ * @throws if called on a child (non-root) shard. All shards in a shard tree share a single session, and only
117
+ * the root shard may finalize that session's IDs with the server; a child's IDs are instead reconciled with the
118
+ * root via {@link IIdCompressorCore.synchronizeWithShard} / {@link IIdCompressorCore.disposeShard}.
88
119
  */
89
120
  takeNextCreationRange(): IdCreationRange;
90
121
 
@@ -143,39 +174,204 @@ export interface IIdCompressorCore {
143
174
  beginGhostSession(ghostSessionId: SessionId, ghostSessionCallback: () => void): void;
144
175
 
145
176
  /**
146
- * Returns a persistable form of the current state of this `IdCompressor` which can be rehydrated via `IdCompressor.deserialize()`.
177
+ * Shards the ID space of this compressor such that multiple local compressors can safely share it without colliding.
178
+ * This can allow multiple local instantiations of the same compressor to safely share an ID space in scenarios where
179
+ * different threads do not have access to a central ID compressor.
180
+ * @param newShardCount - The number of additional different shards to split this compressor into.
181
+ * Must be a positive safe integer.
182
+ * @throws If `newShardCount` is not a positive safe integer or the resulting stride exceeds the sharding limit.
183
+ * @returns An array of `newShardCount` {@link SerializedIdCompressorShard} objects.
184
+ * Note that this method should only be needed when multiple JS runtimes are in play, as sharded compressors essentially
185
+ * attempt to emulate a single static compressor and any code running in the same JS runtime can simply use statics.
186
+ */
187
+ shard(newShardCount: number): SerializedIdCompressorShard[];
188
+
189
+ /**
190
+ * Synchronizes `this` compressor with a child shard. Synchronization will occur for the state of the child at the time `syncToken`
191
+ * was generated, meaning that `this` compressor can use/ingest IDs generated up to that point. Attempts to use IDs from a child shard
192
+ * without first synchronizing will result in an exception.
193
+ *
194
+ * If `syncToken` is a disposal token (its {@link ShardToken.disposed} flag is `true`, as produced by
195
+ * {@link IIdCompressorCore.disposeShard}), this additionally deregisters the child shard and reclaims its subset of the ID space
196
+ * into `this`. Once a shard has been disposed it is no longer safe to use the compressor the token came from, and a shard tree must
197
+ * be disposed from the leaves upwards.
198
+ * @param syncToken - The token for the shard, obtained by calling {@link IIdCompressorCore.getShardSyncToken} (non-destructive
199
+ * synchronization) or {@link IIdCompressorCore.disposeShard} (synchronization plus reclamation of the disposed shard's ID space).
200
+ * @throws `TypeError` if the token's generation count is not a nonnegative safe integer.
201
+ */
202
+ synchronizeWithShard(syncToken: ShardSynchronizationToken): void;
203
+
204
+ /**
205
+ * Gets a synchronization token from this compressor for an active child shard.
206
+ *
207
+ * @remarks
208
+ * The child can apply this token without replacing its compressor.
209
+ *
210
+ * @param childToken - A token from the child shard that will receive this token.
211
+ * @returns This parent's synchronization token for that child.
212
+ * @throws If the child shard is not active on this compressor.
213
+ */
214
+ getChildShardSyncToken(
215
+ childToken: ShardSynchronizationToken,
216
+ ): ParentShardSynchronizationToken;
217
+
218
+ /**
219
+ * Synchronizes this child shard with its parent's token.
220
+ *
221
+ * @remarks
222
+ * This method keeps the child's allocation stride and does not finalize ID creation ranges.
223
+ * Apply creation ranges from {@link IIdCompressorCore.events} separately, in order.
224
+ *
225
+ * @param token - Synchronization token obtained from this child's parent.
226
+ * @throws If the token names another child or contains invalid state.
227
+ * @throws `TypeError` if the token's generation count is not a nonnegative safe integer.
228
+ */
229
+ synchronizeWithParent(token: ParentShardSynchronizationToken): void;
230
+
231
+ /**
232
+ * Returns undefined if this compressor is not part of a shard group, and otherwise returns a synchronization token for this shard
233
+ * that can be used when calling {@link IIdCompressorCore.synchronizeWithShard}. This does NOT dispose the shard.
234
+ *
235
+ * @returns The sync token if this compressor is part of a sharded group, otherwise undefined.
236
+ * The returned token is serializable and can be passed across marshaling boundaries. This token can be used to synchronize a
237
+ * parent with this compressor by calling {@link IIdCompressorCore.synchronizeWithShard}.
238
+ * Unlike {@link IIdCompressorCore.disposeShard}, this is non-destructive and may be called on a shard that still has active
239
+ * child shards, which allows an intermediate shard to propagate its progress upward to its own parent.
240
+ * @throws If this compressor is the root of the shard tree (only non-root shards can produce a token).
241
+ */
242
+ getShardSyncToken(): ShardSynchronizationToken | undefined;
243
+
244
+ /**
245
+ * Returns undefined if this compressor is not part of a shard group, and otherwise disposes this shard and returns
246
+ * the disposal token for this compressor. If this compressor was part of a shard group, the compressor will no longer be usable.
247
+ *
248
+ * @returns The disposal token if this compressor is part of a sharded group, otherwise undefined.
249
+ * The returned token is serializable and can be passed across marshaling boundaries. Passing it to
250
+ * {@link IIdCompressorCore.synchronizeWithShard} on the parent reclaims this shard's subset of the ID space.
251
+ * @throws If this shard has active child shards.
252
+ * This means that a shard tree must be disposed from the leaves upwards.
253
+ */
254
+ disposeShard(): ShardSynchronizationToken | undefined;
255
+
256
+ /**
257
+ * Returns a persistable form of the current state of this `IdCompressor` which can be rehydrated via `deserializeIdCompressor()`.
147
258
  * This includes finalized state as well as un-finalized state and is therefore suitable for use in offline scenarios.
148
259
  */
149
260
  serialize(withSession: true): SerializedIdCompressorWithOngoingSession;
150
261
 
151
262
  /**
152
- * Returns a persistable form of the current state of this `IdCompressor` which can be rehydrated via `IdCompressor.deserialize()`.
263
+ * Returns a persistable form of the current state of this `IdCompressor` which can be rehydrated via `deserializeIdCompressor()`.
153
264
  * This only includes finalized state and is therefore suitable for use in summaries.
154
265
  */
155
266
  serialize(withSession: false): SerializedIdCompressorWithNoSession;
156
267
  }
157
268
 
269
+ /**
270
+ * The serialized state of a new child compressor and its initial synchronization token.
271
+ *
272
+ * @sealed
273
+ * @internal
274
+ */
275
+ export interface SerializedIdCompressorShard {
276
+ /**
277
+ * Serialized child state that can be rehydrated across a marshalling boundary.
278
+ */
279
+ readonly serialized: SerializedIdCompressorWithOngoingSession;
280
+
281
+ /**
282
+ * Initial, non-disposing token for this child shard.
283
+ *
284
+ * @remarks
285
+ * After the child generates IDs, obtain its updated token before synchronizing
286
+ * with the parent or reclaiming the child's ID space.
287
+ *
288
+ * This can be used to immediately dispose of a new shard if it is not used.
289
+ */
290
+ readonly syncToken: ShardSynchronizationToken;
291
+ }
292
+
293
+ /**
294
+ * State shared by shard synchronization tokens.
295
+ * @remarks
296
+ * Treat these tokens as opaque: the compressor owns their interpretation and may add state in the future.
297
+ * The {@link ShardToken.disposed} flag distinguishes ordinary synchronization from shard reclamation.
298
+ * The branded {@link ShardSynchronizationToken} is the concrete child-to-parent token.
299
+ * @internal
300
+ */
301
+ export interface ShardToken {
302
+ /**
303
+ * The sending compressor's generation count, which can include backfilled positions
304
+ * rather than only IDs it generated.
305
+ * @remarks Must be a nonnegative safe integer.
306
+ */
307
+ localGenCount: number;
308
+
309
+ /**
310
+ * Identifies the child shard involved in synchronization with its parent.
311
+ */
312
+ shardId: SessionId;
313
+
314
+ /**
315
+ * Whether this token also signals disposal of the originating shard. When `true`, passing the token to
316
+ * {@link IIdCompressorCore.synchronizeWithShard} reclaims the shard's ID space in addition to synchronizing.
317
+ * Produced as `false` by {@link IIdCompressorCore.getShardSyncToken} and `true` by {@link IIdCompressorCore.disposeShard}.
318
+ */
319
+ disposed: boolean;
320
+ }
321
+
322
+ /**
323
+ * A {@link ShardToken} that identifies a shard for synchronization (and, when {@link ShardToken.disposed} is `true`,
324
+ * reclamation) via {@link IIdCompressorCore.synchronizeWithShard}.
325
+ * @internal
326
+ */
327
+ export type ShardSynchronizationToken = ShardToken & {
328
+ readonly ShardSynchronizationToken: "c79724e1-9103-4415-95b5-bebb932be404";
329
+ };
330
+
331
+ /**
332
+ * A synchronization token from a parent to one of its active child shards.
333
+ * @remarks
334
+ * Unlike the child-to-parent {@link ShardSynchronizationToken}, this token cannot dispose a shard.
335
+ * It does not finalize ID creation ranges.
336
+ * @internal
337
+ */
338
+ export interface ParentShardSynchronizationToken
339
+ extends Readonly<Omit<ShardToken, "disposed">> {
340
+ /** Identifies the parent-to-child token across a message boundary. */
341
+ readonly type: "parentIdSpaceShardSyncToken";
342
+ }
343
+
344
+ /**
345
+ * Events for finalized ID creation ranges.
346
+ * @internal
347
+ */
348
+ export interface IdCompressorEvents {
349
+ /** Reports a range after finalization succeeds. */
350
+ rangeFinalized: (range: IdCreationRange) => void;
351
+ }
158
352
  /**
159
353
  * A distributed UUID generator and compressor.
354
+ *
355
+ * @remarks
160
356
  * `IdCompressor` offers the ability to generate arbitrary non-colliding v4 UUIDs, called stable IDs, while compressing them into small integers
161
357
  * for efficient storage and transmission. It also provides the ability to decompress these integers back into their original UUIDs.
162
358
  *
163
359
  * The compressor is designed to be used in a distributed environment, where multiple clients may be generating IDs concurrently.
164
- * IDs generated by a compressor, via calls to `generateCompressedId`, are created in the compressor's "local" session.
360
+ * IDs generated by a compressor, via calls to {@link IIdCompressor.generateCompressedId}, are created in the compressor's "local" session.
165
361
  * These IDs are unique within the session, but may not be unique across sessions. In the context of Fluid, the scope of a session
166
362
  * is the same as the scope of a container. This means that anytime IDs are transferred between sessions, they must be translated
167
363
  * through a process called "normalization":
364
+ *
168
365
  * - The scope of a local session (in which local IDs are unique) is called the "session space" of the client.
169
366
  * - The context of persisted state (in Fluid, this is the context of ops and summaries) is called "op space".
170
- * - `SessionSpaceCompressedId`s, generated via calls to `generateCompressedId`, should NEVER be serialized directly.
367
+ * - {@link SessionSpaceCompressedId | SessionSpaceCompressedIds}, generated via calls to {@link IIdCompressor.generateCompressedId}, should NEVER be serialized directly.
171
368
  * In Fluid, this means they should not be included in ops or summaries.
172
369
  * - Before serialization, IDs must be normalized to op space to ensure they are interpretable by other clients.
173
370
  * - Upon receipt, IDs should be normalized back to session space before use.
174
371
  *
175
- * Example Usage:
176
- *
177
- * ### Client A (Sender)
372
+ * @example
178
373
  *
374
+ * Client A (Sender)
179
375
  * ```typescript
180
376
  * // Generate several local IDs
181
377
  * const localId1 = idCompressor.generateCompressedId();
@@ -195,7 +391,7 @@ export interface IIdCompressorCore {
195
391
  * };
196
392
  * ```
197
393
  *
198
- * ### Client B (Receiver)
394
+ * Client B (Receiver)
199
395
  *
200
396
  * ```typescript
201
397
  * // Receive the message from Client A
@@ -217,27 +413,34 @@ export interface IIdCompressor {
217
413
 
218
414
  /**
219
415
  * Generates a new compressed ID.
220
- * The returned ID is in session space and should not be serialized directly. See `IIdCompressor` for more details.
416
+ *
417
+ * @remarks The returned ID is in session space and should not be serialized directly.
418
+ * @see {@link IIdCompressor} for more details.
221
419
  * @returns A new local ID in session space.
222
420
  */
223
421
  generateCompressedId(): SessionSpaceCompressedId;
224
422
 
225
423
  /**
226
424
  * Generates a new ID that is guaranteed to be unique across all sessions known to this compressor without the need for any
227
- * normalization. The returned ID is not guaranteed to be a compressed ID (small number); it may be a stable ID (UUID string).
425
+ * normalization.
426
+ *
427
+ * @remarks The returned ID is not guaranteed to be a compressed ID (small number); it may be a stable ID (UUID string).
228
428
  * In Fluid, the likelihood of generating the bulkier stable ID is dictated by network conditions and is highly probable in
229
429
  * scenarios such as offline. This is still useful for use cases where simplicity is more important than performance and
230
430
  * this approach will often be superior to generating a UUID.
231
- * If small numbers are a requirement, `generateCompressedId` and normalization should be used instead.
232
- * See `IIdCompressor` for more details.
431
+ *
432
+ * If small numbers are a requirement, {@link IIdCompressor.generateCompressedId} and normalization should be used instead.
433
+ * @see {@link IIdCompressor} for more details.
233
434
  * @returns A new local ID in session space.
234
435
  */
235
436
  generateDocumentUniqueId(): (SessionSpaceCompressedId & OpSpaceCompressedId) | StableId;
236
437
 
237
438
  /**
238
439
  * Normalizes a session space ID into op space.
239
- * The returned ID is in op space and can be safely serialized.
440
+ *
441
+ * @remarks The returned ID is in op space and can be safely serialized.
240
442
  * However, it should be {@link IIdCompressor.normalizeToSessionSpace|normalized back to session space} before use.
443
+ *
241
444
  * @param id - The local ID to normalize.
242
445
  * @returns The ID in op space.
243
446
  */
@@ -245,23 +448,25 @@ export interface IIdCompressor {
245
448
 
246
449
  /**
247
450
  * Normalizes an ID into session space.
248
- * @param id - The ID to normalize.
249
- * @param originSessionId - The {@link IIdCompressor.localSessionId} of the compressor which {@link IIdCompressor.normalizeToOpSpace|encoded} `id`.
250
- * This might not be the client that created `id` in the first place, but rather the client that serialized it.
251
- * This is an important distinction in the case of a reference, where a client might refer to an ID created by another client.
252
- * @returns The session-space ID in the local session corresponding to `id`.
451
+ *
253
452
  * @remarks
254
453
  * For this to be valid, `originSessionId` must be the {@link IIdCompressor.localSessionId} of the compressor that normalized `id` to op space.
255
- * Additionally, this compressor must know about the relevant {@link IdCreationRange} from the encoding `IIdCompressor`.
454
+ * Additionally, this compressor must know about the relevant {@link IdCreationRange} from the encoding {@link IIdCompressor}.
256
455
  * This can be accomplished using creating the compressor using {@link (deserializeIdCompressor:1)},
257
456
  * or relying on something (like the Fluid runtime) to synchronize the range information using internal mechanisms.
258
457
  *
259
- * The {@link IIdCompressor} provided by the Fluid Framework runtime will automatically track {@link IdCreationRange| Id Creation Ranges} (via its own ops) from other clients in the container.
458
+ * The {@link IIdCompressor} provided by the Fluid Framework runtime will automatically track {@link IdCreationRange | Id Creation Ranges} (via its own ops) from other clients in the container.
260
459
  * It is up to the user of the {@link IIdCompressor} to ensure the relevant session information is available,
261
460
  * either by depending on the originating client's session ID for ops, or by explicitly storing it alongside the data containing the {@link OpSpaceCompressedId}.
262
461
  *
263
462
  * Note that attachment summaries, while they are ops, are also interpreted as summaries without the context of their originating session,
264
463
  * so they should explicitly include session IDs if required to decode them just like regular summaries and exported data.
464
+ *
465
+ * @param id - The ID to normalize.
466
+ * @param originSessionId - The {@link IIdCompressor.localSessionId} of the compressor which {@link IIdCompressor.normalizeToOpSpace|encoded} `id`.
467
+ * This might not be the client that created `id` in the first place, but rather the client that serialized it.
468
+ * This is an important distinction in the case of a reference, where a client might refer to an ID created by another client.
469
+ * @returns The session-space ID in the local session corresponding to `id`.
265
470
  */
266
471
  normalizeToSessionSpace(
267
472
  id: OpSpaceCompressedId,
@@ -10,7 +10,19 @@ export type {
10
10
  SerializedIdCompressorWithOngoingSession,
11
11
  } from "./persisted-types/index.js";
12
12
 
13
- export type { IIdCompressorCore, IIdCompressor } from "./idCompressor.js";
13
+ export type {
14
+ IIdCompressorCore,
15
+ IIdCompressor,
16
+ ShardToken,
17
+ ShardSynchronizationToken,
18
+ SerializedIdCompressorShard,
19
+ ParentShardSynchronizationToken,
20
+ IdCompressorEvents,
21
+ } from "./idCompressor.js";
22
+ export {
23
+ SerializationVersion,
24
+ type SerializationVersion as SerializationVersionType,
25
+ } from "./idCompressor.js";
14
26
 
15
27
  export type {
16
28
  SessionSpaceCompressedId,