@fluid-experimental/tree 0.58.3000-61081 → 0.59.2000-61729

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 (98) hide show
  1. package/README.md +156 -43
  2. package/dist/ChangeTypes.d.ts +9 -14
  3. package/dist/ChangeTypes.d.ts.map +1 -1
  4. package/dist/ChangeTypes.js.map +1 -1
  5. package/dist/Checkout.d.ts.map +1 -1
  6. package/dist/Checkout.js +3 -2
  7. package/dist/Checkout.js.map +1 -1
  8. package/dist/LogViewer.d.ts +2 -3
  9. package/dist/LogViewer.d.ts.map +1 -1
  10. package/dist/LogViewer.js +5 -4
  11. package/dist/LogViewer.js.map +1 -1
  12. package/dist/NodeIdUtilities.d.ts +26 -11
  13. package/dist/NodeIdUtilities.d.ts.map +1 -1
  14. package/dist/NodeIdUtilities.js.map +1 -1
  15. package/dist/SharedTree.d.ts +61 -22
  16. package/dist/SharedTree.d.ts.map +1 -1
  17. package/dist/SharedTree.js +75 -30
  18. package/dist/SharedTree.js.map +1 -1
  19. package/dist/Transaction.d.ts +22 -4
  20. package/dist/Transaction.d.ts.map +1 -1
  21. package/dist/Transaction.js +27 -11
  22. package/dist/Transaction.js.map +1 -1
  23. package/dist/TransactionInternal.d.ts +3 -6
  24. package/dist/TransactionInternal.d.ts.map +1 -1
  25. package/dist/TransactionInternal.js +8 -4
  26. package/dist/TransactionInternal.js.map +1 -1
  27. package/dist/TreeCompressor.d.ts +0 -1
  28. package/dist/TreeCompressor.d.ts.map +1 -1
  29. package/dist/TreeCompressor.js +31 -26
  30. package/dist/TreeCompressor.js.map +1 -1
  31. package/dist/TreeView.d.ts.map +1 -1
  32. package/dist/TreeView.js +3 -2
  33. package/dist/TreeView.js.map +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/docs/Write-Format.md +19 -0
  38. package/lib/ChangeTypes.d.ts +9 -14
  39. package/lib/ChangeTypes.d.ts.map +1 -1
  40. package/lib/ChangeTypes.js.map +1 -1
  41. package/lib/Checkout.d.ts.map +1 -1
  42. package/lib/Checkout.js +3 -2
  43. package/lib/Checkout.js.map +1 -1
  44. package/lib/LogViewer.d.ts +2 -3
  45. package/lib/LogViewer.d.ts.map +1 -1
  46. package/lib/LogViewer.js +5 -4
  47. package/lib/LogViewer.js.map +1 -1
  48. package/lib/NodeIdUtilities.d.ts +26 -11
  49. package/lib/NodeIdUtilities.d.ts.map +1 -1
  50. package/lib/NodeIdUtilities.js.map +1 -1
  51. package/lib/SharedTree.d.ts +61 -22
  52. package/lib/SharedTree.d.ts.map +1 -1
  53. package/lib/SharedTree.js +75 -30
  54. package/lib/SharedTree.js.map +1 -1
  55. package/lib/Transaction.d.ts +22 -4
  56. package/lib/Transaction.d.ts.map +1 -1
  57. package/lib/Transaction.js +26 -10
  58. package/lib/Transaction.js.map +1 -1
  59. package/lib/TransactionInternal.d.ts +3 -6
  60. package/lib/TransactionInternal.d.ts.map +1 -1
  61. package/lib/TransactionInternal.js +8 -4
  62. package/lib/TransactionInternal.js.map +1 -1
  63. package/lib/TreeCompressor.d.ts +0 -1
  64. package/lib/TreeCompressor.d.ts.map +1 -1
  65. package/lib/TreeCompressor.js +31 -26
  66. package/lib/TreeCompressor.js.map +1 -1
  67. package/lib/TreeView.d.ts.map +1 -1
  68. package/lib/TreeView.js +3 -2
  69. package/lib/TreeView.js.map +1 -1
  70. package/lib/index.d.ts +1 -1
  71. package/lib/index.d.ts.map +1 -1
  72. package/lib/index.js.map +1 -1
  73. package/lib/test/LogViewer.tests.js +13 -10
  74. package/lib/test/LogViewer.tests.js.map +1 -1
  75. package/lib/test/Transaction.tests.js +36 -2
  76. package/lib/test/Transaction.tests.js.map +1 -1
  77. package/lib/test/TreeView.tests.js +29 -0
  78. package/lib/test/TreeView.tests.js.map +1 -1
  79. package/lib/test/Virtualization.tests.js +1 -2
  80. package/lib/test/Virtualization.tests.js.map +1 -1
  81. package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -1
  82. package/lib/test/fuzz/SharedTreeFuzzTests.js +19 -2
  83. package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -1
  84. package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -1
  85. package/lib/test/utilities/SharedTreeVersioningTests.js +26 -1
  86. package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -1
  87. package/package.json +18 -18
  88. package/src/ChangeTypes.ts +9 -14
  89. package/src/Checkout.ts +3 -3
  90. package/src/LogViewer.ts +4 -4
  91. package/src/NodeIdUtilities.ts +26 -11
  92. package/src/SharedTree.ts +81 -28
  93. package/src/Transaction.ts +39 -13
  94. package/src/TransactionInternal.ts +9 -10
  95. package/src/TreeCompressor.ts +31 -40
  96. package/src/TreeView.ts +3 -2
  97. package/src/index.ts +0 -1
  98. package/docs/Future.md +0 -155
package/lib/SharedTree.js CHANGED
@@ -21,7 +21,6 @@ import { RevisionView } from './RevisionView';
21
21
  import { SharedTreeEncoder_0_0_2, SharedTreeEncoder_0_1_1 } from './SharedTreeEncoder';
22
22
  import { revert } from './HistoryEditFactory';
23
23
  import { ChangeType } from './ChangeTypes';
24
- import { TransactionInternal } from './TransactionInternal';
25
24
  import { IdCompressor, createSessionId } from './id-compressor';
26
25
  import { convertEditIds } from './IdConversion';
27
26
  import { MutableStringInterner } from './StringInterner';
@@ -33,13 +32,10 @@ import { MutableStringInterner } from './StringInterner';
33
32
  export class SharedTreeFactory {
34
33
  /**
35
34
  * Get a factory for SharedTree to register with the data store.
36
- * @param writeFormat - Determines the format version the SharedTree will write summaries in.
37
- * This format may be updated to a newer (supported) version at runtime if a collaborating shared-tree
38
- * that was initialized with a newer write version connects to the session. Care must be taken when changing this value,
39
- * as a staged rollout must have occurred such that all collaborating clients must have the code to read at least the version
40
- * written.
35
+ * @param writeFormat - Determines the format version the SharedTree will write ops and summaries in. See [the write format
36
+ * documentation](../docs/Write-Format.md) for more information.
41
37
  * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
42
- * See docs/Breaking-Change-Migration for more details on this scheme.
38
+ * See the [breaking change migration documentation](docs/Breaking-Change-Migration) for more details on this scheme.
43
39
  * @param expensiveValidation - Enables expensive asserts on SharedTree.
44
40
  * @returns A factory that creates `SharedTree`s and loads them from storage.
45
41
  */
@@ -106,7 +102,7 @@ const snapshotFileName = 'header';
106
102
  const sortedWriteVersions = [WriteFormat.v0_0_2, WriteFormat.v0_1_1];
107
103
  const sharedTreeTelemetryProperties = { all: { isSharedTreeEvent: true } };
108
104
  /**
109
- * A distributed tree.
105
+ * A [distributed tree](../Readme.md).
110
106
  * @public
111
107
  */
112
108
  export class SharedTree extends SharedObject {
@@ -114,7 +110,8 @@ export class SharedTree extends SharedObject {
114
110
  * Create a new SharedTreeFactory.
115
111
  * @param runtime - The runtime the SharedTree will be associated with
116
112
  * @param id - Unique ID for the SharedTree
117
- * @param writeFormat - Determines the format version the SharedTree will write summaries in.
113
+ * @param writeFormat - Determines the format version the SharedTree will write ops and summaries in. See [the write format
114
+ * documentation](../docs/Write-Format.md) for more information.
118
115
  * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
119
116
  * @param expensiveValidation - Enable expensive asserts.
120
117
  */
@@ -148,7 +145,6 @@ export class SharedTree extends SharedObject {
148
145
  };
149
146
  this.emit(SharedTreeEvent.SequencedEditApplied, eventArguments);
150
147
  };
151
- this.transactionFactory = TransactionInternal.factory;
152
148
  /**
153
149
  * Re-computes currentIsOldest and emits an event if it has changed.
154
150
  * TODO:#55900: Get rid of copy-pasted OldestClientObserver code
@@ -198,6 +194,7 @@ export class SharedTree extends SharedObject {
198
194
  /**
199
195
  * Get a factory for SharedTree to register with the data store.
200
196
  * @param summarizeHistory - Determines if the history is included in summaries and if edit chunks are uploaded when they are full.
197
+ *
201
198
  * On 0.1.1 documents, due to current code limitations, this parameter is only impactful for newly created documents.
202
199
  * `SharedTree`s which load existing documents will summarize history if and only if the loaded summary included history.
203
200
  *
@@ -205,14 +202,19 @@ export class SharedTree extends SharedObject {
205
202
  * In the future we may allow modification of whether or not a particular document saves history, but only via a consensus mechanism.
206
203
  * See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
207
204
  * See docs/Breaking-Change-Migration for more details on the consensus scheme.
208
- * @param writeFormat - Determines the format version the SharedTree will write summaries in.
205
+ * @param writeFormat - Determines the format version the SharedTree will write ops and summaries in.
209
206
  * This format may be updated to a newer (supported) version at runtime if a collaborating shared-tree
210
207
  * that was initialized with a newer write version connects to the session. Care must be taken when changing this value,
211
208
  * as a staged rollout must of occurred such that all collaborating clients must have the code to read at least the version
212
209
  * written.
210
+ * See [the write format documentation](../docs/Write-Format.md) for more information.
213
211
  * @returns A factory that creates `SharedTree`s and loads them from storage.
214
212
  */
215
213
  static getFactory(writeFormat, summarizeHistory = false) {
214
+ // On 0.1.1 documents, due to current code limitations, all clients MUST agree on the value of `summarizeHistory`.
215
+ // Note that this means staged rollout changing this value should not be attempted.
216
+ // It is possible to update shared-tree to correctly handle such a staged rollout, but that hasn't been implemented.
217
+ // See the skipped test in SharedTreeFuzzTests.ts for more details on this issue.
216
218
  return new SharedTreeFactory(writeFormat, summarizeHistory);
217
219
  }
218
220
  /**
@@ -260,27 +262,60 @@ export class SharedTree extends SharedObject {
260
262
  return this.logViewer.getRevisionViewInSession(Number.POSITIVE_INFINITY);
261
263
  }
262
264
  /**
263
- * {@inheritdoc NodeIdGenerator.generateNodeId}
265
+ * Generates a node identifier.
266
+ * The returned IDs may be used as the identifier of a node in the SharedTree.
267
+ * `NodeId`s are *always* unique and stable within the scope of the tree and session that generated them. They are *not* unique within
268
+ * a Fluid container, and *cannot* be compared across instances of a SharedTree. They are *not* stable across sessions/lifetimes of a
269
+ * SharedTree, and *cannot* be persisted (e.g. stored in payloads, uploaded in blobs, etc.). If stable persistence is needed,
270
+ * NodeIdConverter.convertToStableNodeId may be used to return a corresponding UUID that is globally unique and stable.
271
+ * @param override - if supplied, calls to `convertToStableNodeId` using the returned node ID will return the override instead of
272
+ * the UUID. Calls to `generateNodeId` with the same override always return the same ID. Performance note: passing an override string
273
+ * incurs a storage cost that is significantly higher that a node ID without one, and should be avoided if possible.
264
274
  * @public
265
275
  */
266
276
  generateNodeId(override) {
267
277
  return this.idCompressor.generateCompressedId(override);
268
278
  }
269
- /** {@inheritdoc NodeIdConverter.convertToStableNodeId} */
279
+ /**
280
+ * Given a NodeId, returns the corresponding stable ID or throws if the supplied node ID was not generated with this tree (`NodeId`s
281
+ * may not be used across SharedTree instances, see `generateNodeId` for more).
282
+ * The returned value will be a UUID, unless the creation of `id` used an override string (see `generateNodeId` for more).
283
+ * The result is safe to persist and re-use across `SharedTree` instances, unlike `NodeId`.
284
+ * @public
285
+ */
270
286
  convertToStableNodeId(id) {
271
287
  var _a;
272
288
  return (_a = this.idCompressor.tryDecompress(id)) !== null && _a !== void 0 ? _a : fail('Node id is not known to this SharedTree');
273
289
  }
274
- /** {@inheritdoc NodeIdConverter.tryConvertToStableNodeId} */
290
+ /**
291
+ * Given a NodeId, attempt to return the corresponding stable ID.
292
+ * The returned value will be a UUID, unless the creation of `id` used an override string (see `generateNodeId` for more).
293
+ * The returned stable ID is undefined if `id` was never created with this SharedTree. If a stable ID is returned, this does not imply
294
+ * that there is a node with `id` in the current revision of the tree, only that `id` was at some point generated by some instance of
295
+ * this tree.
296
+ * @public
297
+ */
275
298
  tryConvertToStableNodeId(id) {
276
299
  return this.idCompressor.tryDecompress(id);
277
300
  }
278
- /** {@inheritdoc NodeIdConverter.convertToNodeId} */
301
+ /**
302
+ * Given a stable ID, return the corresponding NodeId or throws if the supplied stable ID was never generated with this tree, either
303
+ * as a UUID corresponding to a `NodeId` or as an override passed to `generateNodeId`.
304
+ * If a stable ID is returned, this does not imply that there is a node with `id` in the current revision of the tree, only that
305
+ * `id` was at some point generated by an instance of this SharedTree.
306
+ * @public
307
+ */
279
308
  convertToNodeId(id) {
280
309
  var _a;
281
310
  return ((_a = this.idCompressor.tryRecompress(id)) !== null && _a !== void 0 ? _a : fail('Stable node id is not known to this SharedTree'));
282
311
  }
283
- /** {@inheritdoc NodeIdConverter.tryConvertToNodeId} */
312
+ /**
313
+ * Given a stable ID, return the corresponding NodeId or return undefined if the supplied stable ID was never generated with this tree,
314
+ * either as a UUID corresponding to a `NodeId` or as an override passed to `generateNodeId`.
315
+ * If a stable ID is returned, this does not imply that there is a node with `id` in the current revision of the tree, only that
316
+ * `id` was at some point generated by an instance of this SharedTree.
317
+ * @public
318
+ */
284
319
  tryConvertToNodeId(id) {
285
320
  return this.idCompressor.tryRecompress(id);
286
321
  }
@@ -524,7 +559,7 @@ export class SharedTree extends SharedObject {
524
559
  // TODO:#47830: Store multiple checkpoints in summary.
525
560
  knownRevisions = [[editLog.length, { view: currentView }]];
526
561
  }
527
- const logViewer = new CachingLogViewer(editLog, RevisionView.fromTree(initialTree, this), knownRevisions, this.expensiveValidation, editStatusCallback, sequencedEditResultCallback, this.transactionFactory, 0);
562
+ const logViewer = new CachingLogViewer(editLog, RevisionView.fromTree(initialTree, this), knownRevisions, this.expensiveValidation, editStatusCallback, sequencedEditResultCallback, 0);
528
563
  this.editLog = editLog;
529
564
  this.cachingLogViewer = logViewer;
530
565
  return { editLog, cachingLogViewer: logViewer };
@@ -547,7 +582,7 @@ export class SharedTree extends SharedObject {
547
582
  }
548
583
  }
549
584
  /**
550
- * Compares this shared tree to another for equality. Should only be used for correctness testing.
585
+ * Compares this shared tree to another for equality. Should only be used for internal correctness testing.
551
586
  *
552
587
  * Equality means that the histories as captured by the EditLogs are equivalent.
553
588
  *
@@ -615,7 +650,9 @@ export class SharedTree extends SharedObject {
615
650
  }
616
651
  // TODO: This cast can be removed on typescript 4.6
617
652
  const edit = this.parseSequencedEdit(op);
618
- this.internStringsFromEdit(edit);
653
+ if (op.version === WriteFormat.v0_1_1) {
654
+ this.internStringsFromEdit(edit);
655
+ }
619
656
  this.processSequencedEdit(edit, typedMessage);
620
657
  }
621
658
  }
@@ -735,9 +772,8 @@ export class SharedTree extends SharedObject {
735
772
  }
736
773
  }
737
774
  upgradeFrom_0_0_2_to_0_1_1() {
738
- for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
739
- this.internStringsFromEdit(this.editsInternal.getEditInSessionAtIndex(i));
740
- }
775
+ // Reset the string interner, re-populate only with information that there is consensus on
776
+ this.interner = new MutableStringInterner([initialTree.definition]);
741
777
  const oldIdCompressor = this.idCompressor;
742
778
  // Create the IdCompressor that will be used after the upgrade
743
779
  const newIdCompressor = new IdCompressor(createSessionId(), reservedIdCount); // TODO: attribution info
@@ -760,12 +796,21 @@ export class SharedTree extends SharedObject {
760
796
  // All clients have the full history, and can therefore all "generate" the same final IDs for every ID in the history
761
797
  // via the ghost compressor.
762
798
  unifyHistoricalIds(ghostContext);
799
+ // The same logic applies to string interning, so intern all the strings in the history (superset of those in the current view)
800
+ for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
801
+ this.internStringsFromEdit(this.editsInternal.getEditInSessionAtIndex(i));
802
+ }
763
803
  }
764
804
  else {
765
805
  // Clients do not have the full history, but all share the same current view (sequenced). They can all finalize the same final
766
806
  // IDs for every ID in the view via the ghost compressor.
807
+ // The same logic applies for the string interner.
767
808
  for (const node of this.logViewer.getRevisionViewInSession(this.editLog.numberOfSequencedEdits)) {
768
809
  ghostContext.generateNodeId(this.convertToStableNodeId(node.identifier));
810
+ this.interner.getOrCreateInternedId(node.definition);
811
+ for (const label of [...node.traits.keys()].sort()) {
812
+ this.interner.getOrCreateInternedId(label);
813
+ }
769
814
  }
770
815
  // Every node in this client's history can simply be generated in the new compressor as well, preserving the UUID
771
816
  unifyHistoricalIds(newContext);
@@ -774,13 +819,8 @@ export class SharedTree extends SharedObject {
774
819
  newIdCompressor.finalizeCreationRange(ghostIdCompressor.takeNextCreationRange());
775
820
  this.idCompressor = newIdCompressor;
776
821
  }
777
- /**
778
- * Add an `Edit` directly.
779
- * External users should use one of the more specialized functions, like applyEdit which handles constructing the actual `Edit` object.
780
- * This is exposed as it is useful for testing, particularly with invalid and malformed Edits.
781
- * @internal
782
- */
783
- applyEdit(...changes) {
822
+ applyEdit(headOrChanges, ...tail) {
823
+ const changes = Array.isArray(headOrChanges) ? headOrChanges : [headOrChanges, ...tail];
784
824
  const id = newEditId();
785
825
  const internalEdit = {
786
826
  id,
@@ -791,6 +831,10 @@ export class SharedTree extends SharedObject {
791
831
  return internalEdit;
792
832
  }
793
833
  /**
834
+ * Applies a set of internal changes to this tree. The result will be reflected in `SharedTree.currentView`.
835
+ * External users should use one of the more specialized functions, like `applyEdit` which handles constructing the actual `Edit`
836
+ * and uses public Change types.
837
+ * This is exposed for internal use only.
794
838
  * @internal
795
839
  */
796
840
  applyEditInternal(editOrChanges) {
@@ -807,7 +851,8 @@ export class SharedTree extends SharedObject {
807
851
  return edit;
808
852
  }
809
853
  /**
810
- * Given a newly constructed edit, add any necessary metadata
854
+ * Converts a public Change type to an internal representation.
855
+ * This is exposed for internal use only.
811
856
  * @internal
812
857
  */
813
858
  internalizeChange(change) {