@fluidframework/id-compressor 2.0.0-internal.8.0.1 → 2.0.0-rc.1.0.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 (129) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +0 -6
  3. package/api-extractor-esm.json +4 -0
  4. package/api-extractor-lint.json +1 -10
  5. package/api-extractor.json +1 -9
  6. package/api-report/id-compressor.api.md +7 -5
  7. package/dist/id-compressor-alpha.d.ts +32 -5
  8. package/dist/id-compressor-beta.d.ts +142 -9
  9. package/dist/id-compressor-public.d.ts +142 -9
  10. package/dist/id-compressor-untrimmed.d.ts +32 -5
  11. package/dist/idCompressor.d.ts +6 -0
  12. package/dist/idCompressor.d.ts.map +1 -1
  13. package/dist/idCompressor.js +58 -20
  14. package/dist/idCompressor.js.map +1 -1
  15. package/dist/packageVersion.d.ts +1 -1
  16. package/dist/packageVersion.d.ts.map +1 -1
  17. package/dist/packageVersion.js +1 -1
  18. package/dist/packageVersion.js.map +1 -1
  19. package/dist/sessions.js +3 -6
  20. package/dist/sessions.js.map +1 -1
  21. package/dist/types/idCompressor.d.ts +22 -1
  22. package/dist/types/idCompressor.d.ts.map +1 -1
  23. package/dist/types/idCompressor.js.map +1 -1
  24. package/dist/types/identifiers.d.ts +4 -4
  25. package/dist/types/identifiers.js.map +1 -1
  26. package/lib/{appendOnlySortedMap.d.ts → appendOnlySortedMap.d.mts} +1 -1
  27. package/lib/appendOnlySortedMap.d.mts.map +1 -0
  28. package/lib/{appendOnlySortedMap.js → appendOnlySortedMap.mjs} +1 -3
  29. package/lib/appendOnlySortedMap.mjs.map +1 -0
  30. package/lib/{finalSpace.d.ts → finalSpace.d.mts} +3 -3
  31. package/lib/finalSpace.d.mts.map +1 -0
  32. package/lib/{finalSpace.js → finalSpace.mjs} +2 -2
  33. package/lib/finalSpace.mjs.map +1 -0
  34. package/lib/{id-compressor-alpha.d.ts → id-compressor-alpha.d.mts} +32 -5
  35. package/lib/id-compressor-beta.d.mts +172 -0
  36. package/lib/id-compressor-public.d.mts +172 -0
  37. package/lib/{id-compressor-untrimmed.d.ts → id-compressor-untrimmed.d.mts} +32 -5
  38. package/lib/{idCompressor.d.ts → idCompressor.d.mts} +8 -2
  39. package/lib/idCompressor.d.mts.map +1 -0
  40. package/lib/{idCompressor.js → idCompressor.mjs} +65 -27
  41. package/lib/idCompressor.mjs.map +1 -0
  42. package/lib/{identifiers.d.ts → identifiers.d.mts} +2 -2
  43. package/lib/identifiers.d.mts.map +1 -0
  44. package/lib/{identifiers.js → identifiers.mjs} +1 -1
  45. package/lib/identifiers.mjs.map +1 -0
  46. package/lib/{index.d.ts → index.d.mts} +4 -7
  47. package/lib/index.d.mts.map +1 -0
  48. package/lib/{index.js → index.mjs} +3 -6
  49. package/lib/index.mjs.map +1 -0
  50. package/lib/{packageVersion.d.ts → packageVersion.d.mts} +2 -2
  51. package/lib/packageVersion.d.mts.map +1 -0
  52. package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
  53. package/lib/packageVersion.mjs.map +1 -0
  54. package/lib/{persistanceUtilities.d.ts → persistanceUtilities.d.mts} +2 -2
  55. package/lib/persistanceUtilities.d.mts.map +1 -0
  56. package/lib/{persistanceUtilities.js → persistanceUtilities.mjs} +1 -1
  57. package/lib/persistanceUtilities.mjs.map +1 -0
  58. package/lib/{sessionSpaceNormalizer.d.ts → sessionSpaceNormalizer.d.mts} +3 -3
  59. package/lib/sessionSpaceNormalizer.d.mts.map +1 -0
  60. package/lib/{sessionSpaceNormalizer.js → sessionSpaceNormalizer.mjs} +3 -3
  61. package/lib/sessionSpaceNormalizer.mjs.map +1 -0
  62. package/lib/{sessions.d.ts → sessions.d.mts} +3 -3
  63. package/lib/sessions.d.mts.map +1 -0
  64. package/lib/{sessions.js → sessions.mjs} +3 -3
  65. package/lib/sessions.mjs.map +1 -0
  66. package/lib/types/{idCompressor.d.ts → idCompressor.d.mts} +25 -4
  67. package/lib/types/idCompressor.d.mts.map +1 -0
  68. package/lib/types/{identifiers.js → idCompressor.mjs} +1 -1
  69. package/lib/types/idCompressor.mjs.map +1 -0
  70. package/lib/types/{identifiers.d.ts → identifiers.d.mts} +5 -5
  71. package/lib/types/identifiers.d.mts.map +1 -0
  72. package/lib/types/{idCompressor.js → identifiers.mjs} +1 -1
  73. package/lib/types/identifiers.mjs.map +1 -0
  74. package/lib/types/{index.d.ts → index.d.mts} +4 -4
  75. package/lib/types/index.d.mts.map +1 -0
  76. package/lib/types/{persisted-types/0.0.1.js → index.mjs} +1 -1
  77. package/lib/types/index.mjs.map +1 -0
  78. package/lib/types/persisted-types/{0.0.1.d.ts → 0.0.1.d.mts} +2 -2
  79. package/lib/types/persisted-types/0.0.1.d.mts.map +1 -0
  80. package/lib/types/persisted-types/{index.js → 0.0.1.mjs} +1 -1
  81. package/lib/types/persisted-types/0.0.1.mjs.map +1 -0
  82. package/lib/types/persisted-types/{index.d.ts → index.d.mts} +2 -2
  83. package/lib/types/persisted-types/index.d.mts.map +1 -0
  84. package/lib/types/{index.js → persisted-types/index.mjs} +1 -1
  85. package/lib/types/persisted-types/index.mjs.map +1 -0
  86. package/lib/{utilities.d.ts → utilities.d.mts} +3 -3
  87. package/lib/utilities.d.mts.map +1 -0
  88. package/lib/{utilities.js → utilities.mjs} +1 -2
  89. package/lib/utilities.mjs.map +1 -0
  90. package/package.json +26 -20
  91. package/src/idCompressor.ts +76 -19
  92. package/src/packageVersion.ts +1 -1
  93. package/src/sessions.ts +1 -1
  94. package/src/types/idCompressor.ts +23 -1
  95. package/src/types/identifiers.ts +4 -4
  96. package/tsconfig.json +4 -2
  97. package/lib/appendOnlySortedMap.d.ts.map +0 -1
  98. package/lib/appendOnlySortedMap.js.map +0 -1
  99. package/lib/finalSpace.d.ts.map +0 -1
  100. package/lib/finalSpace.js.map +0 -1
  101. package/lib/id-compressor-beta.d.ts +0 -39
  102. package/lib/id-compressor-public.d.ts +0 -39
  103. package/lib/idCompressor.d.ts.map +0 -1
  104. package/lib/idCompressor.js.map +0 -1
  105. package/lib/identifiers.d.ts.map +0 -1
  106. package/lib/identifiers.js.map +0 -1
  107. package/lib/index.d.ts.map +0 -1
  108. package/lib/index.js.map +0 -1
  109. package/lib/packageVersion.d.ts.map +0 -1
  110. package/lib/packageVersion.js.map +0 -1
  111. package/lib/persistanceUtilities.d.ts.map +0 -1
  112. package/lib/persistanceUtilities.js.map +0 -1
  113. package/lib/sessionSpaceNormalizer.d.ts.map +0 -1
  114. package/lib/sessionSpaceNormalizer.js.map +0 -1
  115. package/lib/sessions.d.ts.map +0 -1
  116. package/lib/sessions.js.map +0 -1
  117. package/lib/types/idCompressor.d.ts.map +0 -1
  118. package/lib/types/idCompressor.js.map +0 -1
  119. package/lib/types/identifiers.d.ts.map +0 -1
  120. package/lib/types/identifiers.js.map +0 -1
  121. package/lib/types/index.d.ts.map +0 -1
  122. package/lib/types/index.js.map +0 -1
  123. package/lib/types/persisted-types/0.0.1.d.ts.map +0 -1
  124. package/lib/types/persisted-types/0.0.1.js.map +0 -1
  125. package/lib/types/persisted-types/index.d.ts.map +0 -1
  126. package/lib/types/persisted-types/index.js.map +0 -1
  127. package/lib/utilities.d.ts.map +0 -1
  128. package/lib/utilities.js.map +0 -1
  129. package/tsconfig.esnext.json +0 -7
@@ -0,0 +1,172 @@
1
+ import { ITelemetryBaseLogger } from '@fluidframework/core-interfaces';
2
+
3
+ /* Excluded from this release type: assertIsStableId */
4
+
5
+ /* Excluded from this release type: createIdCompressor */
6
+
7
+ /* Excluded from this release type: createSessionId */
8
+
9
+ /* Excluded from this release type: deserializeIdCompressor */
10
+
11
+ /* Excluded from this release type: generateStableId */
12
+
13
+ /* Excluded from this release type: IdCompressor */
14
+
15
+ /* Excluded from this release type: IdCreationRange */
16
+
17
+ /**
18
+ * A distributed UUID generator and compressor.
19
+ *
20
+ * Generates arbitrary non-colliding v4 UUIDs, called stable IDs, for multiple "sessions" (which can be distributed across the network),
21
+ * providing each session with the ability to map these UUIDs to `numbers`.
22
+ *
23
+ * A session is a unique identifier that denotes a single compressor. New IDs are created through a single compressor API
24
+ * which should then sent in ranges to the server for total ordering (and are subsequently relayed to other clients). When a new ID is
25
+ * created it is said to be created by the compressor's "local" session.
26
+ *
27
+ * For each stable ID created, two numeric IDs are provided by the compressor:
28
+ *
29
+ * 1. A session-local ID, which is stable for the lifetime of the session (which could be longer than that of the compressor object, as it may
30
+ * be serialized for offline usage). Available as soon as the stable ID is allocated. These IDs are session-unique and are thus only
31
+ * safely usable within the scope of the compressor that created it.
32
+ *
33
+ * 2. A final ID, which is stable across serialization and deserialization of an IdCompressor. Available as soon as the range containing
34
+ * the corresponding session-local ID is totally ordered (via consensus) with respect to other sessions' allocations.
35
+ * Final IDs are known to and publicly usable by any compressor that has received them.
36
+ *
37
+ * Compressors will allocate UUIDs in non-random ways to reduce entropy allowing for optimized storage of the data needed
38
+ * to map the UUIDs to the numbers.
39
+ *
40
+ * The following invariants are upheld by IdCompressor:
41
+ *
42
+ * 1. Session-local IDs will always decompress to the same UUIDs for the lifetime of the session.
43
+ *
44
+ * 2. Final IDs will always decompress to the same UUIDs.
45
+ *
46
+ * 3. After a server-processed range of session-local IDs (from any session) is received by a compressor, any of those session-local IDs may be
47
+ * translated by the compressor into the corresponding final ID. For any given session-local ID, this translation will always yield the
48
+ * same final ID.
49
+ *
50
+ * 4. A UUID will always compress into the same session-local ID for the lifetime of the session.
51
+ *
52
+ * Session-local IDs are sent across the wire in efficiently-represented ranges. These ranges are created by querying the compressor, and *must*
53
+ * be ordered (i.e. sent to the server) in the order they are created in order to preserve the above invariants.
54
+ *
55
+ * Session-local IDs can be used immediately after creation, but will eventually (after being sequenced) have a corresponding final ID. This
56
+ * could make reasoning about equality of those two forms difficult. For example, if a cache is keyed off of a
57
+ * session-local ID but is later queried using the final ID (which is semantically equal, as it decompresses to the same UUID/string) it will
58
+ * produce a cache miss. In order to make using collections of both remotely created and locally created IDs easy, regardless of whether the
59
+ * session-local IDs have been finalized, the compressor defines two "spaces" of IDs:
60
+ *
61
+ * 1. Session space: in this space, all IDs are normalized to their "most local form". This means that all IDs created by the local session
62
+ * will be in local form, regardless of if they have been finalized. Remotely created IDs, which could only have been received after
63
+ * finalizing and will never have a local form for the compressor, will of course be final IDs. This space should be used with consumer APIs
64
+ * and data structures, as the lifetime of the IDs is guaranteed to be the same as the compressor object. Care must be taken to not use
65
+ * these IDs across compressor objects, as the local IDs are specific to the compressor that created them.
66
+ *
67
+ * 2. Op space: in this space, all IDs are normalized to their "most final form". This means that all IDs except session-local IDs that
68
+ * have not yet been finalized will be in final ID form. This space is useful for serialization in ops (e.g. references), as other clients
69
+ * that receive them need not do any work to normalize them to *their* session-space in the common case. Note that IDs in op space may move
70
+ * out of Op space over time, namely, when a session-local ID in this space becomes finalized, and thereafter has a "more final form".
71
+ * Consequentially, it may be useful to restrict parameters of a persisted type to this space (to optimize perf), but it is potentially
72
+ * incorrect to use this type for a runtime variable. This is an asymmetry that does not affect session space, as local IDs are always as
73
+ * "local as possible".
74
+ *
75
+ * These two spaces naturally define a rule: consumers of compressed IDs should use session-space IDs, but serialized forms such as ops
76
+ * should use op-space IDs.
77
+ * @public
78
+ */
79
+ export declare interface IIdCompressor {
80
+ localSessionId: SessionId;
81
+ /**
82
+ * Generates a new compressed ID or returns an existing one.
83
+ * This should ONLY be called to generate IDs for local operations.
84
+ * @returns A new local ID in session space.
85
+ */
86
+ generateCompressedId(): SessionSpaceCompressedId;
87
+ /**
88
+ * Normalizes a session space ID into op space.
89
+ * @param id - the local ID to normalize.
90
+ * @returns the ID in op space.
91
+ */
92
+ normalizeToOpSpace(id: SessionSpaceCompressedId): OpSpaceCompressedId;
93
+ /**
94
+ * Normalizes an ID into session space.
95
+ * @param id - the ID to normalize. If it is a local ID, it is assumed to have been created by the session corresponding
96
+ * to `sessionId`.
97
+ * @param originSessionId - the session from which `id` originated
98
+ * @returns the session-space ID corresponding to `id`, which might not have been a final ID if the client that created it had not yet
99
+ * finalized it. This can occur when a client references an ID during the window of time in which it is waiting to receive the ordered
100
+ * range that contained it from the server.
101
+ */
102
+ normalizeToSessionSpace(id: OpSpaceCompressedId, originSessionId: SessionId): SessionSpaceCompressedId;
103
+ /**
104
+ * Decompresses a previously compressed ID into a UUID.
105
+ * @param id - the compressed ID to be decompressed.
106
+ * @returns the UUID associated with the compressed ID. Fails if the ID was not generated by this compressor.
107
+ */
108
+ decompress(id: SessionSpaceCompressedId): StableId;
109
+ /**
110
+ * Recompresses a UUID.
111
+ * @param uncompressed - the UUID to recompress.
112
+ * @returns the `CompressedId` associated with `uncompressed`. Fails if it has not been previously compressed by this compressor.
113
+ */
114
+ recompress(uncompressed: StableId): SessionSpaceCompressedId;
115
+ /**
116
+ * Attempts to recompresses a UUID.
117
+ * @param uncompressed - the UUID to recompress,
118
+ * @returns the `CompressedId` associated with `uncompressed` or undefined if it has not been previously compressed by this compressor.
119
+ */
120
+ tryRecompress(uncompressed: StableId): SessionSpaceCompressedId | undefined;
121
+ }
122
+
123
+ /* Excluded from this release type: IIdCompressorCore */
124
+
125
+ /* Excluded from this release type: isStableId */
126
+
127
+ /**
128
+ * A compressed ID that has been normalized into "op space".
129
+ * Serialized/persisted structures (e.g. ops) should use op-space IDs as a performance optimization, as they require less normalizing when
130
+ * received by a remote client due to the fact that op space for a given compressor is session space for all other compressors.
131
+ * @public
132
+ */
133
+ export declare type OpSpaceCompressedId = number & {
134
+ readonly OpNormalized: "9209432d-a959-4df7-b2ad-767ead4dbcae";
135
+ };
136
+
137
+ /* Excluded from this release type: SerializedIdCompressor */
138
+
139
+ /* Excluded from this release type: SerializedIdCompressorWithNoSession */
140
+
141
+ /* Excluded from this release type: SerializedIdCompressorWithOngoingSession */
142
+
143
+ /**
144
+ * A StableId which is suitable for use as a session identifier
145
+ * @public
146
+ */
147
+ export declare type SessionId = StableId & {
148
+ readonly SessionId: "4498f850-e14e-4be9-8db0-89ec00997e58";
149
+ };
150
+
151
+ /**
152
+ * A compressed ID that has been normalized into "session space" (see `IdCompressor` for more).
153
+ * Consumer-facing APIs and data structures should use session-space IDs as their lifetime and equality is stable and tied to
154
+ * the scope of the session (i.e. compressor) that produced them.
155
+ * @public
156
+ */
157
+ export declare type SessionSpaceCompressedId = number & {
158
+ readonly SessionUnique: "cea55054-6b82-4cbf-ad19-1fa645ea3b3e";
159
+ };
160
+
161
+ /**
162
+ * A version 4, variant 1 uuid (https://datatracker.ietf.org/doc/html/rfc4122).
163
+ * A 128-bit Universally Unique IDentifier. Represented here
164
+ * with a string of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
165
+ * where x is a lowercase hex digit.
166
+ * @public
167
+ */
168
+ export declare type StableId = string & {
169
+ readonly StableId: "53172b0d-a3d5-41ea-bd75-b43839c97f5a";
170
+ };
171
+
172
+ export { }
@@ -73,6 +73,7 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
73
73
  private nextRequestedClusterSize;
74
74
  private telemetryLocalIdCount;
75
75
  private telemetryEagerFinalIdCount;
76
+ private ongoingGhostSession?;
76
77
  private constructor();
77
78
  /**
78
79
  * @deprecated Use `createIdCompressor` instead.
@@ -83,6 +84,10 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
83
84
  */
84
85
  static create(sessionId: SessionId, logger?: ITelemetryBaseLogger): IIdCompressor & IIdCompressorCore;
85
86
  generateCompressedId(): SessionSpaceCompressedId;
87
+ /**
88
+ * {@inheritdoc IIdCompressorCore.beginGhostSession}
89
+ */
90
+ beginGhostSession(ghostSessionId: SessionId, ghostSessionCallback: () => void): void;
86
91
  private generateNextLocalId;
87
92
  takeNextCreationRange(): IdCreationRange;
88
93
  private static assertValidRange;
@@ -97,6 +102,7 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
97
102
  serialize(withSession: false): SerializedIdCompressorWithNoSession;
98
103
  static deserialize(serialized: SerializedIdCompressorWithOngoingSession): IdCompressor;
99
104
  static deserialize(serialized: SerializedIdCompressorWithNoSession, newSessionId: SessionId): IdCompressor;
105
+ private static deserialize2_0;
100
106
  equals(other: IdCompressor, includeLocalState: boolean): boolean;
101
107
  }
102
108
 
@@ -186,7 +192,7 @@ export declare interface IdCreationRange {
186
192
  *
187
193
  * These two spaces naturally define a rule: consumers of compressed IDs should use session-space IDs, but serialized forms such as ops
188
194
  * should use op-space IDs.
189
- * @alpha
195
+ * @public
190
196
  */
191
197
  export declare interface IIdCompressor {
192
198
  localSessionId: SessionId;
@@ -248,6 +254,27 @@ export declare interface IIdCompressorCore {
248
254
  * @param range - the range of session-local IDs to finalize.
249
255
  */
250
256
  finalizeCreationRange(range: IdCreationRange): void;
257
+ /**
258
+ * Run a callback that is performed from the perspective of a special "ghost" session.
259
+ * Any ids generated by this session will be immediately finalized on the local client as if
260
+ * they were created by a remote client with `ghostSessionId`.
261
+ *
262
+ * *WARNING:* This API requires an external consensus mechanism to safely use:
263
+ * In an attached container (i.e. multiple clients may have the document loaded), all clients must guarantee that:
264
+ * - They invoke this API starting from the same finalized creation ranges
265
+ * - This API is invoked with the same ghost session id
266
+ * - `ghostSessionCallback` deterministically mints the same number of ids on each client within the ghost session
267
+ * Failure to meet these requirement will result in divergence across clients and eventual consistency errors.
268
+ * While the ghost sesion callback is running, IdCompressor does not support serialization.
269
+ * @remarks This API is primarily intended for data migration scenarios which are able to deterministically transform
270
+ * data in some format into data in a new format.
271
+ * The first requirement (that all clients must invoke the API with the same finalized creation ranges) is guaranteed
272
+ * for this scenario because the data transformation callback occurs at a specific ack within the op stream on all
273
+ * clients, and clients don't finalize creation ranges for local changes they might have at this point in time.
274
+ * @param ghostSessionId - The session id that minted ids generated within `ghostSessionCallback` should be attributed to.
275
+ * @param ghostSessionCallback - Callback which mints ids attributed to the ghost session.
276
+ */
277
+ beginGhostSession(ghostSessionId: SessionId, ghostSessionCallback: () => void): any;
251
278
  /**
252
279
  * Returns a persistable form of the current state of this `IdCompressor` which can be rehydrated via `IdCompressor.deserialize()`.
253
280
  * This includes finalized state as well as un-finalized state and is therefore suitable for use in offline scenarios.
@@ -271,7 +298,7 @@ export declare function isStableId(str: string): str is StableId;
271
298
  * A compressed ID that has been normalized into "op space".
272
299
  * Serialized/persisted structures (e.g. ops) should use op-space IDs as a performance optimization, as they require less normalizing when
273
300
  * received by a remote client due to the fact that op space for a given compressor is session space for all other compressors.
274
- * @alpha
301
+ * @public
275
302
  */
276
303
  export declare type OpSpaceCompressedId = number & {
277
304
  readonly OpNormalized: "9209432d-a959-4df7-b2ad-767ead4dbcae";
@@ -303,7 +330,7 @@ export declare type SerializedIdCompressorWithOngoingSession = SerializedIdCompr
303
330
 
304
331
  /**
305
332
  * A StableId which is suitable for use as a session identifier
306
- * @alpha
333
+ * @public
307
334
  */
308
335
  export declare type SessionId = StableId & {
309
336
  readonly SessionId: "4498f850-e14e-4be9-8db0-89ec00997e58";
@@ -313,7 +340,7 @@ export declare type SessionId = StableId & {
313
340
  * A compressed ID that has been normalized into "session space" (see `IdCompressor` for more).
314
341
  * Consumer-facing APIs and data structures should use session-space IDs as their lifetime and equality is stable and tied to
315
342
  * the scope of the session (i.e. compressor) that produced them.
316
- * @alpha
343
+ * @public
317
344
  */
318
345
  export declare type SessionSpaceCompressedId = number & {
319
346
  readonly SessionUnique: "cea55054-6b82-4cbf-ad19-1fa645ea3b3e";
@@ -324,7 +351,7 @@ export declare type SessionSpaceCompressedId = number & {
324
351
  * A 128-bit Universally Unique IDentifier. Represented here
325
352
  * with a string of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
326
353
  * where x is a lowercase hex digit.
327
- * @alpha
354
+ * @public
328
355
  */
329
356
  export declare type StableId = string & {
330
357
  readonly StableId: "53172b0d-a3d5-41ea-bd75-b43839c97f5a";
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
6
- import { IdCreationRange, IIdCompressor, IIdCompressorCore, OpSpaceCompressedId, SerializedIdCompressorWithNoSession, SerializedIdCompressorWithOngoingSession, SessionId, SessionSpaceCompressedId, StableId } from "./types";
6
+ import { IdCreationRange, IIdCompressor, IIdCompressorCore, OpSpaceCompressedId, SerializedIdCompressorWithNoSession, SerializedIdCompressorWithOngoingSession, SessionId, SessionSpaceCompressedId, StableId } from "./types/index.mjs";
7
7
  /**
8
8
  * See {@link IIdCompressor} and {@link IIdCompressorCore}
9
9
  *
@@ -34,6 +34,7 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
34
34
  private nextRequestedClusterSize;
35
35
  private telemetryLocalIdCount;
36
36
  private telemetryEagerFinalIdCount;
37
+ private ongoingGhostSession?;
37
38
  private constructor();
38
39
  /**
39
40
  * @deprecated Use `createIdCompressor` instead.
@@ -44,6 +45,10 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
44
45
  */
45
46
  static create(sessionId: SessionId, logger?: ITelemetryBaseLogger): IIdCompressor & IIdCompressorCore;
46
47
  generateCompressedId(): SessionSpaceCompressedId;
48
+ /**
49
+ * {@inheritdoc IIdCompressorCore.beginGhostSession}
50
+ */
51
+ beginGhostSession(ghostSessionId: SessionId, ghostSessionCallback: () => void): void;
47
52
  private generateNextLocalId;
48
53
  takeNextCreationRange(): IdCreationRange;
49
54
  private static assertValidRange;
@@ -58,6 +63,7 @@ export declare class IdCompressor implements IIdCompressor, IIdCompressorCore {
58
63
  serialize(withSession: false): SerializedIdCompressorWithNoSession;
59
64
  static deserialize(serialized: SerializedIdCompressorWithOngoingSession): IdCompressor;
60
65
  static deserialize(serialized: SerializedIdCompressorWithNoSession, newSessionId: SessionId): IdCompressor;
66
+ private static deserialize2_0;
61
67
  equals(other: IdCompressor, includeLocalState: boolean): boolean;
62
68
  }
63
69
  /**
@@ -81,4 +87,4 @@ export declare function deserializeIdCompressor(serialized: SerializedIdCompress
81
87
  * @alpha
82
88
  */
83
89
  export declare function deserializeIdCompressor(serialized: SerializedIdCompressorWithNoSession, newSessionId: SessionId): IIdCompressor & IIdCompressorCore;
84
- //# sourceMappingURL=idCompressor.d.ts.map
90
+ //# sourceMappingURL=idCompressor.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idCompressor.d.mts","sourceRoot":"","sources":["../src/idCompressor.ts"],"names":[],"mappings":"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"}
@@ -5,17 +5,17 @@
5
5
  import { assert } from "@fluidframework/core-utils";
6
6
  import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
7
7
  import { createChildLogger } from "@fluidframework/telemetry-utils";
8
- import { isFinalId } from "./identifiers";
9
- import { createSessionId, localIdFromGenCount, genCountFromLocalId, numericUuidFromStableId, offsetNumericUuid, stableIdFromNumericUuid, subtractNumericUuids, } from "./utilities";
10
- import { readBoolean, readNumber, readNumericUuid, writeBoolean, writeNumber, writeNumericUuid, } from "./persistanceUtilities";
11
- import { getAlignedLocal, getAlignedFinal, lastFinalizedLocal, Session, Sessions, } from "./sessions";
12
- import { SessionSpaceNormalizer } from "./sessionSpaceNormalizer";
13
- import { FinalSpace } from "./finalSpace";
8
+ import { isFinalId } from "./identifiers.mjs";
9
+ import { createSessionId, localIdFromGenCount, genCountFromLocalId, numericUuidFromStableId, offsetNumericUuid, stableIdFromNumericUuid, subtractNumericUuids, } from "./utilities.mjs";
10
+ import { readBoolean, readNumber, readNumericUuid, writeBoolean, writeNumber, writeNumericUuid, } from "./persistanceUtilities.mjs";
11
+ import { getAlignedLocal, getAlignedFinal, lastFinalizedLocal, Session, Sessions, lastFinalizedFinal, } from "./sessions.mjs";
12
+ import { SessionSpaceNormalizer } from "./sessionSpaceNormalizer.mjs";
13
+ import { FinalSpace } from "./finalSpace.mjs";
14
14
  /**
15
15
  * The version of IdCompressor that is currently persisted.
16
16
  * This should not be changed without careful consideration to compatibility.
17
17
  */
18
- const currentWrittenVersion = 1;
18
+ const currentWrittenVersion = 2.0;
19
19
  /**
20
20
  * See {@link IIdCompressor} and {@link IIdCompressorCore}
21
21
  *
@@ -87,27 +87,52 @@ export class IdCompressor {
87
87
  return compressor;
88
88
  }
89
89
  generateCompressedId() {
90
- this.localGenCount++;
91
- const lastCluster = this.localSession.getLastCluster();
92
- if (lastCluster === undefined) {
90
+ // This ghost session code inside this block should not be changed without a version bump (it is performed at a consensus point)
91
+ if (this.ongoingGhostSession) {
92
+ if (this.ongoingGhostSession.cluster === undefined) {
93
+ this.ongoingGhostSession.cluster = this.addEmptyCluster(this.sessions.getOrCreate(this.ongoingGhostSession.ghostSessionId), 1);
94
+ }
95
+ else {
96
+ this.ongoingGhostSession.cluster.capacity++;
97
+ }
98
+ this.ongoingGhostSession.cluster.count++;
99
+ return lastFinalizedFinal(this.ongoingGhostSession.cluster);
100
+ }
101
+ else {
102
+ this.localGenCount++;
103
+ const lastCluster = this.localSession.getLastCluster();
104
+ if (lastCluster === undefined) {
105
+ this.telemetryLocalIdCount++;
106
+ return this.generateNextLocalId();
107
+ }
108
+ // If there exists a cluster of final IDs already claimed by the local session that still has room in it,
109
+ // it is known prior to range sequencing what a local ID's corresponding final ID will be.
110
+ // In this case, it is safe to return the final ID immediately. This is guaranteed to be safe because
111
+ // any op that the local session sends that contains one of those final IDs are guaranteed to arrive to
112
+ // collaborators *after* the one containing the creation range.
113
+ const clusterOffset = this.localGenCount - genCountFromLocalId(lastCluster.baseLocalId);
114
+ if (lastCluster.capacity > clusterOffset) {
115
+ this.telemetryEagerFinalIdCount++;
116
+ // Space in the cluster: eager final
117
+ return (lastCluster.baseFinalId +
118
+ clusterOffset);
119
+ }
120
+ // No space in the cluster, return next local
93
121
  this.telemetryLocalIdCount++;
94
122
  return this.generateNextLocalId();
95
123
  }
96
- // If there exists a cluster of final IDs already claimed by the local session that still has room in it,
97
- // it is known prior to range sequencing what a local ID's corresponding final ID will be.
98
- // In this case, it is safe to return the final ID immediately. This is guaranteed to be safe because
99
- // any op that the local session sends that contains one of those final IDs are guaranteed to arrive to
100
- // collaborators *after* the one containing the creation range.
101
- const clusterOffset = this.localGenCount - genCountFromLocalId(lastCluster.baseLocalId);
102
- if (lastCluster.capacity > clusterOffset) {
103
- this.telemetryEagerFinalIdCount++;
104
- // Space in the cluster: eager final
105
- return (lastCluster.baseFinalId +
106
- clusterOffset);
107
- }
108
- // No space in the cluster, return next local
109
- this.telemetryLocalIdCount++;
110
- return this.generateNextLocalId();
124
+ }
125
+ /**
126
+ * {@inheritdoc IIdCompressorCore.beginGhostSession}
127
+ */
128
+ beginGhostSession(ghostSessionId, ghostSessionCallback) {
129
+ this.ongoingGhostSession = { ghostSessionId };
130
+ try {
131
+ ghostSessionCallback();
132
+ }
133
+ finally {
134
+ this.ongoingGhostSession = undefined;
135
+ }
111
136
  }
112
137
  generateNextLocalId() {
113
138
  // Must tell the normalizer that we generated a local ID
@@ -115,6 +140,7 @@ export class IdCompressor {
115
140
  return localIdFromGenCount(this.localGenCount);
116
141
  }
117
142
  takeNextCreationRange() {
143
+ assert(!this.ongoingGhostSession, 0x8a6 /* IdCompressor should not be operated normally when in a ghost session */);
118
144
  const count = this.localGenCount - (this.nextRangeBaseGenCount - 1);
119
145
  if (count === 0) {
120
146
  return {
@@ -143,6 +169,7 @@ export class IdCompressor {
143
169
  assert(requestedClusterSize <= IdCompressor.maxClusterSize, 0x877 /* Clusters must not exceed max cluster size. */);
144
170
  }
145
171
  finalizeCreationRange(range) {
172
+ assert(!this.ongoingGhostSession, 0x8a7 /* IdCompressor should not be operated normally when in a ghost session */);
146
173
  // Check if the range has IDs
147
174
  if (range.ids === undefined) {
148
175
  return;
@@ -219,6 +246,7 @@ export class IdCompressor {
219
246
  assert(!session.isEmpty(), 0x757 /* Empty sessions should not be created. */);
220
247
  }
221
248
  addEmptyCluster(session, capacity) {
249
+ assert(!this.ongoingGhostSession?.cluster, 0x8a8 /* IdCompressor should not be operated normally when in a ghost session */);
222
250
  const newCluster = session.addNewCluster(this.finalSpace.getAllocatedIdLimit(), capacity, 0);
223
251
  assert(!this.sessions.clusterCollides(newCluster), 0x758 /* Cluster collision detected. */);
224
252
  this.finalSpace.addCluster(newCluster);
@@ -358,6 +386,7 @@ export class IdCompressor {
358
386
  }
359
387
  }
360
388
  serialize(hasLocalState) {
389
+ assert(!this.ongoingGhostSession, 0x8a9 /* IdCompressor should not be operated normally when in a ghost session */);
361
390
  const { normalizer, finalSpace, sessions } = this;
362
391
  const sessionIndexMap = new Map();
363
392
  let sessionIndex = 0;
@@ -423,7 +452,16 @@ export class IdCompressor {
423
452
  bufferUint: new BigUint64Array(buffer),
424
453
  };
425
454
  const version = readNumber(index);
426
- assert(version === currentWrittenVersion, 0x75c /* Unknown serialized version. */);
455
+ switch (version) {
456
+ case 1.0:
457
+ throw new Error("IdCompressor version 1.0 is no longer supported.");
458
+ case 2.0:
459
+ return IdCompressor.deserialize2_0(index, sessionId);
460
+ default:
461
+ throw new Error("Unknown IdCompressor serialized version.");
462
+ }
463
+ }
464
+ static deserialize2_0(index, sessionId) {
427
465
  const hasLocalState = readBoolean(index);
428
466
  const sessionCount = readNumber(index);
429
467
  const clusterCount = readNumber(index);
@@ -493,4 +531,4 @@ export function deserializeIdCompressor(serialized, sessionId) {
493
531
  ? IdCompressor.deserialize(serialized)
494
532
  : IdCompressor.deserialize(serialized, sessionId);
495
533
  }
496
- //# sourceMappingURL=idCompressor.js.map
534
+ //# sourceMappingURL=idCompressor.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idCompressor.mjs","sourceRoot":"","sources":["../src/idCompressor.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 { assert } from \"@fluidframework/core-utils\";\nimport { bufferToString, stringToBuffer } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { ITelemetryLoggerExt, createChildLogger } from \"@fluidframework/telemetry-utils\";\nimport {\n\tIdCreationRange,\n\tIIdCompressor,\n\tIIdCompressorCore,\n\tOpSpaceCompressedId,\n\tSerializedIdCompressor,\n\tSerializedIdCompressorWithNoSession,\n\tSerializedIdCompressorWithOngoingSession,\n\tSessionId,\n\tSessionSpaceCompressedId,\n\tStableId,\n} from \"./types\";\nimport { FinalCompressedId, isFinalId, LocalCompressedId, NumericUuid } from \"./identifiers\";\nimport {\n\tcreateSessionId,\n\tlocalIdFromGenCount,\n\tgenCountFromLocalId,\n\tnumericUuidFromStableId,\n\toffsetNumericUuid,\n\tstableIdFromNumericUuid,\n\tsubtractNumericUuids,\n} from \"./utilities\";\nimport {\n\tIndex,\n\treadBoolean,\n\treadNumber,\n\treadNumericUuid,\n\twriteBoolean,\n\twriteNumber,\n\twriteNumericUuid,\n} from \"./persistanceUtilities\";\nimport {\n\tgetAlignedLocal,\n\tgetAlignedFinal,\n\tIdCluster,\n\tlastFinalizedLocal,\n\tSession,\n\tSessions,\n\tlastFinalizedFinal,\n} from \"./sessions\";\nimport { SessionSpaceNormalizer } from \"./sessionSpaceNormalizer\";\nimport { FinalSpace } from \"./finalSpace\";\n\n/**\n * The version of IdCompressor that is currently persisted.\n * This should not be changed without careful consideration to compatibility.\n */\nconst currentWrittenVersion = 2.0;\n\n/**\n * See {@link IIdCompressor} and {@link IIdCompressorCore}\n *\n * @alpha\n *\n * @deprecated Not intended for public use and will be removed from the public API in the future.\n * Use `createIdCompressor` instead.\n */\nexport class IdCompressor implements IIdCompressor, IIdCompressorCore {\n\t/**\n\t * Max allowed initial cluster size.\n\t */\n\tpublic static readonly maxClusterSize = 2 ** 20;\n\n\t// #region Local state\n\n\tpublic readonly localSessionId: SessionId;\n\tprivate readonly localSession: Session;\n\tprivate readonly normalizer = new SessionSpaceNormalizer();\n\t// The number of IDs generated by the local session\n\tprivate localGenCount = 0;\n\n\t// #endregion\n\n\t// #region Final state\n\n\t// The gen count to be annotated on the range returned by the next call to `takeNextCreationRange`.\n\t// This is updated to be equal to `generatedIdCount` + 1 each time it is called.\n\tprivate nextRangeBaseGenCount = 1;\n\tprivate readonly sessions = new Sessions();\n\tprivate readonly finalSpace = new FinalSpace();\n\n\t// #endregion\n\n\t// #region Ephemeral state\n\n\t/**\n\t * Roughly equates to a minimum of 1M sessions before we start allocating 64 bit IDs.\n\t * Eventually, this can be adjusted dynamically to have cluster reservation policies that\n\t * optimize the number of eager finals.\n\t * It is not readonly as it is accessed by tests for clear-box testing.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly\n\tprivate nextRequestedClusterSize: number = 512;\n\t// The number of local IDs generated since the last telemetry was sent.\n\tprivate telemetryLocalIdCount = 0;\n\t// The number of eager final IDs generated since the last telemetry was sent.\n\tprivate telemetryEagerFinalIdCount = 0;\n\t// The ongoing ghost session, if one exists.\n\tprivate ongoingGhostSession?: { cluster?: IdCluster; ghostSessionId: SessionId };\n\n\t// #endregion\n\n\tprivate constructor(\n\t\tlocalSessionIdOrDeserialized: SessionId | Sessions,\n\t\tprivate readonly logger?: ITelemetryLoggerExt,\n\t) {\n\t\tif (typeof localSessionIdOrDeserialized === \"string\") {\n\t\t\tthis.localSessionId = localSessionIdOrDeserialized;\n\t\t\tthis.localSession = this.sessions.getOrCreate(localSessionIdOrDeserialized);\n\t\t} else {\n\t\t\t// Deserialize case\n\t\t\tthis.sessions = localSessionIdOrDeserialized;\n\t\t\t// As policy, the first session is always the local session. Preserve this invariant\n\t\t\t// during deserialization.\n\t\t\tconst firstSession = localSessionIdOrDeserialized.sessions().next();\n\t\t\tassert(!firstSession.done, 0x754 /* First session must be present. */);\n\t\t\tthis.localSession = firstSession.value;\n\t\t\tthis.localSessionId = stableIdFromNumericUuid(\n\t\t\t\tthis.localSession.sessionUuid,\n\t\t\t) as SessionId;\n\t\t}\n\t}\n\n\t/**\n\t * @deprecated Use `createIdCompressor` instead.\n\t */\n\tpublic static create(logger?: ITelemetryBaseLogger): IIdCompressor & IIdCompressorCore;\n\t/**\n\t * @deprecated Use `createIdCompressor` instead.\n\t */\n\tpublic static create(\n\t\tsessionId: SessionId,\n\t\tlogger?: ITelemetryBaseLogger,\n\t): IIdCompressor & IIdCompressorCore;\n\tpublic static create(\n\t\tsessionIdOrLogger?: SessionId | ITelemetryBaseLogger,\n\t\tloggerOrUndefined?: ITelemetryBaseLogger,\n\t): IIdCompressor & IIdCompressorCore {\n\t\tlet localSessionId: SessionId;\n\t\tlet logger: ITelemetryBaseLogger | undefined;\n\t\tif (sessionIdOrLogger === undefined) {\n\t\t\tlocalSessionId = createSessionId();\n\t\t} else {\n\t\t\tif (typeof sessionIdOrLogger === \"string\") {\n\t\t\t\tlocalSessionId = sessionIdOrLogger;\n\t\t\t\tlogger = loggerOrUndefined;\n\t\t\t} else {\n\t\t\t\tlocalSessionId = createSessionId();\n\t\t\t\tlogger = loggerOrUndefined;\n\t\t\t}\n\t\t}\n\t\tconst compressor = new IdCompressor(\n\t\t\tlocalSessionId,\n\t\t\tlogger === undefined ? undefined : createChildLogger({ logger }),\n\t\t);\n\t\treturn compressor;\n\t}\n\n\tpublic generateCompressedId(): SessionSpaceCompressedId {\n\t\t// This ghost session code inside this block should not be changed without a version bump (it is performed at a consensus point)\n\t\tif (this.ongoingGhostSession) {\n\t\t\tif (this.ongoingGhostSession.cluster === undefined) {\n\t\t\t\tthis.ongoingGhostSession.cluster = this.addEmptyCluster(\n\t\t\t\t\tthis.sessions.getOrCreate(this.ongoingGhostSession.ghostSessionId),\n\t\t\t\t\t1,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthis.ongoingGhostSession.cluster.capacity++;\n\t\t\t}\n\t\t\tthis.ongoingGhostSession.cluster.count++;\n\t\t\treturn lastFinalizedFinal(\n\t\t\t\tthis.ongoingGhostSession.cluster,\n\t\t\t) as unknown as SessionSpaceCompressedId;\n\t\t} else {\n\t\t\tthis.localGenCount++;\n\t\t\tconst lastCluster = this.localSession.getLastCluster();\n\t\t\tif (lastCluster === undefined) {\n\t\t\t\tthis.telemetryLocalIdCount++;\n\t\t\t\treturn this.generateNextLocalId();\n\t\t\t}\n\n\t\t\t// If there exists a cluster of final IDs already claimed by the local session that still has room in it,\n\t\t\t// it is known prior to range sequencing what a local ID's corresponding final ID will be.\n\t\t\t// In this case, it is safe to return the final ID immediately. This is guaranteed to be safe because\n\t\t\t// any op that the local session sends that contains one of those final IDs are guaranteed to arrive to\n\t\t\t// collaborators *after* the one containing the creation range.\n\t\t\tconst clusterOffset = this.localGenCount - genCountFromLocalId(lastCluster.baseLocalId);\n\t\t\tif (lastCluster.capacity > clusterOffset) {\n\t\t\t\tthis.telemetryEagerFinalIdCount++;\n\t\t\t\t// Space in the cluster: eager final\n\t\t\t\treturn ((lastCluster.baseFinalId as number) +\n\t\t\t\t\tclusterOffset) as SessionSpaceCompressedId;\n\t\t\t}\n\t\t\t// No space in the cluster, return next local\n\t\t\tthis.telemetryLocalIdCount++;\n\t\t\treturn this.generateNextLocalId();\n\t\t}\n\t}\n\n\t/**\n\t * {@inheritdoc IIdCompressorCore.beginGhostSession}\n\t */\n\tpublic beginGhostSession(ghostSessionId: SessionId, ghostSessionCallback: () => void) {\n\t\tthis.ongoingGhostSession = { ghostSessionId };\n\t\ttry {\n\t\t\tghostSessionCallback();\n\t\t} finally {\n\t\t\tthis.ongoingGhostSession = undefined;\n\t\t}\n\t}\n\n\tprivate generateNextLocalId(): LocalCompressedId {\n\t\t// Must tell the normalizer that we generated a local ID\n\t\tthis.normalizer.addLocalRange(this.localGenCount, 1);\n\t\treturn localIdFromGenCount(this.localGenCount);\n\t}\n\n\tpublic takeNextCreationRange(): IdCreationRange {\n\t\tassert(\n\t\t\t!this.ongoingGhostSession,\n\t\t\t0x8a6 /* IdCompressor should not be operated normally when in a ghost session */,\n\t\t);\n\t\tconst count = this.localGenCount - (this.nextRangeBaseGenCount - 1);\n\t\tif (count === 0) {\n\t\t\treturn {\n\t\t\t\tsessionId: this.localSessionId,\n\t\t\t};\n\t\t}\n\t\tconst range: IdCreationRange = {\n\t\t\tsessionId: this.localSessionId,\n\t\t\tids: {\n\t\t\t\tfirstGenCount: this.nextRangeBaseGenCount,\n\t\t\t\tcount,\n\t\t\t\trequestedClusterSize: this.nextRequestedClusterSize,\n\t\t\t},\n\t\t};\n\t\tthis.nextRangeBaseGenCount = this.localGenCount + 1;\n\t\tIdCompressor.assertValidRange(range);\n\t\treturn range;\n\t}\n\n\tprivate static assertValidRange(range: IdCreationRange): void {\n\t\tif (range.ids === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst { count, requestedClusterSize } = range.ids;\n\t\tassert(count > 0, 0x755 /* Malformed ID Range. */);\n\t\tassert(requestedClusterSize > 0, 0x876 /* Clusters must have a positive capacity. */);\n\t\tassert(\n\t\t\trequestedClusterSize <= IdCompressor.maxClusterSize,\n\t\t\t0x877 /* Clusters must not exceed max cluster size. */,\n\t\t);\n\t}\n\n\tpublic finalizeCreationRange(range: IdCreationRange): void {\n\t\tassert(\n\t\t\t!this.ongoingGhostSession,\n\t\t\t0x8a7 /* IdCompressor should not be operated normally when in a ghost session */,\n\t\t);\n\t\t// Check if the range has IDs\n\t\tif (range.ids === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tIdCompressor.assertValidRange(range);\n\t\tconst { sessionId, ids } = range;\n\t\tconst { count, firstGenCount, requestedClusterSize } = ids;\n\t\tconst session = this.sessions.getOrCreate(sessionId);\n\t\tconst isLocal = session === this.localSession;\n\t\tconst rangeBaseLocal = localIdFromGenCount(firstGenCount);\n\t\tlet lastCluster = session.getLastCluster();\n\t\tif (lastCluster === undefined) {\n\t\t\t// This is the first cluster in the session space\n\t\t\tif (rangeBaseLocal !== -1) {\n\t\t\t\tthrow new Error(\"Ranges finalized out of order.\");\n\t\t\t}\n\t\t\tlastCluster = this.addEmptyCluster(session, requestedClusterSize + count);\n\t\t\tif (isLocal) {\n\t\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\t\teventName: \"RuntimeIdCompressor:FirstCluster\",\n\t\t\t\t\tsessionId: this.localSessionId,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst remainingCapacity = lastCluster.capacity - lastCluster.count;\n\t\tif (lastCluster.baseLocalId - lastCluster.count !== rangeBaseLocal) {\n\t\t\tthrow new Error(\"Ranges finalized out of order.\");\n\t\t}\n\n\t\tif (remainingCapacity >= count) {\n\t\t\t// The current range fits in the existing cluster\n\t\t\tlastCluster.count += count;\n\t\t} else {\n\t\t\tconst overflow = count - remainingCapacity;\n\t\t\tconst newClaimedFinalCount = overflow + requestedClusterSize;\n\t\t\tif (lastCluster === this.finalSpace.getLastCluster()) {\n\t\t\t\t// The last cluster in the sessions chain is the last cluster globally, so it can be expanded.\n\t\t\t\tlastCluster.capacity += newClaimedFinalCount;\n\t\t\t\tlastCluster.count += count;\n\t\t\t\tassert(\n\t\t\t\t\t!this.sessions.clusterCollides(lastCluster),\n\t\t\t\t\t0x756 /* Cluster collision detected. */,\n\t\t\t\t);\n\t\t\t\tif (isLocal) {\n\t\t\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\t\t\teventName: \"RuntimeIdCompressor:ClusterExpansion\",\n\t\t\t\t\t\tsessionId: this.localSessionId,\n\t\t\t\t\t\tpreviousCapacity: lastCluster.capacity - newClaimedFinalCount,\n\t\t\t\t\t\tnewCapacity: lastCluster.capacity,\n\t\t\t\t\t\toverflow,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// The last cluster in the sessions chain is *not* the last cluster globally. Fill and overflow to new.\n\t\t\t\tlastCluster.count = lastCluster.capacity;\n\t\t\t\tconst newCluster = this.addEmptyCluster(session, newClaimedFinalCount);\n\t\t\t\tnewCluster.count += overflow;\n\t\t\t\tif (isLocal) {\n\t\t\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\t\t\teventName: \"RuntimeIdCompressor:NewCluster\",\n\t\t\t\t\t\tsessionId: this.localSessionId,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (isLocal) {\n\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\teventName: \"RuntimeIdCompressor:IdCompressorStatus\",\n\t\t\t\teagerFinalIdCount: this.telemetryEagerFinalIdCount,\n\t\t\t\tlocalIdCount: this.telemetryLocalIdCount,\n\t\t\t\tsessionId: this.localSessionId,\n\t\t\t});\n\t\t\tthis.telemetryEagerFinalIdCount = 0;\n\t\t\tthis.telemetryLocalIdCount = 0;\n\t\t}\n\n\t\tassert(!session.isEmpty(), 0x757 /* Empty sessions should not be created. */);\n\t}\n\n\tprivate addEmptyCluster(session: Session, capacity: number): IdCluster {\n\t\tassert(\n\t\t\t!this.ongoingGhostSession?.cluster,\n\t\t\t0x8a8 /* IdCompressor should not be operated normally when in a ghost session */,\n\t\t);\n\t\tconst newCluster = session.addNewCluster(\n\t\t\tthis.finalSpace.getAllocatedIdLimit(),\n\t\t\tcapacity,\n\t\t\t0,\n\t\t);\n\t\tassert(!this.sessions.clusterCollides(newCluster), 0x758 /* Cluster collision detected. */);\n\t\tthis.finalSpace.addCluster(newCluster);\n\t\treturn newCluster;\n\t}\n\n\tpublic normalizeToOpSpace(id: SessionSpaceCompressedId): OpSpaceCompressedId {\n\t\tif (isFinalId(id)) {\n\t\t\treturn id;\n\t\t} else {\n\t\t\tconst local = id as unknown as LocalCompressedId;\n\t\t\tif (!this.normalizer.contains(local)) {\n\t\t\t\tthrow new Error(\"Invalid ID to normalize.\");\n\t\t\t}\n\t\t\tconst finalForm = this.localSession.tryConvertToFinal(local, true);\n\t\t\treturn finalForm === undefined\n\t\t\t\t? (local as unknown as OpSpaceCompressedId)\n\t\t\t\t: (finalForm as OpSpaceCompressedId);\n\t\t}\n\t}\n\n\tpublic normalizeToSessionSpace(\n\t\tid: OpSpaceCompressedId,\n\t\toriginSessionId: SessionId,\n\t): SessionSpaceCompressedId {\n\t\tif (isFinalId(id)) {\n\t\t\tconst containingCluster = this.localSession.getClusterByAllocatedFinal(id);\n\t\t\tif (containingCluster === undefined) {\n\t\t\t\t// Does not exist in local cluster chain\n\t\t\t\tif (id >= this.finalSpace.getFinalizedIdLimit()) {\n\t\t\t\t\tthrow new Error(\"Unknown op space ID.\");\n\t\t\t\t}\n\t\t\t\treturn id as unknown as SessionSpaceCompressedId;\n\t\t\t} else {\n\t\t\t\tconst alignedLocal = getAlignedLocal(containingCluster, id);\n\t\t\t\tif (this.normalizer.contains(alignedLocal)) {\n\t\t\t\t\treturn alignedLocal;\n\t\t\t\t} else {\n\t\t\t\t\tif (genCountFromLocalId(alignedLocal) > this.localGenCount) {\n\t\t\t\t\t\tthrow new Error(\"Unknown op space ID.\");\n\t\t\t\t\t}\n\t\t\t\t\treturn id as unknown as SessionSpaceCompressedId;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst localToNormalize = id as unknown as LocalCompressedId;\n\t\t\tif (originSessionId === this.localSessionId) {\n\t\t\t\tif (this.normalizer.contains(localToNormalize)) {\n\t\t\t\t\treturn localToNormalize;\n\t\t\t\t} else {\n\t\t\t\t\t// We never generated this local ID, so fail\n\t\t\t\t\tthrow new Error(\"Unknown op space ID.\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// LocalId from a remote session\n\t\t\t\tconst remoteSession = this.sessions.get(originSessionId);\n\t\t\t\tif (remoteSession === undefined) {\n\t\t\t\t\tthrow new Error(\"No IDs have ever been finalized by the supplied session.\");\n\t\t\t\t}\n\t\t\t\tconst correspondingFinal = remoteSession.tryConvertToFinal(localToNormalize, false);\n\t\t\t\tif (correspondingFinal === undefined) {\n\t\t\t\t\tthrow new Error(\"Unknown op space ID.\");\n\t\t\t\t}\n\t\t\t\treturn correspondingFinal as unknown as SessionSpaceCompressedId;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic decompress(id: SessionSpaceCompressedId): StableId {\n\t\tif (isFinalId(id)) {\n\t\t\tconst containingCluster = Session.getContainingCluster(id, this.finalSpace.clusters);\n\t\t\tif (containingCluster === undefined) {\n\t\t\t\tthrow new Error(\"Unknown ID\");\n\t\t\t}\n\t\t\tconst alignedLocal = getAlignedLocal(containingCluster, id);\n\t\t\tconst alignedGenCount = genCountFromLocalId(alignedLocal);\n\t\t\tconst lastFinalizedGenCount = genCountFromLocalId(\n\t\t\t\tlastFinalizedLocal(containingCluster),\n\t\t\t);\n\t\t\tif (alignedGenCount > lastFinalizedGenCount) {\n\t\t\t\t// should be an eager final id generated by the local session\n\t\t\t\tif (containingCluster.session === this.localSession) {\n\t\t\t\t\tassert(\n\t\t\t\t\t\t!this.normalizer.contains(alignedLocal),\n\t\t\t\t\t\t0x759 /* Normalizer out of sync. */,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(\"Unknown ID\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn stableIdFromNumericUuid(\n\t\t\t\toffsetNumericUuid(containingCluster.session.sessionUuid, alignedGenCount - 1),\n\t\t\t);\n\t\t} else {\n\t\t\tconst localToDecompress = id as unknown as LocalCompressedId;\n\t\t\tif (!this.normalizer.contains(localToDecompress)) {\n\t\t\t\tthrow new Error(\"Unknown ID\");\n\t\t\t}\n\t\t\treturn stableIdFromNumericUuid(\n\t\t\t\toffsetNumericUuid(\n\t\t\t\t\tthis.localSession.sessionUuid,\n\t\t\t\t\tgenCountFromLocalId(localToDecompress) - 1,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic recompress(uncompressed: StableId): SessionSpaceCompressedId {\n\t\tconst recompressed = this.tryRecompress(uncompressed);\n\t\tif (recompressed === undefined) {\n\t\t\tthrow new Error(\"Could not recompress.\");\n\t\t}\n\t\treturn recompressed;\n\t}\n\n\tpublic tryRecompress(uncompressed: StableId): SessionSpaceCompressedId | undefined {\n\t\tconst match = this.sessions.getContainingCluster(uncompressed);\n\t\tif (match === undefined) {\n\t\t\tconst numericUncompressed = numericUuidFromStableId(uncompressed);\n\t\t\tconst offset = subtractNumericUuids(numericUncompressed, this.localSession.sessionUuid);\n\t\t\tif (offset < Number.MAX_SAFE_INTEGER) {\n\t\t\t\tconst genCountEquivalent = Number(offset) + 1;\n\t\t\t\tconst localEquivalent = localIdFromGenCount(genCountEquivalent);\n\t\t\t\tif (this.normalizer.contains(localEquivalent)) {\n\t\t\t\t\treturn localEquivalent;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t} else {\n\t\t\tconst [containingCluster, alignedLocal] = match;\n\t\t\tif (containingCluster.session === this.localSession) {\n\t\t\t\t// Local session\n\t\t\t\tif (this.normalizer.contains(alignedLocal)) {\n\t\t\t\t\treturn alignedLocal;\n\t\t\t\t} else {\n\t\t\t\t\tassert(\n\t\t\t\t\t\tgenCountFromLocalId(alignedLocal) <= this.localGenCount,\n\t\t\t\t\t\t0x75a /* Clusters out of sync. */,\n\t\t\t\t\t);\n\t\t\t\t\t// Id is an eager final\n\t\t\t\t\treturn getAlignedFinal(containingCluster, alignedLocal) as\n\t\t\t\t\t\t| SessionSpaceCompressedId\n\t\t\t\t\t\t| undefined;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Not the local session\n\t\t\t\treturn genCountFromLocalId(alignedLocal) >= lastFinalizedLocal(containingCluster)\n\t\t\t\t\t? (getAlignedFinal(containingCluster, alignedLocal) as\n\t\t\t\t\t\t\t| SessionSpaceCompressedId\n\t\t\t\t\t\t\t| undefined)\n\t\t\t\t\t: undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic serialize(withSession: true): SerializedIdCompressorWithOngoingSession;\n\tpublic serialize(withSession: false): SerializedIdCompressorWithNoSession;\n\tpublic serialize(hasLocalState: boolean): SerializedIdCompressor {\n\t\tassert(\n\t\t\t!this.ongoingGhostSession,\n\t\t\t0x8a9 /* IdCompressor should not be operated normally when in a ghost session */,\n\t\t);\n\t\tconst { normalizer, finalSpace, sessions } = this;\n\t\tconst sessionIndexMap = new Map<Session, number>();\n\t\tlet sessionIndex = 0;\n\t\tfor (const session of sessions.sessions()) {\n\t\t\t// Filter empty sessions to prevent them accumulating in the serialized state\n\t\t\tif (!session.isEmpty() || hasLocalState) {\n\t\t\t\tsessionIndexMap.set(session, sessionIndex);\n\t\t\t\tsessionIndex++;\n\t\t\t}\n\t\t}\n\t\tconst localStateSize = hasLocalState\n\t\t\t? 1 + // generated ID count\n\t\t\t 1 + // next range base genCount\n\t\t\t 1 + // count of normalizer pairs\n\t\t\t this.normalizer.idRanges.size * 2 // pairs\n\t\t\t: 0;\n\t\t// Layout size, in 8 byte increments\n\t\tconst totalSize =\n\t\t\t1 + // version\n\t\t\t1 + // hasLocalState\n\t\t\t1 + // session count\n\t\t\t1 + // cluster count\n\t\t\tsessionIndexMap.size * 2 + // session IDs\n\t\t\tfinalSpace.clusters.length * 3 + // clusters: (sessionIndex, capacity, count)[]\n\t\t\tlocalStateSize; // local state, if present\n\n\t\tconst serializedFloat = new Float64Array(totalSize);\n\t\tconst serializedUint = new BigUint64Array(serializedFloat.buffer);\n\t\tlet index = 0;\n\t\tindex = writeNumber(serializedFloat, index, currentWrittenVersion);\n\t\tindex = writeBoolean(serializedFloat, index, hasLocalState);\n\t\tindex = writeNumber(serializedFloat, index, sessionIndexMap.size);\n\t\tindex = writeNumber(serializedFloat, index, finalSpace.clusters.length);\n\n\t\tfor (const [session] of sessionIndexMap.entries()) {\n\t\t\tindex = writeNumericUuid(serializedUint, index, session.sessionUuid);\n\t\t}\n\n\t\tfinalSpace.clusters.forEach((cluster) => {\n\t\t\tindex = writeNumber(\n\t\t\t\tserializedFloat,\n\t\t\t\tindex,\n\t\t\t\tsessionIndexMap.get(cluster.session) as number,\n\t\t\t);\n\t\t\tindex = writeNumber(serializedFloat, index, cluster.capacity);\n\t\t\tindex = writeNumber(serializedFloat, index, cluster.count);\n\t\t});\n\n\t\tif (hasLocalState) {\n\t\t\tindex = writeNumber(serializedFloat, index, this.localGenCount);\n\t\t\tindex = writeNumber(serializedFloat, index, this.nextRangeBaseGenCount);\n\t\t\tindex = writeNumber(serializedFloat, index, normalizer.idRanges.size);\n\t\t\tfor (const [leadingGenCount, count] of normalizer.idRanges.entries()) {\n\t\t\t\tindex = writeNumber(serializedFloat, index, leadingGenCount);\n\t\t\t\tindex = writeNumber(serializedFloat, index, count);\n\t\t\t}\n\t\t}\n\n\t\tassert(index === totalSize, 0x75b /* Serialized size was incorrectly calculated. */);\n\t\tthis.logger?.sendTelemetryEvent({\n\t\t\teventName: \"RuntimeIdCompressor:SerializedIdCompressorSize\",\n\t\t\tsize: serializedFloat.byteLength,\n\t\t\tclusterCount: finalSpace.clusters.length,\n\t\t\tsessionCount: sessionIndexMap.size,\n\t\t});\n\n\t\treturn bufferToString(serializedFloat.buffer, \"base64\") as SerializedIdCompressor;\n\t}\n\n\tpublic static deserialize(serialized: SerializedIdCompressorWithOngoingSession): IdCompressor;\n\tpublic static deserialize(\n\t\tserialized: SerializedIdCompressorWithNoSession,\n\t\tnewSessionId: SessionId,\n\t): IdCompressor;\n\tpublic static deserialize(\n\t\tserialized: SerializedIdCompressor,\n\t\tsessionId?: SessionId,\n\t): IdCompressor {\n\t\tconst buffer = stringToBuffer(serialized, \"base64\");\n\t\tconst index: Index = {\n\t\t\tindex: 0,\n\t\t\tbufferFloat: new Float64Array(buffer),\n\t\t\tbufferUint: new BigUint64Array(buffer),\n\t\t};\n\t\tconst version = readNumber(index);\n\t\tswitch (version) {\n\t\t\tcase 1.0:\n\t\t\t\tthrow new Error(\"IdCompressor version 1.0 is no longer supported.\");\n\t\t\tcase 2.0:\n\t\t\t\treturn IdCompressor.deserialize2_0(index, sessionId);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(\"Unknown IdCompressor serialized version.\");\n\t\t}\n\t}\n\n\tprivate static deserialize2_0(index: Index, sessionId?: SessionId): IdCompressor {\n\t\tconst hasLocalState = readBoolean(index);\n\t\tconst sessionCount = readNumber(index);\n\t\tconst clusterCount = readNumber(index);\n\n\t\t// Sessions\n\t\tlet sessionOffset = 0;\n\t\tconst sessions: [NumericUuid, Session][] = [];\n\t\tif (!hasLocalState) {\n\t\t\t// If !hasLocalState, there won't be a serialized local session ID so insert one at the beginning\n\t\t\tassert(sessionId !== undefined, 0x75d /* Local session ID is undefined. */);\n\t\t\tconst localSessionNumeric = numericUuidFromStableId(sessionId);\n\t\t\tsessions.push([localSessionNumeric, new Session(localSessionNumeric)]);\n\t\t\tsessionOffset = 1;\n\t\t} else {\n\t\t\tassert(\n\t\t\t\tsessionId === undefined,\n\t\t\t\t0x75e /* Local state should not exist in serialized form. */,\n\t\t\t);\n\t\t}\n\n\t\tfor (let i = 0; i < sessionCount; i++) {\n\t\t\tconst numeric = readNumericUuid(index);\n\t\t\tsessions.push([numeric, new Session(numeric)]);\n\t\t}\n\n\t\tconst compressor = new IdCompressor(new Sessions(sessions));\n\n\t\t// Clusters\n\t\tlet baseFinalId = 0;\n\t\tfor (let i = 0; i < clusterCount; i++) {\n\t\t\tconst sessionIndex = readNumber(index);\n\t\t\tconst session = sessions[sessionIndex + sessionOffset][1];\n\t\t\tconst capacity = readNumber(index);\n\t\t\tconst count = readNumber(index);\n\t\t\tconst cluster = session.addNewCluster(\n\t\t\t\tbaseFinalId as FinalCompressedId,\n\t\t\t\tcapacity,\n\t\t\t\tcount,\n\t\t\t);\n\t\t\tcompressor.finalSpace.addCluster(cluster);\n\t\t\tbaseFinalId += capacity;\n\t\t}\n\n\t\t// Local state\n\t\tif (hasLocalState) {\n\t\t\tcompressor.localGenCount = readNumber(index);\n\t\t\tcompressor.nextRangeBaseGenCount = readNumber(index);\n\t\t\tconst normalizerCount = readNumber(index);\n\t\t\tfor (let i = 0; i < normalizerCount; i++) {\n\t\t\t\tcompressor.normalizer.addLocalRange(readNumber(index), readNumber(index));\n\t\t\t}\n\t\t}\n\n\t\tassert(\n\t\t\tindex.index === index.bufferFloat.length,\n\t\t\t0x75f /* Failed to read entire serialized compressor. */,\n\t\t);\n\t\treturn compressor;\n\t}\n\n\tpublic equals(other: IdCompressor, includeLocalState: boolean): boolean {\n\t\tif (\n\t\t\tincludeLocalState &&\n\t\t\t(this.localSessionId !== other.localSessionId ||\n\t\t\t\t!this.localSession.equals(other.localSession) ||\n\t\t\t\t!this.normalizer.equals(other.normalizer) ||\n\t\t\t\tthis.nextRangeBaseGenCount !== other.nextRangeBaseGenCount ||\n\t\t\t\tthis.localGenCount !== other.localGenCount)\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (\n\t\t\tthis.sessions.equals(other.sessions, includeLocalState) &&\n\t\t\tthis.finalSpace.equals(other.finalSpace)\n\t\t);\n\t}\n}\n\n/**\n * Create a new {@link IIdCompressor}.\n * @alpha\n */\nexport function createIdCompressor(\n\tlogger?: ITelemetryBaseLogger,\n): IIdCompressor & IIdCompressorCore;\n/**\n * Create a new {@link IIdCompressor}.\n * @param sessionId - The seed ID for the compressor.\n * @alpha\n */\nexport function createIdCompressor(\n\tsessionId: SessionId,\n\tlogger?: ITelemetryBaseLogger,\n): IIdCompressor & IIdCompressorCore;\nexport function createIdCompressor(\n\tsessionIdOrLogger?: SessionId | ITelemetryBaseLogger,\n\tloggerOrUndefined?: ITelemetryBaseLogger,\n): IIdCompressor & IIdCompressorCore {\n\treturn IdCompressor.create(sessionIdOrLogger as SessionId, loggerOrUndefined);\n}\n\n/**\n * Deserializes the supplied state into an ID compressor.\n * @alpha\n */\nexport function deserializeIdCompressor(\n\tserialized: SerializedIdCompressorWithOngoingSession,\n): IIdCompressor & IIdCompressorCore;\n/**\n * Deserializes the supplied state into an ID compressor.\n * @alpha\n */\nexport function deserializeIdCompressor(\n\tserialized: SerializedIdCompressorWithNoSession,\n\tnewSessionId: SessionId,\n): IIdCompressor & IIdCompressorCore;\nexport function deserializeIdCompressor(\n\tserialized: SerializedIdCompressor | SerializedIdCompressorWithNoSession,\n\tsessionId?: SessionId,\n): IIdCompressor & IIdCompressorCore {\n\treturn sessionId === undefined\n\t\t? IdCompressor.deserialize(serialized as SerializedIdCompressorWithOngoingSession)\n\t\t: IdCompressor.deserialize(serialized as SerializedIdCompressorWithNoSession, sessionId);\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { OpSpaceCompressedId, SessionSpaceCompressedId } from "./types";
5
+ import { OpSpaceCompressedId, SessionSpaceCompressedId } from "./types/index.mjs";
6
6
  /**
7
7
  * A compressed ID that is stable and unique within the scope of network of compressors (i.e. a document).
8
8
  * It can only be used/decompressed in the context of the originating document.
@@ -29,4 +29,4 @@ export declare function isFinalId(id: SessionSpaceCompressedId | OpSpaceCompress
29
29
  export type NumericUuid = bigint & {
30
30
  readonly NumericUuid: "be04dd4d-9d7e-4337-a833-eec64c61aa46";
31
31
  };
32
- //# sourceMappingURL=identifiers.d.ts.map
32
+ //# sourceMappingURL=identifiers.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.d.mts","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;GAGG;OAEI,EAAE,mBAAmB,EAAE,wBAAwB,EAAE;AAExD;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG;IACxC,QAAQ,CAAC,iBAAiB,EAAE,sCAAsC,CAAC;IAGnE,QAAQ,CAAC,YAAY,EAAE,sCAAsC,CAAC;CAC9D,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG;IACxC,QAAQ,CAAC,iBAAiB,EAAE,sCAAsC,CAAC;CACnE,GAAG,wBAAwB,CAAC;AAE7B;;GAEG;AACH,wBAAgB,SAAS,CACxB,EAAE,EAAE,wBAAwB,GAAG,mBAAmB,GAChD,EAAE,IAAI,iBAAiB,CAEzB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAClC,QAAQ,CAAC,WAAW,EAAE,sCAAsC,CAAC;CAC7D,CAAC"}
@@ -8,4 +8,4 @@
8
8
  export function isFinalId(id) {
9
9
  return id >= 0;
10
10
  }
11
- //# sourceMappingURL=identifiers.js.map
11
+ //# sourceMappingURL=identifiers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.mjs","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwBH;;GAEG;AACH,MAAM,UAAU,SAAS,CACxB,EAAkD;IAElD,OAAO,EAAE,IAAI,CAAC,CAAC;AAChB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { OpSpaceCompressedId, SessionSpaceCompressedId } from \"./types\";\n\n/**\n * A compressed ID that is stable and unique within the scope of network of compressors (i.e. a document).\n * It can only be used/decompressed in the context of the originating document.\n */\nexport type FinalCompressedId = number & {\n\treadonly FinalCompressedId: \"5d83d1e2-98b7-4e4e-a889-54c855cfa73d\";\n\n\t// Same brand as OpNormalizedCompressedId, as final IDs are always finally normalized\n\treadonly OpNormalized: \"9209432d-a959-4df7-b2ad-767ead4dbcae\";\n};\n\n/**\n * A compressed ID that is local to a session (can only be decompressed when paired with a SessionId).\n * Internally, it should not be persisted outside a scope annotated with the originating SessionId in order to be unambiguous.\n * If external persistence is needed (e.g. by a client), a StableId should be used instead.\n */\nexport type LocalCompressedId = number & {\n\treadonly LocalCompressedId: \"6fccb42f-e2a4-4243-bd29-f13d12b9c6d1\";\n} & SessionSpaceCompressedId; // Same brand as CompressedId, as local IDs are always locally normalized\n\n/**\n * @returns true if the supplied ID is a final ID.\n */\nexport function isFinalId(\n\tid: SessionSpaceCompressedId | OpSpaceCompressedId,\n): id is FinalCompressedId {\n\treturn id >= 0;\n}\n\n/**\n * A StableId which is suitable for use as a session identifier\n */\nexport type NumericUuid = bigint & {\n\treadonly NumericUuid: \"be04dd4d-9d7e-4337-a833-eec64c61aa46\";\n};\n"]}
@@ -2,10 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- /**
6
- * Exports for `id-compressor`
7
- */
8
- export { createIdCompressor, deserializeIdCompressor, IdCompressor } from "./idCompressor";
9
- export { createSessionId, assertIsStableId, generateStableId, isStableId } from "./utilities";
10
- export { IIdCompressorCore, IIdCompressor, SerializedIdCompressor, SerializedIdCompressorWithOngoingSession, SerializedIdCompressorWithNoSession, SessionSpaceCompressedId, OpSpaceCompressedId, SessionId, StableId, IdCreationRange, } from "./types";
11
- //# sourceMappingURL=index.d.ts.map
5
+ export { createIdCompressor, deserializeIdCompressor, IdCompressor } from "./idCompressor.mjs";
6
+ export { createSessionId, assertIsStableId, generateStableId, isStableId } from "./utilities.mjs";
7
+ export { IIdCompressorCore, IIdCompressor, SerializedIdCompressor, SerializedIdCompressorWithOngoingSession, SerializedIdCompressorWithNoSession, SessionSpaceCompressedId, OpSpaceCompressedId, SessionId, StableId, IdCreationRange, } from "./types/index.mjs";
8
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;OAMI,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,YAAY,EAAE;OAC7D,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE;OACnE,EACN,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,wCAAwC,EACxC,mCAAmC,EACnC,wBAAwB,EACxB,mBAAmB,EACnB,SAAS,EACT,QAAQ,EACR,eAAe,GACf"}