@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
|
@@ -0,0 +1,1075 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import { v4, v5 } from 'uuid';
|
|
7
|
+
import { IdCompressor, isFinalId, isLocalId, hasOngoingSession, legacySharedTreeInitialTreeId, } from '../id-compressor/IdCompressor';
|
|
8
|
+
import { assert, assertNotUndefined, fail } from '../Common';
|
|
9
|
+
import { assertIsStableId, createSessionId as createSessionId, incrementUuid, isStableId, numericUuidFromStableId, stableIdFromNumericUuid, } from '../id-compressor/NumericUuid';
|
|
10
|
+
import { getIds } from '../id-compressor/IdRange';
|
|
11
|
+
import { createCompressor, performFuzzActions, sessionIds, IdCompressorTestNetwork, Client, DestinationClient, MetaClient, expectSerializes, roundtrip, sessionNumericUuids, } from './utilities/IdCompressorTestUtilities';
|
|
12
|
+
import { expectDefined } from './utilities/TestCommon';
|
|
13
|
+
describe('IdCompressor', () => {
|
|
14
|
+
it('detects invalid cluster sizes', () => {
|
|
15
|
+
const compressor = createCompressor(Client.Client1, 1);
|
|
16
|
+
expect(() => (compressor.clusterCapacity = -1)).to.throw('Clusters must have a positive capacity');
|
|
17
|
+
expect(() => (compressor.clusterCapacity = 0)).to.throw('Clusters must have a positive capacity');
|
|
18
|
+
expect(() => (compressor.clusterCapacity = IdCompressor.maxClusterSize + 1)).to.throw('Clusters must not exceed max cluster size');
|
|
19
|
+
});
|
|
20
|
+
it('reports the proper session ID', () => {
|
|
21
|
+
const sessionId = createSessionId();
|
|
22
|
+
const compressor = new IdCompressor(sessionId, 0);
|
|
23
|
+
expect(compressor.localSessionId).to.equal(sessionId);
|
|
24
|
+
});
|
|
25
|
+
it('accepts different numbers of reserved IDs', () => {
|
|
26
|
+
for (const reservedIdCount of [0, 1, 5]) {
|
|
27
|
+
const compressor = new IdCompressor(createSessionId(), reservedIdCount);
|
|
28
|
+
if (reservedIdCount > 0) {
|
|
29
|
+
expect(compressor.decompress(compressor.getReservedId(0))).to.equal(legacySharedTreeInitialTreeId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
describe('ID Generation', () => {
|
|
34
|
+
it('can create a compressed ID with an override', () => {
|
|
35
|
+
const compressor = createCompressor(Client.Client1);
|
|
36
|
+
const override = 'override';
|
|
37
|
+
const id = compressor.generateCompressedId(override);
|
|
38
|
+
expect(compressor.decompress(id)).to.equal(override);
|
|
39
|
+
});
|
|
40
|
+
it('can create compressed IDs with v5 overrides', () => {
|
|
41
|
+
const compressor = createCompressor(Client.Client1);
|
|
42
|
+
const uuidA = v5('foo', '7834b437-6e8c-4936-a1a3-0130b1178f17');
|
|
43
|
+
const uuidB = uuidA.slice(0, uuidA.length - 1) + (uuidA.charAt(uuidA.length - 1) === 'a' ? 'b' : 'a');
|
|
44
|
+
const idA = compressor.generateCompressedId(uuidA);
|
|
45
|
+
const idB = compressor.generateCompressedId(uuidB);
|
|
46
|
+
expect(compressor.decompress(idA)).to.equal(uuidA);
|
|
47
|
+
expect(compressor.decompress(idB)).to.equal(uuidB);
|
|
48
|
+
});
|
|
49
|
+
it('can manually create a compressed ID', () => {
|
|
50
|
+
const compressor = createCompressor(Client.Client1);
|
|
51
|
+
const id = compressor.generateCompressedId();
|
|
52
|
+
const uuid = compressor.decompress(id);
|
|
53
|
+
expect(id).to.equal(compressor.recompress(uuid));
|
|
54
|
+
});
|
|
55
|
+
it('will not decompress IDs it did not compress', () => {
|
|
56
|
+
const errorMessage = 'Compressed ID was not generated by this compressor';
|
|
57
|
+
const compressor = createCompressor(Client.Client1);
|
|
58
|
+
expect(() => compressor.decompress(-1)).to.throw(errorMessage);
|
|
59
|
+
expect(() => compressor.decompress(compressor.reservedIdCount)).to.throw(errorMessage);
|
|
60
|
+
});
|
|
61
|
+
it('will not re-compress uuids it did not originally compress', () => {
|
|
62
|
+
const compressor = createCompressor(Client.Client1);
|
|
63
|
+
expect(compressor.tryRecompress('5fff846a-efd4-42fb-8b78-b32ce2672f99')).to.be.undefined;
|
|
64
|
+
});
|
|
65
|
+
it('unifies duplicate overrides originating from the same compressor', () => {
|
|
66
|
+
const override = 'override';
|
|
67
|
+
const compressor = createCompressor(Client.Client1, 3);
|
|
68
|
+
// Client1 compresses a uuid
|
|
69
|
+
const localId1 = compressor.generateCompressedId(override);
|
|
70
|
+
const localId2 = compressor.generateCompressedId(override);
|
|
71
|
+
expect(localId1).to.equal(localId2, 'only one local ID should be allocated for the same override');
|
|
72
|
+
expect(compressor.decompress(localId1)).to.equal(override, 'override incorrectly associated with local ID');
|
|
73
|
+
});
|
|
74
|
+
it('unifies overrides with sequential local IDs', () => {
|
|
75
|
+
const compressor = createCompressor(Client.Client1, 3);
|
|
76
|
+
// Client1 compresses a uuid
|
|
77
|
+
compressor.generateCompressedId();
|
|
78
|
+
const localId2 = compressor.generateCompressedId();
|
|
79
|
+
const stableId2 = assertIsStableId(compressor.decompress(localId2));
|
|
80
|
+
const localId3 = compressor.generateCompressedId(stableId2);
|
|
81
|
+
expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
|
|
82
|
+
});
|
|
83
|
+
it('unifies overrides with sequential local IDs that sort before the reserved session UUID', () => {
|
|
84
|
+
// This is a regression test for an issue where passing a sequential UUID that sorted before the reserved UUID
|
|
85
|
+
// as an override created duplicate overrides in the compressor.
|
|
86
|
+
const newSession = `0${legacySharedTreeInitialTreeId.slice(1)}`;
|
|
87
|
+
const compressor = new IdCompressor(newSession, 1 /* just needs to be > 0 */);
|
|
88
|
+
// Client1 compresses a uuid
|
|
89
|
+
compressor.generateCompressedId();
|
|
90
|
+
const localId2 = compressor.generateCompressedId();
|
|
91
|
+
const stableId2 = assertIsStableId(compressor.decompress(localId2));
|
|
92
|
+
const localId3 = compressor.generateCompressedId(stableId2);
|
|
93
|
+
expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
|
|
94
|
+
});
|
|
95
|
+
it('unifies overrides with sequential local IDs that sort after an existing override', () => {
|
|
96
|
+
// This is a regression test for an issue where passing a sequential UUID that sorted after an existing override
|
|
97
|
+
// as an override created duplicate overrides in the compressor.
|
|
98
|
+
const newSession = `b${v4().slice(1)}`;
|
|
99
|
+
const compressor = new IdCompressor(newSession, 0);
|
|
100
|
+
// Client1 compresses a uuid with some override that will sort before the session uuid
|
|
101
|
+
compressor.generateCompressedId(`a${v4().slice(1)}`);
|
|
102
|
+
const localId2 = compressor.generateCompressedId();
|
|
103
|
+
const stableId2 = assertIsStableId(compressor.decompress(localId2));
|
|
104
|
+
const localId3 = compressor.generateCompressedId(stableId2);
|
|
105
|
+
expect(localId3).to.equal(localId2, 'only one local ID should be allocated for the same sequential uuid');
|
|
106
|
+
});
|
|
107
|
+
it('unifies overrides with sequential local IDs that sort after an existing cluster', () => {
|
|
108
|
+
// This is a regression test for an issue where passing a sequential UUID that sorted after an existing cluster
|
|
109
|
+
// as an override created duplicate overrides in the compressor.
|
|
110
|
+
const newSession1 = `c${v4().slice(1)}`;
|
|
111
|
+
const newSession2 = `b${v4().slice(1)}`;
|
|
112
|
+
const compressor1 = new IdCompressor(newSession1, 0);
|
|
113
|
+
const compressor2 = new IdCompressor(newSession2, 0);
|
|
114
|
+
compressor1.clusterCapacity = 5;
|
|
115
|
+
compressor2.clusterCapacity = 5;
|
|
116
|
+
compressor2.generateCompressedId(); // one ID, enough to make a cluster
|
|
117
|
+
compressor1.finalizeCreationRange(compressor2.takeNextCreationRange());
|
|
118
|
+
const localId = compressor1.generateCompressedId();
|
|
119
|
+
const stableId2 = assertIsStableId(compressor1.decompress(localId));
|
|
120
|
+
const localId3 = compressor1.generateCompressedId(stableId2);
|
|
121
|
+
expect(localId3).to.equal(localId, 'only one local ID should be allocated for the same sequential uuid');
|
|
122
|
+
});
|
|
123
|
+
it('unifies unfinalized local overrides with final IDs from a remote session', () => {
|
|
124
|
+
const compressor1 = createCompressor(Client.Client1, 3);
|
|
125
|
+
const compressor2 = createCompressor(Client.Client2, 3);
|
|
126
|
+
const override = 'override';
|
|
127
|
+
const local1 = compressor1.generateCompressedId(override);
|
|
128
|
+
const local2 = compressor2.generateCompressedId(override);
|
|
129
|
+
const creationRange = compressor2.takeNextCreationRange();
|
|
130
|
+
compressor1.finalizeCreationRange(creationRange);
|
|
131
|
+
compressor2.finalizeCreationRange(creationRange);
|
|
132
|
+
expect(compressor1.decompress(local1)).to.equal(override);
|
|
133
|
+
const final1 = compressor1.normalizeToOpSpace(local1);
|
|
134
|
+
const final2 = compressor2.normalizeToOpSpace(local2);
|
|
135
|
+
expect(isFinalId(final1)).to.be.true;
|
|
136
|
+
expect(final1).to.equal(final2);
|
|
137
|
+
});
|
|
138
|
+
it('unifies overrides with sequential local IDs that have been finalized', () => {
|
|
139
|
+
const compressor = createCompressor(Client.Client1);
|
|
140
|
+
const id = compressor.generateCompressedId();
|
|
141
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
142
|
+
const stableId = assertIsStableId(compressor.decompress(id));
|
|
143
|
+
const localId2 = compressor.generateCompressedId(stableId);
|
|
144
|
+
expect(localId2).to.equal(id, 'only one local ID should be allocated for the same sequential uuid');
|
|
145
|
+
});
|
|
146
|
+
it('cannot create negative amounts of local IDs', () => {
|
|
147
|
+
expect(() => createCompressor(Client.Client1).generateCompressedIdRange(-1)).to.throw('Must generate a nonzero number of IDs.');
|
|
148
|
+
});
|
|
149
|
+
it('cannot create unacceptably large amounts of local IDs', () => {
|
|
150
|
+
expect(() => createCompressor(Client.Client1).generateCompressedIdRange(Number.MAX_SAFE_INTEGER + 2)).to.throw('The number of allocated local IDs must not exceed the JS maximum safe integer.');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe('can enumerate all locally created IDs', () => {
|
|
154
|
+
const idCount = 10;
|
|
155
|
+
it('created without finalization', () => {
|
|
156
|
+
const compressor = createCompressor(Client.Client1);
|
|
157
|
+
const ids = [];
|
|
158
|
+
for (let i = 0; i < idCount; i++) {
|
|
159
|
+
ids.push(compressor.generateCompressedId());
|
|
160
|
+
}
|
|
161
|
+
const returnedIds = [...compressor.getAllIdsFromLocalSession()];
|
|
162
|
+
expect(returnedIds).to.deep.equal(ids);
|
|
163
|
+
});
|
|
164
|
+
it('created with finalization', () => {
|
|
165
|
+
const compressor = createCompressor(Client.Client1, 10);
|
|
166
|
+
const ids = [];
|
|
167
|
+
for (let i = 0; i < idCount; i++) {
|
|
168
|
+
if (i === Math.floor(idCount / 2)) {
|
|
169
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
170
|
+
}
|
|
171
|
+
ids.push(compressor.generateCompressedId());
|
|
172
|
+
}
|
|
173
|
+
const returnedIds = [...compressor.getAllIdsFromLocalSession()];
|
|
174
|
+
expect(returnedIds).to.deep.equal(ids);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
it('only sends attribution info on the first range from each session', () => {
|
|
178
|
+
const compressor = createCompressor(Client.Client1, 5, 'attribution');
|
|
179
|
+
const range1 = compressor.takeNextCreationRange();
|
|
180
|
+
expectDefined(range1.attributionInfo);
|
|
181
|
+
const range2 = compressor.takeNextCreationRange();
|
|
182
|
+
expect(range2.attributionInfo).to.be.undefined;
|
|
183
|
+
});
|
|
184
|
+
describe('can produce a creation range', () => {
|
|
185
|
+
const tests = [
|
|
186
|
+
{ title: 'that is empty', overrideIndices: [], idCount: 0 },
|
|
187
|
+
{ title: 'with only sequential IDs', overrideIndices: [], idCount: 3 },
|
|
188
|
+
{
|
|
189
|
+
title: 'with an overriding ID',
|
|
190
|
+
overrideIndices: [0],
|
|
191
|
+
idCount: 1,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
title: 'with a sequential ID before an overriding ID',
|
|
195
|
+
overrideIndices: [1],
|
|
196
|
+
idCount: 2,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
title: 'with a sequential ID after an overriding ID',
|
|
200
|
+
overrideIndices: [0],
|
|
201
|
+
idCount: 2,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
title: 'with an overriding ID between sequential IDs',
|
|
205
|
+
overrideIndices: [1],
|
|
206
|
+
idCount: 3,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
title: 'with a sequential ID between override IDs',
|
|
210
|
+
overrideIndices: [0, 2],
|
|
211
|
+
idCount: 3,
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
tests.forEach(({ title, overrideIndices, idCount }) => {
|
|
215
|
+
it(title, () => {
|
|
216
|
+
const compressor = createCompressor(Client.Client1);
|
|
217
|
+
validateIdCreationRange(compressor, idCount, new Set(overrideIndices));
|
|
218
|
+
});
|
|
219
|
+
tests.forEach(({ title: title2, overrideIndices: overrideIndices2, idCount: idCount2 }) => {
|
|
220
|
+
it(`${title2} after a range ${title}`, () => {
|
|
221
|
+
const compressor = createCompressor(Client.Client1);
|
|
222
|
+
const lastTaken = validateIdCreationRange(compressor, idCount, new Set(overrideIndices));
|
|
223
|
+
validateIdCreationRange(compressor, idCount2, new Set(overrideIndices2), lastTaken);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
function validateIdCreationRange(compressor, idCount, overrideIndices, lastTakenId = 0) {
|
|
228
|
+
const overrides = [];
|
|
229
|
+
for (let i = 0; i < idCount; i++) {
|
|
230
|
+
const override = overrideIndices.has(i) ? v4() : undefined;
|
|
231
|
+
const id = compressor.generateCompressedId(override);
|
|
232
|
+
overrides.push([id, override]);
|
|
233
|
+
}
|
|
234
|
+
const range = compressor.takeNextCreationRange();
|
|
235
|
+
let newLastTakenId = lastTakenId;
|
|
236
|
+
let idsActual = getIds(range);
|
|
237
|
+
if (overrides.length === 0) {
|
|
238
|
+
expect(idsActual).to.be.undefined;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
idsActual = expectDefined(idsActual);
|
|
242
|
+
expect(overrides[0][0]).to.equal(idsActual.first);
|
|
243
|
+
expect(overrides[overrides.length - 1][0]).to.equal(idsActual.last);
|
|
244
|
+
for (const [id, uuid] of Object.entries(overrideIndices)) {
|
|
245
|
+
expect(overrides[id][1]).to.equal(uuid);
|
|
246
|
+
}
|
|
247
|
+
newLastTakenId = idsActual.last;
|
|
248
|
+
}
|
|
249
|
+
return newLastTakenId;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
describe('Finalizing', () => {
|
|
253
|
+
it('can finalize multiple overrides into the same cluster using different ranges', () => {
|
|
254
|
+
const compressor = createCompressor(Client.Client1);
|
|
255
|
+
const override1 = 'override1';
|
|
256
|
+
const override2 = 'override2';
|
|
257
|
+
const id1 = compressor.generateCompressedId(override1);
|
|
258
|
+
compressor.generateCompressedId();
|
|
259
|
+
const range1 = compressor.takeNextCreationRange();
|
|
260
|
+
const id2 = compressor.generateCompressedId(override2);
|
|
261
|
+
const range2 = compressor.takeNextCreationRange();
|
|
262
|
+
compressor.finalizeCreationRange(range1);
|
|
263
|
+
compressor.finalizeCreationRange(range2);
|
|
264
|
+
const finalId1 = compressor.normalizeToOpSpace(id1);
|
|
265
|
+
const finalId2 = compressor.normalizeToOpSpace(id2);
|
|
266
|
+
expect(isFinalId(finalId1)).to.be.true;
|
|
267
|
+
expect(isFinalId(finalId2)).to.be.true;
|
|
268
|
+
expect(compressor.decompress(finalId1)).to.equal(override1);
|
|
269
|
+
expect(compressor.decompress(finalId2)).to.equal(override2);
|
|
270
|
+
});
|
|
271
|
+
it('prevents attempts to finalize ranges twice', () => {
|
|
272
|
+
const rangeCompressor = createCompressor(Client.Client1);
|
|
273
|
+
rangeCompressor.generateCompressedIdRange(3);
|
|
274
|
+
const batchRange = rangeCompressor.takeNextCreationRange();
|
|
275
|
+
rangeCompressor.finalizeCreationRange(batchRange);
|
|
276
|
+
expect(() => rangeCompressor.finalizeCreationRange(batchRange)).to.throw('Ranges finalized out of order.');
|
|
277
|
+
// Make a new compressor, as the first one will be left in a bad state
|
|
278
|
+
const explicitCompressor = createCompressor(Client.Client1);
|
|
279
|
+
explicitCompressor.generateCompressedId();
|
|
280
|
+
explicitCompressor.generateCompressedId('override');
|
|
281
|
+
const explicitRange = explicitCompressor.takeNextCreationRange();
|
|
282
|
+
explicitCompressor.finalizeCreationRange(explicitRange);
|
|
283
|
+
expect(() => explicitCompressor.finalizeCreationRange(explicitRange)).to.throw('Ranges finalized out of order.');
|
|
284
|
+
});
|
|
285
|
+
it('prevents attempts to finalize ranges out of order', () => {
|
|
286
|
+
const compressor = createCompressor(Client.Client1);
|
|
287
|
+
compressor.generateCompressedId();
|
|
288
|
+
compressor.takeNextCreationRange();
|
|
289
|
+
compressor.generateCompressedId();
|
|
290
|
+
const secondRange = compressor.takeNextCreationRange();
|
|
291
|
+
expect(() => compressor.finalizeCreationRange(secondRange)).to.throw('Ranges finalized out of order.');
|
|
292
|
+
});
|
|
293
|
+
it('prevents finalizing unacceptably enormous amounts of ID allocation', () => {
|
|
294
|
+
const compressor1 = createCompressor(Client.Client1);
|
|
295
|
+
const compressor2 = createCompressor(Client.Client2);
|
|
296
|
+
const integerLargerThanHalfMax = Math.round((Number.MAX_SAFE_INTEGER / 3) * 2);
|
|
297
|
+
compressor1.generateCompressedIdRange(integerLargerThanHalfMax);
|
|
298
|
+
compressor2.generateCompressedIdRange(integerLargerThanHalfMax);
|
|
299
|
+
const range1 = compressor1.takeNextCreationRange();
|
|
300
|
+
const range2 = compressor2.takeNextCreationRange();
|
|
301
|
+
compressor1.finalizeCreationRange(range1);
|
|
302
|
+
expect(() => compressor1.finalizeCreationRange(range2)).to.throw('The number of allocated final IDs must not exceed the JS maximum safe integer.');
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
describe('Compression', () => {
|
|
306
|
+
it('can re-compress a sequential uuid it generated', () => {
|
|
307
|
+
const compressor = createCompressor(Client.Client1);
|
|
308
|
+
const id = compressor.generateCompressedId();
|
|
309
|
+
const uuid = compressor.decompress(id);
|
|
310
|
+
expect(compressor.recompress(uuid)).to.equal(id);
|
|
311
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
312
|
+
expect(compressor.recompress(uuid)).to.equal(id);
|
|
313
|
+
});
|
|
314
|
+
it('can re-compress an override', () => {
|
|
315
|
+
const compressor = createCompressor(Client.Client1);
|
|
316
|
+
const override = 'override';
|
|
317
|
+
const id = compressor.generateCompressedId(override);
|
|
318
|
+
expect(compressor.recompress(override)).to.equal(id);
|
|
319
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
320
|
+
expect(compressor.recompress(override)).to.equal(id);
|
|
321
|
+
});
|
|
322
|
+
it('can re-compress overrides from a remote client it has finalized', () => {
|
|
323
|
+
const compressor = createCompressor(Client.Client1);
|
|
324
|
+
const id = compressor.generateCompressedId();
|
|
325
|
+
const override = 'override';
|
|
326
|
+
compressor.generateCompressedId(override);
|
|
327
|
+
const uuid = compressor.decompress(id);
|
|
328
|
+
const compressor2 = createCompressor(Client.Client2);
|
|
329
|
+
compressor2.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
330
|
+
const finalId1 = compressor2.recompress(uuid);
|
|
331
|
+
const finalId2 = compressor2.recompress(override);
|
|
332
|
+
if (finalId1 === undefined || finalId2 === undefined) {
|
|
333
|
+
expect.fail();
|
|
334
|
+
}
|
|
335
|
+
expect(isFinalId(finalId1)).to.be.true;
|
|
336
|
+
expect(isFinalId(finalId2)).to.be.true;
|
|
337
|
+
});
|
|
338
|
+
it('will not compress an override it never compressed or finalized', () => {
|
|
339
|
+
const compressor = createCompressor(Client.Client1, 5);
|
|
340
|
+
// Leading zeroes to exploit calls to getOrNextLower on uuid maps, as it will be before test session uuids
|
|
341
|
+
const override = 'override';
|
|
342
|
+
expect(compressor.tryRecompress(override)).to.be.undefined;
|
|
343
|
+
expect(compressor.tryRecompress(stableIdFromNumericUuid(sessionNumericUuids.get(Client.Client1), 1))).to.be
|
|
344
|
+
.undefined;
|
|
345
|
+
compressor.generateCompressedId(override);
|
|
346
|
+
compressor.generateCompressedIdRange(2);
|
|
347
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
348
|
+
expect(compressor.tryRecompress(stableIdFromNumericUuid(sessionNumericUuids.get(Client.Client1), 4))).to.be
|
|
349
|
+
.undefined;
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
describe('Decompression', () => {
|
|
353
|
+
it('can decompress a local ID before and after finalizing', () => {
|
|
354
|
+
const compressor = createCompressor(Client.Client1);
|
|
355
|
+
const id = compressor.generateCompressedId();
|
|
356
|
+
const uuid = compressor.decompress(id);
|
|
357
|
+
expect(isStableId(uuid)).to.be.true;
|
|
358
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
359
|
+
expect(compressor.decompress(id)).to.equal(uuid);
|
|
360
|
+
});
|
|
361
|
+
it('can decompress reserved IDs', () => {
|
|
362
|
+
// This is a glass box test in that it increments UUIDs
|
|
363
|
+
const compressor = createCompressor(Client.Client1);
|
|
364
|
+
expect(compressor.decompress(compressor.getReservedId(0))).to.equal(legacySharedTreeInitialTreeId);
|
|
365
|
+
const reservedSessionUuid = numericUuidFromStableId(assertIsStableId(compressor.decompress(compressor.getReservedId(1))));
|
|
366
|
+
for (let i = 1; i < compressor.reservedIdCount; i++) {
|
|
367
|
+
const reservedId = compressor.getReservedId(i);
|
|
368
|
+
const stable = compressor.decompress(reservedId);
|
|
369
|
+
expect(stable).to.equal(stableIdFromNumericUuid(incrementUuid(reservedSessionUuid, i - 1)));
|
|
370
|
+
const finalIdForReserved = compressor.recompress(stable);
|
|
371
|
+
expect(isLocalId(finalIdForReserved)).to.be.false;
|
|
372
|
+
expect(finalIdForReserved).to.equal(reservedId);
|
|
373
|
+
}
|
|
374
|
+
const outOfBoundsError = 'Reserved Id index out of bounds';
|
|
375
|
+
expect(() => compressor.getReservedId(-1)).to.throw(outOfBoundsError);
|
|
376
|
+
expect(() => compressor.getReservedId(compressor.reservedIdCount)).to.throw(outOfBoundsError);
|
|
377
|
+
});
|
|
378
|
+
it('can decompress a final ID', () => {
|
|
379
|
+
const compressor = createCompressor(Client.Client1);
|
|
380
|
+
const range = compressor.generateCompressedIdRange(1);
|
|
381
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
382
|
+
const finalId = compressor.normalizeToOpSpace(compressor.getIdsFromRange(range, compressor.localSessionId).get(0));
|
|
383
|
+
if (isLocalId(finalId)) {
|
|
384
|
+
expect.fail('Op space ID was finalized but is local');
|
|
385
|
+
}
|
|
386
|
+
const uuid = compressor.decompress(finalId);
|
|
387
|
+
expect(isStableId(uuid)).to.be.true;
|
|
388
|
+
});
|
|
389
|
+
it('can decompress a final ID with an override', () => {
|
|
390
|
+
const compressor = createCompressor(Client.Client1);
|
|
391
|
+
const override = 'override';
|
|
392
|
+
const id = compressor.generateCompressedId(override);
|
|
393
|
+
const range = compressor.takeNextCreationRange();
|
|
394
|
+
compressor.finalizeCreationRange(range);
|
|
395
|
+
const finalId = compressor.normalizeToOpSpace(id);
|
|
396
|
+
if (isLocalId(finalId)) {
|
|
397
|
+
expect.fail('Op space ID was finalized but is local');
|
|
398
|
+
}
|
|
399
|
+
const uuid = compressor.decompress(finalId);
|
|
400
|
+
expect(uuid).to.equal(override);
|
|
401
|
+
});
|
|
402
|
+
it('can decompress an override that is an UUID', () => {
|
|
403
|
+
const compressor = createCompressor(Client.Client1, 5);
|
|
404
|
+
const uuid = 'd1302ab1-3c08-4e79-a49a-4c39ac369c16';
|
|
405
|
+
const id = compressor.generateCompressedId(uuid);
|
|
406
|
+
expect(compressor.decompress(id)).to.equal(uuid);
|
|
407
|
+
});
|
|
408
|
+
it('properly sorts UUID-like overrides separately from true UUIDs', () => {
|
|
409
|
+
// This is a glass box test that ensures overrides which would sort in between a cluster base UUID and an UUID higher up in the
|
|
410
|
+
// same cluster are not accidentally retrieved during cluster ID lookup.
|
|
411
|
+
const compressor = createCompressor(Client.Client1, 5);
|
|
412
|
+
const override = `${compressor.localSessionId}6`;
|
|
413
|
+
const id = compressor.generateCompressedId();
|
|
414
|
+
compressor.generateCompressedId(override);
|
|
415
|
+
const decompressedId = compressor.decompress(id);
|
|
416
|
+
expect(compressor.recompress(decompressedId)).to.equal(id);
|
|
417
|
+
});
|
|
418
|
+
it('can decompress an override that starts with the reserved prefix character', () => {
|
|
419
|
+
const compressor = createCompressor(Client.Client1, 5);
|
|
420
|
+
const override = `\ue15e${compressor.localSessionId}`;
|
|
421
|
+
const id = compressor.generateCompressedId(override);
|
|
422
|
+
expect(compressor.decompress(id)).to.equal(override);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
describe('Normalization', () => {
|
|
426
|
+
it('can normalize a local ID to op space before finalizing', () => {
|
|
427
|
+
const compressor = createCompressor(Client.Client1);
|
|
428
|
+
const id = compressor.generateCompressedId();
|
|
429
|
+
const normalized = compressor.normalizeToOpSpace(id);
|
|
430
|
+
expect(isLocalId(id)).to.be.true;
|
|
431
|
+
expect(id).to.equal(normalized);
|
|
432
|
+
});
|
|
433
|
+
it('can normalize a local ID to op space after finalizing', () => {
|
|
434
|
+
const compressor = createCompressor(Client.Client1);
|
|
435
|
+
const id = compressor.generateCompressedId();
|
|
436
|
+
compressor.finalizeCreationRange(compressor.takeNextCreationRange());
|
|
437
|
+
const normalized = compressor.normalizeToOpSpace(id);
|
|
438
|
+
expect(isFinalId(normalized)).to.be.true;
|
|
439
|
+
expect(id).to.not.equal(normalized);
|
|
440
|
+
});
|
|
441
|
+
it('cannot normalize a remote ID to session space if it has not been finalized', () => {
|
|
442
|
+
const compressor1 = createCompressor(Client.Client1);
|
|
443
|
+
const compressor2 = createCompressor(Client.Client2);
|
|
444
|
+
const normalized = compressor1.normalizeToOpSpace(compressor1.generateCompressedId());
|
|
445
|
+
expect(() => compressor2.normalizeToSessionSpace(normalized, compressor1.localSessionId)).to.throw('No IDs have ever been finalized by the supplied session.');
|
|
446
|
+
});
|
|
447
|
+
it('can normalize local and final IDs from a remote session to session space', () => {
|
|
448
|
+
const compressor1 = createCompressor(Client.Client1);
|
|
449
|
+
const compressor2 = createCompressor(Client.Client2);
|
|
450
|
+
const id = compressor1.generateCompressedId();
|
|
451
|
+
const normalizedLocal = compressor1.normalizeToOpSpace(id);
|
|
452
|
+
const range = compressor1.takeNextCreationRange();
|
|
453
|
+
compressor1.finalizeCreationRange(range);
|
|
454
|
+
const normalizedFinal = compressor1.normalizeToOpSpace(id);
|
|
455
|
+
compressor2.finalizeCreationRange(range);
|
|
456
|
+
expect(isLocalId(normalizedLocal)).to.be.true;
|
|
457
|
+
expect(isFinalId(normalizedFinal)).to.be.true;
|
|
458
|
+
expect(compressor2.normalizeToSessionSpace(normalizedFinal, compressor1.localSessionId)).to.equal(normalizedFinal);
|
|
459
|
+
expect(compressor2.normalizeToSessionSpace(normalizedLocal, compressor1.localSessionId)).to.equal(normalizedFinal);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
describe('Serialization', () => {
|
|
463
|
+
it('can serialize an empty compressor', () => {
|
|
464
|
+
const compressor = createCompressor(Client.Client1);
|
|
465
|
+
const [serializedNoSession, serializedWithSession] = expectSerializes(compressor);
|
|
466
|
+
expect(serializedWithSession.clusters.length).to.equal(0, 'reserved cluster should not be serialized');
|
|
467
|
+
expect(serializedNoSession.clusters.length).to.equal(0, 'reserved cluster should not be serialized');
|
|
468
|
+
});
|
|
469
|
+
it('correctly deserializes and resumes a session', () => {
|
|
470
|
+
const compressor1 = createCompressor(Client.Client1, undefined, Client.Client1);
|
|
471
|
+
const compressor2 = createCompressor(Client.Client2, undefined, Client.Client2);
|
|
472
|
+
compressor1.generateCompressedId();
|
|
473
|
+
const creationRange = compressor1.takeNextCreationRange();
|
|
474
|
+
compressor1.finalizeCreationRange(creationRange);
|
|
475
|
+
compressor2.finalizeCreationRange(creationRange);
|
|
476
|
+
const [_, serializedWithSession] = expectSerializes(compressor1);
|
|
477
|
+
const compressorResumed = IdCompressor.deserialize(serializedWithSession);
|
|
478
|
+
compressorResumed.generateCompressedId();
|
|
479
|
+
const range2 = compressorResumed.takeNextCreationRange();
|
|
480
|
+
compressor1.finalizeCreationRange(range2);
|
|
481
|
+
compressor2.finalizeCreationRange(range2);
|
|
482
|
+
expect(IdCompressor.deserialize(compressor1.serialize(false), createSessionId()).equals(IdCompressor.deserialize(compressor2.serialize(false), createSessionId()), false // don't compare local state
|
|
483
|
+
)).to.be.true;
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
// No validation, as these leave the network in a broken state
|
|
487
|
+
describeNetworkNoValidation('detects UUID collision', (itNetwork) => {
|
|
488
|
+
itNetwork('when a client requests an override that is an UUID reserved for later allocation by a cluster', 2, (network) => {
|
|
489
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
490
|
+
network.deliverOperations(Client.Client1);
|
|
491
|
+
const compressor2 = network.getCompressor(Client.Client2);
|
|
492
|
+
const id = network.getIdLog(Client.Client2)[0].id;
|
|
493
|
+
const uuid = assertIsStableId(compressor2.decompress(id));
|
|
494
|
+
const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), 1);
|
|
495
|
+
expect(() => network.allocateAndSendIds(Client.Client1, 1, { 0: nextUuid })).to.throw(`Override '${nextUuid}' collides with another allocated UUID.`);
|
|
496
|
+
});
|
|
497
|
+
itNetwork('when a new cluster is allocated whose base UUID collides with an existing override', 2, (network) => {
|
|
498
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
499
|
+
network.deliverOperations(DestinationClient.All);
|
|
500
|
+
const compressor1 = network.getCompressor(Client.Client1);
|
|
501
|
+
const id = network.getIdLog(Client.Client1)[0].id;
|
|
502
|
+
const uuid = assertIsStableId(compressor1.decompress(id));
|
|
503
|
+
const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), 2);
|
|
504
|
+
network.allocateAndSendIds(Client.Client1, 1, { 0: nextUuid });
|
|
505
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
506
|
+
network.deliverOperations(DestinationClient.All);
|
|
507
|
+
network.allocateAndSendIds(Client.Client1, 1); // new cluster
|
|
508
|
+
expect(() => network.deliverOperations(Client.Client1)).to.throw(`Override '${nextUuid}' collides with another allocated UUID.`);
|
|
509
|
+
});
|
|
510
|
+
itNetwork('detects colliding override UUIDs when expanding a cluster', 1, (network) => {
|
|
511
|
+
// This is a glass box test in that it is testing cluster expansion
|
|
512
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
513
|
+
network.deliverOperations(DestinationClient.All);
|
|
514
|
+
const compressor1 = network.getCompressor(Client.Client1);
|
|
515
|
+
const id = network.getIdLog(Client.Client1)[0].id;
|
|
516
|
+
const uuid = assertIsStableId(compressor1.decompress(id));
|
|
517
|
+
const expansion = 3;
|
|
518
|
+
const nextUuid = stableIdFromNumericUuid(numericUuidFromStableId(uuid), expansion);
|
|
519
|
+
network.allocateAndSendIds(Client.Client1, expansion, { 0: nextUuid });
|
|
520
|
+
expect(() => network.deliverOperations(DestinationClient.All)).to.throw(`Override '${nextUuid}' collides with another allocated UUID.`);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
describeNetwork('Networked', (itNetwork) => {
|
|
524
|
+
describe('can attribute', () => {
|
|
525
|
+
itNetwork('local IDs before and after being finalized', (network) => {
|
|
526
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
527
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
528
|
+
const id = network.getIdLog(Client.Client1)[0].id;
|
|
529
|
+
expect(compressor.attributeId(id)).to.equal(Client.Client1);
|
|
530
|
+
network.deliverOperations(Client.Client1);
|
|
531
|
+
expect(compressor.attributeId(id)).to.equal(Client.Client1);
|
|
532
|
+
});
|
|
533
|
+
itNetwork('final IDs from a remote session', (network) => {
|
|
534
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
535
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
536
|
+
network.deliverOperations(DestinationClient.All);
|
|
537
|
+
const id = network.getSequencedIdLog(Client.Client1)[0].id;
|
|
538
|
+
expect(compressor.attributeId(id)).to.equal(Client.Client2);
|
|
539
|
+
});
|
|
540
|
+
itNetwork('final IDs from multiple remote sessions', 1, (network) => {
|
|
541
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
542
|
+
// Ensure multiple clusters are made by each client. Cluster size === 1.
|
|
543
|
+
network.allocateAndSendIds(Client.Client1, compressor.clusterCapacity);
|
|
544
|
+
network.allocateAndSendIds(Client.Client2, compressor.clusterCapacity);
|
|
545
|
+
network.allocateAndSendIds(Client.Client3, compressor.clusterCapacity);
|
|
546
|
+
network.allocateAndSendIds(Client.Client1, compressor.clusterCapacity);
|
|
547
|
+
network.allocateAndSendIds(Client.Client2, compressor.clusterCapacity);
|
|
548
|
+
network.allocateAndSendIds(Client.Client3, compressor.clusterCapacity);
|
|
549
|
+
network.deliverOperations(DestinationClient.All);
|
|
550
|
+
const log = network.getSequencedIdLog(Client.Client1);
|
|
551
|
+
expect(compressor.attributeId(log[0].id)).to.equal(Client.Client1);
|
|
552
|
+
expect(compressor.attributeId(log[1].id)).to.equal(Client.Client2);
|
|
553
|
+
expect(compressor.attributeId(log[2].id)).to.equal(Client.Client3);
|
|
554
|
+
expect(compressor.attributeId(log[3].id)).to.equal(Client.Client1);
|
|
555
|
+
expect(compressor.attributeId(log[4].id)).to.equal(Client.Client2);
|
|
556
|
+
expect(compressor.attributeId(log[5].id)).to.equal(Client.Client3);
|
|
557
|
+
});
|
|
558
|
+
itNetwork('unified IDs', (network) => {
|
|
559
|
+
const override = 'override';
|
|
560
|
+
const allTargets = network.getTargetCompressors(DestinationClient.All);
|
|
561
|
+
for (const [client, compressor] of allTargets) {
|
|
562
|
+
network.allocateAndSendIds(client, 1, { 0: override });
|
|
563
|
+
for (const { id } of network.getIdLog(client)) {
|
|
564
|
+
expect(compressor.attributeId(id)).to.equal(client);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
network.deliverOperations(DestinationClient.All);
|
|
568
|
+
const firstTarget = allTargets[0][0];
|
|
569
|
+
for (const [client, compressor] of allTargets) {
|
|
570
|
+
for (const { id } of network.getIdLog(client)) {
|
|
571
|
+
expect(compressor.attributeId(id)).to.equal(firstTarget);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
itNetwork('upholds the invariant that IDs always decompress to the same UUID', 2, (network) => {
|
|
577
|
+
var _a;
|
|
578
|
+
network.allocateAndSendIds(Client.Client1, 5, {
|
|
579
|
+
1: 'override1',
|
|
580
|
+
});
|
|
581
|
+
network.allocateAndSendIds(Client.Client2, 5, {
|
|
582
|
+
2: 'override2',
|
|
583
|
+
});
|
|
584
|
+
network.allocateAndSendIds(Client.Client3, 5, {
|
|
585
|
+
3: 'override3',
|
|
586
|
+
});
|
|
587
|
+
const preAckLocals = new Map();
|
|
588
|
+
for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
|
|
589
|
+
const locals = [];
|
|
590
|
+
for (const idData of network.getIdLog(client)) {
|
|
591
|
+
locals.push([idData.id, compressor.decompress(idData.id)]);
|
|
592
|
+
}
|
|
593
|
+
preAckLocals.set(client, locals);
|
|
594
|
+
}
|
|
595
|
+
// Ack all IDs
|
|
596
|
+
network.deliverOperations(DestinationClient.All);
|
|
597
|
+
for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
|
|
598
|
+
const preAckLocalIds = (_a = preAckLocals.get(client)) !== null && _a !== void 0 ? _a : fail();
|
|
599
|
+
let i = 0;
|
|
600
|
+
for (const idData of network.getIdLog(client)) {
|
|
601
|
+
if (idData.originatingClient === client) {
|
|
602
|
+
expect(isFinalId(idData.id)).to.be.false;
|
|
603
|
+
const currentUuid = compressor.decompress(idData.id);
|
|
604
|
+
expect(currentUuid).to.equal(preAckLocalIds[i % preAckLocalIds.length][1]);
|
|
605
|
+
i++;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
itNetwork('can normalize session space IDs to op space', 5, (network) => {
|
|
611
|
+
const clusterCapacity = 5;
|
|
612
|
+
const idCount = clusterCapacity * 2;
|
|
613
|
+
for (let i = 0; i < idCount; i++) {
|
|
614
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
615
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
616
|
+
network.allocateAndSendIds(Client.Client3, 1);
|
|
617
|
+
}
|
|
618
|
+
for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
|
|
619
|
+
for (const idData of network.getIdLog(client)) {
|
|
620
|
+
expect(idData.originatingClient).to.equal(client);
|
|
621
|
+
expect(isLocalId(compressor.normalizeToOpSpace(idData.id))).to.be.true;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
network.deliverOperations(DestinationClient.All);
|
|
625
|
+
for (const [client, compressor] of network.getTargetCompressors(MetaClient.All)) {
|
|
626
|
+
for (const idData of network.getIdLog(client)) {
|
|
627
|
+
expect(isFinalId(compressor.normalizeToOpSpace(idData.id))).to.be.true;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
itNetwork('can normalize local op space IDs from a local session to session space IDs', (network) => {
|
|
632
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
633
|
+
const { range, sessionId } = network.allocateAndSendIds(Client.Client1, 1);
|
|
634
|
+
network.deliverOperations(Client.Client1);
|
|
635
|
+
const id = compressor.normalizeToOpSpace(compressor.getIdsFromRange(range, sessionId).get(0));
|
|
636
|
+
expect(isFinalId(id)).to.be.true;
|
|
637
|
+
expect(isLocalId(compressor.normalizeToSessionSpace(id, compressor.localSessionId))).to.be.true;
|
|
638
|
+
});
|
|
639
|
+
itNetwork('can normalize local op space IDs from a remote session to session space IDs', (network) => {
|
|
640
|
+
const compressor1 = network.getCompressor(Client.Client1);
|
|
641
|
+
const compressor2 = network.getCompressor(Client.Client2);
|
|
642
|
+
const { range, sessionId } = network.allocateAndSendIds(Client.Client1, 1);
|
|
643
|
+
// Mimic sending a reference to an ID that hasn't been acked yet, such as in a slow network
|
|
644
|
+
const id = compressor1.normalizeToOpSpace(compressor1.getIdsFromRange(range, sessionId).get(0));
|
|
645
|
+
const getSessionNormalizedId = () => compressor2.normalizeToSessionSpace(id, compressor1.localSessionId);
|
|
646
|
+
expect(getSessionNormalizedId).to.throw('No IDs have ever been finalized by the supplied session.');
|
|
647
|
+
network.deliverOperations(Client.Client2);
|
|
648
|
+
expect(isFinalId(getSessionNormalizedId())).to.be.true;
|
|
649
|
+
});
|
|
650
|
+
itNetwork('unifies duplicate overrides', 3, (network) => {
|
|
651
|
+
var _a, _b;
|
|
652
|
+
const override = 'override';
|
|
653
|
+
const compressor1 = network.getCompressor(Client.Client1);
|
|
654
|
+
const compressor2 = network.getCompressor(Client.Client2);
|
|
655
|
+
const compressor3 = network.getCompressor(Client.Client3);
|
|
656
|
+
const clusterCapacity = compressor1.clusterCapacity;
|
|
657
|
+
// Ensure some clusters exist to avoid simple case of empty clusters
|
|
658
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity);
|
|
659
|
+
network.allocateAndSendIds(Client.Client2, clusterCapacity);
|
|
660
|
+
network.allocateAndSendIds(Client.Client3, clusterCapacity);
|
|
661
|
+
network.deliverOperations(DestinationClient.All);
|
|
662
|
+
const range1 = network.allocateAndSendIds(Client.Client1, 1, { 0: override });
|
|
663
|
+
const overrides1 = expectDefined((_a = getIds(range1)) === null || _a === void 0 ? void 0 : _a.overrides);
|
|
664
|
+
const id1 = compressor1.normalizeToSessionSpace(overrides1[0][0], compressor1.localSessionId);
|
|
665
|
+
const opNormalizedLocal1 = compressor1.normalizeToOpSpace(id1);
|
|
666
|
+
expect(isLocalId(opNormalizedLocal1)).to.be.true;
|
|
667
|
+
expect(isFinalId(id1)).to.be.false;
|
|
668
|
+
network.deliverOperations(DestinationClient.Client1);
|
|
669
|
+
const finalId1 = compressor1.normalizeToOpSpace(id1);
|
|
670
|
+
expect(isFinalId(finalId1)).to.be.true;
|
|
671
|
+
const range2 = network.allocateAndSendIds(Client.Client2, 2, { 1: override });
|
|
672
|
+
const overrides2 = expectDefined((_b = getIds(range2)) === null || _b === void 0 ? void 0 : _b.overrides);
|
|
673
|
+
const id2 = compressor2.normalizeToSessionSpace(overrides2[0][0], compressor2.localSessionId);
|
|
674
|
+
const opNormalizedLocal2 = compressor2.normalizeToOpSpace(id2);
|
|
675
|
+
expect(isLocalId(opNormalizedLocal2)).to.be.true;
|
|
676
|
+
expect(isFinalId(id2)).to.be.false;
|
|
677
|
+
network.allocateAndSendIds(Client.Client3, 1);
|
|
678
|
+
network.deliverOperations(DestinationClient.All);
|
|
679
|
+
const finalId2 = compressor2.normalizeToOpSpace(id2);
|
|
680
|
+
expect(isFinalId(finalId2)).to.be.true;
|
|
681
|
+
expect(finalId1).to.equal(finalId2);
|
|
682
|
+
expect(compressor1.normalizeToOpSpace(id1)).to.equal(finalId1);
|
|
683
|
+
expect(compressor1.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(id1);
|
|
684
|
+
expect(compressor1.normalizeToSessionSpace(opNormalizedLocal2, compressor2.localSessionId)).to.equal(id1);
|
|
685
|
+
expect(compressor1.decompress(id1)).to.equal(override);
|
|
686
|
+
expect(compressor1.decompress(finalId1)).to.equal(override);
|
|
687
|
+
expect(compressor1.recompress(override)).to.equal(id1);
|
|
688
|
+
expect(compressor2.normalizeToOpSpace(id2)).to.equal(finalId2);
|
|
689
|
+
expect(compressor2.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(id2);
|
|
690
|
+
expect(compressor2.normalizeToSessionSpace(opNormalizedLocal1, compressor1.localSessionId)).to.equal(id2);
|
|
691
|
+
expect(compressor2.decompress(id2)).to.equal(override);
|
|
692
|
+
expect(compressor2.decompress(finalId2)).to.equal(override);
|
|
693
|
+
expect(compressor2.tryRecompress(override)).to.equal(id2);
|
|
694
|
+
expect(compressor3.normalizeToSessionSpace(finalId1, compressor1.localSessionId)).to.equal(finalId1);
|
|
695
|
+
expect(compressor3.normalizeToSessionSpace(opNormalizedLocal1, compressor1.localSessionId)).to.equal(finalId1);
|
|
696
|
+
expect(compressor3.normalizeToSessionSpace(opNormalizedLocal2, compressor2.localSessionId)).to.equal(finalId1);
|
|
697
|
+
expect(compressor3.decompress(finalId1)).to.equal(override);
|
|
698
|
+
expect(compressor3.recompress(override)).to.equal(finalId1);
|
|
699
|
+
});
|
|
700
|
+
itNetwork('maintains alignment after unifying duplicate overrides', 3, (network) => {
|
|
701
|
+
const override = 'override';
|
|
702
|
+
network.allocateAndSendIds(Client.Client1, 1, { 0: override });
|
|
703
|
+
network.allocateAndSendIds(Client.Client2, 2, { 1: override });
|
|
704
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
705
|
+
network.allocateAndSendIds(Client.Client2, 5);
|
|
706
|
+
expectSequencedLogsAlign(network, Client.Client1, Client.Client2, 1);
|
|
707
|
+
});
|
|
708
|
+
function expectSequencedLogsAlign(network, client1, client2, numUnifications = 0) {
|
|
709
|
+
network.deliverOperations(DestinationClient.All);
|
|
710
|
+
assert(client1 !== client2);
|
|
711
|
+
const log1 = network.getSequencedIdLog(client1);
|
|
712
|
+
const log2 = network.getSequencedIdLog(client2);
|
|
713
|
+
expect(log1.length).to.equal(log2.length);
|
|
714
|
+
const compressor1 = network.getCompressor(client1);
|
|
715
|
+
const compressor2 = network.getCompressor(client2);
|
|
716
|
+
const ids = new Set();
|
|
717
|
+
const uuidsOrOverrides = new Set();
|
|
718
|
+
for (let i = 0; i < log1.length; i++) {
|
|
719
|
+
const data1 = log1[i];
|
|
720
|
+
const id1 = compressor1.normalizeToOpSpace(data1.id);
|
|
721
|
+
const id2 = compressor2.normalizeToOpSpace(log2[i].id);
|
|
722
|
+
expect(isFinalId(id1)).to.be.true;
|
|
723
|
+
ids.add(id1);
|
|
724
|
+
expect(id1).to.equal(id2);
|
|
725
|
+
const uuidOrOverride1 = compressor1.decompress(id1);
|
|
726
|
+
uuidsOrOverrides.add(uuidOrOverride1);
|
|
727
|
+
if (data1.expectedOverride === undefined) {
|
|
728
|
+
expect(isStableId(uuidOrOverride1)).to.be.true;
|
|
729
|
+
}
|
|
730
|
+
expect(uuidOrOverride1).to.equal(compressor2.decompress(id2));
|
|
731
|
+
}
|
|
732
|
+
const expectedSize = log1.length - numUnifications;
|
|
733
|
+
expect(ids.size).to.equal(expectedSize);
|
|
734
|
+
expect(uuidsOrOverrides.size).to.equal(expectedSize);
|
|
735
|
+
}
|
|
736
|
+
itNetwork('produces ID spaces correctly', (network) => {
|
|
737
|
+
// This test asserts that IDs returned from IDCompressor APIs are correctly encoded as either local or final.
|
|
738
|
+
// This is a glass box test in that it assumes the negative/positive encoding of CompressedIds (negative = local, positive = final).
|
|
739
|
+
const compressor1 = network.getCompressor(Client.Client1);
|
|
740
|
+
// Client 1 makes two IDs, two explicit (one with an override) and one sequential
|
|
741
|
+
network.allocateAndSendIds(Client.Client1, 3, {
|
|
742
|
+
1: 'override1',
|
|
743
|
+
});
|
|
744
|
+
network.getIdLog(Client.Client1).forEach((id) => expect(id.id).to.be.lessThan(0));
|
|
745
|
+
// Client 1's IDs have not been acked so have no op space equivalent
|
|
746
|
+
network
|
|
747
|
+
.getIdLog(Client.Client1)
|
|
748
|
+
.forEach((idData) => expect(compressor1.normalizeToOpSpace(idData.id)).to.be.lessThan(0));
|
|
749
|
+
// Client 1's IDs are acked
|
|
750
|
+
network.deliverOperations(Client.Client1);
|
|
751
|
+
network.getIdLog(Client.Client1).forEach((id) => expect(id.id).to.be.lessThan(0));
|
|
752
|
+
// Client 3 makes two IDs, two explicit (one with an override) and one sequential
|
|
753
|
+
network.allocateAndSendIds(Client.Client2, 3, {
|
|
754
|
+
1: 'override2',
|
|
755
|
+
});
|
|
756
|
+
network.getIdLog(Client.Client2).forEach((id) => expect(id.id).to.be.lessThan(0));
|
|
757
|
+
// Client 1 receives Client 2's IDs
|
|
758
|
+
network.deliverOperations(Client.Client1);
|
|
759
|
+
network
|
|
760
|
+
.getIdLog(Client.Client1)
|
|
761
|
+
.slice(-3)
|
|
762
|
+
.forEach((id) => expect(id.id).to.be.greaterThan(0));
|
|
763
|
+
// All IDs have been acked or are from another client, and therefore have a final form in op space
|
|
764
|
+
network
|
|
765
|
+
.getIdLog(Client.Client1)
|
|
766
|
+
.forEach((idData) => expect(compressor1.normalizeToOpSpace(idData.id)).to.be.greaterThan(0));
|
|
767
|
+
// Compression should preserve ID space correctness
|
|
768
|
+
network.getIdLog(Client.Client1).forEach((idData) => {
|
|
769
|
+
const roundtripped = compressor1.recompress(compressor1.decompress(idData.id));
|
|
770
|
+
expect(Math.sign(roundtripped)).to.equal(Math.sign(idData.id));
|
|
771
|
+
});
|
|
772
|
+
network.getIdLog(Client.Client1).forEach((idData) => {
|
|
773
|
+
const opNormalized = compressor1.normalizeToOpSpace(idData.id);
|
|
774
|
+
expect(Math.sign(compressor1.normalizeToSessionSpace(opNormalized, idData.sessionId))).to.equal(Math.sign(idData.id));
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
itNetwork('produces consistent IDs with large fuzz input', (network) => {
|
|
778
|
+
performFuzzActions(network, 1984, true, undefined, true, 1000, 25, (network) => network.assertNetworkState());
|
|
779
|
+
network.deliverOperations(DestinationClient.All);
|
|
780
|
+
});
|
|
781
|
+
itNetwork('can set the cluster size via constructor', 2, (network) => {
|
|
782
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
783
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
784
|
+
const { range, sessionId } = network.allocateAndSendIds(Client.Client2, 2);
|
|
785
|
+
network.deliverOperations(DestinationClient.All);
|
|
786
|
+
const id = compressor.getIdsFromRange(range, sessionId).get(0);
|
|
787
|
+
// Glass box test, as it knows the order of final IDs
|
|
788
|
+
expect(id).to.equal(compressor.reservedIdCount + compressor.clusterCapacity);
|
|
789
|
+
});
|
|
790
|
+
itNetwork('can set the cluster size via API', 2, (network) => {
|
|
791
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
792
|
+
const initialClusterCapacity = compressor.clusterCapacity;
|
|
793
|
+
network.allocateAndSendIds(Client.Client1, initialClusterCapacity);
|
|
794
|
+
network.allocateAndSendIds(Client.Client2, initialClusterCapacity);
|
|
795
|
+
network.enqueueCapacityChange(5);
|
|
796
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
797
|
+
const { range, sessionId } = network.allocateAndSendIds(Client.Client2, 1);
|
|
798
|
+
network.deliverOperations(DestinationClient.All);
|
|
799
|
+
const id = compressor.getIdsFromRange(range, sessionId).get(0);
|
|
800
|
+
// Glass box test, as it knows the order of final IDs
|
|
801
|
+
expect(id).to.equal(compressor.reservedIdCount + initialClusterCapacity * 2 + compressor.clusterCapacity);
|
|
802
|
+
});
|
|
803
|
+
describe('can get IDs from ranges', () => {
|
|
804
|
+
itNetwork('unless they are unfinalized and from a remote session', (network) => {
|
|
805
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
806
|
+
const { range: unackedRemoteRange, sessionId } = network.allocateAndSendIds(Client.Client2, 5);
|
|
807
|
+
expect(() => compressor.getIdsFromRange(unackedRemoteRange, sessionId)).to.throw('Unknown session, range may not be finalized.');
|
|
808
|
+
network.deliverOperations(Client.Client1);
|
|
809
|
+
const { range: unackedRemoteRange2, sessionId: sessionId2 } = network.allocateAndSendIds(Client.Client2, 5);
|
|
810
|
+
expect(() => compressor.getIdsFromRange(unackedRemoteRange2, sessionId2)).to.throw('Remote range must be finalized before getting IDs.');
|
|
811
|
+
});
|
|
812
|
+
itNetwork('that are unacked', (network) => {
|
|
813
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
814
|
+
const { range: rangeDescriptor1, sessionId: sessionId1 } = network.allocateAndSendIds(Client.Client1, 5);
|
|
815
|
+
const range1 = compressor.getIdsFromRange(rangeDescriptor1, sessionId1);
|
|
816
|
+
for (let i = 0; i < range1.length; i++) {
|
|
817
|
+
expect(range1.get(i)).to.equal(-(i + 1));
|
|
818
|
+
}
|
|
819
|
+
const { range: rangeDescriptor2, sessionId: sessionId2 } = network.allocateAndSendIds(Client.Client1, 7);
|
|
820
|
+
const range2 = compressor.getIdsFromRange(rangeDescriptor2, sessionId2);
|
|
821
|
+
for (let i = 0; i < range2.length; i++) {
|
|
822
|
+
expect(range2.get(i)).to.equal(-(i + 1 + range1.length));
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
itNetwork('from the local session that are acked', (network) => {
|
|
826
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
827
|
+
const { range, sessionId } = network.allocateAndSendIds(Client.Client1, 5);
|
|
828
|
+
const range1 = compressor.getIdsFromRange(range, sessionId);
|
|
829
|
+
network.deliverOperations(Client.Client1);
|
|
830
|
+
for (let i = 0; i < range1.length; i++) {
|
|
831
|
+
expect(range1.get(i)).to.equal(-(i + 1));
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
itNetwork('from a remote session that are in a single cluster', 5, (network) => {
|
|
835
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
836
|
+
const clusterCapacity = compressor.clusterCapacity;
|
|
837
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
838
|
+
// Spans an entire cluster
|
|
839
|
+
const { range: rangeFullCluster, sessionId: sessionId2 } = network.allocateAndSendIds(Client.Client2, clusterCapacity);
|
|
840
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
841
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
842
|
+
// Spans the middle 3 IDs in a cluster of size 5
|
|
843
|
+
const { range: rangeMiddleCluster } = network.allocateAndSendIds(Client.Client2, clusterCapacity - 2);
|
|
844
|
+
network.deliverOperations(Client.Client1);
|
|
845
|
+
const idsFullCluster = compressor.getIdsFromRange(rangeFullCluster, sessionId2);
|
|
846
|
+
for (let i = 0; i < idsFullCluster.length; i++) {
|
|
847
|
+
expect(idsFullCluster.get(i)).to.equal(compressor.reservedIdCount + clusterCapacity + i);
|
|
848
|
+
}
|
|
849
|
+
const idsMiddleCluster = compressor.getIdsFromRange(rangeMiddleCluster, sessionId2);
|
|
850
|
+
for (let i = 0; i < idsMiddleCluster.length; i++) {
|
|
851
|
+
expect(idsMiddleCluster.get(i)).to.equal(compressor.reservedIdCount + clusterCapacity * 2 + 1 + i);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
itNetwork('from a remote session that span multiple clusters', 5, (network) => {
|
|
855
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
856
|
+
const clusterCapacity = compressor.clusterCapacity;
|
|
857
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity);
|
|
858
|
+
network.allocateAndSendIds(Client.Client2, clusterCapacity - 2);
|
|
859
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
860
|
+
const { range: rangeSpanningClusters, sessionId } = network.allocateAndSendIds(Client.Client2, clusterCapacity);
|
|
861
|
+
network.deliverOperations(Client.Client1);
|
|
862
|
+
const idsSpanningClusters = compressor.getIdsFromRange(rangeSpanningClusters, sessionId);
|
|
863
|
+
for (let i = 0; i < 2; i++) {
|
|
864
|
+
expect(idsSpanningClusters.get(i)).to.equal(compressor.reservedIdCount + clusterCapacity + 3 + i);
|
|
865
|
+
}
|
|
866
|
+
for (let i = 2; i < idsSpanningClusters.length; i++) {
|
|
867
|
+
expect(idsSpanningClusters.get(i)).to.equal(compressor.reservedIdCount + clusterCapacity * 2 + 3 + i);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
itNetwork('does not decompress ids for empty parts of clusters', 2, (network) => {
|
|
872
|
+
// This is a glass box test in that it creates a final ID outside of the ID compressor
|
|
873
|
+
network.allocateAndSendIds(Client.Client1, 1);
|
|
874
|
+
network.deliverOperations(DestinationClient.All);
|
|
875
|
+
const id = network.getSequencedIdLog(Client.Client2)[0].id;
|
|
876
|
+
expect(isFinalId(id)).to.be.true;
|
|
877
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
878
|
+
const emptyId = (id + 1);
|
|
879
|
+
expect(() => network.getCompressor(Client.Client2).decompress(emptyId)).to.throw('Compressed ID was not generated by this compressor');
|
|
880
|
+
});
|
|
881
|
+
describe('Finalizing', () => {
|
|
882
|
+
itNetwork('can finalize IDs from multiple clients', (network) => {
|
|
883
|
+
network.allocateAndSendIds(Client.Client1, 3, {
|
|
884
|
+
1: 'override1',
|
|
885
|
+
});
|
|
886
|
+
network.allocateAndSendIds(Client.Client2, 3, {
|
|
887
|
+
1: 'override2',
|
|
888
|
+
});
|
|
889
|
+
expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
|
|
890
|
+
});
|
|
891
|
+
itNetwork('can finalize a range when the current cluster is full', 5, (network) => {
|
|
892
|
+
const clusterCapacity = network.getCompressor(Client.Client1).clusterCapacity;
|
|
893
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity);
|
|
894
|
+
network.allocateAndSendIds(Client.Client2, clusterCapacity);
|
|
895
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity, {
|
|
896
|
+
0: 'override1',
|
|
897
|
+
1: 'override2',
|
|
898
|
+
2: 'override3',
|
|
899
|
+
});
|
|
900
|
+
expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
|
|
901
|
+
});
|
|
902
|
+
itNetwork('can finalize a range that spans multiple clusters', 5, (network) => {
|
|
903
|
+
const clusterCapacity = network.getCompressor(Client.Client1).clusterCapacity;
|
|
904
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity - 2, {
|
|
905
|
+
0: 'override1',
|
|
906
|
+
1: 'override2',
|
|
907
|
+
});
|
|
908
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
909
|
+
network.allocateAndSendIds(Client.Client1, clusterCapacity, {
|
|
910
|
+
0: 'override3',
|
|
911
|
+
1: 'override4',
|
|
912
|
+
2: 'override5',
|
|
913
|
+
});
|
|
914
|
+
expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
|
|
915
|
+
});
|
|
916
|
+
});
|
|
917
|
+
describe('Serialization', () => {
|
|
918
|
+
itNetwork('prevents attempts to resume a session from a serialized compressor with no session', (network) => {
|
|
919
|
+
const compressor = network.getCompressor(Client.Client1);
|
|
920
|
+
network.allocateAndSendIds(Client.Client2, 1);
|
|
921
|
+
network.allocateAndSendIds(Client.Client3, 1);
|
|
922
|
+
network.deliverOperations(Client.Client1);
|
|
923
|
+
const serializedWithoutLocalState = compressor.serialize(false);
|
|
924
|
+
expect(() => IdCompressor.deserialize(serializedWithoutLocalState, sessionIds.get(Client.Client2))).to.throw('Cannot resume existing session.');
|
|
925
|
+
});
|
|
926
|
+
itNetwork('round-trips local state', 3, (network) => {
|
|
927
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
928
|
+
network.allocateAndSendIds(Client.Client2, 3);
|
|
929
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
930
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
931
|
+
network.allocateAndSendIds(Client.Client3, 3);
|
|
932
|
+
network.allocateAndSendIds(Client.Client2, 3);
|
|
933
|
+
network.deliverOperations(Client.Client1);
|
|
934
|
+
// Some un-acked locals at the end
|
|
935
|
+
network.allocateAndSendIds(Client.Client1, 4);
|
|
936
|
+
const [serializedNoSession, serializedWithSession] = expectSerializes(network.getCompressor(Client.Client1));
|
|
937
|
+
expect(hasOngoingSession(serializedWithSession)).to.be.true;
|
|
938
|
+
expect(hasOngoingSession(serializedNoSession)).to.be.false;
|
|
939
|
+
});
|
|
940
|
+
itNetwork('can serialize a partially empty cluster', 5, (network) => {
|
|
941
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
942
|
+
network.deliverOperations(DestinationClient.All);
|
|
943
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
944
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
945
|
+
});
|
|
946
|
+
itNetwork('can serialize a full cluster', 2, (network) => {
|
|
947
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
948
|
+
network.deliverOperations(DestinationClient.All);
|
|
949
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
950
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
951
|
+
});
|
|
952
|
+
itNetwork('can serialize full clusters from different clients', 2, (network) => {
|
|
953
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
954
|
+
network.allocateAndSendIds(Client.Client2, 2);
|
|
955
|
+
network.deliverOperations(DestinationClient.All);
|
|
956
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
957
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
958
|
+
});
|
|
959
|
+
itNetwork('can serialize clusters of different sizes and clients', 3, (network) => {
|
|
960
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
961
|
+
network.allocateAndSendIds(Client.Client2, 3);
|
|
962
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
963
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
964
|
+
network.allocateAndSendIds(Client.Client2, 3);
|
|
965
|
+
network.deliverOperations(DestinationClient.All);
|
|
966
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
967
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
968
|
+
});
|
|
969
|
+
itNetwork('can serialize clusters with overrides', 3, (network) => {
|
|
970
|
+
network.allocateAndSendIds(Client.Client1, 2, {
|
|
971
|
+
1: 'override',
|
|
972
|
+
});
|
|
973
|
+
network.allocateAndSendIds(Client.Client2, 3, {
|
|
974
|
+
0: 'override1',
|
|
975
|
+
2: 'override2',
|
|
976
|
+
});
|
|
977
|
+
network.deliverOperations(DestinationClient.All);
|
|
978
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
979
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
980
|
+
});
|
|
981
|
+
itNetwork('packs IDs into a single cluster when a single client generates non-overridden ids', 3, (network) => {
|
|
982
|
+
network.allocateAndSendIds(Client.Client1, 20);
|
|
983
|
+
network.deliverOperations(DestinationClient.All);
|
|
984
|
+
const [serialized1WithNoSession, serialized1WithSession] = expectSerializes(network.getCompressor(Client.Client1));
|
|
985
|
+
expect(serialized1WithNoSession.clusters.length).to.equal(1);
|
|
986
|
+
expect(serialized1WithSession.clusters.length).to.equal(1);
|
|
987
|
+
const [serialized3WithNoSession, serialized3WithSession] = expectSerializes(network.getCompressor(Client.Client3));
|
|
988
|
+
expect(serialized3WithNoSession.clusters.length).to.equal(1);
|
|
989
|
+
expect(serialized3WithSession.clusters.length).to.equal(1);
|
|
990
|
+
});
|
|
991
|
+
itNetwork('serializes correctly after unifying duplicate overrides', 3, (network) => {
|
|
992
|
+
const override = 'override';
|
|
993
|
+
network.allocateAndSendIds(Client.Client1, 1, { 0: override });
|
|
994
|
+
network.allocateAndSendIds(Client.Client2, 2, { 1: override });
|
|
995
|
+
network.allocateAndSendIds(Client.Client1, 5);
|
|
996
|
+
network.allocateAndSendIds(Client.Client2, 5);
|
|
997
|
+
network.deliverOperations(DestinationClient.All);
|
|
998
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
999
|
+
expectSerializes(network.getCompressor(Client.Client2));
|
|
1000
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
1001
|
+
});
|
|
1002
|
+
itNetwork('can resume a session and interact with multiple other clients', 3, (network) => {
|
|
1003
|
+
const clusterSize = network.getCompressor(Client.Client1).clusterCapacity;
|
|
1004
|
+
network.allocateAndSendIds(Client.Client1, clusterSize);
|
|
1005
|
+
network.allocateAndSendIds(Client.Client2, clusterSize);
|
|
1006
|
+
network.allocateAndSendIds(Client.Client3, clusterSize);
|
|
1007
|
+
network.allocateAndSendIds(Client.Client1, clusterSize);
|
|
1008
|
+
network.allocateAndSendIds(Client.Client2, clusterSize);
|
|
1009
|
+
network.allocateAndSendIds(Client.Client3, clusterSize);
|
|
1010
|
+
network.deliverOperations(DestinationClient.All);
|
|
1011
|
+
network.goOfflineThenResume(Client.Client1);
|
|
1012
|
+
network.allocateAndSendIds(Client.Client1, 2);
|
|
1013
|
+
network.allocateAndSendIds(Client.Client2, 2);
|
|
1014
|
+
network.allocateAndSendIds(Client.Client3, 2);
|
|
1015
|
+
expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
|
|
1016
|
+
});
|
|
1017
|
+
itNetwork('can serialize after a large fuzz input', 3, (network) => {
|
|
1018
|
+
performFuzzActions(network, Math.PI, true, undefined, true, 1000, 25, (network) => {
|
|
1019
|
+
// Periodically check that everyone in the network has the same serialized state
|
|
1020
|
+
network.deliverOperations(DestinationClient.All);
|
|
1021
|
+
const compressors = network.getTargetCompressors(DestinationClient.All);
|
|
1022
|
+
let deserializedPrev = roundtrip(compressors[0][1], false)[1];
|
|
1023
|
+
for (let i = 1; i < compressors.length; i++) {
|
|
1024
|
+
const deserializedCur = roundtrip(compressors[i][1], false)[1];
|
|
1025
|
+
expect(deserializedPrev.equals(deserializedCur, false)).to.be.true;
|
|
1026
|
+
deserializedPrev = deserializedCur;
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
expectSerializes(network.getCompressor(Client.Client1));
|
|
1030
|
+
expectSerializes(network.getCompressor(Client.Client2));
|
|
1031
|
+
expectSerializes(network.getCompressor(Client.Client3));
|
|
1032
|
+
});
|
|
1033
|
+
itNetwork('stores override indices relative to their clusters', 3, (network) => {
|
|
1034
|
+
var _a, _b;
|
|
1035
|
+
network.allocateAndSendIds(Client.Client1, 3, { 0: 'cluster1' });
|
|
1036
|
+
network.allocateAndSendIds(Client.Client2, 3, { 0: 'cluster2' });
|
|
1037
|
+
network.deliverOperations(Client.Client1);
|
|
1038
|
+
const serialized = network.getCompressor(Client.Client1).serialize(false);
|
|
1039
|
+
expect(serialized.clusters.length).to.equal(2);
|
|
1040
|
+
expect((_a = serialized.clusters[0][2]) === null || _a === void 0 ? void 0 : _a[0][0]).to.equal(0);
|
|
1041
|
+
expect((_b = serialized.clusters[1][2]) === null || _b === void 0 ? void 0 : _b[0][0]).to.equal(0);
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
});
|
|
1046
|
+
function createNetworkTestFunction(validateAfter) {
|
|
1047
|
+
return (title, testOrCapacity, test) => {
|
|
1048
|
+
it(title, () => {
|
|
1049
|
+
const hasCapacity = typeof testOrCapacity === 'number';
|
|
1050
|
+
const capacity = hasCapacity ? testOrCapacity : undefined;
|
|
1051
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1052
|
+
const network = new IdCompressorTestNetwork(capacity);
|
|
1053
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1054
|
+
(hasCapacity ? assertNotUndefined(test) : testOrCapacity)(network);
|
|
1055
|
+
if (validateAfter) {
|
|
1056
|
+
network.deliverOperations(DestinationClient.All);
|
|
1057
|
+
network.assertNetworkState();
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function describeNetwork(title, its) {
|
|
1063
|
+
describe(title, () => {
|
|
1064
|
+
its(createNetworkTestFunction(false));
|
|
1065
|
+
});
|
|
1066
|
+
describe(`${title} (with validation)`, () => {
|
|
1067
|
+
its(createNetworkTestFunction(true));
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
function describeNetworkNoValidation(title, its) {
|
|
1071
|
+
describe(title, () => {
|
|
1072
|
+
its(createNetworkTestFunction(false));
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
//# sourceMappingURL=IdCompressor.tests.js.map
|