@fluid-experimental/tree 0.58.2001 → 0.59.1000-61898
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 +9 -9
- 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 -100
- 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 +59 -31
- 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 +130 -85
- package/dist/LogViewer.d.ts.map +1 -1
- package/dist/LogViewer.js +110 -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 +90 -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 +400 -0
- package/dist/SharedTree.d.ts.map +1 -0
- package/dist/SharedTree.js +1074 -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 +53 -0
- package/dist/Transaction.d.ts.map +1 -0
- package/dist/Transaction.js +76 -0
- package/dist/Transaction.js.map +1 -0
- package/dist/TransactionInternal.d.ts +543 -0
- package/dist/TransactionInternal.d.ts.map +1 -0
- package/dist/TransactionInternal.js +622 -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/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 -100
- 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 +51 -23
- 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 +130 -85
- package/lib/LogViewer.d.ts.map +1 -1
- package/lib/LogViewer.js +102 -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 +90 -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 +400 -0
- package/lib/SharedTree.d.ts.map +1 -0
- package/lib/SharedTree.js +1069 -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 +53 -0
- package/lib/Transaction.d.ts.map +1 -0
- package/lib/Transaction.js +72 -0
- package/lib/Transaction.js.map +1 -0
- package/lib/TransactionInternal.d.ts +543 -0
- package/lib/TransactionInternal.d.ts.map +1 -0
- package/lib/TransactionInternal.js +618 -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 +269 -187
- 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 +147 -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 -120
- package/src/Checkout.ts +81 -52
- 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 +141 -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 +1458 -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 +94 -0
- package/src/TransactionInternal.ts +1088 -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 +119 -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/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,1353 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.hasOngoingSession = exports.IdCompressor = exports.isLocalId = exports.isFinalId = exports.legacySharedTreeInitialTreeId = exports.defaultClusterCapacity = void 0;
|
|
11
|
+
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
|
12
|
+
const sorted_btree_1 = __importDefault(require("sorted-btree"));
|
|
13
|
+
const Common_1 = require("../Common");
|
|
14
|
+
const AppendOnlySortedMap_1 = require("./AppendOnlySortedMap");
|
|
15
|
+
const IdRange_1 = require("./IdRange");
|
|
16
|
+
const NumericUuid_1 = require("./NumericUuid");
|
|
17
|
+
/**
|
|
18
|
+
* Roughly equates to a minimum of 1M sessions before we start allocating 64 bit IDs.
|
|
19
|
+
* This value must *NOT* change without careful consideration to compatibility.
|
|
20
|
+
*/
|
|
21
|
+
exports.defaultClusterCapacity = 512;
|
|
22
|
+
/**
|
|
23
|
+
* The base UUID for the reserved id cluster.
|
|
24
|
+
* This should not be changed without consideration to compatibility.
|
|
25
|
+
*/
|
|
26
|
+
const reservedSessionId = NumericUuid_1.ensureSessionUuid(NumericUuid_1.assertIsStableId('decaf40b-3c1a-47f8-a7a1-e8461ddb69ce'));
|
|
27
|
+
/**
|
|
28
|
+
* The ID override for the initial tree of a SharedTree. An artifact of an unfortunate typo which included an extraneous '6' on the UUID
|
|
29
|
+
* which must be forever preserved for backwards compatibility.
|
|
30
|
+
*/
|
|
31
|
+
exports.legacySharedTreeInitialTreeId = `24e26f0b-3c1a-47f8-a7a1-e8461ddb69ce6`;
|
|
32
|
+
/**
|
|
33
|
+
* @returns true if the supplied ID is a final ID.
|
|
34
|
+
*/
|
|
35
|
+
function isFinalId(id) {
|
|
36
|
+
return id >= 0;
|
|
37
|
+
}
|
|
38
|
+
exports.isFinalId = isFinalId;
|
|
39
|
+
/**
|
|
40
|
+
* @returns true if the supplied ID is a local ID.
|
|
41
|
+
*/
|
|
42
|
+
function isLocalId(id) {
|
|
43
|
+
return id < 0;
|
|
44
|
+
}
|
|
45
|
+
exports.isLocalId = isLocalId;
|
|
46
|
+
/** Prepended to all keys in {@link IdCompressor.clustersAndOverridesInversion} that are override strings and not valid `StableIds` */
|
|
47
|
+
const nonStableOverridePrefix = '\ue15e'; // A character in the Private Use Area of the BMP (https://en.wikipedia.org/wiki/Private_Use_Areas)
|
|
48
|
+
/**
|
|
49
|
+
* A distributed UUID generator and compressor.
|
|
50
|
+
*
|
|
51
|
+
* Generates arbitrary non-colliding v4 UUIDs, called stable IDs, for multiple "sessions" (which can be distributed across the network),
|
|
52
|
+
* providing each session with the ability to map these UUIDs to `numbers`.
|
|
53
|
+
*
|
|
54
|
+
* A session is a unique identifier that denotes a single compressor. New IDs are created through a single compressor API
|
|
55
|
+
* which should then sent in ranges to the server for total ordering (and are subsequently relayed to other clients). When a new ID is
|
|
56
|
+
* created it is said to be created by the compressor's "local" session.
|
|
57
|
+
*
|
|
58
|
+
* For each stable ID created, two numeric IDs are provided by the compressor:
|
|
59
|
+
* 1. A local ID, which is stable for the lifetime of the session (which could be longer than that of the compressor object, as it may
|
|
60
|
+
* be serialized for offline usage). Available as soon as the stable ID is allocated. Local IDs are session-unique and are thus only
|
|
61
|
+
* publicly usable by the compressor that created the stable ID.
|
|
62
|
+
* 2. A final ID, which is stable across serialization and deserialization of an IdCompressor. Available as soon as the range containing
|
|
63
|
+
* the corresponding local ID is totally ordered (via consensus) with respect to other sessions' allocations.
|
|
64
|
+
* Final IDs are known to and publicly usable by any compressor that has received them.
|
|
65
|
+
*
|
|
66
|
+
* Compressors will allocate UUIDs in non-random ways to reduce entropy allowing for optimized storage of the data needed
|
|
67
|
+
* to map the UUIDs to the numbers.
|
|
68
|
+
*
|
|
69
|
+
* A client may optionally supply an "override" for any generated ID, associating an arbitrary string with the local/final ID rather than
|
|
70
|
+
* the UUID that would otherwise be created.
|
|
71
|
+
*
|
|
72
|
+
* The following invariants are upheld by IdCompressor:
|
|
73
|
+
* 1. Local IDs will always decompress to the same UUIDs (or override string) for the lifetime of the session.
|
|
74
|
+
* 2. Final IDs will always decompress to the same UUIDs (or override string).
|
|
75
|
+
* 3. After a server-processed range of local IDs (from any session) is received by a compressor, any of those local IDs may be
|
|
76
|
+
* translated by the compressor into the corresponding final ID. For any given local ID, this translation will always yield the
|
|
77
|
+
* same final ID.
|
|
78
|
+
* 4. A UUID (or override string) will always compress into the same session-space ID for the lifetime of the session.
|
|
79
|
+
*
|
|
80
|
+
* Local IDs are sent across the wire in efficiently-represented ranges. These ranges are created by querying the compressor, and *must*
|
|
81
|
+
* be ordered (i.e. sent to the server) in the order they are created in order to preserve the above invariants.
|
|
82
|
+
*
|
|
83
|
+
* Session-local IDs can be used immediately after creation, but will eventually (after being sequenced) have a corresponding final ID. This
|
|
84
|
+
* could make reasoning about equality of those two forms (the local and final) difficult. For example, if a cache is keyed off of a
|
|
85
|
+
* local ID but is later queried using the final ID (which is semantically equal, as it decompresses to the same UUID/string) it will
|
|
86
|
+
* produce a cache miss. In order to make using collections of both remotely created and locally created IDs easy, regardless of whether the
|
|
87
|
+
* session-local IDs have been finalized, the compressor defines two "spaces" of IDs:
|
|
88
|
+
*
|
|
89
|
+
* 1. Session space: in this space, all IDs are normalized to their "most local form". This means that all IDs created by the local session
|
|
90
|
+
* will be in local form, regardless of if they have been finalized. Remotely created IDs, which could only have been received after
|
|
91
|
+
* finalizing and will never have a local form for the compressor, will of course be final IDs. This space should be used with consumer APIs
|
|
92
|
+
* and data structures, as the lifetime of the IDs is guaranteed to be the same as the compressor object. Care must be taken to not use
|
|
93
|
+
* these IDs across compressor objects, as the local IDs are specific to the compressor that created them.
|
|
94
|
+
*
|
|
95
|
+
* 2. Op space: in this space, all IDs are normalized to their "most final form". This means that all IDs except session-local IDs that
|
|
96
|
+
* have not yet been finalized will be in final ID form. This space is useful for serialization in ops (e.g. references), as other clients
|
|
97
|
+
* that receive them need not do any work to normalize them to *their* session-space in the common case. Note that IDs in op space may move
|
|
98
|
+
* out of Op space over time, namely, when a local ID in this space becomes finalized, and thereafter has a "more final form".
|
|
99
|
+
* Consequentially, it may be useful to restrict parameters of a persisted type to this space (to optimize perf), but it is potentially
|
|
100
|
+
* incorrect to use this type for a runtime variable. This is an asymmetry that does not affect session space, as local IDs are always as
|
|
101
|
+
* "local as possible".
|
|
102
|
+
*
|
|
103
|
+
* These two spaces naturally define a rule: consumers of compressed IDs should use session-space IDs, but serialized forms such as ops
|
|
104
|
+
* should use op-space IDs.
|
|
105
|
+
*
|
|
106
|
+
*/
|
|
107
|
+
class IdCompressor {
|
|
108
|
+
/**
|
|
109
|
+
* @param localSessionId the `IdCompressor`'s current local session ID.
|
|
110
|
+
* @param reservedIdCount the number of IDs that will be known by this compressor without relying on consensus. The reserved ID count
|
|
111
|
+
* for a given session must be constant for any compressor that contains IDs from that session (i.e. any DDS that uses the ID
|
|
112
|
+
* compressor must have the same reservedIdCount forever). Compressors with different reserved ID counts will fail to synchronize their
|
|
113
|
+
* IDs.
|
|
114
|
+
* @param attributionInfo information used by other clients to attribute IDs made by this client
|
|
115
|
+
*/
|
|
116
|
+
constructor(localSessionId, reservedIdCount, attributionInfo) {
|
|
117
|
+
this.localSessionId = localSessionId;
|
|
118
|
+
this.reservedIdCount = reservedIdCount;
|
|
119
|
+
/**
|
|
120
|
+
* Trivially reach consensus on default cluster size and reserved IDs.
|
|
121
|
+
* These initial values must *NOT* change without careful consideration to compatibility.
|
|
122
|
+
*/
|
|
123
|
+
this.newClusterCapacity = exports.defaultClusterCapacity;
|
|
124
|
+
/**
|
|
125
|
+
* Session ID -> data about the session's current cluster.
|
|
126
|
+
* Sessions are mutable, and thus should only be created via `createSession`.
|
|
127
|
+
*/
|
|
128
|
+
this.sessions = new Map();
|
|
129
|
+
/**
|
|
130
|
+
* Boolean to track whether attribution has been sent with an ID range yet. Prevents unnecessary bloat of ranges.
|
|
131
|
+
*/
|
|
132
|
+
this.sentAttributionInfo = false;
|
|
133
|
+
/**
|
|
134
|
+
* The base final ID of the next cluster to be created.
|
|
135
|
+
*/
|
|
136
|
+
this.nextClusterBaseFinalId = 0;
|
|
137
|
+
/**
|
|
138
|
+
* Total number of IDs created locally during the current session.
|
|
139
|
+
*/
|
|
140
|
+
this.localIdCount = 0;
|
|
141
|
+
/**
|
|
142
|
+
* Maps local IDs to override strings. This will contain an entry for every override assigned to a local ID generated during
|
|
143
|
+
* the current session, and retains entries for the lifetime of this compressor.
|
|
144
|
+
*/
|
|
145
|
+
this.localOverrides = new AppendOnlySortedMap_1.AppendOnlySortedMap(compareFiniteNumbersReversed);
|
|
146
|
+
/**
|
|
147
|
+
* Maps local IDs to the cluster they belong to (if any). This can be used to efficiently convert a local ID to a
|
|
148
|
+
* final ID by finding an entry <= a given local ID (to find the cluster it is associated with) and checking
|
|
149
|
+
* it against `numFinalizedLocalIds`.
|
|
150
|
+
*/
|
|
151
|
+
this.localIdToCluster = new AppendOnlySortedMap_1.AppendOnlyDoublySortedMap(compareFiniteNumbersReversed, (value) => value[0], IdCompressor.overrideComparator);
|
|
152
|
+
/**
|
|
153
|
+
* Contains entries for cluster base UUIDs and override strings (both local and final).
|
|
154
|
+
* As a performance optimization, entries for finalized strings also include the containing cluster object.
|
|
155
|
+
* This can be viewed as three separate tables: the inverse table for `localOverrides`, the inverse table for the union of all
|
|
156
|
+
* the overrides of the clusters in `finalIdToCluster`, and the inverse lookup of cluster base UUIDs to their clusters.
|
|
157
|
+
* This is unified as a performance optimization, as the common case does not have overridden IDs. It is a btree due to the need
|
|
158
|
+
* to make range queries.
|
|
159
|
+
*/
|
|
160
|
+
this.clustersAndOverridesInversion = new sorted_btree_1.default(undefined, Common_1.compareStrings);
|
|
161
|
+
/**
|
|
162
|
+
* Maps the first final ID in a cluster to its owning cluster.
|
|
163
|
+
* Can be searched in O(log n) to determine clusters for any final ID.
|
|
164
|
+
*/
|
|
165
|
+
this.finalIdToCluster = new AppendOnlySortedMap_1.AppendOnlySortedMap(Common_1.compareFiniteNumbers);
|
|
166
|
+
Common_1.assert(reservedIdCount >= 0, 'reservedIdCount must be non-negative');
|
|
167
|
+
this.localSession = this.createSession(localSessionId, attributionInfo);
|
|
168
|
+
if (reservedIdCount > 0) {
|
|
169
|
+
const clusterCapacity = this.clusterCapacity;
|
|
170
|
+
this.clusterCapacity = reservedIdCount;
|
|
171
|
+
const reservedIdRange = {
|
|
172
|
+
sessionId: reservedSessionId,
|
|
173
|
+
ids: {
|
|
174
|
+
last: -reservedIdCount,
|
|
175
|
+
overrides: [[-1, exports.legacySharedTreeInitialTreeId]],
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
// Reserved final IDs are implicitly finalized and no one locally created them, so finalizing immediately is safe.
|
|
179
|
+
this.finalizeCreationRange(reservedIdRange);
|
|
180
|
+
this.clusterCapacity = clusterCapacity;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The size of each newly created ID cluster.
|
|
185
|
+
*/
|
|
186
|
+
get clusterCapacity() {
|
|
187
|
+
return this.newClusterCapacity;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Must only be set with a value upon which consensus has been reached. Value must be greater than zero and less than
|
|
191
|
+
* `IdCompressor.maxClusterSize`.
|
|
192
|
+
*/
|
|
193
|
+
set clusterCapacity(value) {
|
|
194
|
+
Common_1.assert(value > 0, 'Clusters must have a positive capacity');
|
|
195
|
+
Common_1.assert(value <= IdCompressor.maxClusterSize, 'Clusters must not exceed max cluster size');
|
|
196
|
+
this.newClusterCapacity = value;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Helper comparator for searching append-only sorted maps.
|
|
200
|
+
*/
|
|
201
|
+
static overrideComparator(search, element) {
|
|
202
|
+
return Common_1.compareFiniteNumbers(search, element[0]);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Creates a session object for the supplied ID.
|
|
206
|
+
* Must only be called once per ID.
|
|
207
|
+
* @param sessionId the ID for the session
|
|
208
|
+
* @returns the session object for the supplied ID
|
|
209
|
+
*/
|
|
210
|
+
createSession(sessionId, attributionInfo) {
|
|
211
|
+
const existingSession = this.sessions.get(sessionId);
|
|
212
|
+
if (existingSession !== undefined) {
|
|
213
|
+
Common_1.fail('createSession must only be called once for each session ID.');
|
|
214
|
+
}
|
|
215
|
+
const sessionUuid = NumericUuid_1.numericUuidFromStableId(sessionId);
|
|
216
|
+
Common_1.assert(!this.clustersAndOverridesInversion.has(sessionId));
|
|
217
|
+
const session = {
|
|
218
|
+
sessionUuid,
|
|
219
|
+
currentClusterDetails: undefined,
|
|
220
|
+
lastFinalizedLocalId: undefined,
|
|
221
|
+
};
|
|
222
|
+
Common_1.setPropertyIfDefined(attributionInfo, session, 'attributionInfo');
|
|
223
|
+
this.sessions.set(sessionId, session);
|
|
224
|
+
return session;
|
|
225
|
+
}
|
|
226
|
+
tryGetSession(sessionId) {
|
|
227
|
+
return this.sessions.get(sessionId);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Return the nth reserved ID.
|
|
231
|
+
* @param index the index of the ID to return
|
|
232
|
+
*/
|
|
233
|
+
getReservedId(index) {
|
|
234
|
+
if (index < 0 || index >= this.reservedIdCount) {
|
|
235
|
+
Common_1.fail('Reserved Id index out of bounds');
|
|
236
|
+
}
|
|
237
|
+
// All reserved IDs are contiguous and finalized during the Compressor's construction, therefore they are always the lowest
|
|
238
|
+
// final IDs, beginning at 0
|
|
239
|
+
return index;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Returns an iterable of all IDs created by this compressor.
|
|
243
|
+
*/
|
|
244
|
+
*getAllIdsFromLocalSession() {
|
|
245
|
+
// TODO: this will change when final IDs are returned eagerly
|
|
246
|
+
for (let i = 1; i <= this.localIdCount; i++) {
|
|
247
|
+
yield -i;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Returns the attribution info associated with the compressor that created the ID, if it exists.
|
|
252
|
+
*/
|
|
253
|
+
attributeId(id) {
|
|
254
|
+
var _a;
|
|
255
|
+
const opSpaceNormalizedId = this.normalizeToOpSpace(id);
|
|
256
|
+
if (isLocalId(opSpaceNormalizedId)) {
|
|
257
|
+
return this.localSession.attributionInfo;
|
|
258
|
+
}
|
|
259
|
+
const [_, cluster] = (_a = this.getClusterForFinalId(opSpaceNormalizedId)) !== null && _a !== void 0 ? _a : Common_1.fail('Cluster does not exist for final ID');
|
|
260
|
+
return cluster.session.attributionInfo;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Provides the session-space IDs corresponding to a range of IDs.
|
|
264
|
+
* See `IdRange` for more details.
|
|
265
|
+
*/
|
|
266
|
+
getIdsFromRange(rangeDescriptor, sessionId) {
|
|
267
|
+
var _a, _b, _c;
|
|
268
|
+
const { first, count } = rangeDescriptor;
|
|
269
|
+
if (sessionId === this.localSessionId) {
|
|
270
|
+
return {
|
|
271
|
+
length: count,
|
|
272
|
+
get: (index) => {
|
|
273
|
+
if (index < 0 || index >= count) {
|
|
274
|
+
Common_1.fail('Index out of bounds of range.');
|
|
275
|
+
}
|
|
276
|
+
return (first - index);
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
const session = (_a = this.tryGetSession(sessionId)) !== null && _a !== void 0 ? _a : Common_1.fail('Unknown session, range may not be finalized.');
|
|
282
|
+
const firstNumericUuid = NumericUuid_1.incrementUuid(session.sessionUuid, -first - 1);
|
|
283
|
+
const firstFinal = (_b = this.compressNumericUuid(firstNumericUuid)) !== null && _b !== void 0 ? _b : Common_1.fail('Remote range must be finalized before getting IDs.');
|
|
284
|
+
Common_1.assert(isFinalId(firstFinal), 'ID from a remote session ID must have final form, as overrides are impossible by definition.');
|
|
285
|
+
const [baseFinalId, cluster] = (_c = this.getClusterForFinalId(firstFinal)) !== null && _c !== void 0 ? _c : Common_1.fail();
|
|
286
|
+
const numIdsRemainingInFirstCluster = cluster.capacity - (firstFinal - baseFinalId);
|
|
287
|
+
let pivotFinal;
|
|
288
|
+
if (count > numIdsRemainingInFirstCluster) {
|
|
289
|
+
const compressedPivot = this.compressNumericUuid(NumericUuid_1.incrementUuid(firstNumericUuid, numIdsRemainingInFirstCluster));
|
|
290
|
+
// Looking up the actual cluster can be avoided, as it is guaranteed that at most one new cluster will be
|
|
291
|
+
// created when finalizing a range (regardless of size) due to the expansion optimization.
|
|
292
|
+
if (compressedPivot === undefined || isLocalId(compressedPivot)) {
|
|
293
|
+
Common_1.fail('ID from a remote session ID must have final form, as overrides are impossible by definition.');
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
pivotFinal = compressedPivot;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
length: count,
|
|
301
|
+
get: (index) => {
|
|
302
|
+
if (index < 0 || index >= count) {
|
|
303
|
+
Common_1.fail('Index out of bounds of range.');
|
|
304
|
+
}
|
|
305
|
+
if (index < numIdsRemainingInFirstCluster) {
|
|
306
|
+
return (firstFinal + index);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
return ((pivotFinal !== null && pivotFinal !== void 0 ? pivotFinal : Common_1.fail('Pivot must exist if range spans clusters.')) +
|
|
310
|
+
(index - numIdsRemainingInFirstCluster));
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Returns a range of local IDs created by this session in a format for sending to the server for finalizing.
|
|
318
|
+
* The range will include all local IDs generated via calls to `generateCompressedId` since the last time this method was called.
|
|
319
|
+
* @returns the range of session-local IDs, which may be empty. This range must be sent to the server for ordering before
|
|
320
|
+
* it is finalized. Ranges must be sent to the server in the order that they are taken via calls to this method.
|
|
321
|
+
*/
|
|
322
|
+
takeNextCreationRange() {
|
|
323
|
+
var _a;
|
|
324
|
+
const lastLocalInRange = -this.localIdCount;
|
|
325
|
+
const lastTakenNormalized = (_a = this.lastTakenLocalId) !== null && _a !== void 0 ? _a : 0;
|
|
326
|
+
Common_1.assert(lastLocalInRange <= lastTakenNormalized);
|
|
327
|
+
let ids;
|
|
328
|
+
if (lastLocalInRange !== lastTakenNormalized) {
|
|
329
|
+
const firstLocalInRange = (lastTakenNormalized - 1);
|
|
330
|
+
const localOverrides = [
|
|
331
|
+
...this.localOverrides.getRange((lastTakenNormalized - 1), lastLocalInRange),
|
|
332
|
+
];
|
|
333
|
+
if (localOverrides.length > 0) {
|
|
334
|
+
// Cast: typecript 4.4.4 doesn't infer that `localOverrides` has at least one element and is therefore an `Overrides`
|
|
335
|
+
const overrides = localOverrides;
|
|
336
|
+
Common_1.assert(overrides[0][0] <= firstLocalInRange);
|
|
337
|
+
Common_1.assert(overrides[overrides.length - 1][0] >= lastLocalInRange);
|
|
338
|
+
ids = {
|
|
339
|
+
overrides,
|
|
340
|
+
};
|
|
341
|
+
const first = firstLocalInRange === overrides[0][0] ? undefined : firstLocalInRange;
|
|
342
|
+
const last = lastLocalInRange === overrides[overrides.length - 1][0] ? undefined : lastLocalInRange;
|
|
343
|
+
Common_1.setPropertyIfDefined(first, ids, 'first');
|
|
344
|
+
Common_1.setPropertyIfDefined(last, ids, 'last');
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
ids = {
|
|
348
|
+
first: firstLocalInRange,
|
|
349
|
+
last: lastLocalInRange,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
this.lastTakenLocalId = lastLocalInRange;
|
|
353
|
+
}
|
|
354
|
+
const range = { sessionId: this.localSessionId };
|
|
355
|
+
if (!this.sentAttributionInfo) {
|
|
356
|
+
Common_1.setPropertyIfDefined(this.localSession.attributionInfo, range, 'attributionInfo');
|
|
357
|
+
this.sentAttributionInfo = true;
|
|
358
|
+
}
|
|
359
|
+
if (ids === undefined) {
|
|
360
|
+
return range;
|
|
361
|
+
}
|
|
362
|
+
Common_1.assert(this.lastTakenLocalId === -this.localIdCount && this.lastTakenLocalId !== lastTakenNormalized, 'Non-empty range must properly consume local IDs');
|
|
363
|
+
range.ids = ids;
|
|
364
|
+
return range;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Finalizes the supplied range of IDs (which may be from either a remote or local session).
|
|
368
|
+
* @param range the range of session-local IDs to finalize.
|
|
369
|
+
*/
|
|
370
|
+
finalizeCreationRange(range) {
|
|
371
|
+
var _a, _b;
|
|
372
|
+
const { sessionId, attributionInfo } = range;
|
|
373
|
+
const isLocal = sessionId === this.localSessionId;
|
|
374
|
+
let session = this.tryGetSession(sessionId);
|
|
375
|
+
Common_1.assert(range.attributionInfo === undefined || session === undefined || isLocal, 'Attribution info can only be supplied on initial range for a session, and never modified.');
|
|
376
|
+
session !== null && session !== void 0 ? session : (session = this.createSession(sessionId, attributionInfo));
|
|
377
|
+
const ids = IdRange_1.getIds(range);
|
|
378
|
+
if (ids === undefined) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const { currentClusterDetails } = session;
|
|
382
|
+
const { cluster: currentCluster, clusterBase: currentBaseFinalId } = currentClusterDetails !== null && currentClusterDetails !== void 0 ? currentClusterDetails : {
|
|
383
|
+
cluster: undefined,
|
|
384
|
+
clusterBase: undefined,
|
|
385
|
+
};
|
|
386
|
+
const normalizedLastFinalized = (_a = session.lastFinalizedLocalId) !== null && _a !== void 0 ? _a : 0;
|
|
387
|
+
const { first: newFirstFinalizedLocalId, last: newLastFinalizedLocalId } = ids;
|
|
388
|
+
Common_1.assert(newFirstFinalizedLocalId === normalizedLastFinalized - 1, 'Ranges finalized out of order.');
|
|
389
|
+
// The total number of session-local IDs to finalize
|
|
390
|
+
const finalizeCount = normalizedLastFinalized - newLastFinalizedLocalId;
|
|
391
|
+
Common_1.assert(finalizeCount >= 1, 'Cannot finalize an empty range.');
|
|
392
|
+
let initialClusterCount = 0;
|
|
393
|
+
let remainingCount = finalizeCount;
|
|
394
|
+
let newBaseUuid;
|
|
395
|
+
if (currentCluster !== undefined && currentBaseFinalId !== undefined) {
|
|
396
|
+
initialClusterCount = currentCluster.count;
|
|
397
|
+
const remainingCapacity = currentCluster.capacity - initialClusterCount;
|
|
398
|
+
const overflow = remainingCount - remainingCapacity;
|
|
399
|
+
const hasRoom = overflow <= 0;
|
|
400
|
+
if (hasRoom || currentBaseFinalId === this.finalIdToCluster.maxKey()) {
|
|
401
|
+
currentCluster.count += remainingCount;
|
|
402
|
+
remainingCount = 0;
|
|
403
|
+
// The common case is that there is room in the cluster, and the new final IDs can simply be added to it
|
|
404
|
+
if (!hasRoom) {
|
|
405
|
+
// The cluster is full but is the last in the list of clusters.
|
|
406
|
+
// This allows it to be expanded instead of allocating a new one.
|
|
407
|
+
const expansionAmount = this.newClusterCapacity + overflow;
|
|
408
|
+
currentCluster.capacity += expansionAmount;
|
|
409
|
+
this.nextClusterBaseFinalId = (this.nextClusterBaseFinalId + expansionAmount);
|
|
410
|
+
Common_1.assert(this.nextClusterBaseFinalId < Number.MAX_SAFE_INTEGER, 'The number of allocated final IDs must not exceed the JS maximum safe integer.');
|
|
411
|
+
this.checkClusterForCollision(currentCluster);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
// The range cannot be fully allocated in the existing cluster, so allocate any space left in it and
|
|
416
|
+
// form a new one by incrementing the previous baseUuid
|
|
417
|
+
newBaseUuid = NumericUuid_1.incrementUuid(currentCluster.baseUuid, currentCluster.capacity);
|
|
418
|
+
currentCluster.count += remainingCapacity;
|
|
419
|
+
remainingCount -= remainingCapacity;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
// Session has never made a cluster, form a new one with the session UUID as the baseUuid
|
|
424
|
+
newBaseUuid = session.sessionUuid;
|
|
425
|
+
}
|
|
426
|
+
// Finalizing a range results in one of three cases:
|
|
427
|
+
// 1. All local IDs are finalized into a new cluster (because there was either never a cluster for that session, or the current
|
|
428
|
+
// cluster for the session was full).
|
|
429
|
+
// 2. All local IDs are finalized into the existing (current) cluster for the session.
|
|
430
|
+
// 3. Local IDs are finalized into both the current cluster and a new one, as the current cluster did not have enough room.
|
|
431
|
+
let newCluster;
|
|
432
|
+
let newBaseFinalId;
|
|
433
|
+
// The first local ID that will be finalized into a new cluster, if there is one.
|
|
434
|
+
// This lets us quickly compare which cluster an override string will go into.
|
|
435
|
+
let localIdPivot;
|
|
436
|
+
// Need to make a new cluster
|
|
437
|
+
if (newBaseUuid !== undefined) {
|
|
438
|
+
if (remainingCount <= 0) {
|
|
439
|
+
Common_1.fail('Should not create an empty cluster.');
|
|
440
|
+
}
|
|
441
|
+
if (currentCluster !== undefined && currentCluster.capacity !== currentCluster.count) {
|
|
442
|
+
Common_1.fail('Cluster must be filled before another is allocated.');
|
|
443
|
+
}
|
|
444
|
+
newBaseFinalId = this.nextClusterBaseFinalId;
|
|
445
|
+
newCluster = {
|
|
446
|
+
baseUuid: newBaseUuid,
|
|
447
|
+
capacity: Math.max(this.newClusterCapacity, remainingCount),
|
|
448
|
+
count: remainingCount,
|
|
449
|
+
session,
|
|
450
|
+
};
|
|
451
|
+
const usedCapacity = finalizeCount - remainingCount;
|
|
452
|
+
localIdPivot = (newFirstFinalizedLocalId - usedCapacity);
|
|
453
|
+
if (isLocal) {
|
|
454
|
+
this.localIdToCluster.append(localIdPivot, [newBaseFinalId, newCluster]);
|
|
455
|
+
}
|
|
456
|
+
this.checkClusterForCollision(newCluster);
|
|
457
|
+
this.clustersAndOverridesInversion.set(NumericUuid_1.stableIdFromNumericUuid(newCluster.baseUuid), {
|
|
458
|
+
clusterBase: newBaseFinalId,
|
|
459
|
+
cluster: newCluster,
|
|
460
|
+
});
|
|
461
|
+
session.currentClusterDetails = { cluster: newCluster, clusterBase: newBaseFinalId };
|
|
462
|
+
this.nextClusterBaseFinalId = (this.nextClusterBaseFinalId + newCluster.capacity);
|
|
463
|
+
Common_1.assert(this.nextClusterBaseFinalId < Number.MAX_SAFE_INTEGER, 'The number of allocated final IDs must not exceed the JS maximum safe integer.');
|
|
464
|
+
this.finalIdToCluster.append(newBaseFinalId, newCluster);
|
|
465
|
+
}
|
|
466
|
+
// If there are overrides, we must determine which cluster object (current or overflow) each belongs to and add it.
|
|
467
|
+
const overrides = ids.overrides;
|
|
468
|
+
if (overrides !== undefined) {
|
|
469
|
+
for (let i = 0; i < overrides.length; i++) {
|
|
470
|
+
const [overriddenLocal, override] = overrides[i];
|
|
471
|
+
// Note: recall that local IDs are negative
|
|
472
|
+
Common_1.assert(i === 0 || overriddenLocal < overrides[i - 1][0], 'Override IDs must be in sorted order.');
|
|
473
|
+
Common_1.assert(overriddenLocal < normalizedLastFinalized, 'Ranges finalized out of order.');
|
|
474
|
+
Common_1.assert(overriddenLocal >= newLastFinalizedLocalId, 'Malformed range: override ID ahead of range start.');
|
|
475
|
+
let cluster;
|
|
476
|
+
let overriddenFinal;
|
|
477
|
+
if (localIdPivot !== undefined && overriddenLocal <= localIdPivot) {
|
|
478
|
+
// Override is at or past the pivot, so it is in a new cluster.
|
|
479
|
+
Common_1.assert(newCluster !== undefined && newBaseFinalId !== undefined, 'No cluster was created when overflow occurred.');
|
|
480
|
+
cluster = newCluster;
|
|
481
|
+
overriddenFinal = (newBaseFinalId + (localIdPivot - overriddenLocal));
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
// Override was finalized into an existing cluster
|
|
485
|
+
Common_1.assert(currentCluster !== undefined && currentBaseFinalId !== undefined, 'No cluster exists but IDs were finalized.');
|
|
486
|
+
cluster = currentCluster;
|
|
487
|
+
overriddenFinal = (currentBaseFinalId +
|
|
488
|
+
initialClusterCount +
|
|
489
|
+
(normalizedLastFinalized - overriddenLocal) -
|
|
490
|
+
1);
|
|
491
|
+
}
|
|
492
|
+
(_b = cluster.overrides) !== null && _b !== void 0 ? _b : (cluster.overrides = new Map());
|
|
493
|
+
const inversionKey = IdCompressor.createInversionKey(override);
|
|
494
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
495
|
+
const existingIds = this.getExistingIdsForNewOverride(inversionKey, true);
|
|
496
|
+
let overrideForCluster;
|
|
497
|
+
let associatedLocal;
|
|
498
|
+
if (existingIds !== undefined) {
|
|
499
|
+
let mostFinalExistingOverride;
|
|
500
|
+
if (typeof existingIds === 'number') {
|
|
501
|
+
mostFinalExistingOverride = existingIds;
|
|
502
|
+
if (isLocalId(mostFinalExistingOverride)) {
|
|
503
|
+
associatedLocal = mostFinalExistingOverride;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
[associatedLocal, mostFinalExistingOverride] = existingIds;
|
|
508
|
+
}
|
|
509
|
+
if (isFinalId(mostFinalExistingOverride)) {
|
|
510
|
+
// A previous range already finalized an ID with this override. See `IdCluster` for more.
|
|
511
|
+
overrideForCluster = mostFinalExistingOverride;
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
Common_1.assert(!isLocal || mostFinalExistingOverride === overriddenLocal, 'Cannot have multiple local IDs with identical overrides.');
|
|
515
|
+
// This session has created an ID with this override before, but has not finalized it yet. The incoming
|
|
516
|
+
// range "wins" and will contain the final ID associated with that override, regardless of if that range was
|
|
517
|
+
// made by this session or not.
|
|
518
|
+
overrideForCluster = override;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
// This is the first time this override has been associated with any ID
|
|
523
|
+
overrideForCluster = override;
|
|
524
|
+
}
|
|
525
|
+
Common_1.assert(!cluster.overrides.has(overriddenFinal), 'Cannot add a second override for final id');
|
|
526
|
+
if (typeof overrideForCluster === 'string') {
|
|
527
|
+
if (isLocal || associatedLocal === undefined) {
|
|
528
|
+
cluster.overrides.set(overriddenFinal, override);
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
cluster.overrides.set(overriddenFinal, {
|
|
532
|
+
override,
|
|
533
|
+
originalOverridingFinal: overriddenFinal,
|
|
534
|
+
associatedLocalId: associatedLocal,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const unifiedOverride = {
|
|
540
|
+
override,
|
|
541
|
+
originalOverridingFinal: overrideForCluster,
|
|
542
|
+
};
|
|
543
|
+
Common_1.setPropertyIfDefined(associatedLocal, unifiedOverride, 'associatedLocalId');
|
|
544
|
+
cluster.overrides.set(overriddenFinal, unifiedOverride);
|
|
545
|
+
}
|
|
546
|
+
const finalizedOverride = {
|
|
547
|
+
cluster,
|
|
548
|
+
originalOverridingFinal: overriddenFinal,
|
|
549
|
+
};
|
|
550
|
+
Common_1.setPropertyIfDefined(associatedLocal, finalizedOverride, 'associatedLocalId');
|
|
551
|
+
const currentOverride = this.clustersAndOverridesInversion.get(inversionKey);
|
|
552
|
+
if (currentOverride === undefined || IdCompressor.isUnfinalizedOverride(currentOverride)) {
|
|
553
|
+
// Update the map to contain a finalized override, but never update it with future finalized overrides with
|
|
554
|
+
// the same string; those should decompress to the first final ID with that override.
|
|
555
|
+
this.clustersAndOverridesInversion.set(inversionKey, finalizedOverride);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
session.lastFinalizedLocalId = newLastFinalizedLocalId;
|
|
560
|
+
}
|
|
561
|
+
checkClusterForCollision(cluster) {
|
|
562
|
+
const maxClusterUuid = NumericUuid_1.incrementUuid(cluster.baseUuid, cluster.capacity - 1);
|
|
563
|
+
const maxClusterStableId = NumericUuid_1.stableIdFromNumericUuid(maxClusterUuid);
|
|
564
|
+
const closestMatch = this.clustersAndOverridesInversion.getPairOrNextLower(maxClusterStableId);
|
|
565
|
+
if (closestMatch !== undefined) {
|
|
566
|
+
const [inversionKey, compressionMapping] = closestMatch;
|
|
567
|
+
if (!IdCompressor.isClusterInfo(compressionMapping)) {
|
|
568
|
+
if (NumericUuid_1.isStableId(inversionKey) &&
|
|
569
|
+
IdCompressor.uuidsMightCollide(inversionKey, maxClusterStableId, cluster.capacity)) {
|
|
570
|
+
const numericOverride = NumericUuid_1.numericUuidFromStableId(inversionKey);
|
|
571
|
+
const delta = NumericUuid_1.getPositiveDelta(maxClusterUuid, numericOverride, cluster.capacity - 1);
|
|
572
|
+
if (delta !== undefined) {
|
|
573
|
+
IdCompressor.failWithCollidingOverride(inversionKey);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
static failWithCollidingOverride(override) {
|
|
580
|
+
Common_1.fail(`Override '${override}' collides with another allocated UUID.`);
|
|
581
|
+
}
|
|
582
|
+
static isClusterInfo(compressionMapping) {
|
|
583
|
+
return compressionMapping.clusterBase !== undefined;
|
|
584
|
+
}
|
|
585
|
+
static isUnfinalizedOverride(compressionMapping) {
|
|
586
|
+
return typeof compressionMapping === 'number';
|
|
587
|
+
}
|
|
588
|
+
static createInversionKey(inversionKey) {
|
|
589
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
590
|
+
return NumericUuid_1.isStableId(inversionKey) ? inversionKey : `${nonStableOverridePrefix}${inversionKey}`;
|
|
591
|
+
}
|
|
592
|
+
static isStableInversionKey(inversionKey) {
|
|
593
|
+
return inversionKey.charAt(0) !== nonStableOverridePrefix;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Returns an existing ID associated with an override, or undefined if none exists.
|
|
597
|
+
*/
|
|
598
|
+
getExistingIdsForNewOverride(inversionKey, isFinalOverride) {
|
|
599
|
+
var _a;
|
|
600
|
+
const closestMatch = this.clustersAndOverridesInversion.getPairOrNextLower(inversionKey, reusedArray);
|
|
601
|
+
let numericOverride;
|
|
602
|
+
let stableOverride;
|
|
603
|
+
if (closestMatch !== undefined) {
|
|
604
|
+
const [key, compressionMapping] = closestMatch;
|
|
605
|
+
if (!IdCompressor.isClusterInfo(compressionMapping)) {
|
|
606
|
+
if (key === inversionKey) {
|
|
607
|
+
if (IdCompressor.isUnfinalizedOverride(compressionMapping)) {
|
|
608
|
+
return compressionMapping;
|
|
609
|
+
}
|
|
610
|
+
const finalizedOverride = compressionMapping;
|
|
611
|
+
return finalizedOverride.associatedLocalId !== undefined
|
|
612
|
+
? [finalizedOverride.associatedLocalId, finalizedOverride.originalOverridingFinal]
|
|
613
|
+
: finalizedOverride.originalOverridingFinal;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else if (IdCompressor.isStableInversionKey(inversionKey)) {
|
|
617
|
+
stableOverride = inversionKey;
|
|
618
|
+
const cluster = compressionMapping.cluster;
|
|
619
|
+
if (IdCompressor.uuidsMightCollide(inversionKey, key, cluster.capacity)) {
|
|
620
|
+
numericOverride = NumericUuid_1.numericUuidFromStableId(stableOverride);
|
|
621
|
+
const delta = NumericUuid_1.getPositiveDelta(numericOverride, cluster.baseUuid, cluster.capacity - 1);
|
|
622
|
+
if (delta !== undefined) {
|
|
623
|
+
if (isFinalOverride) {
|
|
624
|
+
IdCompressor.failWithCollidingOverride(inversionKey);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
if (delta < cluster.count) {
|
|
628
|
+
return this.normalizeToSessionSpace((compressionMapping.clusterBase + delta));
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
IdCompressor.failWithCollidingOverride(inversionKey);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const override = (_a = numericOverride !== null && numericOverride !== void 0 ? numericOverride : stableOverride) !== null && _a !== void 0 ? _a : (IdCompressor.isStableInversionKey(inversionKey) ? inversionKey : undefined);
|
|
639
|
+
if (override !== undefined) {
|
|
640
|
+
const localId = this.getLocalIdForStableId(override);
|
|
641
|
+
if (localId !== undefined) {
|
|
642
|
+
return localId;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Check if `a` might be within `range` of `b`, where both are treated as hex numbers.
|
|
649
|
+
* @param range an integer
|
|
650
|
+
*/
|
|
651
|
+
static uuidsMightCollide(a, b, range) {
|
|
652
|
+
// Check if any of the UUIDs in the cluster collide (i.e. any in [base, base + capacity)).
|
|
653
|
+
// Optimization: All UUIDs in a cluster are the same string up until the last few characters which encode the offset from
|
|
654
|
+
// the cluster base. So, first compute the length of that shared string, and early out if it is different from the override
|
|
655
|
+
// UUID. This way we usually need not do the more expensive check below.
|
|
656
|
+
const hexDigitsToCheck = 32 - Math.ceil(Math.log2(range) / 2);
|
|
657
|
+
if (a.startsWith(b.slice(0, hexDigitsToCheck))) {
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Helper for retrieving an override.
|
|
664
|
+
*/
|
|
665
|
+
static tryGetOverride(cluster, finalId) {
|
|
666
|
+
var _a;
|
|
667
|
+
const override = (_a = cluster.overrides) === null || _a === void 0 ? void 0 : _a.get(finalId);
|
|
668
|
+
if (override === undefined) {
|
|
669
|
+
return undefined;
|
|
670
|
+
}
|
|
671
|
+
if (typeof override === 'string') {
|
|
672
|
+
return override;
|
|
673
|
+
}
|
|
674
|
+
return override.override;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Generates a new compressed ID or returns an existing one.
|
|
678
|
+
* This should ONLY be called to generate IDs for local operations.
|
|
679
|
+
* @param override Specifies a specific string to be associated with the returned compressed ID.
|
|
680
|
+
* Performance note: assigning override strings incurs a performance overhead.
|
|
681
|
+
* @returns an existing ID if one already exists for `override`, and a new local ID otherwise. The returned ID is in session space.
|
|
682
|
+
*/
|
|
683
|
+
generateCompressedId(override) {
|
|
684
|
+
// If any ID exists for this override (locally or remotely allocated), return it (after ensuring it is in session-space).
|
|
685
|
+
if (override !== undefined) {
|
|
686
|
+
const inversionKey = IdCompressor.createInversionKey(override);
|
|
687
|
+
const existingIds = this.getExistingIdsForNewOverride(inversionKey, false);
|
|
688
|
+
if (existingIds !== undefined) {
|
|
689
|
+
return typeof existingIds === 'number' ? existingIds : existingIds[0];
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
const newLocalId = this.generateNextLocalId();
|
|
693
|
+
this.localOverrides.append(newLocalId, override);
|
|
694
|
+
// Since the local ID was just created, it is in both session and op space
|
|
695
|
+
const compressionMapping = newLocalId;
|
|
696
|
+
this.clustersAndOverridesInversion.set(inversionKey, compressionMapping);
|
|
697
|
+
return newLocalId;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
return this.generateNextLocalId();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Generates a range of compressed IDs.
|
|
706
|
+
* This should ONLY be called to generate IDs for local operations.
|
|
707
|
+
* @param count the number of IDs to generate, must be > 0.
|
|
708
|
+
* @returns a persistable descriptor of the ID range.
|
|
709
|
+
*/
|
|
710
|
+
generateCompressedIdRange(count) {
|
|
711
|
+
Common_1.assert(count > 0, 'Must generate a nonzero number of IDs.');
|
|
712
|
+
Common_1.assert(count <= Number.MAX_SAFE_INTEGER, 'The number of allocated local IDs must not exceed the JS maximum safe integer.');
|
|
713
|
+
const first = this.generateNextLocalId();
|
|
714
|
+
this.localIdCount += count - 1;
|
|
715
|
+
return { first, count };
|
|
716
|
+
}
|
|
717
|
+
generateNextLocalId() {
|
|
718
|
+
return -++this.localIdCount;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Decompresses a previously compressed ID into a UUID or override string.
|
|
722
|
+
* @param id the compressed ID to be decompressed.
|
|
723
|
+
* @returns the UUID or override string associated with the compressed ID. Fails if the ID was not generated by this compressor.
|
|
724
|
+
*/
|
|
725
|
+
decompress(id) {
|
|
726
|
+
var _a;
|
|
727
|
+
return (_a = this.tryDecompress(id)) !== null && _a !== void 0 ? _a : Common_1.fail('Compressed ID was not generated by this compressor');
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Attempts to decompress a previously compressed ID into a UUID or override string.
|
|
731
|
+
* @param id the compressed ID to be decompressed.
|
|
732
|
+
* @returns the UUID or override string associated with the compressed ID, or undefined if the ID was not generated by this compressor.
|
|
733
|
+
*/
|
|
734
|
+
tryDecompress(id) {
|
|
735
|
+
var _a;
|
|
736
|
+
if (isFinalId(id)) {
|
|
737
|
+
const possibleCluster = this.getClusterForFinalId(id);
|
|
738
|
+
if (possibleCluster === undefined) {
|
|
739
|
+
return undefined;
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
const [baseFinalId, cluster] = possibleCluster;
|
|
743
|
+
const override = IdCompressor.tryGetOverride(cluster, id);
|
|
744
|
+
if (override !== undefined) {
|
|
745
|
+
return override;
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
const offsetInCluster = id - baseFinalId;
|
|
749
|
+
return NumericUuid_1.stableIdFromNumericUuid(cluster.baseUuid, offsetInCluster);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
const idOffset = -id; // Convert to a positive number
|
|
755
|
+
if (idOffset > this.localIdCount) {
|
|
756
|
+
// This local ID was never allocated.
|
|
757
|
+
return undefined;
|
|
758
|
+
}
|
|
759
|
+
// If this is a local ID with an override, then it must have been allocated on this machine and will be contained in
|
|
760
|
+
// `localOverrides`s. Otherwise, it is a sequential allocation from the session UUID and can simply be negated and
|
|
761
|
+
// added to that UUID to obtain the stable ID associated with it.
|
|
762
|
+
const localOverride = (_a = this.localOverrides) === null || _a === void 0 ? void 0 : _a.get(id);
|
|
763
|
+
if (localOverride !== undefined) {
|
|
764
|
+
return localOverride;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
return NumericUuid_1.stableIdFromNumericUuid(this.localSession.sessionUuid, idOffset - 1);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Recompresses a decompressed ID, which could be a UUID or an override string.
|
|
773
|
+
* @param uncompressed the UUID or override string to recompress.
|
|
774
|
+
* @returns the `CompressedId` associated with `uncompressed`. Fails if it has not been previously compressed by this compressor.
|
|
775
|
+
*/
|
|
776
|
+
recompress(uncompressed) {
|
|
777
|
+
var _a;
|
|
778
|
+
return (_a = this.tryRecompress(uncompressed)) !== null && _a !== void 0 ? _a : Common_1.fail('No such string has ever been compressed');
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Attempts to recompresses a decompressed ID, which could be a UUID or an override string.
|
|
782
|
+
* @param uncompressed the UUID or override string to recompress,
|
|
783
|
+
* @returns the `CompressedId` associated with `uncompressed` or undefined if it has not been previously compressed by this compressor.
|
|
784
|
+
*/
|
|
785
|
+
tryRecompress(uncompressed) {
|
|
786
|
+
return this.recompressInternal(uncompressed);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Helper to compress an uncompressed UUID. It can optionally be supplied with the numeric form of `uncompressedUuid` as a
|
|
790
|
+
* performance optimization.
|
|
791
|
+
*/
|
|
792
|
+
recompressInternal(uncompressed, uncompressedUuidNumeric) {
|
|
793
|
+
var _a, _b;
|
|
794
|
+
let numericUuid = uncompressedUuidNumeric;
|
|
795
|
+
// TODO: This cast can be removed on typescript 4.6, and should give improved typesafety.
|
|
796
|
+
const inversionKey = IdCompressor.createInversionKey(uncompressed);
|
|
797
|
+
const isStable = IdCompressor.isStableInversionKey(inversionKey);
|
|
798
|
+
const closestMatch = this.clustersAndOverridesInversion.getPairOrNextLower(inversionKey, reusedArray);
|
|
799
|
+
if (closestMatch !== undefined) {
|
|
800
|
+
const [key, compressionMapping] = closestMatch;
|
|
801
|
+
if (!IdCompressor.isClusterInfo(compressionMapping)) {
|
|
802
|
+
if (key === inversionKey) {
|
|
803
|
+
if (IdCompressor.isUnfinalizedOverride(compressionMapping)) {
|
|
804
|
+
return compressionMapping;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
const cluster = compressionMapping.cluster;
|
|
808
|
+
Common_1.assert(IdCompressor.tryGetOverride(cluster, compressionMapping.originalOverridingFinal) !==
|
|
809
|
+
undefined, 'No override for cluster marked as having one.');
|
|
810
|
+
return ((_a = compressionMapping.associatedLocalId) !== null && _a !== void 0 ? _a : compressionMapping.originalOverridingFinal);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
if (!isStable) {
|
|
816
|
+
return undefined;
|
|
817
|
+
}
|
|
818
|
+
const { clusterBase: closestBaseFinalId, cluster: closestCluster } = compressionMapping;
|
|
819
|
+
numericUuid !== null && numericUuid !== void 0 ? numericUuid : (numericUuid = NumericUuid_1.numericUuidFromStableId(inversionKey));
|
|
820
|
+
const uuidOffset = NumericUuid_1.getPositiveDelta(numericUuid, closestCluster.baseUuid, closestCluster.count - 1);
|
|
821
|
+
if (uuidOffset !== undefined) {
|
|
822
|
+
let targetFinalId = (closestBaseFinalId + uuidOffset);
|
|
823
|
+
const override = (_b = closestCluster.overrides) === null || _b === void 0 ? void 0 : _b.get(targetFinalId);
|
|
824
|
+
if (typeof override === 'object') {
|
|
825
|
+
if (override.associatedLocalId !== undefined) {
|
|
826
|
+
return override.associatedLocalId;
|
|
827
|
+
}
|
|
828
|
+
// This may be a UUID that should actually compress into a different final ID that it aligns with, due to
|
|
829
|
+
// another session having an identical override (see `IdCluster` for more).
|
|
830
|
+
targetFinalId = override.originalOverridingFinal;
|
|
831
|
+
}
|
|
832
|
+
return this.normalizeToSessionSpace(targetFinalId);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (isStable) {
|
|
837
|
+
// May have already computed the numeric UUID, so avoid recomputing if possible
|
|
838
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
839
|
+
const localId = this.getLocalIdForStableId(numericUuid !== null && numericUuid !== void 0 ? numericUuid : inversionKey);
|
|
840
|
+
if (localId !== undefined) {
|
|
841
|
+
return localId;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return undefined;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Normalizes a session space ID into op space.
|
|
848
|
+
* @param id the local ID to normalize.
|
|
849
|
+
* @returns the ID in op space.
|
|
850
|
+
*/
|
|
851
|
+
normalizeToOpSpace(id) {
|
|
852
|
+
var _a, _b;
|
|
853
|
+
if (isFinalId(id)) {
|
|
854
|
+
return id;
|
|
855
|
+
}
|
|
856
|
+
// Check if this local ID has not been allocated yet
|
|
857
|
+
if (-id > this.localIdCount) {
|
|
858
|
+
Common_1.fail('Supplied local ID was not created by this compressor.');
|
|
859
|
+
}
|
|
860
|
+
// Check if this local ID has not been finalized yet
|
|
861
|
+
const { lastFinalizedLocalId } = this.localSession;
|
|
862
|
+
if (lastFinalizedLocalId === undefined || id < lastFinalizedLocalId) {
|
|
863
|
+
const override = this.localOverrides.get(id);
|
|
864
|
+
if (override !== undefined) {
|
|
865
|
+
const inversionKey = IdCompressor.createInversionKey(override);
|
|
866
|
+
const compressionMapping = (_a = this.clustersAndOverridesInversion.get(inversionKey)) !== null && _a !== void 0 ? _a : Common_1.fail('Bimap is malformed.');
|
|
867
|
+
if (!IdCompressor.isClusterInfo(compressionMapping) &&
|
|
868
|
+
!IdCompressor.isUnfinalizedOverride(compressionMapping) &&
|
|
869
|
+
compressionMapping.associatedLocalId === id) {
|
|
870
|
+
return compressionMapping.originalOverridingFinal;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return id;
|
|
874
|
+
}
|
|
875
|
+
const [localBase, [finalBase, cluster]] = (_b = this.localIdToCluster.getPairOrNextLower(id)) !== null && _b !== void 0 ? _b : Common_1.fail('Locally created cluster should be added to the map when allocated');
|
|
876
|
+
const correspondingFinal = (finalBase + (localBase - id));
|
|
877
|
+
if (cluster.overrides) {
|
|
878
|
+
const override = cluster.overrides.get(correspondingFinal);
|
|
879
|
+
if (typeof override === 'object' && override.originalOverridingFinal !== undefined) {
|
|
880
|
+
// Rare case of two local IDs with same overrides are created concurrently. See `IdCluster` for more.
|
|
881
|
+
return override.originalOverridingFinal;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return correspondingFinal;
|
|
885
|
+
}
|
|
886
|
+
normalizeToSessionSpace(id, originSessionId) {
|
|
887
|
+
var _a, _b, _c;
|
|
888
|
+
const isLocalSession = originSessionId === this.localSessionId;
|
|
889
|
+
if (isLocalId(id)) {
|
|
890
|
+
if (isLocalSession) {
|
|
891
|
+
const localIndex = -id;
|
|
892
|
+
if (localIndex > this.localIdCount) {
|
|
893
|
+
Common_1.fail('Supplied local ID was not created by this compressor.');
|
|
894
|
+
}
|
|
895
|
+
return id;
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
const session = this.tryGetSession(originSessionId !== null && originSessionId !== void 0 ? originSessionId : Common_1.fail());
|
|
899
|
+
if (session === undefined) {
|
|
900
|
+
Common_1.fail('No IDs have ever been finalized by the supplied session.');
|
|
901
|
+
}
|
|
902
|
+
const localCount = -id;
|
|
903
|
+
const numericUuid = NumericUuid_1.incrementUuid(session.sessionUuid, localCount - 1);
|
|
904
|
+
return (_a = this.compressNumericUuid(numericUuid)) !== null && _a !== void 0 ? _a : Common_1.fail('ID is not known to this compressor.');
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const closestResult = this.localIdToCluster.getPairOrNextLowerByValue(id);
|
|
908
|
+
if (closestResult !== undefined) {
|
|
909
|
+
const [localBase, [finalBase, cluster]] = closestResult;
|
|
910
|
+
const indexInCluster = id - finalBase;
|
|
911
|
+
if (indexInCluster < cluster.count) {
|
|
912
|
+
return (localBase - indexInCluster);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
// Check for a unified override finalized first by another session but to which the local session
|
|
916
|
+
// still has an associated local ID.
|
|
917
|
+
const [_, cluster] = (_b = this.getClusterForFinalId(id)) !== null && _b !== void 0 ? _b : Common_1.fail('Supplied final ID was not finalized by this compressor.');
|
|
918
|
+
const override = (_c = cluster.overrides) === null || _c === void 0 ? void 0 : _c.get(id);
|
|
919
|
+
if (typeof override === 'object' && override.associatedLocalId !== undefined) {
|
|
920
|
+
return override.associatedLocalId;
|
|
921
|
+
}
|
|
922
|
+
return id;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Returns the session-space compressed ID corresponding to the numeric UUID, or undefined if it is not known to this compressor.
|
|
926
|
+
* Typically, it will return the session-space ID sequentially aligned with it (which will be local if `numericUuid` was made by
|
|
927
|
+
* the local session, or final otherwise). However, in the event that the aligned session-space ID was overridden with a UUID
|
|
928
|
+
* *and* that override UUID was concurrently used in an older ID (earlier, w.r.t. sequencing), this method can return the first
|
|
929
|
+
* ID to correspond to that override.
|
|
930
|
+
*
|
|
931
|
+
* As an example, consider the following two clients:
|
|
932
|
+
* ClientA, session UUID: A0000000-0000-0000-0000-000000000000
|
|
933
|
+
* ClientB, session UUID: B0000000-0000-0000-0000-000000000000
|
|
934
|
+
*
|
|
935
|
+
* If concurrently, two clients performed:
|
|
936
|
+
* ClientA: generateCompressedId(override: 'X0000000-0000-0000-0000-000000000000') // aligned with A0000000-0000-0000-0000-000000000000
|
|
937
|
+
*
|
|
938
|
+
* ClientB: generateCompressedId() // aligned with B0000000-0000-0000-0000-000000000000
|
|
939
|
+
* ClientB: generateCompressedId(override: 'X0000000-0000-0000-0000-000000000000') // aligned with B0000000-0000-0000-0000-000000000001
|
|
940
|
+
*
|
|
941
|
+
* After sequencing, calling this method and passing the numeric UUID for B0000000-0000-0000-0000-000000000001 would return the
|
|
942
|
+
* session-space ID corresponding to A0000000-0000-0000-0000-000000000000 (with override X0000000-0000-0000-0000-000000000000).
|
|
943
|
+
*/
|
|
944
|
+
compressNumericUuid(numericUuid) {
|
|
945
|
+
const stableId = NumericUuid_1.stableIdFromNumericUuid(numericUuid);
|
|
946
|
+
const sessionSpaceId = this.recompressInternal(stableId, numericUuid);
|
|
947
|
+
if (sessionSpaceId === undefined) {
|
|
948
|
+
return undefined;
|
|
949
|
+
}
|
|
950
|
+
return sessionSpaceId;
|
|
951
|
+
}
|
|
952
|
+
getLocalIdForStableId(stableId) {
|
|
953
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
954
|
+
const numericUuid = (typeof stableId === 'string' ? NumericUuid_1.numericUuidFromStableId(stableId) : stableId);
|
|
955
|
+
const offset = NumericUuid_1.getPositiveDelta(numericUuid, this.localSession.sessionUuid, this.localIdCount - 1);
|
|
956
|
+
if (offset === undefined) {
|
|
957
|
+
return undefined;
|
|
958
|
+
}
|
|
959
|
+
return (-offset - 1);
|
|
960
|
+
}
|
|
961
|
+
getClusterForFinalId(finalId) {
|
|
962
|
+
const possibleCluster = this.finalIdToCluster.getPairOrNextLower(finalId);
|
|
963
|
+
if (possibleCluster === undefined) {
|
|
964
|
+
return undefined;
|
|
965
|
+
}
|
|
966
|
+
const [clusterBase, cluster] = possibleCluster;
|
|
967
|
+
if (finalId - clusterBase >= cluster.count) {
|
|
968
|
+
return undefined;
|
|
969
|
+
}
|
|
970
|
+
return possibleCluster;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* @returns if `other` is equal to this `IdCompressor`. The equality check includes local session state.
|
|
974
|
+
* @testOnly
|
|
975
|
+
*/
|
|
976
|
+
equals(other, compareLocalState) {
|
|
977
|
+
if (compareLocalState) {
|
|
978
|
+
if (this.localIdCount !== other.localIdCount ||
|
|
979
|
+
this.localSessionId !== other.localSessionId ||
|
|
980
|
+
this.lastTakenLocalId !== other.lastTakenLocalId ||
|
|
981
|
+
this.sentAttributionInfo !== other.sentAttributionInfo) {
|
|
982
|
+
return false;
|
|
983
|
+
}
|
|
984
|
+
if (!this.localOverrides.equals(other.localOverrides, (a, b) => a === b)) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
if (!Common_1.compareMaps(this.sessions, other.sessions, (a, b) => IdCompressor.sessionDataEqual(a, b, true, compareLocalState))) {
|
|
988
|
+
return false;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
for (const [keyA, valueA] of this.sessions) {
|
|
993
|
+
const valueB = other.sessions.get(keyA);
|
|
994
|
+
if (valueB === undefined) {
|
|
995
|
+
if (valueA.lastFinalizedLocalId !== undefined) {
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
else if (!IdCompressor.sessionDataEqual(valueA, valueB, true, compareLocalState)) {
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
for (const [keyB, valueB] of other.sessions) {
|
|
1004
|
+
const valueA = this.sessions.get(keyB);
|
|
1005
|
+
if (valueA === undefined) {
|
|
1006
|
+
if (valueB.lastFinalizedLocalId !== undefined) {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (this.nextClusterBaseFinalId !== other.nextClusterBaseFinalId ||
|
|
1013
|
+
this.newClusterCapacity !== other.newClusterCapacity) {
|
|
1014
|
+
return false;
|
|
1015
|
+
}
|
|
1016
|
+
if (!this.finalIdToCluster.equals(other.finalIdToCluster, (a, b) => IdCompressor.idClustersEqual(a, b, true, compareLocalState))) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
const missingInOne = (_, value) => {
|
|
1020
|
+
if (!compareLocalState && IdCompressor.isUnfinalizedOverride(value)) {
|
|
1021
|
+
return undefined;
|
|
1022
|
+
}
|
|
1023
|
+
return { break: true };
|
|
1024
|
+
};
|
|
1025
|
+
const compareCompressionMappings = (a, b) => {
|
|
1026
|
+
const unfinalizedA = IdCompressor.isUnfinalizedOverride(a);
|
|
1027
|
+
const unfinalizedB = IdCompressor.isUnfinalizedOverride(b);
|
|
1028
|
+
if (unfinalizedA) {
|
|
1029
|
+
if (unfinalizedB) {
|
|
1030
|
+
return a === b;
|
|
1031
|
+
}
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
else if (unfinalizedB) {
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
if (IdCompressor.isClusterInfo(a)) {
|
|
1038
|
+
if (!IdCompressor.isClusterInfo(b) || a.clusterBase !== b.clusterBase) {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
else {
|
|
1043
|
+
if (IdCompressor.isClusterInfo(b) ||
|
|
1044
|
+
(compareLocalState && a.associatedLocalId !== b.associatedLocalId) ||
|
|
1045
|
+
a.originalOverridingFinal !== b.originalOverridingFinal) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (!IdCompressor.idClustersEqual(a.cluster, b.cluster, true, compareLocalState)) {
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
return true;
|
|
1053
|
+
};
|
|
1054
|
+
const diff = this.clustersAndOverridesInversion.diffAgainst(other.clustersAndOverridesInversion, missingInOne, missingInOne, (_, valA, valB) => {
|
|
1055
|
+
if (!compareCompressionMappings(valA, valB)) {
|
|
1056
|
+
return { break: true };
|
|
1057
|
+
}
|
|
1058
|
+
return undefined;
|
|
1059
|
+
});
|
|
1060
|
+
return diff === undefined;
|
|
1061
|
+
}
|
|
1062
|
+
static sessionDataEqual(a, b, checkCluster = true, compareLocalState = true) {
|
|
1063
|
+
if (a.attributionInfo !== b.attributionInfo ||
|
|
1064
|
+
!NumericUuid_1.numericUuidEquals(a.sessionUuid, b.sessionUuid) ||
|
|
1065
|
+
a.lastFinalizedLocalId !== b.lastFinalizedLocalId) {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
if (a.currentClusterDetails === undefined || b.currentClusterDetails === undefined) {
|
|
1069
|
+
if (a.currentClusterDetails !== b.currentClusterDetails) {
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
if (checkCluster &&
|
|
1075
|
+
!IdCompressor.idClustersEqual(a.currentClusterDetails.cluster, b.currentClusterDetails.cluster, false, compareLocalState)) {
|
|
1076
|
+
return false;
|
|
1077
|
+
}
|
|
1078
|
+
return true;
|
|
1079
|
+
}
|
|
1080
|
+
static idClustersEqual(a, b, checkSessionData = true, compareLocalState = true) {
|
|
1081
|
+
const areEqual = NumericUuid_1.numericUuidEquals(a.baseUuid, b.baseUuid) &&
|
|
1082
|
+
a.capacity === b.capacity &&
|
|
1083
|
+
a.count === b.count &&
|
|
1084
|
+
(!checkSessionData || IdCompressor.sessionDataEqual(a.session, b.session, false, compareLocalState)) &&
|
|
1085
|
+
(a.overrides === undefined) === (b.overrides === undefined) &&
|
|
1086
|
+
(a.overrides === undefined ||
|
|
1087
|
+
Common_1.compareMaps(Common_1.assertNotUndefined(a.overrides), Common_1.assertNotUndefined(b.overrides), (a, b) => {
|
|
1088
|
+
if (compareLocalState) {
|
|
1089
|
+
if (typeof a === 'string' || typeof b === 'string') {
|
|
1090
|
+
return a === b;
|
|
1091
|
+
}
|
|
1092
|
+
const overridesEqual = a.override === b.override &&
|
|
1093
|
+
a.originalOverridingFinal === b.originalOverridingFinal &&
|
|
1094
|
+
(!compareLocalState || a.associatedLocalId === b.associatedLocalId);
|
|
1095
|
+
return overridesEqual;
|
|
1096
|
+
}
|
|
1097
|
+
const uuidA = typeof a === 'string' ? a : a.override;
|
|
1098
|
+
const uuidB = typeof b === 'string' ? b : b.override;
|
|
1099
|
+
if (typeof a !== 'string' &&
|
|
1100
|
+
typeof b !== 'string' &&
|
|
1101
|
+
a.originalOverridingFinal !== b.originalOverridingFinal) {
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
return uuidA === uuidB;
|
|
1105
|
+
}));
|
|
1106
|
+
return areEqual;
|
|
1107
|
+
}
|
|
1108
|
+
serialize(withSession) {
|
|
1109
|
+
var _a;
|
|
1110
|
+
const serializedSessions = [];
|
|
1111
|
+
const sessionIdToSessionIndex = new Map();
|
|
1112
|
+
for (const [sessionId, session] of this.sessions) {
|
|
1113
|
+
const isLocalSession = sessionId === this.localSessionId;
|
|
1114
|
+
const includeSession = sessionId !== reservedSessionId && // Ignore reserved clusters, but
|
|
1115
|
+
(session.lastFinalizedLocalId !== undefined || // always serialize sessions that made final IDs,
|
|
1116
|
+
(isLocalSession && withSession)); // include the un-acked local session if requested
|
|
1117
|
+
if (includeSession) {
|
|
1118
|
+
const sessionData = [sessionId];
|
|
1119
|
+
if (session.attributionInfo !== undefined) {
|
|
1120
|
+
sessionData.push(session.attributionInfo);
|
|
1121
|
+
}
|
|
1122
|
+
sessionIdToSessionIndex.set(sessionId, serializedSessions.length);
|
|
1123
|
+
serializedSessions.push(sessionData);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const serializedClusters = [];
|
|
1127
|
+
for (const [baseFinalId, cluster] of this.finalIdToCluster.entries()) {
|
|
1128
|
+
const sessionId = NumericUuid_1.stableIdFromNumericUuid(cluster.session.sessionUuid);
|
|
1129
|
+
if (sessionId !== reservedSessionId) {
|
|
1130
|
+
const sessionIndex = (_a = sessionIdToSessionIndex.get(sessionId)) !== null && _a !== void 0 ? _a : Common_1.fail('Session object contains wrong session numeric UUID');
|
|
1131
|
+
const serializedCluster = [sessionIndex, cluster.capacity];
|
|
1132
|
+
if (cluster.count !== cluster.capacity) {
|
|
1133
|
+
serializedCluster.push(cluster.count);
|
|
1134
|
+
}
|
|
1135
|
+
if (cluster.overrides !== undefined) {
|
|
1136
|
+
const serializedOverrides = [];
|
|
1137
|
+
for (const [finalId, override] of cluster.overrides) {
|
|
1138
|
+
const finalIdIndex = finalId - baseFinalId;
|
|
1139
|
+
if (typeof override === 'string') {
|
|
1140
|
+
serializedOverrides.push([finalIdIndex, override]);
|
|
1141
|
+
}
|
|
1142
|
+
else if (override.originalOverridingFinal === finalId) {
|
|
1143
|
+
serializedOverrides.push([finalIdIndex, override.override]);
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
serializedOverrides.push([
|
|
1147
|
+
finalIdIndex,
|
|
1148
|
+
override.override,
|
|
1149
|
+
override.originalOverridingFinal,
|
|
1150
|
+
]);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
serializedCluster.push(serializedOverrides);
|
|
1154
|
+
}
|
|
1155
|
+
serializedClusters.push(serializedCluster);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
// Reserved session not serialized, and local session is present but may not make IDs
|
|
1159
|
+
Common_1.assert(serializedSessions.length - this.sessions.size <= 2, 'session not serialized');
|
|
1160
|
+
const serializedIdCompressor = {
|
|
1161
|
+
version: currentWrittenVersion,
|
|
1162
|
+
reservedIdCount: this.reservedIdCount,
|
|
1163
|
+
clusterCapacity: this.clusterCapacity,
|
|
1164
|
+
sessions: serializedSessions,
|
|
1165
|
+
clusters: serializedClusters,
|
|
1166
|
+
};
|
|
1167
|
+
if (withSession) {
|
|
1168
|
+
const serializedWithSession = serializedIdCompressor;
|
|
1169
|
+
serializedWithSession.localSessionIndex = serializedWithSession.sessions.findIndex(([sessionId]) => sessionId === this.localSessionId);
|
|
1170
|
+
if (this.localIdCount > 0) {
|
|
1171
|
+
serializedWithSession.localState = {
|
|
1172
|
+
localIdCount: this.localIdCount,
|
|
1173
|
+
overrides: [...this.localOverrides.entries()].map((entry) => [...entry]),
|
|
1174
|
+
lastTakenLocalId: this.lastTakenLocalId,
|
|
1175
|
+
sentAttributionInfo: this.sentAttributionInfo,
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
return serializedWithSession;
|
|
1179
|
+
}
|
|
1180
|
+
return serializedIdCompressor;
|
|
1181
|
+
}
|
|
1182
|
+
static deserialize(serialized, newSessionIdMaybe, attributionInfoMaybe) {
|
|
1183
|
+
const hasSession = hasOngoingSession(serialized);
|
|
1184
|
+
let localSessionId;
|
|
1185
|
+
let attributionInfo;
|
|
1186
|
+
let serializedLocalState;
|
|
1187
|
+
if (hasSession) {
|
|
1188
|
+
Common_1.assert(newSessionIdMaybe === undefined && attributionInfoMaybe === undefined);
|
|
1189
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1190
|
+
[localSessionId, attributionInfo] =
|
|
1191
|
+
serialized.sessions[serialized.localSessionIndex];
|
|
1192
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1193
|
+
serializedLocalState = serialized.localState;
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
Common_1.assert(newSessionIdMaybe !== undefined);
|
|
1197
|
+
localSessionId = newSessionIdMaybe;
|
|
1198
|
+
attributionInfo = attributionInfoMaybe;
|
|
1199
|
+
}
|
|
1200
|
+
const { clusterCapacity, reservedIdCount, sessions: serializedSessions, clusters: serializedClusters, } = serialized;
|
|
1201
|
+
const compressor = new IdCompressor(localSessionId, reservedIdCount, attributionInfo);
|
|
1202
|
+
compressor.clusterCapacity = clusterCapacity;
|
|
1203
|
+
const localOverridesInverse = new Map();
|
|
1204
|
+
if (serializedLocalState !== undefined) {
|
|
1205
|
+
// Do this part of local rehydration first since the cluster map population needs to query to local overrides
|
|
1206
|
+
compressor.localIdCount = serializedLocalState.localIdCount;
|
|
1207
|
+
compressor.lastTakenLocalId = serializedLocalState.lastTakenLocalId;
|
|
1208
|
+
compressor.sentAttributionInfo = serializedLocalState.sentAttributionInfo;
|
|
1209
|
+
if (serializedLocalState.overrides !== undefined) {
|
|
1210
|
+
for (const [localId, override] of serializedLocalState.overrides) {
|
|
1211
|
+
compressor.localOverrides.append(localId, override);
|
|
1212
|
+
localOverridesInverse.set(override, localId);
|
|
1213
|
+
compressor.clustersAndOverridesInversion.set(IdCompressor.createInversionKey(override), localId);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
const sessionInfos = [];
|
|
1218
|
+
for (const serializedSession of serializedSessions) {
|
|
1219
|
+
const [sessionId, attributionInfo] = serializedSession;
|
|
1220
|
+
if (sessionId === localSessionId) {
|
|
1221
|
+
Common_1.assert(hasSession, 'Cannot resume existing session.');
|
|
1222
|
+
sessionInfos.push({ session: compressor.localSession, sessionId });
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
const session = compressor.createSession(sessionId, attributionInfo);
|
|
1226
|
+
sessionInfos.push({ session, sessionId });
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
for (const serializedCluster of serializedClusters) {
|
|
1230
|
+
const { sessionIndex, capacity, count, overrides } = deserializeCluster(serializedCluster);
|
|
1231
|
+
const { session, sessionId } = sessionInfos[sessionIndex];
|
|
1232
|
+
const { lastFinalizedLocalId, sessionUuid } = session;
|
|
1233
|
+
const currentIdCount = lastFinalizedLocalId === undefined ? 0 : -lastFinalizedLocalId;
|
|
1234
|
+
const cluster = {
|
|
1235
|
+
capacity,
|
|
1236
|
+
count,
|
|
1237
|
+
baseUuid: NumericUuid_1.incrementUuid(sessionUuid, currentIdCount),
|
|
1238
|
+
session,
|
|
1239
|
+
};
|
|
1240
|
+
const lastFinalizedNormalized = lastFinalizedLocalId !== null && lastFinalizedLocalId !== void 0 ? lastFinalizedLocalId : 0;
|
|
1241
|
+
const clusterBase = compressor.nextClusterBaseFinalId;
|
|
1242
|
+
if (serializedLocalState !== undefined && sessionId === compressor.localSessionId) {
|
|
1243
|
+
compressor.localIdToCluster.append((lastFinalizedNormalized - 1), [
|
|
1244
|
+
clusterBase,
|
|
1245
|
+
cluster,
|
|
1246
|
+
]);
|
|
1247
|
+
}
|
|
1248
|
+
session.lastFinalizedLocalId = (lastFinalizedNormalized - count);
|
|
1249
|
+
session.currentClusterDetails = { clusterBase, cluster };
|
|
1250
|
+
compressor.nextClusterBaseFinalId = (compressor.nextClusterBaseFinalId + capacity);
|
|
1251
|
+
compressor.finalIdToCluster.append(clusterBase, cluster);
|
|
1252
|
+
compressor.clustersAndOverridesInversion.set(NumericUuid_1.stableIdFromNumericUuid(cluster.baseUuid), {
|
|
1253
|
+
clusterBase,
|
|
1254
|
+
cluster,
|
|
1255
|
+
});
|
|
1256
|
+
if (overrides !== undefined) {
|
|
1257
|
+
cluster.overrides = new Map();
|
|
1258
|
+
for (const [finalIdIndex, override, originalOverridingFinal] of overrides) {
|
|
1259
|
+
const finalId = (clusterBase + finalIdIndex);
|
|
1260
|
+
if (originalOverridingFinal !== undefined) {
|
|
1261
|
+
const unifiedOverride = {
|
|
1262
|
+
override,
|
|
1263
|
+
originalOverridingFinal,
|
|
1264
|
+
};
|
|
1265
|
+
if (serializedLocalState !== undefined) {
|
|
1266
|
+
Common_1.setPropertyIfDefined(localOverridesInverse.get(override), unifiedOverride, 'associatedLocalId');
|
|
1267
|
+
}
|
|
1268
|
+
cluster.overrides.set(finalId, unifiedOverride);
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
const associatedLocal = localOverridesInverse.get(override);
|
|
1272
|
+
if (associatedLocal !== undefined && sessionId !== localSessionId) {
|
|
1273
|
+
// In this case, there is a local ID associated with this override, but this is the first cluster to contain
|
|
1274
|
+
// that override (because only the first cluster will have the string serialized). In this case, the override
|
|
1275
|
+
// needs to hold that local value.
|
|
1276
|
+
cluster.overrides.set(finalId, {
|
|
1277
|
+
override,
|
|
1278
|
+
originalOverridingFinal: finalId,
|
|
1279
|
+
associatedLocalId: associatedLocal,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
else {
|
|
1283
|
+
cluster.overrides.set(finalId, override);
|
|
1284
|
+
}
|
|
1285
|
+
const finalizedOverride = {
|
|
1286
|
+
cluster,
|
|
1287
|
+
originalOverridingFinal: finalId,
|
|
1288
|
+
};
|
|
1289
|
+
if (serializedLocalState !== undefined) {
|
|
1290
|
+
Common_1.setPropertyIfDefined(associatedLocal, finalizedOverride, 'associatedLocalId');
|
|
1291
|
+
}
|
|
1292
|
+
compressor.clustersAndOverridesInversion.set(IdCompressor.createInversionKey(override), finalizedOverride);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
Common_1.assert(compressor.localSession.lastFinalizedLocalId === undefined ||
|
|
1298
|
+
compressor.localIdCount >= -compressor.localSession.lastFinalizedLocalId);
|
|
1299
|
+
return compressor;
|
|
1300
|
+
}
|
|
1301
|
+
static convertToCurrentVersion(serializedCompressor, hasSession) {
|
|
1302
|
+
if (serializedCompressor.version !== currentWrittenVersion) {
|
|
1303
|
+
Common_1.fail('Unknown SerializedIdCompressor version number');
|
|
1304
|
+
}
|
|
1305
|
+
const serialized = serializedCompressor;
|
|
1306
|
+
if (hasSession !== hasOngoingSession(serialized)) {
|
|
1307
|
+
return undefined;
|
|
1308
|
+
}
|
|
1309
|
+
return serialized;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
exports.IdCompressor = IdCompressor;
|
|
1313
|
+
/**
|
|
1314
|
+
* Max allowed cluster size
|
|
1315
|
+
*/
|
|
1316
|
+
IdCompressor.maxClusterSize = 2 ** 20;
|
|
1317
|
+
/**
|
|
1318
|
+
* The version of `IdCompressor` that is currently persisted.
|
|
1319
|
+
*/
|
|
1320
|
+
const currentWrittenVersion = '0.0.1';
|
|
1321
|
+
/**
|
|
1322
|
+
* @returns whether or not the given serialized ID compressor has an ongoing session.
|
|
1323
|
+
*/
|
|
1324
|
+
function hasOngoingSession(serialized) {
|
|
1325
|
+
return serialized.localSessionIndex !== undefined;
|
|
1326
|
+
}
|
|
1327
|
+
exports.hasOngoingSession = hasOngoingSession;
|
|
1328
|
+
function deserializeCluster(serializedCluster) {
|
|
1329
|
+
const [sessionIndex, capacity, countOrOverrides, overrides] = serializedCluster;
|
|
1330
|
+
const hasCount = typeof countOrOverrides === 'number';
|
|
1331
|
+
return {
|
|
1332
|
+
sessionIndex,
|
|
1333
|
+
capacity,
|
|
1334
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1335
|
+
count: (hasCount ? countOrOverrides : capacity),
|
|
1336
|
+
// TODO: This cast can be removed on typescript 4.6
|
|
1337
|
+
overrides: (hasCount ? overrides : countOrOverrides),
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Optimization used by the sorted-btree library to avoid allocating tuples every time a lookup method is called.
|
|
1342
|
+
* Lookup methods on BTree accept a pre-allocated array that it populates with the result of the lookup and retains no ownership
|
|
1343
|
+
* of after the call, so this array may be supplied to any of them. References to this array should not be retained elsewhere and
|
|
1344
|
+
* lookup results should be extracted from the tuple immediately after invocation.
|
|
1345
|
+
*/
|
|
1346
|
+
const reusedArray = [];
|
|
1347
|
+
/**
|
|
1348
|
+
* A numeric comparator used for sorting in descending order.
|
|
1349
|
+
*/
|
|
1350
|
+
function compareFiniteNumbersReversed(a, b) {
|
|
1351
|
+
return b - a;
|
|
1352
|
+
}
|
|
1353
|
+
//# sourceMappingURL=IdCompressor.js.map
|