@fluid-experimental/tree 0.58.2002 → 0.59.1000
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/README.md +159 -46
- package/dist/ChangeCompression.d.ts +39 -0
- package/dist/ChangeCompression.d.ts.map +1 -0
- package/dist/ChangeCompression.js +117 -0
- package/dist/ChangeCompression.js.map +1 -0
- package/{lib/default-edits/PersistedTypes.d.ts → dist/ChangeTypes.d.ts} +58 -105
- package/dist/ChangeTypes.d.ts.map +1 -0
- package/dist/{default-edits/PersistedTypes.js → ChangeTypes.js} +21 -76
- package/dist/ChangeTypes.js.map +1 -0
- package/dist/Checkout.d.ts +39 -27
- package/dist/Checkout.d.ts.map +1 -1
- package/dist/Checkout.js +61 -32
- package/dist/Checkout.js.map +1 -1
- package/dist/Common.d.ts +175 -38
- package/dist/Common.d.ts.map +1 -1
- package/dist/Common.js +240 -103
- package/dist/Common.js.map +1 -1
- package/dist/EagerCheckout.d.ts +24 -0
- package/dist/EagerCheckout.d.ts.map +1 -0
- package/dist/{BasicCheckout.js → EagerCheckout.js} +9 -6
- package/dist/EagerCheckout.js.map +1 -0
- package/dist/EditLog.d.ts +77 -63
- package/dist/EditLog.d.ts.map +1 -1
- package/dist/EditLog.js +85 -48
- package/dist/EditLog.js.map +1 -1
- package/dist/EditUtilities.d.ts +168 -0
- package/dist/EditUtilities.d.ts.map +1 -0
- package/dist/EditUtilities.js +373 -0
- package/dist/EditUtilities.js.map +1 -0
- package/dist/EventTypes.d.ts +73 -0
- package/dist/EventTypes.d.ts.map +1 -0
- package/dist/EventTypes.js +78 -0
- package/dist/EventTypes.js.map +1 -0
- package/dist/Forest.d.ts +29 -7
- package/dist/Forest.d.ts.map +1 -1
- package/dist/Forest.js +60 -36
- package/dist/Forest.js.map +1 -1
- package/dist/HistoryEditFactory.d.ts +20 -0
- package/dist/HistoryEditFactory.d.ts.map +1 -0
- package/dist/HistoryEditFactory.js +226 -0
- package/dist/HistoryEditFactory.js.map +1 -0
- package/dist/IdConversion.d.ts +12 -0
- package/dist/IdConversion.d.ts.map +1 -0
- package/dist/IdConversion.js +98 -0
- package/dist/IdConversion.js.map +1 -0
- package/dist/Identifiers.d.ts +89 -2
- package/dist/Identifiers.d.ts.map +1 -1
- package/dist/Identifiers.js +10 -0
- package/dist/Identifiers.js.map +1 -1
- package/dist/InitialTree.d.ts +2 -2
- package/dist/InitialTree.d.ts.map +1 -1
- package/dist/InitialTree.js +2 -1
- package/dist/InitialTree.js.map +1 -1
- package/dist/LazyCheckout.d.ts +28 -0
- package/dist/LazyCheckout.d.ts.map +1 -0
- package/dist/LazyCheckout.js +44 -0
- package/dist/LazyCheckout.js.map +1 -0
- package/dist/LogViewer.d.ts +129 -85
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +111 -85
- package/dist/LogViewer.js.map +1 -1
- package/dist/MergeHealth.d.ts +221 -0
- package/dist/MergeHealth.d.ts.map +1 -0
- package/dist/MergeHealth.js +263 -0
- package/dist/MergeHealth.js.map +1 -0
- package/dist/NodeIdUtilities.d.ts +105 -0
- package/dist/NodeIdUtilities.d.ts.map +1 -0
- package/dist/NodeIdUtilities.js +60 -0
- package/dist/NodeIdUtilities.js.map +1 -0
- package/dist/PayloadUtilities.d.ts +42 -0
- package/dist/PayloadUtilities.d.ts.map +1 -0
- package/dist/PayloadUtilities.js +114 -0
- package/dist/PayloadUtilities.js.map +1 -0
- package/dist/ReconciliationPath.d.ts +18 -13
- package/dist/ReconciliationPath.d.ts.map +1 -1
- package/dist/ReconciliationPath.js.map +1 -1
- package/dist/RevisionValueCache.d.ts +11 -2
- package/dist/RevisionValueCache.d.ts.map +1 -1
- package/dist/RevisionValueCache.js +2 -3
- package/dist/RevisionValueCache.js.map +1 -1
- package/dist/RevisionView.d.ts +83 -0
- package/dist/RevisionView.d.ts.map +1 -0
- package/dist/RevisionView.js +182 -0
- package/dist/RevisionView.js.map +1 -0
- package/dist/SerializationUtilities.d.ts +36 -0
- package/dist/SerializationUtilities.d.ts.map +1 -0
- package/dist/SerializationUtilities.js +102 -0
- package/dist/SerializationUtilities.js.map +1 -0
- package/dist/SharedTree.d.ts +439 -0
- package/dist/SharedTree.d.ts.map +1 -0
- package/dist/SharedTree.js +1109 -0
- package/dist/SharedTree.js.map +1 -0
- package/dist/SharedTreeEncoder.d.ts +102 -0
- package/dist/SharedTreeEncoder.d.ts.map +1 -0
- package/dist/SharedTreeEncoder.js +313 -0
- package/dist/SharedTreeEncoder.js.map +1 -0
- package/dist/StringInterner.d.ts +46 -0
- package/dist/StringInterner.d.ts.map +1 -0
- package/dist/StringInterner.js +61 -0
- package/dist/StringInterner.js.map +1 -0
- package/dist/Summary.d.ts +40 -0
- package/dist/Summary.d.ts.map +1 -0
- package/dist/Summary.js +23 -0
- package/dist/Summary.js.map +1 -0
- package/dist/SummaryBackCompatibility.d.ts +22 -22
- package/dist/SummaryBackCompatibility.d.ts.map +1 -1
- package/dist/SummaryBackCompatibility.js +30 -33
- package/dist/SummaryBackCompatibility.js.map +1 -1
- package/dist/SummaryTestUtilities.d.ts +31 -0
- package/dist/SummaryTestUtilities.d.ts.map +1 -0
- package/dist/SummaryTestUtilities.js +37 -0
- package/dist/SummaryTestUtilities.js.map +1 -0
- package/dist/Transaction.d.ts +52 -0
- package/dist/Transaction.d.ts.map +1 -0
- package/dist/Transaction.js +72 -0
- package/dist/Transaction.js.map +1 -0
- package/dist/TransactionInternal.d.ts +540 -0
- package/dist/TransactionInternal.d.ts.map +1 -0
- package/dist/TransactionInternal.js +626 -0
- package/dist/TransactionInternal.js.map +1 -0
- package/dist/TreeCompressor.d.ts +36 -0
- package/dist/TreeCompressor.d.ts.map +1 -0
- package/dist/TreeCompressor.js +137 -0
- package/dist/TreeCompressor.js.map +1 -0
- package/dist/TreeNodeHandle.d.ts +12 -18
- package/dist/TreeNodeHandle.d.ts.map +1 -1
- package/dist/TreeNodeHandle.js +13 -23
- package/dist/TreeNodeHandle.js.map +1 -1
- package/dist/TreeView.d.ts +166 -0
- package/dist/TreeView.d.ts.map +1 -0
- package/dist/TreeView.js +218 -0
- package/dist/TreeView.js.map +1 -0
- package/dist/TreeViewUtilities.d.ts +21 -0
- package/dist/TreeViewUtilities.d.ts.map +1 -0
- package/dist/TreeViewUtilities.js +77 -0
- package/dist/TreeViewUtilities.js.map +1 -0
- package/dist/{default-edits/UndoRedoHandler.d.ts → UndoRedoHandler.d.ts} +2 -2
- package/dist/UndoRedoHandler.d.ts.map +1 -0
- package/dist/{default-edits/UndoRedoHandler.js → UndoRedoHandler.js} +5 -9
- package/dist/UndoRedoHandler.js.map +1 -0
- package/dist/id-compressor/AppendOnlySortedMap.d.ts +127 -0
- package/dist/id-compressor/AppendOnlySortedMap.d.ts.map +1 -0
- package/dist/id-compressor/AppendOnlySortedMap.js +283 -0
- package/dist/id-compressor/AppendOnlySortedMap.js.map +1 -0
- package/dist/id-compressor/IdCompressor.d.ts +389 -0
- package/dist/id-compressor/IdCompressor.d.ts.map +1 -0
- package/dist/id-compressor/IdCompressor.js +1353 -0
- package/dist/id-compressor/IdCompressor.js.map +1 -0
- package/dist/id-compressor/IdRange.d.ts +11 -0
- package/dist/id-compressor/IdRange.d.ts.map +1 -0
- package/dist/id-compressor/IdRange.js +29 -0
- package/dist/id-compressor/IdRange.js.map +1 -0
- package/dist/id-compressor/NumericUuid.d.ts +63 -0
- package/dist/id-compressor/NumericUuid.d.ts.map +1 -0
- package/dist/id-compressor/NumericUuid.js +377 -0
- package/dist/id-compressor/NumericUuid.js.map +1 -0
- package/dist/id-compressor/index.d.ts +12 -0
- package/dist/id-compressor/index.d.ts.map +1 -0
- package/dist/id-compressor/index.js +26 -0
- package/dist/id-compressor/index.js.map +1 -0
- package/dist/id-compressor/persisted-types/0.0.1.d.ts +156 -0
- package/dist/id-compressor/persisted-types/0.0.1.d.ts.map +1 -0
- package/dist/id-compressor/persisted-types/0.0.1.js +7 -0
- package/dist/id-compressor/persisted-types/0.0.1.js.map +1 -0
- package/dist/id-compressor/persisted-types/index.d.ts +6 -0
- package/dist/id-compressor/persisted-types/index.d.ts.map +1 -0
- package/dist/id-compressor/persisted-types/index.js +18 -0
- package/dist/id-compressor/persisted-types/index.js.map +1 -0
- package/dist/index.d.ts +29 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -35
- package/dist/index.js.map +1 -1
- package/dist/persisted-types/0.0.2.d.ts +385 -0
- package/dist/persisted-types/0.0.2.d.ts.map +1 -0
- package/dist/persisted-types/0.0.2.js +113 -0
- package/dist/persisted-types/0.0.2.js.map +1 -0
- package/dist/persisted-types/0.1.1.d.ts +314 -0
- package/dist/persisted-types/0.1.1.d.ts.map +1 -0
- package/dist/persisted-types/0.1.1.js +153 -0
- package/dist/persisted-types/0.1.1.js.map +1 -0
- package/dist/persisted-types/index.d.ts +7 -0
- package/dist/persisted-types/index.d.ts.map +1 -0
- package/dist/persisted-types/index.js +20 -0
- package/dist/persisted-types/index.js.map +1 -0
- package/docs/0-1-1-Compression.md +228 -0
- package/docs/Breaking-Change-Migration.md +52 -0
- package/docs/Compression.md +2 -2
- package/docs/Telemetry.md +43 -0
- package/docs/Write-Format.md +19 -0
- package/lib/ChangeCompression.d.ts +39 -0
- package/lib/ChangeCompression.d.ts.map +1 -0
- package/lib/ChangeCompression.js +111 -0
- package/lib/ChangeCompression.js.map +1 -0
- package/{dist/default-edits/PersistedTypes.d.ts → lib/ChangeTypes.d.ts} +58 -105
- package/lib/ChangeTypes.d.ts.map +1 -0
- package/lib/{default-edits/PersistedTypes.js → ChangeTypes.js} +15 -68
- package/lib/ChangeTypes.js.map +1 -0
- package/lib/Checkout.d.ts +39 -27
- package/lib/Checkout.d.ts.map +1 -1
- package/lib/Checkout.js +53 -24
- package/lib/Checkout.js.map +1 -1
- package/lib/Common.d.ts +175 -38
- package/lib/Common.d.ts.map +1 -1
- package/lib/Common.js +226 -101
- package/lib/Common.js.map +1 -1
- package/lib/EagerCheckout.d.ts +24 -0
- package/lib/EagerCheckout.d.ts.map +1 -0
- package/lib/{BasicCheckout.js → EagerCheckout.js} +7 -4
- package/lib/EagerCheckout.js.map +1 -0
- package/lib/EditLog.d.ts +77 -63
- package/lib/EditLog.d.ts.map +1 -1
- package/lib/EditLog.js +83 -47
- package/lib/EditLog.js.map +1 -1
- package/lib/EditUtilities.d.ts +168 -0
- package/lib/EditUtilities.d.ts.map +1 -0
- package/lib/EditUtilities.js +353 -0
- package/lib/EditUtilities.js.map +1 -0
- package/lib/EventTypes.d.ts +73 -0
- package/lib/EventTypes.d.ts.map +1 -0
- package/lib/EventTypes.js +75 -0
- package/lib/EventTypes.js.map +1 -0
- package/lib/Forest.d.ts +29 -7
- package/lib/Forest.d.ts.map +1 -1
- package/lib/Forest.js +58 -35
- package/lib/Forest.js.map +1 -1
- package/lib/HistoryEditFactory.d.ts +20 -0
- package/lib/HistoryEditFactory.d.ts.map +1 -0
- package/lib/{default-edits/HistoryEditFactory.js → HistoryEditFactory.js} +78 -39
- package/lib/HistoryEditFactory.js.map +1 -0
- package/lib/IdConversion.d.ts +12 -0
- package/lib/IdConversion.d.ts.map +1 -0
- package/lib/IdConversion.js +91 -0
- package/lib/IdConversion.js.map +1 -0
- package/lib/Identifiers.d.ts +89 -2
- package/lib/Identifiers.d.ts.map +1 -1
- package/lib/Identifiers.js +8 -1
- package/lib/Identifiers.js.map +1 -1
- package/lib/InitialTree.d.ts +2 -2
- package/lib/InitialTree.d.ts.map +1 -1
- package/lib/InitialTree.js +2 -1
- package/lib/InitialTree.js.map +1 -1
- package/lib/LazyCheckout.d.ts +28 -0
- package/lib/LazyCheckout.d.ts.map +1 -0
- package/lib/LazyCheckout.js +40 -0
- package/lib/LazyCheckout.js.map +1 -0
- package/lib/LogViewer.d.ts +129 -85
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +103 -77
- package/lib/LogViewer.js.map +1 -1
- package/lib/MergeHealth.d.ts +221 -0
- package/lib/MergeHealth.d.ts.map +1 -0
- package/lib/MergeHealth.js +258 -0
- package/lib/MergeHealth.js.map +1 -0
- package/lib/NodeIdUtilities.d.ts +105 -0
- package/lib/NodeIdUtilities.d.ts.map +1 -0
- package/lib/NodeIdUtilities.js +53 -0
- package/lib/NodeIdUtilities.js.map +1 -0
- package/lib/PayloadUtilities.d.ts +42 -0
- package/lib/PayloadUtilities.d.ts.map +1 -0
- package/lib/PayloadUtilities.js +110 -0
- package/lib/PayloadUtilities.js.map +1 -0
- package/lib/ReconciliationPath.d.ts +18 -13
- package/lib/ReconciliationPath.d.ts.map +1 -1
- package/lib/ReconciliationPath.js.map +1 -1
- package/lib/RevisionValueCache.d.ts +11 -2
- package/lib/RevisionValueCache.d.ts.map +1 -1
- package/lib/RevisionValueCache.js +2 -3
- package/lib/RevisionValueCache.js.map +1 -1
- package/lib/RevisionView.d.ts +83 -0
- package/lib/RevisionView.d.ts.map +1 -0
- package/lib/RevisionView.js +175 -0
- package/lib/RevisionView.js.map +1 -0
- package/lib/SerializationUtilities.d.ts +36 -0
- package/lib/SerializationUtilities.d.ts.map +1 -0
- package/lib/SerializationUtilities.js +95 -0
- package/lib/SerializationUtilities.js.map +1 -0
- package/lib/SharedTree.d.ts +439 -0
- package/lib/SharedTree.d.ts.map +1 -0
- package/lib/SharedTree.js +1104 -0
- package/lib/SharedTree.js.map +1 -0
- package/lib/SharedTreeEncoder.d.ts +102 -0
- package/lib/SharedTreeEncoder.d.ts.map +1 -0
- package/lib/SharedTreeEncoder.js +308 -0
- package/lib/SharedTreeEncoder.js.map +1 -0
- package/lib/StringInterner.d.ts +46 -0
- package/lib/StringInterner.d.ts.map +1 -0
- package/lib/StringInterner.js +57 -0
- package/lib/StringInterner.js.map +1 -0
- package/lib/Summary.d.ts +40 -0
- package/lib/Summary.d.ts.map +1 -0
- package/lib/Summary.js +19 -0
- package/lib/Summary.js.map +1 -0
- package/lib/SummaryBackCompatibility.d.ts +22 -22
- package/lib/SummaryBackCompatibility.d.ts.map +1 -1
- package/lib/SummaryBackCompatibility.js +29 -32
- package/lib/SummaryBackCompatibility.js.map +1 -1
- package/lib/SummaryTestUtilities.d.ts +31 -0
- package/lib/SummaryTestUtilities.d.ts.map +1 -0
- package/lib/SummaryTestUtilities.js +32 -0
- package/lib/SummaryTestUtilities.js.map +1 -0
- package/lib/Transaction.d.ts +52 -0
- package/lib/Transaction.d.ts.map +1 -0
- package/lib/Transaction.js +68 -0
- package/lib/Transaction.js.map +1 -0
- package/lib/TransactionInternal.d.ts +540 -0
- package/lib/TransactionInternal.d.ts.map +1 -0
- package/lib/TransactionInternal.js +622 -0
- package/lib/TransactionInternal.js.map +1 -0
- package/lib/TreeCompressor.d.ts +36 -0
- package/lib/TreeCompressor.d.ts.map +1 -0
- package/lib/TreeCompressor.js +133 -0
- package/lib/TreeCompressor.js.map +1 -0
- package/lib/TreeNodeHandle.d.ts +12 -18
- package/lib/TreeNodeHandle.d.ts.map +1 -1
- package/lib/TreeNodeHandle.js +14 -24
- package/lib/TreeNodeHandle.js.map +1 -1
- package/lib/TreeView.d.ts +166 -0
- package/lib/TreeView.d.ts.map +1 -0
- package/lib/TreeView.js +214 -0
- package/lib/TreeView.js.map +1 -0
- package/lib/TreeViewUtilities.d.ts +21 -0
- package/lib/TreeViewUtilities.d.ts.map +1 -0
- package/lib/TreeViewUtilities.js +71 -0
- package/lib/TreeViewUtilities.js.map +1 -0
- package/lib/{default-edits/UndoRedoHandler.d.ts → UndoRedoHandler.d.ts} +2 -2
- package/lib/UndoRedoHandler.d.ts.map +1 -0
- package/lib/{default-edits/UndoRedoHandler.js → UndoRedoHandler.js} +3 -7
- package/lib/UndoRedoHandler.js.map +1 -0
- package/lib/id-compressor/AppendOnlySortedMap.d.ts +127 -0
- package/lib/id-compressor/AppendOnlySortedMap.d.ts.map +1 -0
- package/lib/id-compressor/AppendOnlySortedMap.js +278 -0
- package/lib/id-compressor/AppendOnlySortedMap.js.map +1 -0
- package/lib/id-compressor/IdCompressor.d.ts +389 -0
- package/lib/id-compressor/IdCompressor.d.ts.map +1 -0
- package/lib/id-compressor/IdCompressor.js +1343 -0
- package/lib/id-compressor/IdCompressor.js.map +1 -0
- package/lib/id-compressor/IdRange.d.ts +11 -0
- package/lib/id-compressor/IdRange.d.ts.map +1 -0
- package/lib/id-compressor/IdRange.js +25 -0
- package/lib/id-compressor/IdRange.js.map +1 -0
- package/lib/id-compressor/NumericUuid.d.ts +63 -0
- package/lib/id-compressor/NumericUuid.d.ts.map +1 -0
- package/lib/id-compressor/NumericUuid.js +365 -0
- package/lib/id-compressor/NumericUuid.js.map +1 -0
- package/lib/id-compressor/index.d.ts +12 -0
- package/lib/id-compressor/index.d.ts.map +1 -0
- package/lib/id-compressor/index.js +12 -0
- package/lib/id-compressor/index.js.map +1 -0
- package/lib/id-compressor/persisted-types/0.0.1.d.ts +156 -0
- package/lib/id-compressor/persisted-types/0.0.1.d.ts.map +1 -0
- package/lib/{test/Snapshot.tests.d.ts → id-compressor/persisted-types/0.0.1.js} +1 -1
- package/lib/id-compressor/persisted-types/0.0.1.js.map +1 -0
- package/lib/id-compressor/persisted-types/index.d.ts +6 -0
- package/lib/id-compressor/persisted-types/index.d.ts.map +1 -0
- package/lib/id-compressor/persisted-types/index.js +6 -0
- package/lib/id-compressor/persisted-types/index.js.map +1 -0
- package/lib/index.d.ts +29 -9
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +23 -6
- package/lib/index.js.map +1 -1
- package/lib/persisted-types/0.0.2.d.ts +385 -0
- package/lib/persisted-types/0.0.2.d.ts.map +1 -0
- package/lib/persisted-types/0.0.2.js +110 -0
- package/lib/persisted-types/0.0.2.js.map +1 -0
- package/lib/persisted-types/0.1.1.d.ts +314 -0
- package/lib/persisted-types/0.1.1.d.ts.map +1 -0
- package/lib/persisted-types/0.1.1.js +150 -0
- package/lib/persisted-types/0.1.1.js.map +1 -0
- package/lib/persisted-types/index.d.ts +7 -0
- package/lib/persisted-types/index.d.ts.map +1 -0
- package/lib/persisted-types/index.js +8 -0
- package/lib/persisted-types/index.js.map +1 -0
- package/lib/test/AppendOnlySortedMap.tests.d.ts +6 -0
- package/lib/test/AppendOnlySortedMap.tests.d.ts.map +1 -0
- package/lib/test/AppendOnlySortedMap.tests.js +169 -0
- package/lib/test/AppendOnlySortedMap.tests.js.map +1 -0
- package/lib/test/{SnapshotUtilities.tests.d.ts → ChangeCompression.tests.d.ts} +1 -1
- package/lib/test/ChangeCompression.tests.d.ts.map +1 -0
- package/lib/test/ChangeCompression.tests.js +145 -0
- package/lib/test/ChangeCompression.tests.js.map +1 -0
- package/lib/test/Checkout.tests.d.ts +2 -3
- package/lib/test/Checkout.tests.d.ts.map +1 -1
- package/lib/test/Checkout.tests.js +126 -69
- package/lib/test/Checkout.tests.js.map +1 -1
- package/lib/test/Common.tests.js +60 -2
- package/lib/test/Common.tests.js.map +1 -1
- package/lib/test/{BasicCheckout.tests.d.ts → EagerCheckout.tests.d.ts} +1 -1
- package/lib/test/EagerCheckout.tests.d.ts.map +1 -0
- package/lib/test/EagerCheckout.tests.js +20 -0
- package/lib/test/EagerCheckout.tests.js.map +1 -0
- package/lib/test/Edit.tests.js +22 -14
- package/lib/test/Edit.tests.js.map +1 -1
- package/lib/test/{Anchors.glassBox.tests.d.ts → EditLog.perf.tests.d.ts} +1 -1
- package/lib/test/EditLog.perf.tests.d.ts.map +1 -0
- package/lib/test/EditLog.perf.tests.js +30 -0
- package/lib/test/EditLog.perf.tests.js.map +1 -0
- package/lib/test/EditLog.tests.js +10 -6
- package/lib/test/EditLog.tests.js.map +1 -1
- package/lib/test/EditUtilities.tests.d.ts +6 -0
- package/lib/test/EditUtilities.tests.d.ts.map +1 -0
- package/lib/test/EditUtilities.tests.js +503 -0
- package/lib/test/EditUtilities.tests.js.map +1 -0
- package/lib/test/Forest.perf.tests.d.ts +6 -0
- package/lib/test/Forest.perf.tests.d.ts.map +1 -0
- package/lib/test/Forest.perf.tests.js +133 -0
- package/lib/test/Forest.perf.tests.js.map +1 -0
- package/lib/test/Forest.tests.js +54 -27
- package/lib/test/Forest.tests.js.map +1 -1
- package/lib/test/GenericTransaction.tests.js +12 -3
- package/lib/test/GenericTransaction.tests.js.map +1 -1
- package/lib/test/HistoryEditFactory.tests.d.ts +6 -0
- package/lib/test/HistoryEditFactory.tests.d.ts.map +1 -0
- package/lib/test/HistoryEditFactory.tests.js +90 -0
- package/lib/test/HistoryEditFactory.tests.js.map +1 -0
- package/lib/test/IdCompressor.perf.tests.d.ts +6 -0
- package/lib/test/IdCompressor.perf.tests.d.ts.map +1 -0
- package/lib/test/IdCompressor.perf.tests.js +304 -0
- package/lib/test/IdCompressor.perf.tests.js.map +1 -0
- package/lib/test/IdCompressor.tests.d.ts +6 -0
- package/lib/test/IdCompressor.tests.d.ts.map +1 -0
- package/lib/test/IdCompressor.tests.js +1075 -0
- package/lib/test/IdCompressor.tests.js.map +1 -0
- package/lib/test/IdConversion.tests.d.ts +6 -0
- package/lib/test/IdConversion.tests.d.ts.map +1 -0
- package/lib/test/IdConversion.tests.js +36 -0
- package/lib/test/IdConversion.tests.js.map +1 -0
- package/lib/test/LazyCheckout.tests.d.ts +6 -0
- package/lib/test/LazyCheckout.tests.d.ts.map +1 -0
- package/lib/test/LazyCheckout.tests.js +22 -0
- package/lib/test/LazyCheckout.tests.js.map +1 -0
- package/lib/test/LogViewer.tests.js +276 -191
- package/lib/test/LogViewer.tests.js.map +1 -1
- package/lib/test/{SharedTreeWithAnchors.tests.d.ts → MergeHealthTelemetryHeartbeat.tests.d.ts} +1 -1
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.d.ts.map +1 -0
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.js +342 -0
- package/lib/test/MergeHealthTelemetryHeartbeat.tests.js.map +1 -0
- package/lib/test/NumericUuid.perf.tests.d.ts +6 -0
- package/lib/test/NumericUuid.perf.tests.d.ts.map +1 -0
- package/lib/test/NumericUuid.perf.tests.js +68 -0
- package/lib/test/NumericUuid.perf.tests.js.map +1 -0
- package/lib/test/NumericUuid.tests.d.ts +6 -0
- package/lib/test/NumericUuid.tests.d.ts.map +1 -0
- package/lib/test/NumericUuid.tests.js +191 -0
- package/lib/test/NumericUuid.tests.js.map +1 -0
- package/lib/test/RevisionView.tests.d.ts +6 -0
- package/lib/test/RevisionView.tests.d.ts.map +1 -0
- package/lib/test/RevisionView.tests.js +133 -0
- package/lib/test/RevisionView.tests.js.map +1 -0
- package/lib/test/SharedTree.perf.tests.d.ts +6 -0
- package/lib/test/SharedTree.perf.tests.d.ts.map +1 -0
- package/lib/test/SharedTree.perf.tests.js +39 -0
- package/lib/test/SharedTree.perf.tests.js.map +1 -0
- package/lib/test/SharedTree.tests.js +15 -3
- package/lib/test/SharedTree.tests.js.map +1 -1
- package/lib/test/StringInterner.tests.d.ts +6 -0
- package/lib/test/StringInterner.tests.d.ts.map +1 -0
- package/lib/test/StringInterner.tests.js +71 -0
- package/lib/test/StringInterner.tests.js.map +1 -0
- package/lib/test/Summary.tests.d.ts +8 -0
- package/lib/test/Summary.tests.d.ts.map +1 -0
- package/lib/test/Summary.tests.js +407 -0
- package/lib/test/Summary.tests.js.map +1 -0
- package/lib/test/Transaction.tests.js +76 -330
- package/lib/test/Transaction.tests.js.map +1 -1
- package/lib/test/TransactionInternal.tests.d.ts +6 -0
- package/lib/test/TransactionInternal.tests.d.ts.map +1 -0
- package/lib/test/TransactionInternal.tests.js +568 -0
- package/lib/test/TransactionInternal.tests.js.map +1 -0
- package/lib/test/TreeCompression.tests.d.ts +6 -0
- package/lib/test/TreeCompression.tests.d.ts.map +1 -0
- package/lib/test/TreeCompression.tests.js +292 -0
- package/lib/test/TreeCompression.tests.js.map +1 -0
- package/lib/test/TreeView.tests.d.ts +6 -0
- package/lib/test/TreeView.tests.d.ts.map +1 -0
- package/lib/test/TreeView.tests.js +176 -0
- package/lib/test/TreeView.tests.js.map +1 -0
- package/lib/test/UndoRedoHandler.tests.js +2 -2
- package/lib/test/UndoRedoHandler.tests.js.map +1 -1
- package/lib/test/Virtualization.tests.js +146 -62
- package/lib/test/Virtualization.tests.js.map +1 -1
- package/lib/test/fuzz/Generators.d.ts +19 -0
- package/lib/test/fuzz/Generators.d.ts.map +1 -0
- package/lib/test/fuzz/Generators.js +420 -0
- package/lib/test/fuzz/Generators.js.map +1 -0
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts +20 -0
- package/lib/test/fuzz/SharedTreeFuzzTests.d.ts.map +1 -0
- package/lib/test/fuzz/SharedTreeFuzzTests.js +217 -0
- package/lib/test/fuzz/SharedTreeFuzzTests.js.map +1 -0
- package/lib/test/fuzz/Types.d.ts +133 -0
- package/lib/test/fuzz/Types.d.ts.map +1 -0
- package/lib/test/{GenericTransactionWithAnchors.tests.d.ts → fuzz/Types.js} +2 -2
- package/lib/test/fuzz/Types.js.map +1 -0
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts +180 -0
- package/lib/test/utilities/IdCompressorTestUtilities.d.ts.map +1 -0
- package/lib/test/utilities/IdCompressorTestUtilities.js +528 -0
- package/lib/test/utilities/IdCompressorTestUtilities.js.map +1 -0
- package/lib/test/utilities/MockTransaction.d.ts +26 -7
- package/lib/test/utilities/MockTransaction.d.ts.map +1 -1
- package/lib/test/utilities/MockTransaction.js +40 -11
- package/lib/test/utilities/MockTransaction.js.map +1 -1
- package/lib/test/utilities/PendingLocalStateTests.d.ts +12 -0
- package/lib/test/utilities/PendingLocalStateTests.d.ts.map +1 -0
- package/lib/test/utilities/PendingLocalStateTests.js +105 -0
- package/lib/test/utilities/PendingLocalStateTests.js.map +1 -0
- package/lib/test/utilities/SharedTreeTests.d.ts +3 -4
- package/lib/test/utilities/SharedTreeTests.d.ts.map +1 -1
- package/lib/test/utilities/SharedTreeTests.js +696 -439
- package/lib/test/utilities/SharedTreeTests.js.map +1 -1
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts +11 -0
- package/lib/test/utilities/SharedTreeVersioningTests.d.ts.map +1 -0
- package/lib/test/utilities/SharedTreeVersioningTests.js +370 -0
- package/lib/test/utilities/SharedTreeVersioningTests.js.map +1 -0
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts +10 -0
- package/lib/test/utilities/SummaryLoadPerfTests.d.ts.map +1 -0
- package/lib/test/utilities/SummaryLoadPerfTests.js +102 -0
- package/lib/test/utilities/SummaryLoadPerfTests.js.map +1 -0
- package/lib/test/utilities/SummarySizeTests.d.ts +11 -0
- package/lib/test/utilities/SummarySizeTests.d.ts.map +1 -0
- package/lib/test/utilities/SummarySizeTests.js +158 -0
- package/lib/test/utilities/SummarySizeTests.js.map +1 -0
- package/lib/test/utilities/TestCommon.d.ts +9 -0
- package/lib/test/utilities/TestCommon.d.ts.map +1 -0
- package/lib/test/utilities/TestCommon.js +13 -0
- package/lib/test/utilities/TestCommon.js.map +1 -0
- package/lib/test/utilities/TestNode.d.ts +140 -0
- package/lib/test/utilities/TestNode.d.ts.map +1 -0
- package/lib/test/utilities/TestNode.js +292 -0
- package/lib/test/utilities/TestNode.js.map +1 -0
- package/lib/test/utilities/TestUtilities.d.ts +84 -70
- package/lib/test/utilities/TestUtilities.d.ts.map +1 -1
- package/lib/test/utilities/TestUtilities.js +218 -143
- package/lib/test/utilities/TestUtilities.js.map +1 -1
- package/lib/test/utilities/UndoRedoTests.d.ts +4 -5
- package/lib/test/utilities/UndoRedoTests.d.ts.map +1 -1
- package/lib/test/utilities/UndoRedoTests.js +138 -149
- package/lib/test/utilities/UndoRedoTests.js.map +1 -1
- package/package.json +22 -17
- package/src/ChangeCompression.ts +159 -0
- package/src/{default-edits/PersistedTypes.ts → ChangeTypes.ts} +62 -125
- package/src/Checkout.ts +82 -53
- package/src/Common.ts +317 -117
- package/src/EagerCheckout.ts +38 -0
- package/src/EditLog.ts +153 -100
- package/src/EditUtilities.ts +559 -0
- package/src/EventTypes.ts +74 -0
- package/src/Forest.ts +81 -73
- package/src/{default-edits/HistoryEditFactory.ts → HistoryEditFactory.ts} +103 -53
- package/src/IdConversion.ts +125 -0
- package/src/Identifiers.ts +101 -1
- package/src/InitialTree.ts +5 -4
- package/src/LazyCheckout.ts +51 -0
- package/src/LogViewer.ts +242 -166
- package/src/MergeHealth.ts +447 -0
- package/src/NodeIdUtilities.ts +156 -0
- package/src/PayloadUtilities.ts +124 -0
- package/src/ReconciliationPath.ts +18 -13
- package/src/RevisionValueCache.ts +14 -5
- package/src/RevisionView.ts +252 -0
- package/src/SerializationUtilities.ts +130 -0
- package/src/SharedTree.ts +1501 -0
- package/src/SharedTreeEncoder.ts +493 -0
- package/src/StringInterner.ts +72 -0
- package/src/Summary.ts +48 -0
- package/src/SummaryBackCompatibility.ts +47 -57
- package/src/SummaryTestUtilities.ts +54 -0
- package/src/Transaction.ts +89 -0
- package/src/TransactionInternal.ts +1087 -0
- package/src/TreeCompressor.ts +213 -0
- package/src/TreeNodeHandle.ts +19 -32
- package/src/TreeView.ts +322 -0
- package/src/TreeViewUtilities.ts +77 -0
- package/src/{default-edits/UndoRedoHandler.ts → UndoRedoHandler.ts} +8 -13
- package/src/id-compressor/AppendOnlySortedMap.ts +325 -0
- package/src/id-compressor/IdCompressor.md +3 -0
- package/src/id-compressor/IdCompressor.ts +1848 -0
- package/src/id-compressor/IdRange.ts +33 -0
- package/src/id-compressor/NumericUuid.ts +414 -0
- package/src/id-compressor/index.ts +13 -0
- package/src/id-compressor/persisted-types/0.0.1.ts +179 -0
- package/src/id-compressor/persisted-types/README.md +3 -0
- package/src/id-compressor/persisted-types/index.ts +6 -0
- package/src/index.ts +118 -59
- package/src/persisted-types/0.0.2.ts +442 -0
- package/src/persisted-types/0.1.1.ts +476 -0
- package/src/persisted-types/README.md +22 -0
- package/src/persisted-types/index.ts +9 -0
- package/.mocharc.js +0 -41
- package/api/tree.api.md +0 -729
- package/dist/BasicCheckout.d.ts +0 -23
- package/dist/BasicCheckout.d.ts.map +0 -1
- package/dist/BasicCheckout.js.map +0 -1
- package/dist/Snapshot.d.ts +0 -198
- package/dist/Snapshot.d.ts.map +0 -1
- package/dist/Snapshot.js +0 -267
- package/dist/Snapshot.js.map +0 -1
- package/dist/SnapshotUtilities.d.ts +0 -29
- package/dist/SnapshotUtilities.d.ts.map +0 -1
- package/dist/SnapshotUtilities.js +0 -73
- package/dist/SnapshotUtilities.js.map +0 -1
- package/dist/anchored-edits/AnchorResolution.d.ts +0 -144
- package/dist/anchored-edits/AnchorResolution.d.ts.map +0 -1
- package/dist/anchored-edits/AnchorResolution.js +0 -162
- package/dist/anchored-edits/AnchorResolution.js.map +0 -1
- package/dist/anchored-edits/Factory.d.ts +0 -56
- package/dist/anchored-edits/Factory.d.ts.map +0 -1
- package/dist/anchored-edits/Factory.js +0 -79
- package/dist/anchored-edits/Factory.js.map +0 -1
- package/dist/anchored-edits/PersistedTypes.d.ts +0 -245
- package/dist/anchored-edits/PersistedTypes.d.ts.map +0 -1
- package/dist/anchored-edits/PersistedTypes.js +0 -131
- package/dist/anchored-edits/PersistedTypes.js.map +0 -1
- package/dist/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
- package/dist/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
- package/dist/anchored-edits/SharedTreeWithAnchors.js +0 -115
- package/dist/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
- package/dist/anchored-edits/TransactionWithAnchors.d.ts +0 -28
- package/dist/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
- package/dist/anchored-edits/TransactionWithAnchors.js +0 -36
- package/dist/anchored-edits/TransactionWithAnchors.js.map +0 -1
- package/dist/anchored-edits/index.d.ts +0 -10
- package/dist/anchored-edits/index.d.ts.map +0 -1
- package/dist/anchored-edits/index.js +0 -34
- package/dist/anchored-edits/index.js.map +0 -1
- package/dist/default-edits/EditUtilities.d.ts +0 -57
- package/dist/default-edits/EditUtilities.d.ts.map +0 -1
- package/dist/default-edits/EditUtilities.js +0 -192
- package/dist/default-edits/EditUtilities.js.map +0 -1
- package/dist/default-edits/Factory.d.ts +0 -56
- package/dist/default-edits/Factory.d.ts.map +0 -1
- package/dist/default-edits/Factory.js +0 -79
- package/dist/default-edits/Factory.js.map +0 -1
- package/dist/default-edits/HistoryEditFactory.d.ts +0 -19
- package/dist/default-edits/HistoryEditFactory.d.ts.map +0 -1
- package/dist/default-edits/HistoryEditFactory.js +0 -187
- package/dist/default-edits/HistoryEditFactory.js.map +0 -1
- package/dist/default-edits/PersistedTypes.d.ts.map +0 -1
- package/dist/default-edits/PersistedTypes.js.map +0 -1
- package/dist/default-edits/SharedTree.d.ts +0 -111
- package/dist/default-edits/SharedTree.d.ts.map +0 -1
- package/dist/default-edits/SharedTree.js +0 -124
- package/dist/default-edits/SharedTree.js.map +0 -1
- package/dist/default-edits/Summary.d.ts +0 -15
- package/dist/default-edits/Summary.d.ts.map +0 -1
- package/dist/default-edits/Summary.js +0 -35
- package/dist/default-edits/Summary.js.map +0 -1
- package/dist/default-edits/Transaction.d.ts +0 -41
- package/dist/default-edits/Transaction.d.ts.map +0 -1
- package/dist/default-edits/Transaction.js +0 -225
- package/dist/default-edits/Transaction.js.map +0 -1
- package/dist/default-edits/UndoRedoHandler.d.ts.map +0 -1
- package/dist/default-edits/UndoRedoHandler.js.map +0 -1
- package/dist/default-edits/index.d.ts +0 -13
- package/dist/default-edits/index.d.ts.map +0 -1
- package/dist/default-edits/index.js +0 -41
- package/dist/default-edits/index.js.map +0 -1
- package/dist/generic/GenericEditUtilities.d.ts +0 -26
- package/dist/generic/GenericEditUtilities.d.ts.map +0 -1
- package/dist/generic/GenericEditUtilities.js +0 -45
- package/dist/generic/GenericEditUtilities.js.map +0 -1
- package/dist/generic/GenericSharedTree.d.ts +0 -221
- package/dist/generic/GenericSharedTree.d.ts.map +0 -1
- package/dist/generic/GenericSharedTree.js +0 -447
- package/dist/generic/GenericSharedTree.js.map +0 -1
- package/dist/generic/GenericTransaction.d.ts +0 -87
- package/dist/generic/GenericTransaction.d.ts.map +0 -1
- package/dist/generic/GenericTransaction.js +0 -144
- package/dist/generic/GenericTransaction.js.map +0 -1
- package/dist/generic/PersistedTypes.d.ts +0 -194
- package/dist/generic/PersistedTypes.d.ts.map +0 -1
- package/dist/generic/PersistedTypes.js +0 -42
- package/dist/generic/PersistedTypes.js.map +0 -1
- package/dist/generic/Summary.d.ts +0 -63
- package/dist/generic/Summary.d.ts.map +0 -1
- package/dist/generic/Summary.js +0 -64
- package/dist/generic/Summary.js.map +0 -1
- package/dist/generic/index.d.ts +0 -10
- package/dist/generic/index.d.ts.map +0 -1
- package/dist/generic/index.js +0 -26
- package/dist/generic/index.js.map +0 -1
- package/docs/Future.md +0 -155
- package/lib/BasicCheckout.d.ts +0 -23
- package/lib/BasicCheckout.d.ts.map +0 -1
- package/lib/BasicCheckout.js.map +0 -1
- package/lib/Snapshot.d.ts +0 -198
- package/lib/Snapshot.d.ts.map +0 -1
- package/lib/Snapshot.js +0 -263
- package/lib/Snapshot.js.map +0 -1
- package/lib/SnapshotUtilities.d.ts +0 -29
- package/lib/SnapshotUtilities.d.ts.map +0 -1
- package/lib/SnapshotUtilities.js +0 -67
- package/lib/SnapshotUtilities.js.map +0 -1
- package/lib/anchored-edits/AnchorResolution.d.ts +0 -144
- package/lib/anchored-edits/AnchorResolution.d.ts.map +0 -1
- package/lib/anchored-edits/AnchorResolution.js +0 -152
- package/lib/anchored-edits/AnchorResolution.js.map +0 -1
- package/lib/anchored-edits/Factory.d.ts +0 -56
- package/lib/anchored-edits/Factory.d.ts.map +0 -1
- package/lib/anchored-edits/Factory.js +0 -74
- package/lib/anchored-edits/Factory.js.map +0 -1
- package/lib/anchored-edits/PersistedTypes.d.ts +0 -245
- package/lib/anchored-edits/PersistedTypes.d.ts.map +0 -1
- package/lib/anchored-edits/PersistedTypes.js +0 -128
- package/lib/anchored-edits/PersistedTypes.js.map +0 -1
- package/lib/anchored-edits/SharedTreeWithAnchors.d.ts +0 -120
- package/lib/anchored-edits/SharedTreeWithAnchors.d.ts.map +0 -1
- package/lib/anchored-edits/SharedTreeWithAnchors.js +0 -110
- package/lib/anchored-edits/SharedTreeWithAnchors.js.map +0 -1
- package/lib/anchored-edits/TransactionWithAnchors.d.ts +0 -28
- package/lib/anchored-edits/TransactionWithAnchors.d.ts.map +0 -1
- package/lib/anchored-edits/TransactionWithAnchors.js +0 -32
- package/lib/anchored-edits/TransactionWithAnchors.js.map +0 -1
- package/lib/anchored-edits/index.d.ts +0 -10
- package/lib/anchored-edits/index.d.ts.map +0 -1
- package/lib/anchored-edits/index.js +0 -11
- package/lib/anchored-edits/index.js.map +0 -1
- package/lib/default-edits/EditUtilities.d.ts +0 -57
- package/lib/default-edits/EditUtilities.d.ts.map +0 -1
- package/lib/default-edits/EditUtilities.js +0 -181
- package/lib/default-edits/EditUtilities.js.map +0 -1
- package/lib/default-edits/Factory.d.ts +0 -56
- package/lib/default-edits/Factory.d.ts.map +0 -1
- package/lib/default-edits/Factory.js +0 -74
- package/lib/default-edits/Factory.js.map +0 -1
- package/lib/default-edits/HistoryEditFactory.d.ts +0 -19
- package/lib/default-edits/HistoryEditFactory.d.ts.map +0 -1
- package/lib/default-edits/HistoryEditFactory.js.map +0 -1
- package/lib/default-edits/PersistedTypes.d.ts.map +0 -1
- package/lib/default-edits/PersistedTypes.js.map +0 -1
- package/lib/default-edits/SharedTree.d.ts +0 -111
- package/lib/default-edits/SharedTree.d.ts.map +0 -1
- package/lib/default-edits/SharedTree.js +0 -100
- package/lib/default-edits/SharedTree.js.map +0 -1
- package/lib/default-edits/Summary.d.ts +0 -15
- package/lib/default-edits/Summary.d.ts.map +0 -1
- package/lib/default-edits/Summary.js +0 -31
- package/lib/default-edits/Summary.js.map +0 -1
- package/lib/default-edits/Transaction.d.ts +0 -41
- package/lib/default-edits/Transaction.d.ts.map +0 -1
- package/lib/default-edits/Transaction.js +0 -221
- package/lib/default-edits/Transaction.js.map +0 -1
- package/lib/default-edits/UndoRedoHandler.d.ts.map +0 -1
- package/lib/default-edits/UndoRedoHandler.js.map +0 -1
- package/lib/default-edits/index.d.ts +0 -13
- package/lib/default-edits/index.d.ts.map +0 -1
- package/lib/default-edits/index.js +0 -14
- package/lib/default-edits/index.js.map +0 -1
- package/lib/generic/GenericEditUtilities.d.ts +0 -26
- package/lib/generic/GenericEditUtilities.d.ts.map +0 -1
- package/lib/generic/GenericEditUtilities.js +0 -38
- package/lib/generic/GenericEditUtilities.js.map +0 -1
- package/lib/generic/GenericSharedTree.d.ts +0 -221
- package/lib/generic/GenericSharedTree.d.ts.map +0 -1
- package/lib/generic/GenericSharedTree.js +0 -443
- package/lib/generic/GenericSharedTree.js.map +0 -1
- package/lib/generic/GenericTransaction.d.ts +0 -87
- package/lib/generic/GenericTransaction.d.ts.map +0 -1
- package/lib/generic/GenericTransaction.js +0 -140
- package/lib/generic/GenericTransaction.js.map +0 -1
- package/lib/generic/PersistedTypes.d.ts +0 -194
- package/lib/generic/PersistedTypes.d.ts.map +0 -1
- package/lib/generic/PersistedTypes.js +0 -39
- package/lib/generic/PersistedTypes.js.map +0 -1
- package/lib/generic/Summary.d.ts +0 -63
- package/lib/generic/Summary.d.ts.map +0 -1
- package/lib/generic/Summary.js +0 -58
- package/lib/generic/Summary.js.map +0 -1
- package/lib/generic/index.d.ts +0 -10
- package/lib/generic/index.d.ts.map +0 -1
- package/lib/generic/index.js +0 -11
- package/lib/generic/index.js.map +0 -1
- package/lib/test/Anchors.glassBox.tests.d.ts.map +0 -1
- package/lib/test/Anchors.glassBox.tests.js +0 -410
- package/lib/test/Anchors.glassBox.tests.js.map +0 -1
- package/lib/test/BasicCheckout.tests.d.ts.map +0 -1
- package/lib/test/BasicCheckout.tests.js +0 -8
- package/lib/test/BasicCheckout.tests.js.map +0 -1
- package/lib/test/GenericTransactionWithAnchors.tests.d.ts.map +0 -1
- package/lib/test/GenericTransactionWithAnchors.tests.js +0 -25
- package/lib/test/GenericTransactionWithAnchors.tests.js.map +0 -1
- package/lib/test/SharedTreeWithAnchors.tests.d.ts.map +0 -1
- package/lib/test/SharedTreeWithAnchors.tests.js +0 -420
- package/lib/test/SharedTreeWithAnchors.tests.js.map +0 -1
- package/lib/test/Snapshot.tests.d.ts.map +0 -1
- package/lib/test/Snapshot.tests.js +0 -96
- package/lib/test/Snapshot.tests.js.map +0 -1
- package/lib/test/SnapshotUtilities.tests.d.ts.map +0 -1
- package/lib/test/SnapshotUtilities.tests.js +0 -168
- package/lib/test/SnapshotUtilities.tests.js.map +0 -1
- package/lib/test/undoRedoStackManager.d.ts +0 -26
- package/lib/test/undoRedoStackManager.d.ts.map +0 -1
- package/lib/test/undoRedoStackManager.js +0 -176
- package/lib/test/undoRedoStackManager.js.map +0 -1
- package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts +0 -13
- package/lib/test/utilities/SummaryFormatCompatibilityTests.d.ts.map +0 -1
- package/lib/test/utilities/SummaryFormatCompatibilityTests.js +0 -154
- package/lib/test/utilities/SummaryFormatCompatibilityTests.js.map +0 -1
- package/src/BasicCheckout.ts +0 -34
- package/src/Snapshot.ts +0 -363
- package/src/SnapshotUtilities.ts +0 -88
- package/src/anchored-edits/AnchorResolution.ts +0 -442
- package/src/anchored-edits/Factory.ts +0 -94
- package/src/anchored-edits/PersistedTypes.ts +0 -310
- package/src/anchored-edits/SharedTreeWithAnchors.ts +0 -200
- package/src/anchored-edits/TransactionWithAnchors.ts +0 -39
- package/src/anchored-edits/index.ts +0 -21
- package/src/default-edits/EditUtilities.ts +0 -220
- package/src/default-edits/Factory.ts +0 -94
- package/src/default-edits/SharedTree.ts +0 -174
- package/src/default-edits/Summary.ts +0 -44
- package/src/default-edits/Transaction.ts +0 -262
- package/src/default-edits/index.ts +0 -29
- package/src/generic/GenericEditUtilities.ts +0 -46
- package/src/generic/GenericSharedTree.ts +0 -593
- package/src/generic/GenericTransaction.ts +0 -194
- package/src/generic/PersistedTypes.ts +0 -221
- package/src/generic/Summary.ts +0 -113
- package/src/generic/index.ts +0 -41
|
@@ -3,34 +3,50 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { assert, expect } from 'chai';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { MockFluidDataStoreRuntime } from '@fluidframework/test-runtime-utils';
|
|
9
|
-
import { assertArrayOfOne, assertNotUndefined, isSharedTreeEvent } from '../../Common';
|
|
10
|
-
import {
|
|
11
|
-
import { Change, ChangeType, Delete, Insert, StablePlace, StableRange } from '../../default-edits';
|
|
12
|
-
import { Snapshot } from '../../Snapshot';
|
|
6
|
+
import { IsoBuffer } from '@fluidframework/common-utils';
|
|
7
|
+
import { LoaderHeader } from '@fluidframework/container-definitions';
|
|
8
|
+
import { MockFluidDataStoreRuntime, } from '@fluidframework/test-runtime-utils';
|
|
9
|
+
import { assertArrayOfOne, assertNotUndefined, fail, isSharedTreeEvent } from '../../Common';
|
|
10
|
+
import { EditLog } from '../../EditLog';
|
|
13
11
|
import { initialTree } from '../../InitialTree';
|
|
14
12
|
import { TreeNodeHandle } from '../../TreeNodeHandle';
|
|
15
13
|
import { deserialize } from '../../SummaryBackCompatibility';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
14
|
+
import { useFailedSequencedEditTelemetry } from '../../MergeHealth';
|
|
15
|
+
import { MutableStringInterner } from '../../StringInterner';
|
|
16
|
+
import { getChangeNodeFromView } from '../../SerializationUtilities';
|
|
17
|
+
import { ChangeInternal, ChangeTypeInternal, editsPerChunk, EditStatus, WriteFormat, } from '../../persisted-types';
|
|
18
|
+
import { SharedTreeDiagnosticEvent, SharedTreeEvent } from '../../EventTypes';
|
|
19
|
+
import { Change, ChangeType, StablePlace, StableRange } from '../../ChangeTypes';
|
|
20
|
+
import { convertTreeNodes, deepCompareNodes } from '../../EditUtilities';
|
|
21
|
+
import { serialize } from '../../Summary';
|
|
22
|
+
import { InterningTreeCompressor } from '../../TreeCompressor';
|
|
23
|
+
import { SharedTreeEncoder_0_0_2, SharedTreeEncoder_0_1_1 } from '../../SharedTreeEncoder';
|
|
24
|
+
import { sequencedIdNormalizer } from '../../NodeIdUtilities';
|
|
25
|
+
import { convertNodeDataIds } from '../../IdConversion';
|
|
26
|
+
import { buildLeaf, SimpleTestTree } from './TestNode';
|
|
18
27
|
import { TestFluidHandle, TestFluidSerializer } from './TestSerializer';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
import { runSharedTreeUndoRedoTestSuite } from './UndoRedoTests';
|
|
29
|
+
import { areNodesEquivalent, assertNoDelta, setUpTestTree, testTrait, testTraitLabel, translateId, spyOnSubmittedOps, normalizeIds, normalizeId, normalizeEdit, setUpLocalServerTestSharedTree, applyNoop, getIdNormalizerFromSharedTree, waitForSummary, } from './TestUtilities';
|
|
30
|
+
function revertEditInTree(tree, edit) {
|
|
31
|
+
return tree.revert(edit);
|
|
32
|
+
}
|
|
23
33
|
// Options for the undo/redo test suite. The undo and redo functions are the same.
|
|
24
34
|
const undoRedoOptions = {
|
|
25
35
|
title: 'Revert',
|
|
26
|
-
undo:
|
|
27
|
-
redo:
|
|
36
|
+
undo: revertEditInTree,
|
|
37
|
+
redo: revertEditInTree,
|
|
28
38
|
};
|
|
29
39
|
/**
|
|
30
|
-
* Runs a test suite for operations on `SharedTree`.
|
|
40
|
+
* Runs a test suite for operations on `SharedTree` writing ops at `writeFormat`.
|
|
31
41
|
* This suite can be used to test other implementations that aim to fulfill `SharedTree`'s contract.
|
|
32
42
|
*/
|
|
33
|
-
export function runSharedTreeOperationsTests(title,
|
|
43
|
+
export function runSharedTreeOperationsTests(title, writeFormat, setUpTestSharedTreeWithDefaultVersion) {
|
|
44
|
+
const setUpTestSharedTree = (options) => setUpTestSharedTreeWithDefaultVersion(Object.assign({ writeFormat }, options));
|
|
45
|
+
function createSimpleTestTree(options) {
|
|
46
|
+
const { tree: sharedTree, componentRuntime, containerRuntimeFactory } = setUpTestSharedTree(options);
|
|
47
|
+
const testTree = setUpTestTree(sharedTree);
|
|
48
|
+
return { sharedTree, testTree, componentRuntime, containerRuntimeFactory };
|
|
49
|
+
}
|
|
34
50
|
describe(title, () => {
|
|
35
51
|
const testSerializer = new TestFluidSerializer();
|
|
36
52
|
describe('SharedTree before initialization', () => {
|
|
@@ -40,664 +56,905 @@ export function runSharedTreeOperationsTests(title, setUpTestSharedTree) {
|
|
|
40
56
|
});
|
|
41
57
|
it('valid without initial tree', () => {
|
|
42
58
|
const { tree } = setUpTestSharedTree();
|
|
43
|
-
expect(tree.currentView.getTrait(testTrait)).deep.equals([], 'Root should exist, and child traits should be valid but empty.');
|
|
59
|
+
expect(tree.currentView.getTrait(testTrait(tree.currentView))).deep.equals([], 'Root should exist, and child traits should be valid but empty.');
|
|
44
60
|
});
|
|
45
61
|
});
|
|
46
62
|
describe('SharedTree in local state', () => {
|
|
47
63
|
it('does not emit change events for each change in a batch of changes', () => {
|
|
48
|
-
const {
|
|
64
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
49
65
|
let changeCount = 0;
|
|
50
|
-
|
|
51
|
-
const leftTrait =
|
|
52
|
-
const rightTrait =
|
|
66
|
+
sharedTree.on(SharedTreeEvent.EditCommitted, () => {
|
|
67
|
+
const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
|
|
68
|
+
const rightTrait = sharedTree.currentView.getTrait(testTree.right.traitLocation);
|
|
53
69
|
expect(leftTrait.length).to.equal(0); // "left" child is deleted...
|
|
54
70
|
expect(rightTrait.length).to.equal(2); // ...and added to "right" trait
|
|
55
71
|
changeCount += 1;
|
|
56
72
|
});
|
|
57
|
-
|
|
73
|
+
sharedTree.applyEdit(...Change.move(StableRange.only(testTree.left), StablePlace.after(testTree.right)));
|
|
58
74
|
expect(changeCount).equals(1);
|
|
59
75
|
});
|
|
60
76
|
it('can insert a wrapped tree', () => {
|
|
61
|
-
const {
|
|
62
|
-
const childNode =
|
|
77
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
78
|
+
const childNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
63
79
|
const childId = 0;
|
|
64
80
|
const childrenTraitLabel = 'children';
|
|
65
81
|
const parentNode = {
|
|
66
|
-
identifier:
|
|
82
|
+
identifier: testTree.generateNodeId(),
|
|
67
83
|
definition: 'node',
|
|
68
84
|
traits: {
|
|
69
|
-
[childrenTraitLabel]:
|
|
85
|
+
[childrenTraitLabel]: childId,
|
|
70
86
|
},
|
|
71
87
|
};
|
|
72
88
|
const parentId = 1;
|
|
73
|
-
const buildChild = Change.build(
|
|
74
|
-
const buildParent = Change.build(
|
|
75
|
-
const insertParent = Change.insert(parentId, StablePlace.before(left));
|
|
76
|
-
|
|
77
|
-
const leftTrait =
|
|
89
|
+
const buildChild = Change.build(childNode, childId);
|
|
90
|
+
const buildParent = Change.build(parentNode, parentId);
|
|
91
|
+
const insertParent = Change.insert(parentId, StablePlace.before(testTree.left));
|
|
92
|
+
sharedTree.applyEdit(buildChild, buildParent, insertParent);
|
|
93
|
+
const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
|
|
78
94
|
expect(leftTrait.length).to.equal(2);
|
|
79
95
|
expect(leftTrait[0]).to.equal(parentNode.identifier);
|
|
80
|
-
const childrenTrait =
|
|
96
|
+
const childrenTrait = sharedTree.currentView.getTrait({
|
|
81
97
|
parent: parentNode.identifier,
|
|
82
98
|
label: childrenTraitLabel,
|
|
83
99
|
});
|
|
84
100
|
expect(childrenTrait.length).to.equal(1);
|
|
85
101
|
expect(childrenTrait[0]).to.equal(childNode.identifier);
|
|
86
102
|
});
|
|
87
|
-
it('prevents
|
|
88
|
-
const {
|
|
89
|
-
const childNode =
|
|
103
|
+
it('prevents multi-parenting detached trees', () => {
|
|
104
|
+
const { sharedTree, testTree } = createSimpleTestTree({ allowMalformed: true });
|
|
105
|
+
const childNode = testTree.buildLeaf();
|
|
90
106
|
const childId = 0;
|
|
91
107
|
const childrenTraitLabel = 'children';
|
|
92
108
|
const parentNode = {
|
|
93
|
-
identifier:
|
|
109
|
+
identifier: testTree.generateNodeId(),
|
|
94
110
|
definition: 'node',
|
|
95
111
|
traits: {
|
|
96
|
-
[childrenTraitLabel]:
|
|
112
|
+
[childrenTraitLabel]: childId,
|
|
97
113
|
},
|
|
98
114
|
};
|
|
99
|
-
const parentId = 1;
|
|
100
115
|
const parentNode2 = {
|
|
101
|
-
identifier:
|
|
116
|
+
identifier: testTree.generateNodeId(),
|
|
102
117
|
definition: 'node',
|
|
103
118
|
traits: {
|
|
104
|
-
[childrenTraitLabel]:
|
|
119
|
+
[childrenTraitLabel]: childId,
|
|
105
120
|
},
|
|
106
121
|
};
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
assertNoDelta(tree, () => {
|
|
122
|
+
const buildChild = Change.build(childNode, childId);
|
|
123
|
+
const buildParent = Change.build(parentNode, 1);
|
|
124
|
+
const buildParent2 = Change.build(parentNode2, 2);
|
|
125
|
+
assertNoDelta(sharedTree, () => {
|
|
112
126
|
// we don't expect this edit application to change anything
|
|
113
|
-
|
|
127
|
+
sharedTree.applyEdit(buildChild, buildParent, buildParent2);
|
|
114
128
|
});
|
|
115
129
|
});
|
|
116
130
|
// TODO:#58052: Make this test pass.
|
|
117
131
|
it.skip('prevents setting the value of a node in a detached subtree', () => {
|
|
118
|
-
const {
|
|
119
|
-
const detachedNode =
|
|
132
|
+
const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
|
|
133
|
+
const detachedNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
120
134
|
const detachedSequenceId = 0;
|
|
121
|
-
const
|
|
122
|
-
const logViewer =
|
|
123
|
-
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(
|
|
124
|
-
|
|
135
|
+
const { id } = sharedTree.applyEdit(Change.build(detachedNode, detachedSequenceId), Change.setPayload(detachedNode.identifier, 42), Change.insert(detachedSequenceId, StablePlace.before(testTree.left)));
|
|
136
|
+
const logViewer = sharedTree.logViewer;
|
|
137
|
+
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(id)).status).equals(EditStatus.Invalid);
|
|
138
|
+
sharedTree.currentView.assertConsistent();
|
|
125
139
|
});
|
|
126
140
|
// TODO:#58052: Make this test pass.
|
|
127
141
|
it.skip('prevents inserting a node in a detached subtree through a local edit', () => {
|
|
128
|
-
const {
|
|
129
|
-
const detachedNewNode =
|
|
142
|
+
const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
|
|
143
|
+
const detachedNewNode = testTree.buildLeaf();
|
|
130
144
|
const detachedNewNodeSequenceId = 0;
|
|
131
145
|
const detachedRightNodeSequenceId = 1;
|
|
132
|
-
const
|
|
146
|
+
const { id } = sharedTree.applyEdit(Change.build(detachedNewNode, detachedNewNodeSequenceId), Change.detach(StableRange.only(testTree.right), detachedRightNodeSequenceId),
|
|
133
147
|
// This change attempts to insert a node under a detached node
|
|
134
|
-
Change.insert(detachedNewNodeSequenceId, StablePlace.atStartOf({ parent: right.identifier, label: 'foo' })), Change.insert(detachedRightNodeSequenceId, StablePlace.before(left)));
|
|
135
|
-
const logViewer =
|
|
136
|
-
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(
|
|
137
|
-
|
|
148
|
+
Change.insert(detachedNewNodeSequenceId, StablePlace.atStartOf({ parent: testTree.right.identifier, label: 'foo' })), Change.insert(detachedRightNodeSequenceId, StablePlace.before(testTree.left)));
|
|
149
|
+
const logViewer = sharedTree.logViewer;
|
|
150
|
+
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(id)).status).equals(EditStatus.Invalid);
|
|
151
|
+
sharedTree.currentView.assertConsistent();
|
|
138
152
|
});
|
|
139
153
|
it('prevents deletion of the root', () => {
|
|
140
|
-
const {
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
const { sharedTree } = createSimpleTestTree({ allowInvalid: true });
|
|
155
|
+
const rootId = sharedTree.convertToNodeId(initialTree.identifier);
|
|
156
|
+
expect(sharedTree.currentView.hasNode(rootId));
|
|
157
|
+
assertNoDelta(sharedTree, () => {
|
|
143
158
|
// Try to delete the root
|
|
144
|
-
|
|
159
|
+
sharedTree.applyEdit(Change.delete(StableRange.only(rootId)));
|
|
145
160
|
});
|
|
146
161
|
});
|
|
147
162
|
it('can apply multiple local edits without ack from server', () => {
|
|
148
|
-
const {
|
|
149
|
-
const newNode =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const leftTrait =
|
|
163
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
164
|
+
const newNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
165
|
+
sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
|
|
166
|
+
sharedTree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
|
|
167
|
+
const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
|
|
153
168
|
expect(leftTrait.length).equals(2);
|
|
154
169
|
expect(leftTrait[0]).equals(newNode.identifier);
|
|
155
170
|
});
|
|
156
|
-
it('is not equal to a tree with different
|
|
157
|
-
const
|
|
158
|
-
const { tree } = setUpTestSharedTree(
|
|
159
|
-
|
|
160
|
-
expect(tree.equals(secondTree)).to.be.false;
|
|
171
|
+
it('is not equal to a tree with different current views', () => {
|
|
172
|
+
const { sharedTree: sharedTree1 } = createSimpleTestTree();
|
|
173
|
+
const { tree: sharedTree2 } = setUpTestSharedTree();
|
|
174
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.false;
|
|
161
175
|
});
|
|
162
|
-
it('is not equal to a tree with the same view but different edit
|
|
163
|
-
const {
|
|
164
|
-
const {
|
|
176
|
+
it('is not equal to a tree with the same current view but different edit logs', () => {
|
|
177
|
+
const { sharedTree: sharedTree1 } = createSimpleTestTree();
|
|
178
|
+
const { sharedTree: sharedTree2 } = createSimpleTestTree();
|
|
165
179
|
// The edits that create the initial tree have different identities.
|
|
166
|
-
expect(
|
|
180
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.false;
|
|
167
181
|
});
|
|
168
182
|
it('tolerates invalid inserts', () => {
|
|
169
|
-
const {
|
|
170
|
-
const firstNode =
|
|
171
|
-
const secondNode =
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
|
|
184
|
+
const firstNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
185
|
+
const secondNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
186
|
+
sharedTree.applyEdit(...Change.insertTree(firstNode, StablePlace.after(testTree.left)));
|
|
187
|
+
sharedTree.applyEdit(Change.delete(StableRange.only(firstNode)));
|
|
174
188
|
// Trying to insert next to the deleted node should drop, confirm that it doesn't
|
|
175
|
-
// change the
|
|
176
|
-
assertNoDelta(
|
|
177
|
-
|
|
189
|
+
// change the view
|
|
190
|
+
assertNoDelta(sharedTree, () => {
|
|
191
|
+
sharedTree.applyEdit(...Change.insertTree(secondNode, StablePlace.after(firstNode)));
|
|
178
192
|
});
|
|
179
|
-
const leftTrait =
|
|
193
|
+
const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
|
|
180
194
|
expect(leftTrait.length).to.equal(1);
|
|
181
195
|
});
|
|
182
196
|
it('tolerates invalid detaches', () => {
|
|
183
|
-
const {
|
|
184
|
-
const firstNode =
|
|
185
|
-
const secondNode =
|
|
186
|
-
const thirdNode =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
assertNoDelta(
|
|
197
|
+
const { sharedTree, testTree } = createSimpleTestTree({ allowInvalid: true });
|
|
198
|
+
const firstNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
199
|
+
const secondNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
200
|
+
const thirdNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
201
|
+
sharedTree.applyEdit(...Change.insertTree([firstNode, secondNode, thirdNode], StablePlace.after(testTree.left)));
|
|
202
|
+
sharedTree.applyEdit(Change.delete(StableRange.only(secondNode)));
|
|
203
|
+
assertNoDelta(sharedTree, () => {
|
|
190
204
|
// Trying to delete from before firstNode to after secondNode should drop
|
|
191
|
-
|
|
192
|
-
Delete.create(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode))),
|
|
193
|
-
]));
|
|
205
|
+
sharedTree.applyEdit(Change.delete(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode))));
|
|
194
206
|
// Trying to delete from after thirdNode to before firstNode should drop
|
|
195
|
-
|
|
196
|
-
Delete.create(StableRange.from(StablePlace.after(thirdNode)).to(StablePlace.before(firstNode))),
|
|
197
|
-
]));
|
|
207
|
+
sharedTree.applyEdit(Change.delete(StableRange.from(StablePlace.after(thirdNode)).to(StablePlace.before(firstNode))));
|
|
198
208
|
});
|
|
199
209
|
// Expect that firstNode did not get deleted
|
|
200
|
-
const leftTrait =
|
|
210
|
+
const leftTrait = sharedTree.currentView.getTrait(testTree.left.traitLocation);
|
|
201
211
|
expect(leftTrait.length).to.equal(3);
|
|
202
212
|
});
|
|
203
213
|
it('tolerates malformed inserts', () => {
|
|
204
|
-
const {
|
|
205
|
-
assertNoDelta(
|
|
206
|
-
|
|
214
|
+
const { sharedTree } = createSimpleTestTree({ allowMalformed: true });
|
|
215
|
+
assertNoDelta(sharedTree, () => {
|
|
216
|
+
sharedTree.applyEdit(Change.build([], 0));
|
|
207
217
|
});
|
|
208
218
|
});
|
|
209
219
|
runSharedTreeUndoRedoTestSuite(Object.assign({ localMode: true }, undoRedoOptions));
|
|
210
220
|
});
|
|
211
221
|
describe('SharedTree in connected state with a remote SharedTree', () => {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
};
|
|
222
|
+
/**
|
|
223
|
+
* Initial tree options for multi-tree tests below.
|
|
224
|
+
* Intended to be passed to {@link createSimpleTestTree}.
|
|
225
|
+
*/
|
|
226
|
+
const tree1Options = { localMode: false };
|
|
227
|
+
/**
|
|
228
|
+
* Secondary tree options derived from some initial tree.
|
|
229
|
+
*/
|
|
230
|
+
function createSecondTreeOptions(containerRuntimeFactory) {
|
|
231
|
+
return {
|
|
232
|
+
containerRuntimeFactory,
|
|
233
|
+
id: 'secondTestSharedTree',
|
|
234
|
+
localMode: false,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
217
237
|
it('should apply remote changes and converge', () => {
|
|
218
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(
|
|
219
|
-
const { tree:
|
|
238
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
|
|
239
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
|
|
240
|
+
const newNodeId1 = sharedTree1.generateNodeId();
|
|
241
|
+
sharedTree1.applyEdit(...Change.insertTree([buildLeaf(newNodeId1)], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
|
|
220
242
|
// Sync initial tree
|
|
221
243
|
containerRuntimeFactory.processAllMessages();
|
|
244
|
+
const newNodeId2 = translateId(newNodeId1, sharedTree1, sharedTree2);
|
|
222
245
|
// Both trees should contain 'left'
|
|
223
|
-
expect(
|
|
224
|
-
expect(
|
|
225
|
-
|
|
226
|
-
containerRuntimeFactory.processAllMessages();
|
|
227
|
-
const rootA = tree.currentView.getSnapshotNode(simpleTestTree.identifier);
|
|
228
|
-
expect(rootA.traits.get(leftTraitLabel)).to.be.undefined;
|
|
229
|
-
const rootB = secondTree.currentView.getSnapshotNode(simpleTestTree.identifier);
|
|
230
|
-
expect(rootB.traits.get(leftTraitLabel)).to.be.undefined;
|
|
231
|
-
});
|
|
232
|
-
it('should apply local edits after all sequenced edits', () => {
|
|
233
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(treeOptions);
|
|
234
|
-
const { tree: secondTree } = setUpTestSharedTree(Object.assign({ containerRuntimeFactory }, secondTreeOptions));
|
|
235
|
-
// Sync initial tree
|
|
236
|
-
containerRuntimeFactory.processAllMessages();
|
|
237
|
-
const newNode = makeEmptyNode();
|
|
238
|
-
tree.editor.insert(newNode, StablePlace.after(left));
|
|
239
|
-
containerRuntimeFactory.processAllMessages();
|
|
240
|
-
// Concurrently perform edit that will be sequenced before the move below.
|
|
241
|
-
// If local edits are sorted incorrectly (before sequenced edits), this will cause evaluation of a state in which the local
|
|
242
|
-
// move is evaluated before all other edits. Since it is causally dependant on the initial insert edit, it would fail.
|
|
243
|
-
secondTree.editor.delete(right);
|
|
244
|
-
// Attempt to move the new node.
|
|
245
|
-
tree.editor.move(newNode, StablePlace.before(left));
|
|
246
|
-
// Deliver the remote edit. The move should be applied after all sequenced edits and succeed.
|
|
246
|
+
expect(sharedTree1.currentView.getViewNode(newNodeId1)).to.not.be.undefined;
|
|
247
|
+
expect(sharedTree2.currentView.getViewNode(newNodeId2)).to.not.be.undefined;
|
|
248
|
+
sharedTree2.applyEdit(Change.delete(StableRange.only(newNodeId2)));
|
|
247
249
|
containerRuntimeFactory.processAllMessages();
|
|
250
|
+
const rootA = sharedTree1.currentView.getViewNode(sharedTree1.currentView.root);
|
|
251
|
+
expect(rootA.traits.get(testTraitLabel)).to.be.undefined;
|
|
252
|
+
const rootB = sharedTree2.currentView.getViewNode(sharedTree2.currentView.root);
|
|
253
|
+
expect(rootB.traits.get(testTraitLabel)).to.be.undefined;
|
|
248
254
|
});
|
|
249
255
|
it('converges in the face of concurrent changes', () => {
|
|
250
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(
|
|
251
|
-
const {
|
|
252
|
-
|
|
256
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
|
|
257
|
+
const { sharedTree: sharedTree2 } = createSimpleTestTree(createSecondTreeOptions(containerRuntimeFactory));
|
|
258
|
+
const newNodeId1 = sharedTree1.generateNodeId();
|
|
259
|
+
sharedTree1.applyEdit(...Change.insertTree([buildLeaf(newNodeId1)], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
|
|
253
260
|
containerRuntimeFactory.processAllMessages();
|
|
254
261
|
// First client deletes a trait containing a node in the initial tree
|
|
255
|
-
|
|
262
|
+
sharedTree1.applyEdit(Change.delete(StableRange.all(testTrait(sharedTree1.currentView))));
|
|
256
263
|
// Second client concurrently adds a new node to that trait
|
|
257
|
-
const
|
|
258
|
-
|
|
264
|
+
const newNodeId2 = sharedTree2.generateNodeId();
|
|
265
|
+
sharedTree2.applyEdit(...Change.insertTree([buildLeaf(newNodeId2)], StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
|
|
259
266
|
containerRuntimeFactory.processAllMessages();
|
|
260
267
|
// Second client's change gets sequenced after the deletion, so the trait
|
|
261
|
-
// should exist and contain the new node on both clients after messages are delivered.
|
|
262
|
-
const leftTrait =
|
|
263
|
-
const secondLeftTrait =
|
|
268
|
+
// should exist and contain the second new node on both clients after messages are delivered.
|
|
269
|
+
const leftTrait = normalizeIds(sharedTree1, ...sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView)));
|
|
270
|
+
const secondLeftTrait = normalizeIds(sharedTree2, ...sharedTree2.currentView.getTrait(testTrait(sharedTree2.currentView)));
|
|
264
271
|
expect(leftTrait.length).equals(1);
|
|
265
|
-
expect(leftTrait[0]).equals(
|
|
272
|
+
expect(leftTrait[0]).equals(normalizeId(sharedTree2, newNodeId2));
|
|
266
273
|
expect(leftTrait).deep.equals(secondLeftTrait);
|
|
267
274
|
});
|
|
268
275
|
it('is equal to a tree with the same state', () => {
|
|
269
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(
|
|
270
|
-
const { tree:
|
|
276
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(tree1Options);
|
|
277
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
|
|
278
|
+
const newNodeId1 = sharedTree1.generateNodeId();
|
|
279
|
+
sharedTree1.applyEdit(...Change.insertTree([
|
|
280
|
+
{
|
|
281
|
+
identifier: newNodeId1,
|
|
282
|
+
definition: 'foo',
|
|
283
|
+
traits: { left: buildLeaf(), right: buildLeaf() },
|
|
284
|
+
},
|
|
285
|
+
], StablePlace.atStartOf(testTrait(sharedTree1.currentView))));
|
|
271
286
|
containerRuntimeFactory.processAllMessages();
|
|
272
|
-
expect(
|
|
273
|
-
|
|
287
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.true;
|
|
288
|
+
sharedTree2.applyEdit(Change.delete(StableRange.only(translateId(newNodeId1, sharedTree1, sharedTree2))));
|
|
274
289
|
containerRuntimeFactory.processAllMessages();
|
|
275
|
-
expect(
|
|
290
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.true;
|
|
276
291
|
});
|
|
277
292
|
// TODO:#58052: Make this test pass.
|
|
278
293
|
it.skip('prevents inserting a node in a detached subtree as the result of merged edits', () => {
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const
|
|
294
|
+
const { testTree } = createSimpleTestTree();
|
|
295
|
+
const rootNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
296
|
+
const parent1Node = testTree.buildLeaf(testTree.generateNodeId());
|
|
297
|
+
const parent2Node = testTree.buildLeaf(testTree.generateNodeId());
|
|
298
|
+
const parent2Id = parent2Node.identifier;
|
|
299
|
+
const childNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
300
|
+
const childId = childNode.identifier;
|
|
301
|
+
const initialTree = Object.assign(Object.assign({}, rootNode), { traits: {
|
|
286
302
|
parents: [
|
|
287
|
-
Object.assign(Object.assign({},
|
|
288
|
-
|
|
303
|
+
Object.assign(Object.assign({}, parent1Node), { traits: { child: childNode } }),
|
|
304
|
+
parent2Node,
|
|
289
305
|
],
|
|
290
306
|
} });
|
|
291
|
-
const
|
|
292
|
-
const
|
|
307
|
+
const childTraitUnderParent2 = { parent: parent2Id, label: 'child' };
|
|
308
|
+
const badTraitUnderChild = { parent: childId, label: 'whatever' };
|
|
309
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { initialTree, allowInvalid: true }));
|
|
310
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
|
|
293
311
|
containerRuntimeFactory.processAllMessages();
|
|
294
312
|
// Move the child under parent2
|
|
295
313
|
// This first edit should succeed locally and globally
|
|
296
|
-
const
|
|
314
|
+
const edit1 = sharedTree1.applyEdit(...Change.move(StableRange.only(childId), StablePlace.atStartOf(childTraitUnderParent2)));
|
|
297
315
|
// Concurrently move parent2 under child
|
|
298
316
|
// This first edit should succeed locally but fail globally
|
|
299
|
-
const
|
|
317
|
+
const edit2 = sharedTree2.applyEdit(...Change.move(StableRange.only(parent2Id), StablePlace.atStartOf(badTraitUnderChild)));
|
|
300
318
|
containerRuntimeFactory.processAllMessages();
|
|
301
|
-
const logViewer =
|
|
302
|
-
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(
|
|
303
|
-
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(
|
|
304
|
-
|
|
319
|
+
const logViewer = sharedTree1.logViewer;
|
|
320
|
+
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit1.id)).status).equals(EditStatus.Applied);
|
|
321
|
+
expect(logViewer.getEditResultInSession(logViewer.log.getIndexOfId(edit2.id)).status).equals(EditStatus.Invalid);
|
|
322
|
+
sharedTree1.currentView.assertConsistent();
|
|
305
323
|
});
|
|
306
324
|
it('tolerates invalid inserts', () => {
|
|
307
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({},
|
|
308
|
-
const { tree:
|
|
325
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
|
|
326
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
|
|
309
327
|
containerRuntimeFactory.processAllMessages();
|
|
310
|
-
const
|
|
311
|
-
const
|
|
328
|
+
const firstNode2 = buildLeaf(sharedTree2.generateNodeId());
|
|
329
|
+
const firstEdit = sharedTree2.applyEdit(...Change.insertTree(firstNode2, StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
|
|
312
330
|
containerRuntimeFactory.processAllMessages();
|
|
313
331
|
// Concurrently edit, creating invalid insert.
|
|
314
332
|
// Create delete. This will apply.
|
|
315
|
-
const
|
|
316
|
-
let
|
|
317
|
-
assertNoDelta(
|
|
333
|
+
const secondEdit = sharedTree1.applyEdit(Change.delete(StableRange.only(translateId(firstNode2, sharedTree2, sharedTree1))));
|
|
334
|
+
let thirdEdit;
|
|
335
|
+
assertNoDelta(sharedTree1, () => {
|
|
318
336
|
// concurrently insert next to the deleted node: this will become invalid.
|
|
319
|
-
const
|
|
320
|
-
|
|
337
|
+
const secondNode2 = buildLeaf();
|
|
338
|
+
thirdEdit = sharedTree2.applyEdit(...Change.insertTree(secondNode2, StablePlace.after(firstNode2)));
|
|
321
339
|
containerRuntimeFactory.processAllMessages();
|
|
322
340
|
});
|
|
323
|
-
const leftTrait =
|
|
324
|
-
expect(leftTrait.length).to.equal(
|
|
325
|
-
const editIds =
|
|
326
|
-
|
|
327
|
-
expect(editIds[1]).is.equal(
|
|
328
|
-
expect(editIds[2]).is.equal(
|
|
329
|
-
expect(editIds[3]).is.equal(thirdEditId);
|
|
341
|
+
const leftTrait = sharedTree2.currentView.getTrait(testTrait(sharedTree2.currentView));
|
|
342
|
+
expect(leftTrait.length).to.equal(0);
|
|
343
|
+
const editIds = sharedTree1.edits.editIds;
|
|
344
|
+
expect(editIds[0]).is.equal(firstEdit.id);
|
|
345
|
+
expect(editIds[1]).is.equal(secondEdit.id);
|
|
346
|
+
expect(editIds[2]).is.equal(thirdEdit.id);
|
|
330
347
|
});
|
|
331
348
|
it('tolerates invalid detaches', () => {
|
|
332
|
-
const { tree, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({},
|
|
333
|
-
const { tree:
|
|
349
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
|
|
350
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
|
|
334
351
|
containerRuntimeFactory.processAllMessages();
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const thirdNode =
|
|
338
|
-
const
|
|
352
|
+
const firstNode2 = buildLeaf(sharedTree2.generateNodeId());
|
|
353
|
+
const secondNode2 = buildLeaf(sharedTree2.generateNodeId());
|
|
354
|
+
const thirdNode = buildLeaf();
|
|
355
|
+
const firstEdit = sharedTree2.applyEdit(...Change.insertTree([firstNode2, secondNode2, thirdNode], StablePlace.atStartOf(testTrait(sharedTree2.currentView))));
|
|
339
356
|
containerRuntimeFactory.processAllMessages();
|
|
340
|
-
// Concurrently edit, creating invalid insert.
|
|
341
357
|
// Create delete. This will apply.
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// concurrently delete from before firstNode to after secondNode: this should become invalid
|
|
346
|
-
thirdEditId = secondTree.editor.delete(StableRange.from(StablePlace.before(firstNode)).to(StablePlace.after(secondNode)));
|
|
347
|
-
});
|
|
358
|
+
const secondEdit = sharedTree1.applyEdit(Change.delete(StableRange.only(translateId(secondNode2, sharedTree2, sharedTree1))));
|
|
359
|
+
// concurrently delete from before firstNode to after secondNode: this should become invalid
|
|
360
|
+
const thirdEdit = sharedTree2.applyEdit(Change.delete(StableRange.from(StablePlace.before(firstNode2)).to(StablePlace.after(secondNode2))));
|
|
348
361
|
containerRuntimeFactory.processAllMessages();
|
|
349
362
|
// Expect that firstNode did not get deleted
|
|
350
|
-
const leftTrait =
|
|
351
|
-
expect(leftTrait.length).to.equal(
|
|
352
|
-
const editIds =
|
|
353
|
-
|
|
354
|
-
expect(editIds[1]).to.equal(
|
|
355
|
-
expect(editIds[2]).to.equal(
|
|
356
|
-
expect(editIds[3]).to.equal(thirdEditId);
|
|
363
|
+
const leftTrait = sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView));
|
|
364
|
+
expect(leftTrait.length).to.equal(2);
|
|
365
|
+
const editIds = sharedTree1.edits.editIds;
|
|
366
|
+
expect(editIds[0]).to.equal(firstEdit.id);
|
|
367
|
+
expect(editIds[1]).to.equal(secondEdit.id);
|
|
368
|
+
expect(editIds[2]).to.equal(thirdEdit.id);
|
|
357
369
|
});
|
|
358
370
|
it('tolerates malformed inserts', () => {
|
|
359
|
-
const {
|
|
360
|
-
const { tree:
|
|
371
|
+
const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree(Object.assign(Object.assign({}, tree1Options), { allowMalformed: true }));
|
|
372
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(createSecondTreeOptions(containerRuntimeFactory));
|
|
361
373
|
containerRuntimeFactory.processAllMessages();
|
|
362
|
-
let
|
|
363
|
-
assertNoDelta(
|
|
374
|
+
let editId;
|
|
375
|
+
assertNoDelta(sharedTree1, () => {
|
|
364
376
|
const build = Change.build([], 0);
|
|
365
|
-
|
|
366
|
-
secondTree.processLocalEdit(edit);
|
|
377
|
+
editId = sharedTree2.applyEdit(build).id;
|
|
367
378
|
containerRuntimeFactory.processAllMessages();
|
|
368
379
|
});
|
|
369
380
|
// Edit 0 creates initial tree
|
|
370
|
-
expect(
|
|
381
|
+
expect(sharedTree1.edits.getIdAtIndex(1)).to.equal(editId);
|
|
371
382
|
});
|
|
372
383
|
runSharedTreeUndoRedoTestSuite(Object.assign({ localMode: false }, undoRedoOptions));
|
|
373
384
|
// This is a regression test for documents corrupted by the following github issue:
|
|
374
385
|
// https://github.com/microsoft/FluidFramework/issues/4399
|
|
375
386
|
it('tolerates duplicate edits in trailing operations', () => {
|
|
376
|
-
const {
|
|
387
|
+
const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree(tree1Options);
|
|
377
388
|
const remoteRuntime = containerRuntimeFactory.createContainerRuntime(new MockFluidDataStoreRuntime());
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
389
|
+
const ops = spyOnSubmittedOps(containerRuntimeFactory);
|
|
390
|
+
const initialEditCount = sharedTree1.edits.length;
|
|
391
|
+
sharedTree1.applyEdit(Change.setPayload(sharedTree1.currentView.root, 42));
|
|
392
|
+
remoteRuntime.submit(ops[0], /* localOpMetadata */ undefined);
|
|
383
393
|
containerRuntimeFactory.processAllMessages();
|
|
384
|
-
expect(
|
|
394
|
+
expect(sharedTree1.edits.length).to.equal(initialEditCount + 1);
|
|
385
395
|
});
|
|
396
|
+
it('detects concurrent duplicate IDs', () => {
|
|
397
|
+
const { tree: sharedTree1, containerRuntimeFactory } = setUpTestSharedTree(Object.assign(Object.assign({}, tree1Options), { allowInvalid: true }));
|
|
398
|
+
const { tree: sharedTree2 } = setUpTestSharedTree(Object.assign(Object.assign({}, createSecondTreeOptions(containerRuntimeFactory)), { allowInvalid: true }));
|
|
399
|
+
containerRuntimeFactory.processAllMessages();
|
|
400
|
+
const duplicateId = 'duplicate';
|
|
401
|
+
sharedTree1.applyEdit(...Change.insertTree([buildLeaf(sharedTree1.generateNodeId(duplicateId))], StablePlace.atEndOf(testTrait(sharedTree1.currentView))));
|
|
402
|
+
sharedTree2.applyEdit(...Change.insertTree([buildLeaf(sharedTree2.generateNodeId(duplicateId))], StablePlace.atEndOf(testTrait(sharedTree2.currentView))));
|
|
403
|
+
containerRuntimeFactory.processAllMessages();
|
|
404
|
+
expect(sharedTree1.currentView.size).to.equal(2);
|
|
405
|
+
const trait1 = sharedTree1.currentView.getTrait(testTrait(sharedTree1.currentView));
|
|
406
|
+
expect(trait1.length).to.equal(1);
|
|
407
|
+
expect(sharedTree1.convertToStableNodeId(trait1[0])).to.equal(duplicateId);
|
|
408
|
+
expect(sharedTree1.equals(sharedTree2));
|
|
409
|
+
});
|
|
410
|
+
if (writeFormat !== WriteFormat.v0_0_2) {
|
|
411
|
+
// This is a regression test for an issue where edits containing Fluid handles weren't properly
|
|
412
|
+
// serialized by chunk uploading code: rather than use an IFluidSerializer, we previously just
|
|
413
|
+
// JSON.stringify'd.
|
|
414
|
+
it('can round-trip edits containing handles through chunking', async () => {
|
|
415
|
+
const blobbedPayload = 'blobbed-string-payload';
|
|
416
|
+
const { tree, testObjectProvider } = await setUpLocalServerTestSharedTree({
|
|
417
|
+
writeFormat,
|
|
418
|
+
});
|
|
419
|
+
const testTree = setUpTestTree(tree);
|
|
420
|
+
const buffer = IsoBuffer.from(blobbedPayload, 'utf8');
|
|
421
|
+
const blob = await tree.getRuntime().uploadBlob(buffer);
|
|
422
|
+
const nodeWithPayload = testTree.buildLeaf(testTree.generateNodeId());
|
|
423
|
+
tree.applyEdit(...Change.insertTree(nodeWithPayload, StablePlace.after(testTree.left)), Change.setPayload(nodeWithPayload.identifier, { blob }));
|
|
424
|
+
// Apply enough edits for the upload of an edit chunk
|
|
425
|
+
for (let i = 0; i < tree.edits.editsPerChunk; i++) {
|
|
426
|
+
applyNoop(tree);
|
|
427
|
+
}
|
|
428
|
+
// `ensureSynchronized` does not guarantee blob upload
|
|
429
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
430
|
+
// Wait for the ops to to be submitted and processed across the containers
|
|
431
|
+
await testObjectProvider.ensureSynchronized();
|
|
432
|
+
const summary = tree.saveSummary();
|
|
433
|
+
const { editHistory } = summary;
|
|
434
|
+
const { editChunks } = assertNotUndefined(editHistory);
|
|
435
|
+
expect(editChunks.length).to.equal(2);
|
|
436
|
+
const chunkHandle = editChunks[0].chunk;
|
|
437
|
+
expect(typeof chunkHandle.get).to.equal('function');
|
|
438
|
+
const { tree: secondTree } = await setUpLocalServerTestSharedTree({
|
|
439
|
+
writeFormat,
|
|
440
|
+
testObjectProvider,
|
|
441
|
+
});
|
|
442
|
+
secondTree.loadSummary(summary);
|
|
443
|
+
expect(tree.equals(secondTree)).to.be.true;
|
|
444
|
+
const { blob: blobHandle } = new TreeNodeHandle(secondTree.currentView, translateId(nodeWithPayload.identifier, tree, secondTree)).payload;
|
|
445
|
+
expect(blobHandle).to.not.be.undefined;
|
|
446
|
+
const blobContents = await blobHandle.get();
|
|
447
|
+
expect(IsoBuffer.from(blobContents, 'utf8').toString()).to.equal(blobbedPayload);
|
|
448
|
+
});
|
|
449
|
+
}
|
|
386
450
|
});
|
|
387
451
|
describe('SharedTree summarizing', () => {
|
|
388
|
-
const treeOptions = { initialTree: simpleTestTree, localMode: false };
|
|
389
|
-
const newNode = makeEmptyNode();
|
|
390
452
|
const testHandle = new TestFluidHandle();
|
|
391
|
-
it('
|
|
392
|
-
assert.
|
|
393
|
-
assert.
|
|
394
|
-
assert.
|
|
453
|
+
it('throws error when given bad json input', () => {
|
|
454
|
+
assert.throws(() => deserialize('', testSerializer));
|
|
455
|
+
assert.throws(() => deserialize('~ malformed JSON ~', testSerializer));
|
|
456
|
+
assert.throws(() => deserialize('{ unrecognizedKey: 42 }', testSerializer));
|
|
395
457
|
});
|
|
396
458
|
it('correctly handles snapshots of default trees', () => {
|
|
397
459
|
const { tree: uninitializedTree } = setUpTestSharedTree();
|
|
398
460
|
// Serialize the state of one uninitialized tree into a second tree
|
|
399
461
|
const serialized = serialize(uninitializedTree.saveSummary(), testSerializer, testHandle);
|
|
400
462
|
const parsedTree = deserialize(serialized, testSerializer);
|
|
401
|
-
|
|
402
|
-
|
|
463
|
+
if (writeFormat === WriteFormat.v0_0_2) {
|
|
464
|
+
const summary = parsedTree;
|
|
465
|
+
expect(summary.sequencedEdits).to.deep.equal([]);
|
|
466
|
+
expect(deepCompareNodes(summary.currentTree, initialTree)).to.be.true;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
const summary = parsedTree;
|
|
470
|
+
expect(summary.editHistory).to.deep.equal({ editChunks: [], editIds: [] });
|
|
471
|
+
expect(summary.currentTree).to.be.instanceOf(Array);
|
|
472
|
+
expect(summary.internedStrings).to.have.length(1);
|
|
473
|
+
}
|
|
403
474
|
});
|
|
404
475
|
[true, false].forEach((hasLocalEdits) => {
|
|
405
|
-
it(`produces correct snapshot for a tree with ${hasLocalEdits ? 'local' : 'acked'} edits`, () => {
|
|
476
|
+
it(`produces correct snapshot for a tree with ${hasLocalEdits ? 'local' : 'acked'} edits`, async () => {
|
|
477
|
+
var _a;
|
|
406
478
|
// The initial tree results in an edit.
|
|
407
|
-
const {
|
|
408
|
-
initialTree: treeOptions.initialTree,
|
|
479
|
+
const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
|
|
409
480
|
localMode: hasLocalEdits,
|
|
410
481
|
});
|
|
411
|
-
|
|
482
|
+
const newNode = testTree.buildLeaf();
|
|
483
|
+
sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree.left)));
|
|
412
484
|
if (!hasLocalEdits) {
|
|
413
485
|
containerRuntimeFactory.processAllMessages();
|
|
414
486
|
}
|
|
415
|
-
const serialized = serialize(
|
|
487
|
+
const serialized = serialize(sharedTree.saveSummary(), testSerializer, testHandle);
|
|
416
488
|
const treeContent = JSON.parse(serialized);
|
|
417
|
-
|
|
489
|
+
let parsedTree;
|
|
490
|
+
if (writeFormat === WriteFormat.v0_1_1) {
|
|
491
|
+
parsedTree = new SharedTreeEncoder_0_1_1(true).decodeSummary(treeContent);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
parsedTree = new SharedTreeEncoder_0_0_2(true).decodeSummary(treeContent);
|
|
495
|
+
}
|
|
418
496
|
expect(parsedTree.currentTree).to.not.be.undefined;
|
|
419
|
-
const testRoot = assertArrayOfOne(parsedTree.currentTree.traits[
|
|
497
|
+
const testRoot = assertArrayOfOne(assertNotUndefined((_a = parsedTree.currentTree) === null || _a === void 0 ? void 0 : _a.traits[testTree.traitLabel]));
|
|
420
498
|
expect(testRoot).to.not.be.undefined;
|
|
421
499
|
expect(testRoot.traits.left).to.not.be.undefined;
|
|
422
500
|
expect(testRoot.traits.right).to.not.be.undefined;
|
|
423
501
|
expect(testRoot.traits.left.length).to.equal(2);
|
|
424
|
-
|
|
425
|
-
const sequencedEdits = assertNotUndefined(parsedTree.sequencedEdits);
|
|
502
|
+
const editLog = new EditLog(parsedTree.editHistory);
|
|
426
503
|
// Expect there to be a change in the edit history in addition to the one from setUpTestSharedTree
|
|
427
|
-
expect(
|
|
504
|
+
expect(editLog.length).to.equal(2);
|
|
428
505
|
// The first operation to be sequenced is the tree init
|
|
429
|
-
|
|
430
|
-
expect(
|
|
431
|
-
expect(
|
|
506
|
+
const treeInitEdit = await editLog.getEditAtIndex(1);
|
|
507
|
+
expect(treeInitEdit.changes.length).to.equal(2);
|
|
508
|
+
expect(treeInitEdit.changes[0].type).to.equal(ChangeType.Build);
|
|
509
|
+
expect(treeInitEdit.changes[1].type).to.equal(ChangeType.Insert);
|
|
432
510
|
});
|
|
433
511
|
});
|
|
434
512
|
it('can be used to initialize a tree', () => {
|
|
435
|
-
const {
|
|
436
|
-
const { tree:
|
|
437
|
-
|
|
513
|
+
const { sharedTree: sharedTree1, testTree: testTree1, containerRuntimeFactory, } = createSimpleTestTree({ localMode: false });
|
|
514
|
+
const { tree: sharedTree2 } = setUpTestSharedTree();
|
|
515
|
+
const newNode = testTree1.buildLeaf();
|
|
516
|
+
sharedTree1.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree1.left)));
|
|
438
517
|
containerRuntimeFactory.processAllMessages();
|
|
439
|
-
|
|
518
|
+
sharedTree2.loadSummary(sharedTree1.saveSummary());
|
|
440
519
|
// Trees should have equal state since we deserialized the first tree's state into the second tree
|
|
441
|
-
expect(
|
|
520
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.true;
|
|
442
521
|
});
|
|
443
522
|
it('can be used to initialize a tree with an empty edit list', () => {
|
|
444
|
-
const {
|
|
445
|
-
const { tree:
|
|
523
|
+
const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree({ localMode: false });
|
|
524
|
+
const { tree: sharedTree2 } = setUpTestSharedTree();
|
|
446
525
|
containerRuntimeFactory.processAllMessages();
|
|
447
526
|
// The second tree is not caught up to the first tree yet
|
|
448
|
-
expect(
|
|
449
|
-
|
|
527
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.false;
|
|
528
|
+
sharedTree2.loadSummary(sharedTree1.saveSummary());
|
|
450
529
|
// Trees should have equal state since we deserialized the first tree's state into the second tree
|
|
451
|
-
expect(
|
|
530
|
+
expect(sharedTree1.equals(sharedTree2)).to.be.true;
|
|
452
531
|
});
|
|
453
532
|
it('asserts when loading a summary with duplicated edits', () => {
|
|
454
|
-
const {
|
|
455
|
-
|
|
456
|
-
|
|
533
|
+
const { sharedTree: sharedTree1, testTree: testTree1, containerRuntimeFactory, } = createSimpleTestTree({
|
|
534
|
+
localMode: false,
|
|
535
|
+
summarizeHistory: true,
|
|
536
|
+
writeFormat: WriteFormat.v0_0_2,
|
|
537
|
+
});
|
|
538
|
+
const { tree: sharedTree2 } = setUpTestSharedTree();
|
|
539
|
+
const newNode = testTree1.buildLeaf();
|
|
540
|
+
sharedTree1.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree1.left)));
|
|
457
541
|
containerRuntimeFactory.processAllMessages();
|
|
458
|
-
const summary =
|
|
542
|
+
const summary = sharedTree1.saveSummary();
|
|
459
543
|
const sequencedEdits = assertNotUndefined(summary.sequencedEdits).slice();
|
|
460
544
|
sequencedEdits.push(sequencedEdits[0]);
|
|
461
545
|
const corruptedSummary = Object.assign(Object.assign({}, summary), { sequencedEdits });
|
|
462
|
-
expect(() =>
|
|
546
|
+
expect(() => sharedTree2.loadSummary(corruptedSummary))
|
|
463
547
|
.to.throw(Error)
|
|
464
548
|
.that.has.property('message')
|
|
465
549
|
.which.matches(/Duplicate/);
|
|
466
550
|
});
|
|
467
551
|
it('can be used without history preservation', async () => {
|
|
468
|
-
const {
|
|
469
|
-
initialTree: simpleTestTree,
|
|
552
|
+
const { sharedTree, testTree } = createSimpleTestTree({
|
|
470
553
|
localMode: true,
|
|
471
554
|
summarizeHistory: false,
|
|
472
555
|
});
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
|
|
556
|
+
const newNode = testTree.buildLeaf();
|
|
557
|
+
const { id } = sharedTree.applyEdit(...Change.insertTree(newNode, StablePlace.before(testTree.left)));
|
|
558
|
+
const treeBefore = convertTreeNodes(getChangeNodeFromView(sharedTree.currentView), (node) => convertNodeDataIds(node, (id) => sharedTree.convertToStableNodeId(id)));
|
|
559
|
+
const summary = sharedTree.saveSummary();
|
|
560
|
+
const { tree: sharedTree2 } = setUpTestSharedTree();
|
|
561
|
+
sharedTree2.loadSummary(summary);
|
|
562
|
+
const treeAfter = convertTreeNodes(getChangeNodeFromView(sharedTree2.currentView), (node) => convertNodeDataIds(node, (id) => sharedTree2.convertToStableNodeId(id)));
|
|
477
563
|
// The current state of the tree should be identical to the one contained in the old summary.
|
|
478
|
-
expect(
|
|
564
|
+
expect(deepCompareNodes(treeBefore, treeAfter)).to.be.true;
|
|
479
565
|
// The history should have been dropped by the default handling behavior.
|
|
480
566
|
// It will contain a single entry setting the tree to equal the head revision.
|
|
481
|
-
expect(
|
|
482
|
-
expect(await
|
|
567
|
+
expect(sharedTree2.edits.length).to.equal(1);
|
|
568
|
+
expect(await sharedTree2.edits.tryGetEdit(id)).to.be.undefined;
|
|
569
|
+
});
|
|
570
|
+
it('correctly handles payloads at the root', () => {
|
|
571
|
+
var _a;
|
|
572
|
+
const payload = 'foo';
|
|
573
|
+
const { tree, containerRuntimeFactory } = setUpTestSharedTree({ summarizeHistory: false });
|
|
574
|
+
tree.applyEdit(Change.setPayload(tree.currentView.root, payload));
|
|
575
|
+
containerRuntimeFactory.processAllMessages();
|
|
576
|
+
const summary = tree.saveSummary();
|
|
577
|
+
const { tree: tree2 } = setUpTestSharedTree({ summarizeHistory: false });
|
|
578
|
+
tree2.loadSummary(summary);
|
|
579
|
+
expect((_a = tree2.currentView.tryGetViewNode(tree2.currentView.root)) === null || _a === void 0 ? void 0 : _a.payload).to.equal(payload);
|
|
483
580
|
});
|
|
484
581
|
// TODO:#49901: Enable these tests once we write edit chunk handles to summaries
|
|
485
582
|
it.skip('does not swallow errors in asynchronous blob uploading', async () => {
|
|
486
583
|
const errorMessage = 'Simulated exception in uploadBlob';
|
|
487
|
-
const {
|
|
584
|
+
const { sharedTree, testTree, componentRuntime, containerRuntimeFactory } = createSimpleTestTree({
|
|
585
|
+
localMode: false,
|
|
586
|
+
});
|
|
488
587
|
componentRuntime.uploadBlob = async () => {
|
|
489
588
|
throw new Error(errorMessage);
|
|
490
589
|
};
|
|
491
590
|
let treeErrorEventWasInvoked = false;
|
|
492
|
-
|
|
591
|
+
sharedTree.on('error', (error) => {
|
|
493
592
|
treeErrorEventWasInvoked = true;
|
|
494
593
|
expect(error).to.have.property('message').which.equals(errorMessage);
|
|
495
594
|
});
|
|
496
595
|
// Generate enough edits to cause a chunk upload.
|
|
497
|
-
for (let i = 0; i <
|
|
498
|
-
const insertee =
|
|
499
|
-
|
|
500
|
-
|
|
596
|
+
for (let i = 0; i < sharedTree.edits.editsPerChunk / 2 + 1; i++) {
|
|
597
|
+
const insertee = testTree.buildLeaf(testTree.generateNodeId());
|
|
598
|
+
sharedTree.applyEdit(...Change.insertTree(insertee, StablePlace.before(testTree.left)));
|
|
599
|
+
sharedTree.applyEdit(Change.delete(StableRange.only(insertee)));
|
|
501
600
|
}
|
|
502
601
|
containerRuntimeFactory.processAllMessages();
|
|
503
|
-
|
|
602
|
+
sharedTree.saveSummary();
|
|
504
603
|
// Just waiting for the ChunksEmitted event here isn't sufficient, as the SharedTree error
|
|
505
604
|
// will propagate in a separate promise chain.
|
|
506
|
-
await
|
|
605
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
507
606
|
expect(treeErrorEventWasInvoked).to.equal(true, 'SharedTree error was never raised');
|
|
508
607
|
});
|
|
509
608
|
});
|
|
510
|
-
describe('correctly diffs snapshots', () => {
|
|
511
|
-
it('that are the same object', () => {
|
|
512
|
-
const id = uuidv4();
|
|
513
|
-
const snapshot = Snapshot.fromTree(makeEmptyNode(id));
|
|
514
|
-
expect(snapshot.delta(snapshot)).deep.equals({
|
|
515
|
-
changed: [],
|
|
516
|
-
added: [],
|
|
517
|
-
removed: [],
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
it('that have the same tree', () => {
|
|
521
|
-
const node = makeEmptyNode();
|
|
522
|
-
const snapshotA = Snapshot.fromTree(node);
|
|
523
|
-
const snapshotB = Snapshot.fromTree(node);
|
|
524
|
-
expect(snapshotA.delta(snapshotB)).deep.equals({
|
|
525
|
-
changed: [],
|
|
526
|
-
added: [],
|
|
527
|
-
removed: [],
|
|
528
|
-
});
|
|
529
|
-
});
|
|
530
|
-
it('with different root ids', () => {
|
|
531
|
-
const snapshotA = Snapshot.fromTree(makeEmptyNode());
|
|
532
|
-
const snapshotB = Snapshot.fromTree(makeEmptyNode());
|
|
533
|
-
expect(() => snapshotA.delta(snapshotB)).to.throw('Delta can only be calculated between snapshots that share a root');
|
|
534
|
-
});
|
|
535
|
-
it('with different subtrees', () => {
|
|
536
|
-
const rootId = uuidv4();
|
|
537
|
-
const subtreeA = uuidv4();
|
|
538
|
-
const subtreeB = uuidv4();
|
|
539
|
-
const leafA = makeEmptyNode();
|
|
540
|
-
const leafB = makeEmptyNode();
|
|
541
|
-
const rootA = {
|
|
542
|
-
identifier: rootId,
|
|
543
|
-
definition: 'node',
|
|
544
|
-
traits: {
|
|
545
|
-
children: [
|
|
546
|
-
{
|
|
547
|
-
identifier: subtreeA,
|
|
548
|
-
definition: 'node',
|
|
549
|
-
traits: { children: [leafA] },
|
|
550
|
-
},
|
|
551
|
-
],
|
|
552
|
-
},
|
|
553
|
-
};
|
|
554
|
-
const rootB = {
|
|
555
|
-
identifier: rootId,
|
|
556
|
-
definition: 'node',
|
|
557
|
-
traits: {
|
|
558
|
-
children: [
|
|
559
|
-
{
|
|
560
|
-
identifier: subtreeB,
|
|
561
|
-
definition: 'node',
|
|
562
|
-
traits: { children: [leafB] },
|
|
563
|
-
},
|
|
564
|
-
],
|
|
565
|
-
},
|
|
566
|
-
};
|
|
567
|
-
const snapshotA = Snapshot.fromTree(rootA);
|
|
568
|
-
const snapshotB = Snapshot.fromTree(rootB);
|
|
569
|
-
const delta = snapshotA.delta(snapshotB);
|
|
570
|
-
expect(delta.changed).deep.equals([rootId]);
|
|
571
|
-
expect(delta.removed.length).equals(2);
|
|
572
|
-
expect(delta.added.length).equals(2);
|
|
573
|
-
expect(delta.removed).contains(subtreeA);
|
|
574
|
-
expect(delta.removed).contains(leafA.identifier);
|
|
575
|
-
expect(delta.added).contains(subtreeB);
|
|
576
|
-
expect(delta.added).contains(leafB.identifier);
|
|
577
|
-
});
|
|
578
|
-
it('with different payloads', () => {
|
|
579
|
-
const rootId = uuidv4();
|
|
580
|
-
const nodeA = {
|
|
581
|
-
identifier: rootId,
|
|
582
|
-
definition: 'node',
|
|
583
|
-
payload: 'test1',
|
|
584
|
-
traits: {},
|
|
585
|
-
};
|
|
586
|
-
const nodeB = {
|
|
587
|
-
identifier: rootId,
|
|
588
|
-
definition: 'node',
|
|
589
|
-
payload: 'test2',
|
|
590
|
-
traits: {},
|
|
591
|
-
};
|
|
592
|
-
const snapshotA = Snapshot.fromTree(nodeA);
|
|
593
|
-
const snapshotB = Snapshot.fromTree(nodeB);
|
|
594
|
-
const delta = snapshotA.delta(snapshotB);
|
|
595
|
-
expect(delta.changed).deep.equals([rootId]);
|
|
596
|
-
expect(delta.removed).deep.equals([]);
|
|
597
|
-
expect(delta.added).deep.equals([]);
|
|
598
|
-
});
|
|
599
|
-
it('after an insert', () => {
|
|
600
|
-
const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
|
|
601
|
-
const snapshotA = tree.currentView;
|
|
602
|
-
const insertedNode = makeEmptyNode();
|
|
603
|
-
tree.editor.insert(insertedNode, StablePlace.before(left));
|
|
604
|
-
const snapshotB = tree.currentView;
|
|
605
|
-
const delta = snapshotA.delta(snapshotB);
|
|
606
|
-
assert(delta);
|
|
607
|
-
expect(delta.changed).deep.equals([simpleTestTree.identifier]);
|
|
608
|
-
expect(delta.removed).deep.equals([]);
|
|
609
|
-
expect(delta.added).deep.equals([insertedNode.identifier]);
|
|
610
|
-
});
|
|
611
|
-
it('after a delete', () => {
|
|
612
|
-
const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
|
|
613
|
-
const snapshotA = tree.currentView;
|
|
614
|
-
tree.editor.delete(left);
|
|
615
|
-
const snapshotB = tree.currentView;
|
|
616
|
-
const delta = snapshotA.delta(snapshotB);
|
|
617
|
-
assert(delta);
|
|
618
|
-
expect(delta.changed).deep.equals([simpleTestTree.identifier]);
|
|
619
|
-
expect(delta.removed).deep.equals([left.identifier]);
|
|
620
|
-
expect(delta.added).deep.equals([]);
|
|
621
|
-
});
|
|
622
|
-
it('after a move', () => {
|
|
623
|
-
const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
|
|
624
|
-
const snapshotA = tree.currentView;
|
|
625
|
-
tree.editor.move(left, StablePlace.after(right));
|
|
626
|
-
const snapshotB = tree.currentView;
|
|
627
|
-
const delta = snapshotA.delta(snapshotB);
|
|
628
|
-
assert(delta);
|
|
629
|
-
expect(delta.changed).deep.equals([simpleTestTree.identifier]);
|
|
630
|
-
expect(delta.removed).deep.equals([]);
|
|
631
|
-
expect(delta.added).deep.equals([]);
|
|
632
|
-
});
|
|
633
|
-
});
|
|
634
609
|
describe('handles', () => {
|
|
635
610
|
it('can reference a node', () => {
|
|
636
611
|
// Test that a handle can wrap a node and retrieve that node's properties
|
|
637
|
-
const {
|
|
638
|
-
const leftHandle = new TreeNodeHandle(
|
|
639
|
-
expect(areNodesEquivalent(left, leftHandle)).to.be.true;
|
|
640
|
-
expect(areNodesEquivalent(right, leftHandle)).to.be.false;
|
|
612
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
613
|
+
const leftHandle = new TreeNodeHandle(sharedTree.currentView, testTree.left.identifier);
|
|
614
|
+
expect(areNodesEquivalent(testTree.left, leftHandle)).to.be.true;
|
|
615
|
+
expect(areNodesEquivalent(testTree.right, leftHandle)).to.be.false;
|
|
641
616
|
});
|
|
642
617
|
it('can create handles from children', () => {
|
|
643
618
|
// Test that when retrieving children via the "traits" property of a handle, the
|
|
644
619
|
// children are also wrapped in handles
|
|
645
|
-
const {
|
|
646
|
-
const rootHandle = new TreeNodeHandle(
|
|
647
|
-
expect(areNodesEquivalent(
|
|
620
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
621
|
+
const rootHandle = new TreeNodeHandle(sharedTree.currentView, testTree.identifier);
|
|
622
|
+
expect(areNodesEquivalent(testTree, rootHandle)).to.be.true;
|
|
648
623
|
const leftHandle = rootHandle.traits.left[0];
|
|
649
|
-
expect(areNodesEquivalent(left, leftHandle)).to.be.true;
|
|
624
|
+
expect(areNodesEquivalent(testTree.left, leftHandle)).to.be.true;
|
|
650
625
|
expect(leftHandle instanceof TreeNodeHandle).to.be.true;
|
|
651
626
|
});
|
|
652
627
|
it('do not update when the current view of the tree changes', () => {
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
const leftHandle = new TreeNodeHandle(tree.currentView, left.identifier);
|
|
628
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
629
|
+
const leftHandle = new TreeNodeHandle(sharedTree.currentView, testTree.left.identifier);
|
|
656
630
|
expect(leftHandle.traits.right).to.be.undefined;
|
|
657
631
|
// Move "right" under "left"
|
|
658
|
-
|
|
632
|
+
sharedTree.applyEdit(...Change.move(StableRange.only(testTree.right), StablePlace.atStartOf({ parent: testTree.left.identifier, label: testTree.right.traitLabel })));
|
|
659
633
|
expect(leftHandle.traits.right).to.be.undefined;
|
|
660
634
|
});
|
|
661
|
-
it('can be fully demanded', () => {
|
|
662
|
-
const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
|
|
663
|
-
const rootHandle = new TreeNodeHandle(tree.currentView, simpleTestTree.identifier);
|
|
664
|
-
const rootNode = rootHandle.demandTree();
|
|
665
|
-
expect(areNodesEquivalent(simpleTestTree, rootNode)).to.be.true;
|
|
666
|
-
const printBeforeDemand = JSON.stringify(rootNode);
|
|
667
|
-
// Demand the tree by walking into its traits. If they were lazy, this would change the `rootNode` object.
|
|
668
|
-
expect(areNodesEquivalent(left, rootNode.traits.left[0])).to.be.true;
|
|
669
|
-
expect(areNodesEquivalent(right, rootNode.traits.right[0])).to.be.true;
|
|
670
|
-
// Ensure that they were _not_ lazy by comparing with the initial print of the tree
|
|
671
|
-
expect(JSON.stringify(rootNode)).equals(printBeforeDemand);
|
|
672
|
-
});
|
|
673
|
-
it('implement toString', () => {
|
|
674
|
-
const { tree } = setUpTestSharedTree({ initialTree: simpleTestTree });
|
|
675
|
-
const rootHandle = new TreeNodeHandle(tree.currentView, simpleTestTree.identifier);
|
|
676
|
-
const print = `${rootHandle}`;
|
|
677
|
-
// Shouldn't print the default toString for objects
|
|
678
|
-
expect(print.startsWith('[object')).to.be.false;
|
|
679
|
-
});
|
|
680
635
|
});
|
|
681
636
|
describe('telemetry', () => {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
637
|
+
describe('useFailedSequencedEditTelemetry', () => {
|
|
638
|
+
it('decorates events with the correct properties', async () => {
|
|
639
|
+
// Test that a handle can wrap a node and retrieve that node's properties
|
|
640
|
+
const events = [];
|
|
641
|
+
const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
|
|
642
|
+
logger: { send: (event) => events.push(event) },
|
|
643
|
+
allowInvalid: true,
|
|
644
|
+
});
|
|
645
|
+
useFailedSequencedEditTelemetry(sharedTree);
|
|
646
|
+
// Invalid edit
|
|
647
|
+
sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
648
|
+
containerRuntimeFactory.processAllMessages();
|
|
649
|
+
// Force demand, which will cause a telemetry event for the invalid edit to be emitted
|
|
650
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
651
|
+
expect(events.length).is.greaterThan(0);
|
|
652
|
+
events.forEach((event) => {
|
|
653
|
+
expect(isSharedTreeEvent(event)).is.true;
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
it('is logged for invalid locally generated edits when those edits are sequenced', async () => {
|
|
657
|
+
const events = [];
|
|
658
|
+
const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
|
|
659
|
+
logger: { send: (event) => events.push(event) },
|
|
660
|
+
allowInvalid: true,
|
|
661
|
+
});
|
|
662
|
+
useFailedSequencedEditTelemetry(sharedTree);
|
|
663
|
+
// Invalid edit
|
|
664
|
+
sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
665
|
+
expect(events.length).equals(0);
|
|
666
|
+
containerRuntimeFactory.processAllMessages();
|
|
667
|
+
// Force demand, which will cause a telemetry event for the invalid edit to be emitted
|
|
668
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
669
|
+
expect(events.length).equals(1);
|
|
670
|
+
expect(events[0].category).equals('generic');
|
|
671
|
+
expect(events[0].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
|
|
672
|
+
});
|
|
673
|
+
it('can be disabled and re-enabled', async () => {
|
|
674
|
+
const events = [];
|
|
675
|
+
const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
|
|
676
|
+
logger: { send: (event) => events.push(event) },
|
|
677
|
+
allowInvalid: true,
|
|
678
|
+
});
|
|
679
|
+
const { disable } = useFailedSequencedEditTelemetry(sharedTree);
|
|
680
|
+
sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
681
|
+
expect(events.length).equals(0);
|
|
682
|
+
containerRuntimeFactory.processAllMessages();
|
|
683
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
684
|
+
expect(events.length).equals(1);
|
|
685
|
+
expect(events[0].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
|
|
686
|
+
disable();
|
|
687
|
+
sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
688
|
+
containerRuntimeFactory.processAllMessages();
|
|
689
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
690
|
+
expect(events.length).equals(1);
|
|
691
|
+
useFailedSequencedEditTelemetry(sharedTree);
|
|
692
|
+
sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
693
|
+
containerRuntimeFactory.processAllMessages();
|
|
694
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
695
|
+
expect(events.length).equals(2);
|
|
696
|
+
expect(events[1].eventName).equals('SharedTree:SequencedEditApplied:InvalidSharedTreeEdit');
|
|
697
|
+
});
|
|
698
|
+
it('is not logged for valid edits', async () => {
|
|
699
|
+
const events = [];
|
|
700
|
+
const { sharedTree, testTree, containerRuntimeFactory } = createSimpleTestTree({
|
|
701
|
+
logger: { send: (event) => events.push(event) },
|
|
702
|
+
});
|
|
703
|
+
useFailedSequencedEditTelemetry(sharedTree);
|
|
704
|
+
sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
|
|
705
|
+
containerRuntimeFactory.processAllMessages();
|
|
706
|
+
await sharedTree.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
707
|
+
expect(events.length).equals(0);
|
|
708
|
+
});
|
|
709
|
+
it('is not logged for remote edits', async () => {
|
|
710
|
+
const events = [];
|
|
711
|
+
const { sharedTree: sharedTree1, containerRuntimeFactory } = createSimpleTestTree({
|
|
712
|
+
logger: { send: (event) => events.push(event) },
|
|
713
|
+
allowInvalid: true,
|
|
714
|
+
localMode: false,
|
|
715
|
+
});
|
|
716
|
+
const { sharedTree: sharedTree2, testTree: testTree2 } = createSimpleTestTree({
|
|
717
|
+
containerRuntimeFactory,
|
|
718
|
+
id: 'secondTestSharedTree',
|
|
719
|
+
localMode: false,
|
|
720
|
+
});
|
|
721
|
+
useFailedSequencedEditTelemetry(sharedTree1);
|
|
722
|
+
sharedTree2.applyEdit(...Change.insertTree([testTree2.buildLeaf()], StablePlace.after(testTree2.buildLeaf(testTree2.generateNodeId()))));
|
|
723
|
+
containerRuntimeFactory.processAllMessages();
|
|
724
|
+
await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
725
|
+
expect(events.length).equals(0);
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
describe('Events', () => {
|
|
730
|
+
it('fires an event when an edit is committed', () => {
|
|
731
|
+
const { sharedTree, testTree } = createSimpleTestTree();
|
|
732
|
+
let eventCount = 0;
|
|
733
|
+
let editIdFromEvent;
|
|
734
|
+
sharedTree.on(SharedTreeEvent.EditCommitted, (args) => {
|
|
735
|
+
expect(args.local).true;
|
|
736
|
+
expect(args.tree).equals(sharedTree);
|
|
737
|
+
editIdFromEvent = args.editId;
|
|
738
|
+
eventCount += 1;
|
|
739
|
+
});
|
|
740
|
+
// Invalid change
|
|
741
|
+
const invalidEdit = sharedTree.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.buildLeaf(testTree.generateNodeId()))));
|
|
742
|
+
expect(editIdFromEvent).equals(invalidEdit.id);
|
|
743
|
+
expect(eventCount).equals(1);
|
|
744
|
+
// Valid change
|
|
745
|
+
const { id } = sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
|
|
746
|
+
expect(editIdFromEvent).equals(id);
|
|
747
|
+
expect(eventCount).equals(2);
|
|
748
|
+
});
|
|
749
|
+
it('fires an event when a sequenced edit is applied', async () => {
|
|
750
|
+
const { sharedTree: sharedTree1, testTree, containerRuntimeFactory, } = createSimpleTestTree({
|
|
688
751
|
allowInvalid: true,
|
|
752
|
+
localMode: false,
|
|
689
753
|
});
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
await tree.logViewer.getSnapshot(Number.POSITIVE_INFINITY);
|
|
695
|
-
expect(events.length).is.greaterThan(0);
|
|
696
|
-
events.forEach((event) => {
|
|
697
|
-
expect(isSharedTreeEvent(event)).is.true;
|
|
754
|
+
const { tree: sharedTree2 } = setUpTestSharedTree({
|
|
755
|
+
containerRuntimeFactory,
|
|
756
|
+
id: 'secondTestSharedTree',
|
|
757
|
+
localMode: false,
|
|
698
758
|
});
|
|
759
|
+
containerRuntimeFactory.processAllMessages();
|
|
760
|
+
await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
761
|
+
const eventArgs = [];
|
|
762
|
+
sharedTree1.on(SharedTreeEvent.SequencedEditApplied, (args) => eventArgs.push(args));
|
|
763
|
+
// Invalid change
|
|
764
|
+
const change = Change.setPayload(testTree.generateNodeId(), 42);
|
|
765
|
+
const invalidEdit = sharedTree1.applyEdit(change);
|
|
766
|
+
containerRuntimeFactory.processAllMessages();
|
|
767
|
+
await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
768
|
+
expect(eventArgs.length).equals(1);
|
|
769
|
+
expect(eventArgs[0].edit.id).equals(invalidEdit.id);
|
|
770
|
+
expect(eventArgs[0].wasLocal).equals(true);
|
|
771
|
+
expect(eventArgs[0].reconciliationPath.length).equals(0);
|
|
772
|
+
expect(eventArgs[0].outcome.status).equals(EditStatus.Invalid);
|
|
773
|
+
// Valid change
|
|
774
|
+
const validEdit1 = sharedTree2.applyEdit(...Change.insertTree([testTree.buildLeaf()], StablePlace.after(testTree.left.translateId(sharedTree2))));
|
|
775
|
+
// Valid change
|
|
776
|
+
const validEdit2 = sharedTree1.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
|
|
777
|
+
containerRuntimeFactory.processAllMessages();
|
|
778
|
+
await sharedTree1.logViewer.getRevisionView(Number.POSITIVE_INFINITY);
|
|
779
|
+
expect(eventArgs.length).equals(3);
|
|
780
|
+
expect(eventArgs[1].edit.id).equals(validEdit1.id);
|
|
781
|
+
expect(eventArgs[1].wasLocal).equals(false);
|
|
782
|
+
expect(eventArgs[1].reconciliationPath.length).equals(0);
|
|
783
|
+
expect(eventArgs[1].outcome.status).equals(EditStatus.Applied);
|
|
784
|
+
expect(eventArgs[2].edit.id).equals(validEdit2.id);
|
|
785
|
+
expect(eventArgs[2].wasLocal).equals(true);
|
|
786
|
+
expect(eventArgs[2].reconciliationPath.length).equals(1);
|
|
787
|
+
expect(eventArgs[2].outcome.status).equals(EditStatus.Applied);
|
|
699
788
|
});
|
|
700
789
|
});
|
|
790
|
+
/**
|
|
791
|
+
* This test is a slightly minified regression test for an issue discovered by fuzz testing.
|
|
792
|
+
* It demonstrates issues with clients using writeFormat v0.1.1 and mixed `summarizeHistory` values.
|
|
793
|
+
* The problem is illustrated by the following scenario:
|
|
794
|
+
* 1. Client A and client B join a session. A does not summarize history, but B does.
|
|
795
|
+
* 2. A is elected to be the summarizer.
|
|
796
|
+
* 3. Client A and B make 50 edits (half a chunks' worth), then idle.
|
|
797
|
+
* 4. Client A summarizes. Since it does not summarize history, the summary it produces has a single edit.
|
|
798
|
+
* 5. Client C joins, configured to write history.
|
|
799
|
+
* 6. The three clients collaborate further for another 50/51 edits.
|
|
800
|
+
*
|
|
801
|
+
* At this point in time, client B thinks the first edit chunk is full, but client C thinks it's only half-full.
|
|
802
|
+
* The entire edit compression scheme is built upon assuming clients agree where the chunk boundaries are, so this
|
|
803
|
+
* generally leads to correctness issues. The fuzz test reproed a similar scenario, and what ultimately caused
|
|
804
|
+
* failure is a newly-loaded client being shocked at a chunk with `startRevision: 400` uploaded (when it thinks
|
|
805
|
+
* there has only been one edit).
|
|
806
|
+
*
|
|
807
|
+
* To fix this, we need to incorporate a scheme where all clients agree on chunk boundaries (e.g., by including the
|
|
808
|
+
* total number of edits even in no-history summaries).
|
|
809
|
+
*
|
|
810
|
+
* In the meantime, we are forbidding collaboration of no-history clients and history clients.
|
|
811
|
+
*/
|
|
812
|
+
it('can be initialized on multiple clients with different `summarizeHistory` values', async () => {
|
|
813
|
+
const { tree, testObjectProvider, container } = await setUpLocalServerTestSharedTree({
|
|
814
|
+
writeFormat,
|
|
815
|
+
summarizeHistory: false,
|
|
816
|
+
});
|
|
817
|
+
testObjectProvider.logger.registerExpectedEvent({ eventName: 'fluid:telemetry:Batching:LengthTooBig' }, { eventName: 'fluid:telemetry:Batching:LengthTooBig' }, { eventName: 'fluid:telemetry:Batching:LengthTooBig' });
|
|
818
|
+
applyNoop(tree);
|
|
819
|
+
await testObjectProvider.ensureSynchronized();
|
|
820
|
+
const firstSummaryVersion = await waitForSummary(container);
|
|
821
|
+
const { tree: tree2 } = await setUpLocalServerTestSharedTree({
|
|
822
|
+
writeFormat,
|
|
823
|
+
testObjectProvider,
|
|
824
|
+
summarizeHistory: true,
|
|
825
|
+
headers: { [LoaderHeader.version]: firstSummaryVersion },
|
|
826
|
+
});
|
|
827
|
+
// Apply enough edits for the upload of a few edit chunks, and some extra so future chunks are misaligned
|
|
828
|
+
for (let i = 0; i < (5 * editsPerChunk) / 2; i++) {
|
|
829
|
+
applyNoop(tree);
|
|
830
|
+
}
|
|
831
|
+
const secondSummaryVersion = await waitForSummary(container);
|
|
832
|
+
const { tree: tree3 } = await setUpLocalServerTestSharedTree({
|
|
833
|
+
writeFormat,
|
|
834
|
+
testObjectProvider,
|
|
835
|
+
summarizeHistory: true,
|
|
836
|
+
headers: { [LoaderHeader.version]: secondSummaryVersion },
|
|
837
|
+
});
|
|
838
|
+
// Verify we loaded a no-history summary.
|
|
839
|
+
expect(tree3.editsInternal.length).to.equal(1);
|
|
840
|
+
let unexpectedHistoryChunkCount = 0;
|
|
841
|
+
tree3.on(SharedTreeDiagnosticEvent.UnexpectedHistoryChunk, () => unexpectedHistoryChunkCount++);
|
|
842
|
+
await testObjectProvider.ensureSynchronized();
|
|
843
|
+
// Apply enough edits to guarantee another chunk upload occurs.
|
|
844
|
+
for (let i = 0; i < editsPerChunk; i++) {
|
|
845
|
+
applyNoop(tree2);
|
|
846
|
+
}
|
|
847
|
+
await testObjectProvider.ensureSynchronized();
|
|
848
|
+
// If tree 2 didn't change its write format, it would attempt to upload the above chunk with start revision 200, which is past
|
|
849
|
+
// how many sequenced edits tree 3 thinks there are.
|
|
850
|
+
expect(unexpectedHistoryChunkCount).to.equal(0);
|
|
851
|
+
}).timeout(/* double summarization can take some time */ 20000);
|
|
852
|
+
// This functionality was only implemented in format 0.1.1.
|
|
853
|
+
if (writeFormat !== WriteFormat.v0_0_2) {
|
|
854
|
+
describe('String interning and tree compression', () => {
|
|
855
|
+
function getMutableStringInterner(tree) {
|
|
856
|
+
const summary = tree.saveSummary();
|
|
857
|
+
switch (summary.version) {
|
|
858
|
+
case WriteFormat.v0_0_2:
|
|
859
|
+
return new MutableStringInterner();
|
|
860
|
+
case WriteFormat.v0_1_1:
|
|
861
|
+
return new MutableStringInterner(summary.internedStrings);
|
|
862
|
+
default:
|
|
863
|
+
fail(`Invalid summary format: ${summary.version}`);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
it('compress ops via interning and tree compression and decompress when processing edits', () => {
|
|
867
|
+
const { sharedTree: tree, testTree, containerRuntimeFactory, } = createSimpleTestTree({ writeFormat });
|
|
868
|
+
const { tree: secondTree } = setUpTestSharedTree({ containerRuntimeFactory, writeFormat });
|
|
869
|
+
const remoteRuntime = containerRuntimeFactory.createContainerRuntime(new MockFluidDataStoreRuntime());
|
|
870
|
+
const newNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
871
|
+
tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
|
|
872
|
+
tree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
|
|
873
|
+
const factory = remoteRuntime.factory;
|
|
874
|
+
const messages = factory.messages;
|
|
875
|
+
expect(messages.length).to.equal(3);
|
|
876
|
+
for (const message of messages.slice(1)) {
|
|
877
|
+
// After the initial setup edit, common definitions should be interned
|
|
878
|
+
for (const change of message.contents.edit.changes) {
|
|
879
|
+
if (change.type === ChangeTypeInternal.CompressedBuild) {
|
|
880
|
+
const stringifiedContents = JSON.stringify(message.contents);
|
|
881
|
+
expect(stringifiedContents).to.not.include(SimpleTestTree.leftTraitLabel);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
expect(tree.equals(secondTree)).to.be.false;
|
|
886
|
+
containerRuntimeFactory.processAllMessages();
|
|
887
|
+
const { internedStrings } = tree.saveSummary();
|
|
888
|
+
const insertEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(1));
|
|
889
|
+
const moveEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(2));
|
|
890
|
+
const insertEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(1));
|
|
891
|
+
const moveEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(2));
|
|
892
|
+
expect(insertEdit).to.deep.equal(insertEdit2);
|
|
893
|
+
expect(moveEdit).to.deep.equal(moveEdit2);
|
|
894
|
+
expect(tree.equals(secondTree)).to.be.true;
|
|
895
|
+
expect(internedStrings).to.include(SimpleTestTree.leftTraitLabel);
|
|
896
|
+
expect(internedStrings).to.include(newNode.definition);
|
|
897
|
+
});
|
|
898
|
+
it('compress summaries via interning and tree compression on save and decompress on load', () => {
|
|
899
|
+
const { sharedTree: tree, testTree: testTree, containerRuntimeFactory, } = createSimpleTestTree({ writeFormat });
|
|
900
|
+
const newNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
901
|
+
tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
|
|
902
|
+
tree.applyEdit(...Change.move(StableRange.only(newNode), StablePlace.before(testTree.left)));
|
|
903
|
+
containerRuntimeFactory.processAllMessages();
|
|
904
|
+
const summary = tree.saveSummary();
|
|
905
|
+
expect(summary.internedStrings).to.not.be.undefined;
|
|
906
|
+
expect(summary.internedStrings.length).to.equal(5);
|
|
907
|
+
const interner = new MutableStringInterner(summary.internedStrings);
|
|
908
|
+
const treeCompressor = new InterningTreeCompressor();
|
|
909
|
+
const expectedCompressedTree = treeCompressor.compress(getChangeNodeFromView(tree.currentView), interner, sequencedIdNormalizer(getIdNormalizerFromSharedTree(tree)));
|
|
910
|
+
expect(summary.currentTree).deep.equal(expectedCompressedTree);
|
|
911
|
+
const { tree: secondTree } = setUpTestSharedTree({ writeFormat });
|
|
912
|
+
expect(tree.equals(secondTree)).to.be.false;
|
|
913
|
+
secondTree.loadSummary(summary);
|
|
914
|
+
expect(tree.equals(secondTree)).to.be.true;
|
|
915
|
+
});
|
|
916
|
+
it('compress and decompress edit chunks via interning and tree compression', async () => {
|
|
917
|
+
const { tree, testObjectProvider } = await setUpLocalServerTestSharedTree({
|
|
918
|
+
writeFormat,
|
|
919
|
+
});
|
|
920
|
+
const testTree = setUpTestTree(tree);
|
|
921
|
+
const uncompressedEdits = [
|
|
922
|
+
{
|
|
923
|
+
changes: tree.edits.getEditInSessionAtIndex(0).changes,
|
|
924
|
+
},
|
|
925
|
+
];
|
|
926
|
+
// Apply enough edits for the upload of an edit chunk
|
|
927
|
+
for (let i = 0; i < tree.edits.editsPerChunk - 1; i++) {
|
|
928
|
+
const newNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
929
|
+
const edit = tree.applyEditInternal(ChangeInternal.insertTree([newNode], StablePlace.after(testTree.left)));
|
|
930
|
+
uncompressedEdits.push({ changes: edit.changes });
|
|
931
|
+
}
|
|
932
|
+
await testObjectProvider.ensureSynchronized();
|
|
933
|
+
const interner = getMutableStringInterner(tree);
|
|
934
|
+
const expectedCompressedEdits = new SharedTreeEncoder_0_1_1(true).encodeEditChunk(uncompressedEdits, sequencedIdNormalizer(testTree), interner).edits;
|
|
935
|
+
// Apply one more edit so that an edit chunk gets uploaded
|
|
936
|
+
const newNode = testTree.buildLeaf(testTree.generateNodeId());
|
|
937
|
+
tree.applyEdit(...Change.insertTree(newNode, StablePlace.after(testTree.left)));
|
|
938
|
+
// `ensureSynchronized` does not guarantee blob upload
|
|
939
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
940
|
+
// Wait for the ops to to be submitted and processed across the containers
|
|
941
|
+
await testObjectProvider.ensureSynchronized();
|
|
942
|
+
const summary = tree.saveSummary();
|
|
943
|
+
const { editHistory } = summary;
|
|
944
|
+
const { editChunks } = assertNotUndefined(editHistory);
|
|
945
|
+
expect(editChunks.length).to.equal(2);
|
|
946
|
+
const handle = editChunks[0].chunk;
|
|
947
|
+
expect(typeof handle.get).to.equal('function');
|
|
948
|
+
const chunkContents = JSON.parse(IsoBuffer.from(await handle.get()).toString());
|
|
949
|
+
expect(chunkContents.edits).to.deep.equal(expectedCompressedEdits);
|
|
950
|
+
const { tree: secondTree } = setUpTestSharedTree({ writeFormat });
|
|
951
|
+
expect(tree.equals(secondTree)).to.be.false;
|
|
952
|
+
secondTree.loadSummary(summary);
|
|
953
|
+
expect(tree.equals(secondTree)).to.be.true;
|
|
954
|
+
expect((await tree.edits.getEditAtIndex(2)).id).to.equal((await secondTree.edits.getEditAtIndex(2)).id);
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
}
|
|
701
958
|
});
|
|
702
959
|
}
|
|
703
960
|
//# sourceMappingURL=SharedTreeTests.js.map
|