@fluid-experimental/tree 0.59.2001 → 0.59.3000
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/.vscode/SharedTree.code-workspace +15 -0
- package/.vscode/settings.json +6 -0
- package/dist/ChangeCompression.js +9 -9
- package/dist/ChangeCompression.js.map +1 -1
- package/dist/ChangeTypes.d.ts +1 -6
- package/dist/ChangeTypes.d.ts.map +1 -1
- package/dist/ChangeTypes.js +5 -5
- package/dist/ChangeTypes.js.map +1 -1
- package/dist/Checkout.js +14 -14
- package/dist/Checkout.js.map +1 -1
- package/dist/Common.d.ts +21 -3
- package/dist/Common.d.ts.map +1 -1
- package/dist/Common.js +29 -4
- package/dist/Common.js.map +1 -1
- package/dist/EditLog.js +26 -25
- package/dist/EditLog.js.map +1 -1
- package/dist/EditUtilities.js +17 -17
- package/dist/EditUtilities.js.map +1 -1
- package/dist/Forest.js +31 -31
- package/dist/Forest.js.map +1 -1
- package/dist/HistoryEditFactory.js +9 -9
- package/dist/HistoryEditFactory.js.map +1 -1
- package/dist/IdConversion.js +9 -9
- package/dist/IdConversion.js.map +1 -1
- package/dist/Identifiers.d.ts +4 -0
- package/dist/Identifiers.d.ts.map +1 -1
- package/dist/Identifiers.js.map +1 -1
- package/dist/LogViewer.d.ts +1 -5
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +11 -19
- package/dist/LogViewer.js.map +1 -1
- package/dist/MergeHealth.js +2 -2
- package/dist/MergeHealth.js.map +1 -1
- package/dist/NodeIdUtilities.js +2 -2
- package/dist/NodeIdUtilities.js.map +1 -1
- package/dist/PayloadUtilities.js +1 -1
- package/dist/PayloadUtilities.js.map +1 -1
- package/dist/RevisionValueCache.d.ts +13 -10
- package/dist/RevisionValueCache.d.ts.map +1 -1
- package/dist/RevisionValueCache.js +14 -11
- package/dist/RevisionValueCache.js.map +1 -1
- package/dist/RevisionView.js +4 -4
- package/dist/RevisionView.js.map +1 -1
- package/dist/SerializationUtilities.js +4 -4
- package/dist/SerializationUtilities.js.map +1 -1
- package/dist/SharedTree.d.ts +93 -31
- package/dist/SharedTree.d.ts.map +1 -1
- package/dist/SharedTree.js +160 -131
- package/dist/SharedTree.js.map +1 -1
- package/dist/SharedTreeEncoder.d.ts +3 -3
- package/dist/SharedTreeEncoder.d.ts.map +1 -1
- package/dist/SharedTreeEncoder.js +36 -36
- package/dist/SharedTreeEncoder.js.map +1 -1
- package/dist/StringInterner.js +1 -1
- package/dist/StringInterner.js.map +1 -1
- package/dist/Summary.js +1 -1
- package/dist/Summary.js.map +1 -1
- package/dist/SummaryBackCompatibility.js +8 -8
- package/dist/SummaryBackCompatibility.js.map +1 -1
- package/dist/Transaction.js +1 -1
- package/dist/Transaction.js.map +1 -1
- package/dist/TransactionInternal.js +17 -17
- package/dist/TransactionInternal.js.map +1 -1
- package/dist/TreeCompressor.d.ts.map +1 -1
- package/dist/TreeCompressor.js +6 -8
- package/dist/TreeCompressor.js.map +1 -1
- package/dist/TreeNodeHandle.js +4 -4
- package/dist/TreeNodeHandle.js.map +1 -1
- package/dist/TreeView.js +7 -7
- package/dist/TreeView.js.map +1 -1
- package/dist/TreeViewUtilities.js +2 -2
- package/dist/TreeViewUtilities.js.map +1 -1
- package/dist/UndoRedoHandler.js +1 -1
- package/dist/UndoRedoHandler.js.map +1 -1
- package/dist/UuidUtilities.d.ts +30 -0
- package/dist/UuidUtilities.d.ts.map +1 -0
- package/dist/UuidUtilities.js +106 -0
- package/dist/UuidUtilities.js.map +1 -0
- package/dist/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/AppendOnlySortedMap.js +167 -90
- package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/IdCompressor.d.ts +43 -42
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -1
- package/dist/id-compressor/IdCompressor.js +179 -177
- package/dist/id-compressor/IdCompressor.js.map +1 -1
- package/dist/id-compressor/IdRange.js +1 -1
- package/dist/id-compressor/IdRange.js.map +1 -1
- package/dist/id-compressor/NumericUuid.d.ts +6 -14
- package/dist/id-compressor/NumericUuid.d.ts.map +1 -1
- package/dist/id-compressor/NumericUuid.js +15 -76
- package/dist/id-compressor/NumericUuid.js.map +1 -1
- package/dist/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/dist/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/SessionIdNormalizer.js +418 -0
- package/dist/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/dist/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/persisted-types/0.1.1.d.ts +1 -6
- package/dist/persisted-types/0.1.1.d.ts.map +1 -1
- package/dist/persisted-types/0.1.1.js +3 -3
- package/dist/persisted-types/0.1.1.js.map +1 -1
- package/lib/ChangeTypes.d.ts +1 -6
- package/lib/ChangeTypes.d.ts.map +1 -1
- package/lib/Checkout.js.map +1 -1
- package/lib/Common.d.ts +21 -3
- package/lib/Common.d.ts.map +1 -1
- package/lib/Common.js +25 -3
- package/lib/Common.js.map +1 -1
- package/lib/EditLog.js +2 -1
- package/lib/EditLog.js.map +1 -1
- package/lib/EditUtilities.js.map +1 -1
- package/lib/Forest.js.map +1 -1
- package/lib/HistoryEditFactory.js.map +1 -1
- package/lib/Identifiers.d.ts +4 -0
- package/lib/Identifiers.d.ts.map +1 -1
- package/lib/Identifiers.js.map +1 -1
- package/lib/LogViewer.d.ts +1 -5
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +5 -13
- package/lib/LogViewer.js.map +1 -1
- package/lib/MergeHealth.js.map +1 -1
- package/lib/NodeIdUtilities.js.map +1 -1
- package/lib/RevisionValueCache.d.ts +13 -10
- package/lib/RevisionValueCache.d.ts.map +1 -1
- package/lib/RevisionValueCache.js +10 -7
- package/lib/RevisionValueCache.js.map +1 -1
- package/lib/RevisionView.js.map +1 -1
- package/lib/SharedTree.d.ts +93 -31
- package/lib/SharedTree.d.ts.map +1 -1
- package/lib/SharedTree.js +107 -78
- package/lib/SharedTree.js.map +1 -1
- package/lib/SharedTreeEncoder.d.ts +3 -3
- package/lib/SharedTreeEncoder.d.ts.map +1 -1
- package/lib/SharedTreeEncoder.js +4 -4
- package/lib/SharedTreeEncoder.js.map +1 -1
- package/lib/StringInterner.js.map +1 -1
- package/lib/Summary.js.map +1 -1
- package/lib/TreeCompressor.d.ts.map +1 -1
- package/lib/TreeCompressor.js +1 -3
- package/lib/TreeCompressor.js.map +1 -1
- package/lib/TreeNodeHandle.js.map +1 -1
- package/lib/TreeView.js.map +1 -1
- package/lib/TreeViewUtilities.js.map +1 -1
- package/lib/UuidUtilities.d.ts +30 -0
- package/lib/UuidUtilities.d.ts.map +1 -0
- package/lib/UuidUtilities.js +98 -0
- package/lib/UuidUtilities.js.map +1 -0
- package/lib/id-compressor/AppendOnlySortedMap.d.ts +52 -28
- package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/AppendOnlySortedMap.js +165 -88
- package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/IdCompressor.d.ts +43 -42
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -1
- package/lib/id-compressor/IdCompressor.js +97 -95
- package/lib/id-compressor/IdCompressor.js.map +1 -1
- package/lib/id-compressor/NumericUuid.d.ts +6 -14
- package/lib/id-compressor/NumericUuid.d.ts.map +1 -1
- package/lib/id-compressor/NumericUuid.js +11 -70
- package/lib/id-compressor/NumericUuid.js.map +1 -1
- package/lib/id-compressor/SessionIdNormalizer.d.ts +122 -0
- package/lib/id-compressor/SessionIdNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/SessionIdNormalizer.js +414 -0
- package/lib/id-compressor/SessionIdNormalizer.js.map +1 -0
- package/lib/id-compressor/persisted-types/0.0.1.d.ts +6 -13
- package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/persisted-types/0.1.1.d.ts +1 -6
- package/lib/persisted-types/0.1.1.d.ts.map +1 -1
- package/lib/persisted-types/0.1.1.js.map +1 -1
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts +6 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.d.ts.map +1 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js +49 -0
- package/lib/test/AppendOnlySortedMap.perf.tests.js.map +1 -0
- package/lib/test/AppendOnlySortedMap.tests.js +56 -14
- package/lib/test/AppendOnlySortedMap.tests.js.map +1 -1
- package/lib/test/Checkout.tests.js +2 -2
- package/lib/test/Checkout.tests.js.map +1 -1
- package/lib/test/Forest.tests.js.map +1 -1
- package/lib/test/IdCompressor.perf.tests.js +8 -2
- package/lib/test/IdCompressor.perf.tests.js.map +1 -1
- package/lib/test/IdCompressor.tests.js +75 -24
- package/lib/test/IdCompressor.tests.js.map +1 -1
- package/lib/test/LogViewer.tests.js +3 -5
- package/lib/test/LogViewer.tests.js.map +1 -1
- package/lib/test/NumericUuid.perf.tests.js +4 -4
- package/lib/test/NumericUuid.perf.tests.js.map +1 -1
- package/lib/test/NumericUuid.tests.js +5 -4
- package/lib/test/NumericUuid.tests.js.map +1 -1
- package/lib/test/RevisionValueCache.tests.js.map +1 -1
- package/lib/test/RevisionView.tests.js.map +1 -1
- package/lib/test/SessionIdNormalizer.tests.d.ts +6 -0
- package/lib/test/SessionIdNormalizer.tests.d.ts.map +1 -0
- package/lib/test/SessionIdNormalizer.tests.js +299 -0
- package/lib/test/SessionIdNormalizer.tests.js.map +1 -0
- package/lib/test/Summary.tests.js +1 -1
- package/lib/test/Summary.tests.js.map +1 -1
- package/lib/test/TreeCompression.tests.js +1 -1
- package/lib/test/TreeCompression.tests.js.map +1 -1
- package/lib/test/Virtualization.tests.js +1 -1
- package/lib/test/Virtualization.tests.js.map +1 -1
- package/lib/test/fuzz/Generators.d.ts +3 -14
- package/lib/test/fuzz/Generators.d.ts.map +1 -1
- package/lib/test/fuzz/Generators.js +60 -151
- package/lib/test/fuzz/Generators.js.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +10 -7
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
- package/lib/test/fuzz/SharedTreeFuzzTests.js +94 -104
- package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
- package/lib/test/fuzz/Types.d.ts +2 -9
- package/lib/test/fuzz/Types.d.ts.map +1 -1
- package/lib/test/fuzz/Types.js +1 -1
- package/lib/test/fuzz/Types.js.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +57 -11
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/IdCompressorTestUtilities.js +112 -98
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.js +2 -1
- package/lib/test/utilities/PendingLocalStateTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeTests.js +30 -1
- package/lib/test/utilities/SharedTreeTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.js +20 -0
- package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -1
- package/lib/test/utilities/SummaryLoadPerfTests.js +6 -3
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -1
- package/lib/test/utilities/TestNode.js.map +1 -1
- package/lib/test/utilities/TestUtilities.d.ts +9 -1
- package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/TestUtilities.js +27 -13
- package/lib/test/utilities/TestUtilities.js.map +1 -1
- package/package.json +19 -17
- package/src/Common.ts +42 -4
- package/src/EditLog.ts +1 -1
- package/src/Identifiers.ts +5 -0
- package/src/LogViewer.ts +4 -20
- package/src/RevisionValueCache.ts +11 -8
- package/src/SharedTree.ts +222 -75
- package/src/SharedTreeEncoder.ts +17 -11
- package/src/TreeCompressor.ts +2 -4
- package/src/UuidUtilities.ts +123 -0
- package/src/id-compressor/AppendOnlySortedMap.ts +183 -94
- package/src/id-compressor/IdCompressor.ts +144 -132
- package/src/id-compressor/NumericUuid.ts +11 -80
- package/src/id-compressor/SessionIdNormalizer.ts +497 -0
- package/src/id-compressor/persisted-types/0.0.1.ts +12 -15
- package/src/index.ts +5 -0
package/src/SharedTree.ts
CHANGED
|
@@ -25,9 +25,17 @@ import {
|
|
|
25
25
|
import { ITelemetryLogger, ITelemetryProperties } from '@fluidframework/common-definitions';
|
|
26
26
|
import { ChildLogger, ITelemetryLoggerPropertyBags, PerformanceEvent } from '@fluidframework/telemetry-utils';
|
|
27
27
|
import { ISummaryTreeWithStats } from '@fluidframework/runtime-definitions';
|
|
28
|
-
import { assert, assertNotUndefined, fail, copyPropertyIfDefined } from './Common';
|
|
28
|
+
import { assert, assertNotUndefined, fail, copyPropertyIfDefined, noop } from './Common';
|
|
29
29
|
import { EditHandle, EditLog, getNumberOfHandlesFromEditLogSummary, OrderedEditSet } from './EditLog';
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
EditId,
|
|
32
|
+
NodeId,
|
|
33
|
+
StableNodeId,
|
|
34
|
+
DetachedSequenceId,
|
|
35
|
+
OpSpaceNodeId,
|
|
36
|
+
isDetachedSequenceId,
|
|
37
|
+
AttributionId,
|
|
38
|
+
} from './Identifiers';
|
|
31
39
|
import { initialTree } from './InitialTree';
|
|
32
40
|
import {
|
|
33
41
|
CachingLogViewer,
|
|
@@ -88,6 +96,75 @@ import { TransactionInternal } from './TransactionInternal';
|
|
|
88
96
|
import { IdCompressor, createSessionId } from './id-compressor';
|
|
89
97
|
import { convertEditIds } from './IdConversion';
|
|
90
98
|
import { MutableStringInterner } from './StringInterner';
|
|
99
|
+
import { nilUuid } from './UuidUtilities';
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The write format and associated options used to construct a `SharedTree`
|
|
103
|
+
* @public
|
|
104
|
+
*/
|
|
105
|
+
export type SharedTreeArgs<WF extends WriteFormat = WriteFormat> = [writeFormat: WF, options?: SharedTreeOptions<WF>];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The type of shared tree options for a given write format
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
export type SharedTreeOptions<
|
|
112
|
+
WF extends WriteFormat,
|
|
113
|
+
HistoryCompatibility extends 'Forwards' | 'None' = 'Forwards'
|
|
114
|
+
> = Omit<
|
|
115
|
+
WF extends WriteFormat.v0_0_2
|
|
116
|
+
? SharedTreeOptions_0_0_2
|
|
117
|
+
: WF extends WriteFormat.v0_1_1
|
|
118
|
+
? SharedTreeOptions_0_1_1
|
|
119
|
+
: never,
|
|
120
|
+
HistoryCompatibility extends 'Forwards' ? 'summarizeHistory' : never
|
|
121
|
+
>;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Configuration options for a SharedTree with write format 0.0.2
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
export interface SharedTreeOptions_0_0_2 {
|
|
128
|
+
/**
|
|
129
|
+
* Determines if the history is included in summaries.
|
|
130
|
+
*
|
|
131
|
+
* Warning: enabling history summarization incurs a permanent cost in the document. It is not possible to disable history summarization
|
|
132
|
+
* later once it has been enabled, and thus the history cannot be safely deleted.
|
|
133
|
+
*
|
|
134
|
+
* On 0.1.1 documents, due to current code limitations, this parameter is only impactful for newly created documents.
|
|
135
|
+
* `SharedTree`s which load existing documents will summarize history if and only if the loaded summary included history.
|
|
136
|
+
*
|
|
137
|
+
* The technical limitations here relate to clients with mixed versions collaborating.
|
|
138
|
+
* In the future we may allow modification of whether or not a particular document saves history, but only via a consensus mechanism.
|
|
139
|
+
* See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
|
|
140
|
+
* See docs/Breaking-Change-Migration for more details on the consensus scheme.
|
|
141
|
+
*/
|
|
142
|
+
summarizeHistory?: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Configuration options for a SharedTree with write format 0.1.1
|
|
147
|
+
* @public
|
|
148
|
+
*/
|
|
149
|
+
export interface SharedTreeOptions_0_1_1 {
|
|
150
|
+
/**
|
|
151
|
+
* Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
|
|
152
|
+
*
|
|
153
|
+
* Warning: enabling history summarization incurs a permanent cost in the document. It is not possible to disable history summarization
|
|
154
|
+
* later once it has been enabled, and thus the history cannot be safely deleted.
|
|
155
|
+
*
|
|
156
|
+
* On 0.1.1 documents, due to current code limitations, this parameter is only impactful for newly created documents.
|
|
157
|
+
* `SharedTree`s which load existing documents will summarize history if and only if the loaded summary included history.
|
|
158
|
+
*
|
|
159
|
+
* The technical limitations here relate to clients with mixed versions collaborating.
|
|
160
|
+
* In the future we may allow modification of whether or not a particular document saves history, but only via a consensus mechanism.
|
|
161
|
+
* See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
|
|
162
|
+
* See docs/Breaking-Change-Migration for more details on the consensus scheme.
|
|
163
|
+
*/
|
|
164
|
+
summarizeHistory?: false | { uploadEditChunks: boolean };
|
|
165
|
+
/** a UUID that identifies the user of this tree; all node IDs generated by this tree will be associated with this UUID */
|
|
166
|
+
attributionId?: AttributionId;
|
|
167
|
+
}
|
|
91
168
|
|
|
92
169
|
/**
|
|
93
170
|
* Factory for SharedTree.
|
|
@@ -109,20 +186,20 @@ export class SharedTreeFactory implements IChannelFactory {
|
|
|
109
186
|
packageVersion: '0.1',
|
|
110
187
|
};
|
|
111
188
|
|
|
189
|
+
private readonly args: SharedTreeArgs;
|
|
190
|
+
|
|
112
191
|
/**
|
|
113
192
|
* Get a factory for SharedTree to register with the data store.
|
|
114
193
|
* @param writeFormat - Determines the format version the SharedTree will write ops and summaries in. See [the write format
|
|
115
194
|
* documentation](../docs/Write-Format.md) for more information.
|
|
116
|
-
* @param
|
|
117
|
-
* See the [breaking change migration documentation](docs/Breaking-Change-Migration) for more details on this scheme.
|
|
118
|
-
* @param expensiveValidation - Enables expensive asserts on SharedTree.
|
|
195
|
+
* @param options - Configuration options for this tree
|
|
119
196
|
* @returns A factory that creates `SharedTree`s and loads them from storage.
|
|
120
197
|
*/
|
|
121
|
-
constructor(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
198
|
+
constructor(...args: SharedTreeArgs<WriteFormat.v0_0_2>);
|
|
199
|
+
constructor(...args: SharedTreeArgs<WriteFormat.v0_1_1>);
|
|
200
|
+
constructor(...args: SharedTreeArgs) {
|
|
201
|
+
this.args = args;
|
|
202
|
+
}
|
|
126
203
|
|
|
127
204
|
/**
|
|
128
205
|
* {@inheritDoc @fluidframework/shared-object-base#ISharedObjectFactory."type"}
|
|
@@ -157,22 +234,22 @@ export class SharedTreeFactory implements IChannelFactory {
|
|
|
157
234
|
* @param runtime - data store runtime that owns the new SharedTree
|
|
158
235
|
* @param id - optional name for the SharedTree
|
|
159
236
|
*/
|
|
160
|
-
public create(runtime: IFluidDataStoreRuntime, id: string
|
|
161
|
-
this.expensiveValidation = expensiveValidation;
|
|
237
|
+
public create(runtime: IFluidDataStoreRuntime, id: string): SharedTree {
|
|
162
238
|
const sharedTree = this.createSharedTree(runtime, id);
|
|
163
239
|
sharedTree.initializeLocal();
|
|
164
240
|
return sharedTree;
|
|
165
241
|
}
|
|
166
242
|
|
|
167
243
|
private createSharedTree(runtime: IFluidDataStoreRuntime, id: string): SharedTree {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
244
|
+
const [writeFormat] = this.args;
|
|
245
|
+
switch (writeFormat) {
|
|
246
|
+
case WriteFormat.v0_0_2:
|
|
247
|
+
return new SharedTree(runtime, id, ...(this.args as SharedTreeArgs<WriteFormat.v0_0_2>));
|
|
248
|
+
case WriteFormat.v0_1_1:
|
|
249
|
+
return new SharedTree(runtime, id, ...(this.args as SharedTreeArgs<WriteFormat.v0_1_1>));
|
|
250
|
+
default:
|
|
251
|
+
fail('Unknown write format');
|
|
252
|
+
}
|
|
176
253
|
}
|
|
177
254
|
}
|
|
178
255
|
|
|
@@ -281,35 +358,56 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
281
358
|
|
|
282
359
|
/**
|
|
283
360
|
* Get a factory for SharedTree to register with the data store.
|
|
284
|
-
* @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
|
|
285
|
-
*
|
|
286
|
-
* On 0.1.1 documents, due to current code limitations, this parameter is only impactful for newly created documents.
|
|
287
|
-
* `SharedTree`s which load existing documents will summarize history if and only if the loaded summary included history.
|
|
288
|
-
*
|
|
289
|
-
* The technical limitations here relate to clients with mixed versions collaborating.
|
|
290
|
-
* In the future we may allow modification of whether or not a particular document saves history, but only via a consensus mechanism.
|
|
291
|
-
* See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
|
|
292
|
-
* See docs/Breaking-Change-Migration for more details on the consensus scheme.
|
|
293
361
|
* @param writeFormat - Determines the format version the SharedTree will write ops and summaries in.
|
|
294
362
|
* This format may be updated to a newer (supported) version at runtime if a collaborating shared-tree
|
|
295
363
|
* that was initialized with a newer write version connects to the session. Care must be taken when changing this value,
|
|
296
364
|
* as a staged rollout must of occurred such that all collaborating clients must have the code to read at least the version
|
|
297
365
|
* written.
|
|
298
366
|
* See [the write format documentation](../docs/Write-Format.md) for more information.
|
|
367
|
+
* @param options - Configuration options for this tree
|
|
299
368
|
* @returns A factory that creates `SharedTree`s and loads them from storage.
|
|
300
369
|
*/
|
|
301
|
-
public static getFactory(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
370
|
+
public static getFactory(...args: SharedTreeArgs<WriteFormat.v0_0_2>): SharedTreeFactory;
|
|
371
|
+
|
|
372
|
+
public static getFactory(...args: SharedTreeArgs<WriteFormat.v0_1_1>): SharedTreeFactory;
|
|
373
|
+
|
|
374
|
+
public static getFactory(...args: SharedTreeArgs): SharedTreeFactory {
|
|
375
|
+
const [writeFormat] = args;
|
|
305
376
|
// On 0.1.1 documents, due to current code limitations, all clients MUST agree on the value of `summarizeHistory`.
|
|
306
377
|
// Note that this means staged rollout changing this value should not be attempted.
|
|
307
378
|
// It is possible to update shared-tree to correctly handle such a staged rollout, but that hasn't been implemented.
|
|
308
379
|
// See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
|
|
309
|
-
|
|
380
|
+
switch (writeFormat) {
|
|
381
|
+
case WriteFormat.v0_0_2:
|
|
382
|
+
return new SharedTreeFactory(...(args as SharedTreeArgs<WriteFormat.v0_0_2>));
|
|
383
|
+
case WriteFormat.v0_1_1:
|
|
384
|
+
return new SharedTreeFactory(...(args as SharedTreeArgs<WriteFormat.v0_1_1>));
|
|
385
|
+
default:
|
|
386
|
+
fail('Unknown write format');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The UUID used for attribution of nodes created by this SharedTree. All shared trees with a write format of 0.1.1 or
|
|
392
|
+
* greater have a unique attribution ID which may be configured in the constructor. All other shared trees (i.e. those
|
|
393
|
+
* with a write format of 0.0.2) use the nil UUID as their attribution ID.
|
|
394
|
+
* @public
|
|
395
|
+
*/
|
|
396
|
+
public get attributionId(): AttributionId {
|
|
397
|
+
switch (this.writeFormat) {
|
|
398
|
+
case WriteFormat.v0_0_2:
|
|
399
|
+
return nilUuid;
|
|
400
|
+
default: {
|
|
401
|
+
const { attributionId } = this.idCompressor;
|
|
402
|
+
if (attributionId === ghostSessionId) {
|
|
403
|
+
return nilUuid;
|
|
404
|
+
}
|
|
405
|
+
return attributionId;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
310
408
|
}
|
|
311
409
|
|
|
312
|
-
private idCompressor: IdCompressor
|
|
410
|
+
private idCompressor: IdCompressor;
|
|
313
411
|
private readonly idNormalizer: NodeIdNormalizer<OpSpaceNodeId> & { tree: SharedTree } = {
|
|
314
412
|
tree: this,
|
|
315
413
|
get localSessionId() {
|
|
@@ -375,25 +473,46 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
375
473
|
private summarizeHistory: boolean;
|
|
376
474
|
private uploadEditChunks: boolean;
|
|
377
475
|
|
|
476
|
+
private getHistoryPolicy(options: SharedTreeOptions<WriteFormat, 'Forwards' | 'None'>): {
|
|
477
|
+
summarizeHistory: boolean;
|
|
478
|
+
uploadEditChunks: boolean;
|
|
479
|
+
} {
|
|
480
|
+
const noCompatOptions = options as SharedTreeOptions<WriteFormat, 'None'>;
|
|
481
|
+
if (typeof noCompatOptions.summarizeHistory === 'object') {
|
|
482
|
+
return {
|
|
483
|
+
summarizeHistory: true,
|
|
484
|
+
uploadEditChunks: noCompatOptions.summarizeHistory.uploadEditChunks,
|
|
485
|
+
};
|
|
486
|
+
} else {
|
|
487
|
+
return {
|
|
488
|
+
summarizeHistory: noCompatOptions.summarizeHistory ?? false,
|
|
489
|
+
uploadEditChunks: false,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
378
494
|
/**
|
|
379
|
-
* Create a new
|
|
495
|
+
* Create a new SharedTree.
|
|
380
496
|
* @param runtime - The runtime the SharedTree will be associated with
|
|
381
497
|
* @param id - Unique ID for the SharedTree
|
|
382
498
|
* @param writeFormat - Determines the format version the SharedTree will write ops and summaries in. See [the write format
|
|
383
499
|
* documentation](../docs/Write-Format.md) for more information.
|
|
384
|
-
* @param
|
|
385
|
-
* @param expensiveValidation - Enable expensive asserts.
|
|
500
|
+
* @param options - Configuration options for this tree
|
|
386
501
|
*/
|
|
502
|
+
public constructor(runtime: IFluidDataStoreRuntime, id: string, ...args: SharedTreeArgs<WriteFormat.v0_0_2>);
|
|
503
|
+
|
|
504
|
+
public constructor(runtime: IFluidDataStoreRuntime, id: string, ...args: SharedTreeArgs<WriteFormat.v0_1_1>);
|
|
505
|
+
|
|
387
506
|
public constructor(
|
|
388
507
|
runtime: IFluidDataStoreRuntime,
|
|
389
508
|
id: string,
|
|
390
509
|
private writeFormat: WriteFormat,
|
|
391
|
-
|
|
392
|
-
private readonly expensiveValidation = false
|
|
510
|
+
options: SharedTreeOptions<typeof writeFormat> = {}
|
|
393
511
|
) {
|
|
394
512
|
super(id, runtime, SharedTreeFactory.Attributes);
|
|
395
|
-
|
|
396
|
-
this.
|
|
513
|
+
const historyPolicy = this.getHistoryPolicy(options);
|
|
514
|
+
this.summarizeHistory = historyPolicy.summarizeHistory;
|
|
515
|
+
this.uploadEditChunks = historyPolicy.uploadEditChunks;
|
|
397
516
|
|
|
398
517
|
// This code is somewhat duplicated from OldestClientObserver because it currently depends on the container runtime
|
|
399
518
|
// which SharedTree does not have access to.
|
|
@@ -412,13 +531,15 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
412
531
|
sharedTreeTelemetryProperties
|
|
413
532
|
);
|
|
414
533
|
|
|
534
|
+
const attributionId = (options as SharedTreeOptions<WriteFormat.v0_1_1>).attributionId;
|
|
535
|
+
this.idCompressor = new IdCompressor(createSessionId(), reservedIdCount, attributionId);
|
|
415
536
|
const { editLog, cachingLogViewer } = this.initializeNewEditLogFromSummary(
|
|
416
537
|
{
|
|
417
538
|
editChunks: [],
|
|
418
539
|
editIds: [],
|
|
419
540
|
},
|
|
420
541
|
undefined,
|
|
421
|
-
this.idCompressor,
|
|
542
|
+
this.idCompressor,
|
|
422
543
|
this.processEditResult,
|
|
423
544
|
this.processSequencedEditResult,
|
|
424
545
|
WriteFormat.v0_1_1
|
|
@@ -429,6 +550,11 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
429
550
|
this.encoder_0_1_1 = new SharedTreeEncoder_0_1_1(this.summarizeHistory);
|
|
430
551
|
}
|
|
431
552
|
|
|
553
|
+
/**
|
|
554
|
+
* The write format version currently used by this `SharedTree`. This is always initialized to the write format
|
|
555
|
+
* passed to the tree's constructor, but it may automatically upgrade over time (e.g. when connected to another
|
|
556
|
+
* SharedTree with a higher write format, or when loading a summary with a higher write format).
|
|
557
|
+
*/
|
|
432
558
|
public getWriteFormat(): WriteFormat {
|
|
433
559
|
return this.writeFormat;
|
|
434
560
|
}
|
|
@@ -555,6 +681,26 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
555
681
|
return this.idCompressor.tryRecompress(id) as NodeId | undefined;
|
|
556
682
|
}
|
|
557
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Returns the attribution ID associated with the SharedTree that generated the given node ID. This is generally only useful for clients
|
|
686
|
+
* with a write format of 0.1.1 or greater since older clients cannot be given an attribution ID and will always use the default
|
|
687
|
+
* `attributionId` of the tree.
|
|
688
|
+
* @public
|
|
689
|
+
*/
|
|
690
|
+
public attributeNodeId(id: NodeId): AttributionId {
|
|
691
|
+
switch (this.writeFormat) {
|
|
692
|
+
case WriteFormat.v0_0_2:
|
|
693
|
+
return nilUuid;
|
|
694
|
+
default: {
|
|
695
|
+
const attributionId = this.idCompressor.attributeId(id);
|
|
696
|
+
if (attributionId === ghostSessionId) {
|
|
697
|
+
return nilUuid;
|
|
698
|
+
}
|
|
699
|
+
return attributionId;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
558
704
|
/**
|
|
559
705
|
* @returns the edit history of the tree.
|
|
560
706
|
* @public
|
|
@@ -570,9 +716,19 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
570
716
|
}
|
|
571
717
|
|
|
572
718
|
/**
|
|
573
|
-
* Uploads the edit chunk and
|
|
719
|
+
* Uploads the edit chunk and submits a `SharedTreeHandleOp`.
|
|
720
|
+
* This method is fire-and-forget and will swallow any errors that occur during upload or the `onUploadComplete` hook.
|
|
721
|
+
* If the upload or op submission does fail then a future client will attempt the submission instead.
|
|
574
722
|
*/
|
|
575
|
-
private
|
|
723
|
+
private uploadEditChunk(
|
|
724
|
+
edits: readonly EditWithoutId<ChangeInternal>[],
|
|
725
|
+
startRevision: number,
|
|
726
|
+
onUploadComplete?: () => void
|
|
727
|
+
): void {
|
|
728
|
+
this.uploadEditChunkAsync(edits, startRevision).then(onUploadComplete).catch(noop);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
private async uploadEditChunkAsync(
|
|
576
732
|
edits: readonly EditWithoutId<ChangeInternal>[],
|
|
577
733
|
startRevision: number
|
|
578
734
|
): Promise<void> {
|
|
@@ -734,7 +890,10 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
734
890
|
let convertedSummary: SummaryContents;
|
|
735
891
|
switch (loadedSummaryVersion) {
|
|
736
892
|
case WriteFormat.v0_0_2:
|
|
737
|
-
convertedSummary = this.encoder_0_0_2.decodeSummary(
|
|
893
|
+
convertedSummary = this.encoder_0_0_2.decodeSummary(
|
|
894
|
+
summary as SharedTreeSummary_0_0_2,
|
|
895
|
+
this.attributionId
|
|
896
|
+
);
|
|
738
897
|
break;
|
|
739
898
|
case WriteFormat.v0_1_1: {
|
|
740
899
|
const typedSummary = summary as SharedTreeSummary;
|
|
@@ -746,7 +905,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
746
905
|
this.encoder_0_1_1 = new SharedTreeEncoder_0_1_1(this.summarizeHistory);
|
|
747
906
|
}
|
|
748
907
|
|
|
749
|
-
convertedSummary = this.encoder_0_1_1.decodeSummary(summary as SharedTreeSummary);
|
|
908
|
+
convertedSummary = this.encoder_0_1_1.decodeSummary(summary as SharedTreeSummary, this.attributionId);
|
|
750
909
|
break;
|
|
751
910
|
}
|
|
752
911
|
default:
|
|
@@ -860,7 +1019,6 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
860
1019
|
editLog,
|
|
861
1020
|
RevisionView.fromTree(initialTree, this),
|
|
862
1021
|
knownRevisions,
|
|
863
|
-
this.expensiveValidation,
|
|
864
1022
|
editStatusCallback,
|
|
865
1023
|
sequencedEditResultCallback,
|
|
866
1024
|
0
|
|
@@ -877,14 +1035,10 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
877
1035
|
private uploadCatchUpBlobs(): void {
|
|
878
1036
|
if (this.writeFormat !== WriteFormat.v0_0_2 && this.uploadEditChunks) {
|
|
879
1037
|
for (const [startRevision, chunk] of this.editLog.getEditChunksReadyForUpload()) {
|
|
880
|
-
this.uploadEditChunk(chunk, startRevision)
|
|
881
|
-
.
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
})
|
|
885
|
-
// It is safe to swallow errors from edit chunk upload because the next summary load will
|
|
886
|
-
// do another attempt to upload the edit chunks that couldn't previously be uploaded
|
|
887
|
-
.catch((error) => {});
|
|
1038
|
+
this.uploadEditChunk(chunk, startRevision, () => {
|
|
1039
|
+
this.emit(SharedTreeDiagnosticEvent.CatchUpBlobUploaded);
|
|
1040
|
+
this.logger.sendTelemetryEvent({ eventName: 'CatchUpBlobUpload', chunkSize: chunk.length });
|
|
1041
|
+
});
|
|
888
1042
|
}
|
|
889
1043
|
}
|
|
890
1044
|
}
|
|
@@ -947,7 +1101,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
947
1101
|
// Update ops should only be processed if they're not the same version.
|
|
948
1102
|
if (sameVersion) {
|
|
949
1103
|
if (type === SharedTreeOpType.Handle) {
|
|
950
|
-
const { editHandle, startRevision } = op
|
|
1104
|
+
const { editHandle, startRevision } = op;
|
|
951
1105
|
const baseHandle = this.deserializeHandle(editHandle);
|
|
952
1106
|
const decodedHandle: EditHandle<ChangeInternal> = {
|
|
953
1107
|
get: async () => {
|
|
@@ -964,11 +1118,9 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
964
1118
|
this.editLog.processEditChunkHandle(decodedHandle, startRevision);
|
|
965
1119
|
} else if (type === SharedTreeOpType.Edit) {
|
|
966
1120
|
if (op.version === WriteFormat.v0_1_1) {
|
|
967
|
-
|
|
968
|
-
this.idCompressor.finalizeCreationRange((op as SharedTreeEditOp).idRange);
|
|
1121
|
+
this.idCompressor.finalizeCreationRange(op.idRange);
|
|
969
1122
|
}
|
|
970
|
-
|
|
971
|
-
const edit = this.parseSequencedEdit(op as SharedTreeEditOp | SharedTreeEditOp_0_0_2);
|
|
1123
|
+
const edit = this.parseSequencedEdit(op);
|
|
972
1124
|
if (op.version === WriteFormat.v0_1_1) {
|
|
973
1125
|
this.internStringsFromEdit(edit);
|
|
974
1126
|
}
|
|
@@ -1056,16 +1208,12 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
1056
1208
|
const [startRevision, chunk] = lastPair;
|
|
1057
1209
|
const edits = assertNotUndefined(chunk.edits);
|
|
1058
1210
|
if (edits.length === this.editLog.editsPerChunk) {
|
|
1059
|
-
this.uploadEditChunk(edits, startRevision)
|
|
1060
|
-
.
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
})
|
|
1066
|
-
// It is safe to swallow errors from edit chunk upload because the next summary load will
|
|
1067
|
-
// do another attempt to upload the edit chunks that couldn't previously be uploaded
|
|
1068
|
-
.catch((error) => {});
|
|
1211
|
+
this.uploadEditChunk(edits, startRevision, () => {
|
|
1212
|
+
this.logger.sendTelemetryEvent({
|
|
1213
|
+
eventName: 'EditChunkUpload',
|
|
1214
|
+
chunkSize: edits.length,
|
|
1215
|
+
});
|
|
1216
|
+
});
|
|
1069
1217
|
}
|
|
1070
1218
|
}
|
|
1071
1219
|
}
|
|
@@ -1116,7 +1264,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
1116
1264
|
this.interner = new MutableStringInterner([initialTree.definition]);
|
|
1117
1265
|
const oldIdCompressor = this.idCompressor;
|
|
1118
1266
|
// Create the IdCompressor that will be used after the upgrade
|
|
1119
|
-
const newIdCompressor = new IdCompressor(createSessionId(), reservedIdCount);
|
|
1267
|
+
const newIdCompressor = new IdCompressor(createSessionId(), reservedIdCount, this.attributionId);
|
|
1120
1268
|
const newContext = getNodeIdContext(newIdCompressor);
|
|
1121
1269
|
// Generate all local IDs in the new compressor that were in the old compressor and preserve their UUIDs.
|
|
1122
1270
|
// This will allow the client to continue to use local IDs that were allocated pre-upgrade
|
|
@@ -1131,7 +1279,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
1131
1279
|
}
|
|
1132
1280
|
};
|
|
1133
1281
|
// Construct a temporary "ghost" compressor which is used to generate final IDs that will be consistent across all upgrading clients
|
|
1134
|
-
const ghostIdCompressor = new IdCompressor(ghostSessionId, reservedIdCount);
|
|
1282
|
+
const ghostIdCompressor = new IdCompressor(ghostSessionId, reservedIdCount);
|
|
1135
1283
|
const ghostContext = getNodeIdContext(ghostIdCompressor);
|
|
1136
1284
|
if (this.summarizeHistory) {
|
|
1137
1285
|
// All clients have the full history, and can therefore all "generate" the same final IDs for every ID in the history
|
|
@@ -1303,8 +1451,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
|
|
|
1303
1451
|
private applyEditLocally(edit: Edit<ChangeInternal>, message: ISequencedDocumentMessage | undefined): void {
|
|
1304
1452
|
const isSequenced = message !== undefined;
|
|
1305
1453
|
if (isSequenced) {
|
|
1306
|
-
|
|
1307
|
-
this.editLog.addSequencedEdit(edit, message as ISequencedDocumentMessage);
|
|
1454
|
+
this.editLog.addSequencedEdit(edit, message);
|
|
1308
1455
|
} else {
|
|
1309
1456
|
this.editLog.addLocalEdit(edit);
|
|
1310
1457
|
}
|
package/src/SharedTreeEncoder.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { IsoBuffer } from '@fluidframework/common-utils';
|
|
|
7
7
|
import { assert, fail } from './Common';
|
|
8
8
|
import { EditLog } from './EditLog';
|
|
9
9
|
import { convertTreeNodes, newEdit } from './EditUtilities';
|
|
10
|
-
import { DetachedSequenceId, FinalNodeId, OpSpaceNodeId, TraitLabel } from './Identifiers';
|
|
10
|
+
import { AttributionId, DetachedSequenceId, FinalNodeId, OpSpaceNodeId, TraitLabel } from './Identifiers';
|
|
11
11
|
import { initialTree } from './InitialTree';
|
|
12
12
|
import {
|
|
13
13
|
ContextualizedNodeIdNormalizer,
|
|
@@ -151,19 +151,22 @@ export class SharedTreeEncoder_0_1_1 {
|
|
|
151
151
|
/**
|
|
152
152
|
* Decodes an encoded summary.
|
|
153
153
|
*/
|
|
154
|
-
public decodeSummary(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
154
|
+
public decodeSummary(
|
|
155
|
+
{
|
|
156
|
+
editHistory,
|
|
157
|
+
currentTree: compressedTree,
|
|
158
|
+
internedStrings,
|
|
159
|
+
idCompressor: serializedIdCompressor,
|
|
160
|
+
version,
|
|
161
|
+
}: SharedTreeSummary,
|
|
162
|
+
attributionId: AttributionId
|
|
163
|
+
): SummaryContents {
|
|
161
164
|
assert(version === WriteFormat.v0_1_1, `Invalid summary version to decode: ${version}, expected: 0.1.1`);
|
|
162
165
|
assert(typeof editHistory === 'object', '0.1.1 summary encountered with non-object edit history.');
|
|
163
166
|
|
|
164
167
|
const idCompressor = hasOngoingSession(serializedIdCompressor)
|
|
165
168
|
? IdCompressor.deserialize(serializedIdCompressor)
|
|
166
|
-
: IdCompressor.deserialize(serializedIdCompressor, createSessionId());
|
|
169
|
+
: IdCompressor.deserialize(serializedIdCompressor, createSessionId(), attributionId);
|
|
167
170
|
|
|
168
171
|
const interner = new MutableStringInterner(internedStrings);
|
|
169
172
|
const sequencedNormalizer = sequencedIdNormalizer(getNodeIdContext(idCompressor));
|
|
@@ -371,9 +374,12 @@ export class SharedTreeEncoder_0_0_2 {
|
|
|
371
374
|
/**
|
|
372
375
|
* Decodes an encoded summary.
|
|
373
376
|
*/
|
|
374
|
-
public decodeSummary(
|
|
377
|
+
public decodeSummary(
|
|
378
|
+
{ currentTree, sequencedEdits }: SharedTreeSummary_0_0_2,
|
|
379
|
+
attributionId?: AttributionId
|
|
380
|
+
): SummaryContents {
|
|
375
381
|
assert(sequencedEdits !== undefined, '0.0.2 summary encountered with missing sequencedEdits field.');
|
|
376
|
-
const idCompressor = new IdCompressor(createSessionId(), reservedIdCount);
|
|
382
|
+
const idCompressor = new IdCompressor(createSessionId(), reservedIdCount, attributionId);
|
|
377
383
|
const idGenerator = getNodeIdContext(idCompressor);
|
|
378
384
|
const generateId = (id) => idGenerator.generateNodeId(id);
|
|
379
385
|
|
package/src/TreeCompressor.ts
CHANGED
|
@@ -135,16 +135,14 @@ export class InterningTreeCompressor<TPlaceholder extends DetachedSequenceId | n
|
|
|
135
135
|
compressedTraits = payloadTraits;
|
|
136
136
|
}
|
|
137
137
|
} else {
|
|
138
|
-
|
|
139
|
-
compressedTraits = idOrPayloadTraits as typeof compressedTraits;
|
|
138
|
+
compressedTraits = idOrPayloadTraits;
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
const definition =
|
|
144
143
|
typeof maybeInternedDefinition === 'string'
|
|
145
144
|
? maybeInternedDefinition
|
|
146
|
-
:
|
|
147
|
-
(interner.getString(maybeInternedDefinition as number) as Definition);
|
|
145
|
+
: (interner.getString(maybeInternedDefinition) as Definition);
|
|
148
146
|
|
|
149
147
|
let identifier: TId;
|
|
150
148
|
if (compressedId !== undefined) {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { v4, NIL } from 'uuid';
|
|
7
|
+
import { assert } from './Common';
|
|
8
|
+
import { StableId, UuidString } from './Identifiers';
|
|
9
|
+
|
|
10
|
+
const hexadecimalCharCodes = Array.from('09afAF').map((c) => c.charCodeAt(0)) as [
|
|
11
|
+
zero: number,
|
|
12
|
+
nine: number,
|
|
13
|
+
a: number,
|
|
14
|
+
f: number,
|
|
15
|
+
A: number,
|
|
16
|
+
F: number
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function isHexadecimalCharacter(charCode: number): boolean {
|
|
20
|
+
return (
|
|
21
|
+
(charCode >= hexadecimalCharCodes[0] && charCode <= hexadecimalCharCodes[1]) ||
|
|
22
|
+
(charCode >= hexadecimalCharCodes[2] && charCode <= hexadecimalCharCodes[3]) ||
|
|
23
|
+
(charCode >= hexadecimalCharCodes[4] && charCode <= hexadecimalCharCodes[5])
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The null (lowest/all-zeros) UUID */
|
|
28
|
+
export const nilUuid = assertIsUuidString(NIL);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Asserts that the given string is a UUID
|
|
32
|
+
*/
|
|
33
|
+
export function assertIsUuidString(uuidString: string): UuidString {
|
|
34
|
+
assert(isUuidString(uuidString), `${uuidString} is not an UuidString`);
|
|
35
|
+
return uuidString;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns true iff the given string is a valid UUID-like string of hexadecimal characters
|
|
40
|
+
* 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
|
41
|
+
*/
|
|
42
|
+
export function isUuidString(str: string): str is UuidString {
|
|
43
|
+
for (let i = 0; i < str.length; i++) {
|
|
44
|
+
switch (i) {
|
|
45
|
+
case 8:
|
|
46
|
+
case 13:
|
|
47
|
+
case 18:
|
|
48
|
+
case 23:
|
|
49
|
+
if (str.charAt(i) !== '-') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
if (!isHexadecimalCharacter(str.charCodeAt(i))) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a random stable ID
|
|
67
|
+
*/
|
|
68
|
+
export function generateStableId(): StableId {
|
|
69
|
+
return assertIsStableId(v4());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Asserts that the given string is a stable ID.
|
|
74
|
+
*/
|
|
75
|
+
export function assertIsStableId(stableId: string): StableId {
|
|
76
|
+
assert(isStableId(stableId), `${stableId} is not a StableId.`);
|
|
77
|
+
return stableId;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns true iff the given string is a valid Version 4, variant 2 UUID
|
|
82
|
+
* 'xxxxxxxx-xxxx-4xxx-vxxx-xxxxxxxxxxxx'
|
|
83
|
+
*/
|
|
84
|
+
export function isStableId(str: string): str is StableId {
|
|
85
|
+
if (str.length !== 36) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < str.length; i++) {
|
|
90
|
+
switch (i) {
|
|
91
|
+
case 8:
|
|
92
|
+
case 13:
|
|
93
|
+
case 18:
|
|
94
|
+
case 23:
|
|
95
|
+
if (str.charAt(i) !== '-') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
case 14:
|
|
101
|
+
if (str.charAt(i) !== '4') {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case 19: {
|
|
107
|
+
const char = str.charAt(i);
|
|
108
|
+
if (char !== '8' && char !== '9' && char !== 'a' && char !== 'b') {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
if (!isHexadecimalCharacter(str.charCodeAt(i))) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return true;
|
|
123
|
+
}
|