@fluidframework/tree 2.1.0-281041 → 2.1.1
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/.vscode/Tree.code-workspace +2 -1
- package/CHANGELOG.md +38 -0
- package/README.md +6 -6
- package/api-report/tree.alpha.api.md +1 -1
- package/api-report/tree.beta.api.md +1 -1
- package/api-report/tree.public.api.md +1 -1
- package/dist/core/forest/editableForest.d.ts.map +1 -1
- package/dist/core/forest/editableForest.js +4 -2
- package/dist/core/forest/editableForest.js.map +1 -1
- package/dist/core/tree/anchorSet.d.ts +1 -0
- package/dist/core/tree/anchorSet.d.ts.map +1 -1
- package/dist/core/tree/anchorSet.js +13 -0
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/core/tree/detachedFieldIndex.d.ts +48 -11
- package/dist/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndex.js +144 -20
- package/dist/core/tree/detachedFieldIndex.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexCodec.js +13 -4
- package/dist/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/dist/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- package/dist/core/tree/detachedFieldIndexTypes.d.ts.map +1 -1
- package/dist/core/tree/detachedFieldIndexTypes.js.map +1 -1
- package/dist/core/tree/index.d.ts +2 -1
- package/dist/core/tree/index.d.ts.map +1 -1
- package/dist/core/tree/index.js.map +1 -1
- package/dist/core/tree/visitDelta.d.ts +3 -1
- package/dist/core/tree/visitDelta.d.ts.map +1 -1
- package/dist/core/tree/visitDelta.js +31 -15
- package/dist/core/tree/visitDelta.js.map +1 -1
- package/dist/core/tree/visitorUtils.d.ts +3 -3
- package/dist/core/tree/visitorUtils.d.ts.map +1 -1
- package/dist/core/tree/visitorUtils.js +4 -4
- package/dist/core/tree/visitorUtils.js.map +1 -1
- package/dist/feature-libraries/editableTreeBinder.js +1 -1
- package/dist/feature-libraries/editableTreeBinder.js.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -8
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js +0 -52
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -13
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/flex-tree/index.d.ts +2 -1
- package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/index.js +5 -1
- package/dist/feature-libraries/flex-tree/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/dist/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts +1 -2
- package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +10 -18
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts +1 -4
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +0 -27
- package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/dist/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +1 -1
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +4 -1
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.js +3 -3
- package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js +14 -17
- package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/dist/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/dist/feature-libraries/object-forest/objectForest.js +5 -4
- package/dist/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/sharedTree.d.ts +5 -1
- package/dist/shared-tree/sharedTree.d.ts.map +1 -1
- package/dist/shared-tree/sharedTree.js +8 -1
- package/dist/shared-tree/sharedTree.js.map +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/dist/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/dist/shared-tree/treeApi.js +1 -1
- package/dist/shared-tree/treeApi.js.map +1 -1
- package/dist/shared-tree/treeCheckout.d.ts +10 -1
- package/dist/shared-tree/treeCheckout.d.ts.map +1 -1
- package/dist/shared-tree/treeCheckout.js +47 -4
- package/dist/shared-tree/treeCheckout.js.map +1 -1
- package/dist/shared-tree/treeView.d.ts.map +1 -1
- package/dist/shared-tree/treeView.js +7 -3
- package/dist/shared-tree/treeView.js.map +1 -1
- package/dist/shared-tree-core/branch.d.ts +6 -0
- package/dist/shared-tree-core/branch.d.ts.map +1 -1
- package/dist/shared-tree-core/branch.js +2 -0
- package/dist/shared-tree-core/branch.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts +4 -0
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +6 -0
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/dist/simple-tree/arrayNode.js +1 -1
- package/dist/simple-tree/arrayNode.js.map +1 -1
- package/dist/simple-tree/index.d.ts +3 -3
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js +2 -1
- package/dist/simple-tree/index.js.map +1 -1
- package/dist/simple-tree/proxies.d.ts.map +1 -1
- package/dist/simple-tree/proxies.js +7 -21
- package/dist/simple-tree/proxies.js.map +1 -1
- package/dist/simple-tree/proxyBinding.d.ts +4 -0
- package/dist/simple-tree/proxyBinding.d.ts.map +1 -1
- package/dist/simple-tree/proxyBinding.js +23 -1
- package/dist/simple-tree/proxyBinding.js.map +1 -1
- package/dist/simple-tree/tree.d.ts.map +1 -1
- package/dist/simple-tree/tree.js +1 -1
- package/dist/simple-tree/tree.js.map +1 -1
- package/dist/simple-tree/treeNodeApi.d.ts +2 -75
- package/dist/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/dist/simple-tree/treeNodeApi.js +7 -15
- package/dist/simple-tree/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/treeNodeKernel.d.ts +26 -0
- package/dist/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/dist/simple-tree/treeNodeKernel.js +83 -0
- package/dist/simple-tree/treeNodeKernel.js.map +1 -0
- package/dist/simple-tree/types.d.ts +73 -0
- package/dist/simple-tree/types.d.ts.map +1 -1
- package/dist/simple-tree/types.js +89 -1
- package/dist/simple-tree/types.js.map +1 -1
- package/dist/util/breakable.js +1 -1
- package/dist/util/breakable.js.map +1 -1
- package/dist/util/index.d.ts +1 -1
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js.map +1 -1
- package/lib/core/forest/editableForest.d.ts.map +1 -1
- package/lib/core/forest/editableForest.js +4 -2
- package/lib/core/forest/editableForest.js.map +1 -1
- package/lib/core/tree/anchorSet.d.ts +1 -0
- package/lib/core/tree/anchorSet.d.ts.map +1 -1
- package/lib/core/tree/anchorSet.js +13 -0
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/core/tree/detachedFieldIndex.d.ts +48 -11
- package/lib/core/tree/detachedFieldIndex.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndex.js +145 -21
- package/lib/core/tree/detachedFieldIndex.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexCodec.js +13 -4
- package/lib/core/tree/detachedFieldIndexCodec.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexFormat.js.map +1 -1
- package/lib/core/tree/detachedFieldIndexTypes.d.ts +39 -4
- package/lib/core/tree/detachedFieldIndexTypes.d.ts.map +1 -1
- package/lib/core/tree/detachedFieldIndexTypes.js.map +1 -1
- package/lib/core/tree/index.d.ts +2 -1
- package/lib/core/tree/index.d.ts.map +1 -1
- package/lib/core/tree/index.js.map +1 -1
- package/lib/core/tree/visitDelta.d.ts +3 -1
- package/lib/core/tree/visitDelta.d.ts.map +1 -1
- package/lib/core/tree/visitDelta.js +31 -15
- package/lib/core/tree/visitDelta.js.map +1 -1
- package/lib/core/tree/visitorUtils.d.ts +3 -3
- package/lib/core/tree/visitorUtils.d.ts.map +1 -1
- package/lib/core/tree/visitorUtils.js +4 -4
- package/lib/core/tree/visitorUtils.js.map +1 -1
- package/lib/feature-libraries/editableTreeBinder.js +1 -1
- package/lib/feature-libraries/editableTreeBinder.js.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts +1 -8
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js +2 -54
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +1 -13
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js +0 -2
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/flex-tree/index.d.ts +2 -1
- package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/index.js +2 -1
- package/lib/feature-libraries/flex-tree/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts +1 -2
- package/lib/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts +1 -2
- package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +12 -20
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts +1 -4
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +3 -30
- package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.d.ts.map +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js +1 -1
- package/lib/feature-libraries/forest-summary/forestSummarizer.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +1 -1
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +1 -1
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.js +3 -3
- package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeCodecs.js.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js +14 -17
- package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.d.ts +3 -2
- package/lib/feature-libraries/object-forest/objectForest.d.ts.map +1 -1
- package/lib/feature-libraries/object-forest/objectForest.js +5 -4
- package/lib/feature-libraries/object-forest/objectForest.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/sharedTree.d.ts +5 -1
- package/lib/shared-tree/sharedTree.d.ts.map +1 -1
- package/lib/shared-tree/sharedTree.js +8 -1
- package/lib/shared-tree/sharedTree.js.map +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js +1 -1
- package/lib/shared-tree/sharedTreeChangeEnricher.js.map +1 -1
- package/lib/shared-tree/treeApi.js +1 -1
- package/lib/shared-tree/treeApi.js.map +1 -1
- package/lib/shared-tree/treeCheckout.d.ts +10 -1
- package/lib/shared-tree/treeCheckout.d.ts.map +1 -1
- package/lib/shared-tree/treeCheckout.js +47 -4
- package/lib/shared-tree/treeCheckout.js.map +1 -1
- package/lib/shared-tree/treeView.d.ts.map +1 -1
- package/lib/shared-tree/treeView.js +4 -0
- package/lib/shared-tree/treeView.js.map +1 -1
- package/lib/shared-tree-core/branch.d.ts +6 -0
- package/lib/shared-tree-core/branch.d.ts.map +1 -1
- package/lib/shared-tree-core/branch.js +2 -0
- package/lib/shared-tree-core/branch.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts +4 -0
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +6 -0
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/simple-tree/arrayNode.js +2 -2
- package/lib/simple-tree/arrayNode.js.map +1 -1
- package/lib/simple-tree/index.d.ts +3 -3
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js +1 -1
- package/lib/simple-tree/index.js.map +1 -1
- package/lib/simple-tree/proxies.d.ts.map +1 -1
- package/lib/simple-tree/proxies.js +7 -21
- package/lib/simple-tree/proxies.js.map +1 -1
- package/lib/simple-tree/proxyBinding.d.ts +4 -0
- package/lib/simple-tree/proxyBinding.d.ts.map +1 -1
- package/lib/simple-tree/proxyBinding.js +19 -0
- package/lib/simple-tree/proxyBinding.js.map +1 -1
- package/lib/simple-tree/tree.d.ts.map +1 -1
- package/lib/simple-tree/tree.js +1 -1
- package/lib/simple-tree/tree.js.map +1 -1
- package/lib/simple-tree/treeNodeApi.d.ts +2 -75
- package/lib/simple-tree/treeNodeApi.d.ts.map +1 -1
- package/lib/simple-tree/treeNodeApi.js +9 -17
- package/lib/simple-tree/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/treeNodeKernel.d.ts +26 -0
- package/lib/simple-tree/treeNodeKernel.d.ts.map +1 -0
- package/lib/simple-tree/treeNodeKernel.js +79 -0
- package/lib/simple-tree/treeNodeKernel.js.map +1 -0
- package/lib/simple-tree/types.d.ts +73 -0
- package/lib/simple-tree/types.d.ts.map +1 -1
- package/lib/simple-tree/types.js +90 -2
- package/lib/simple-tree/types.js.map +1 -1
- package/lib/util/breakable.js +1 -1
- package/lib/util/breakable.js.map +1 -1
- package/lib/util/index.d.ts +1 -1
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js.map +1 -1
- package/package.json +23 -23
- package/src/core/forest/editableForest.ts +10 -2
- package/src/core/tree/anchorSet.ts +14 -0
- package/src/core/tree/detachedFieldIndex.ts +217 -35
- package/src/core/tree/detachedFieldIndexCodec.ts +17 -8
- package/src/core/tree/detachedFieldIndexFormat.ts +1 -1
- package/src/core/tree/detachedFieldIndexTypes.ts +41 -5
- package/src/core/tree/index.ts +2 -1
- package/src/core/tree/visitDelta.ts +57 -16
- package/src/core/tree/visitorUtils.ts +7 -4
- package/src/feature-libraries/editableTreeBinder.ts +1 -1
- package/src/feature-libraries/flex-map-tree/mapTreeNode.ts +1 -65
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +0 -19
- package/src/feature-libraries/flex-tree/index.ts +7 -1
- package/src/feature-libraries/flex-tree/lazyEntity.ts +0 -3
- package/src/feature-libraries/flex-tree/lazyField.ts +14 -26
- package/src/feature-libraries/flex-tree/lazyNode.ts +1 -42
- package/src/feature-libraries/forest-summary/forestSummarizer.ts +1 -0
- package/src/feature-libraries/index.ts +3 -0
- package/src/feature-libraries/modular-schema/discrepancies.ts +3 -3
- package/src/feature-libraries/modular-schema/modularChangeCodecs.ts +1 -1
- package/src/feature-libraries/modular-schema/modularChangeFamily.ts +22 -20
- package/src/feature-libraries/object-forest/objectForest.ts +7 -3
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/sharedTree.ts +8 -1
- package/src/shared-tree/sharedTreeChangeEnricher.ts +1 -1
- package/src/shared-tree/treeApi.ts +1 -1
- package/src/shared-tree/treeCheckout.ts +56 -5
- package/src/shared-tree/treeView.ts +5 -0
- package/src/shared-tree-core/branch.ts +9 -0
- package/src/shared-tree-core/sharedTreeCore.ts +7 -0
- package/src/simple-tree/arrayNode.ts +2 -2
- package/src/simple-tree/index.ts +3 -3
- package/src/simple-tree/proxies.ts +8 -29
- package/src/simple-tree/proxyBinding.ts +23 -0
- package/src/simple-tree/tree.ts +4 -1
- package/src/simple-tree/treeNodeApi.ts +14 -96
- package/src/simple-tree/treeNodeKernel.ts +91 -0
- package/src/simple-tree/types.ts +233 -2
- package/src/util/breakable.ts +1 -1
- package/src/util/index.ts +1 -0
|
@@ -387,6 +387,11 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
387
387
|
SharedTreeBranch<SharedTreeEditBuilder, SharedTreeChange>
|
|
388
388
|
>();
|
|
389
389
|
|
|
390
|
+
/**
|
|
391
|
+
* copies of the removed roots used as snapshots for reverting to previous state when transactions are aborted
|
|
392
|
+
*/
|
|
393
|
+
private readonly removedRootsSnapshots: DetachedFieldIndex[] = [];
|
|
394
|
+
|
|
390
395
|
/**
|
|
391
396
|
* The name of the telemetry event logged for calls to {@link TreeCheckout.revertRevertible}.
|
|
392
397
|
* @privateRemarks Exposed for testing purposes.
|
|
@@ -405,7 +410,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
405
410
|
private readonly mintRevisionTag: () => RevisionTag,
|
|
406
411
|
private readonly revisionTagCodec: RevisionTagCodec,
|
|
407
412
|
private readonly idCompressor: IIdCompressor,
|
|
408
|
-
private
|
|
413
|
+
private removedRoots: DetachedFieldIndex = makeDetachedFieldIndex(
|
|
409
414
|
"repair",
|
|
410
415
|
revisionTagCodec,
|
|
411
416
|
idCompressor,
|
|
@@ -413,18 +418,38 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
413
418
|
/** Optional logger for telemetry. */
|
|
414
419
|
private readonly logger?: ITelemetryLoggerExt,
|
|
415
420
|
) {
|
|
421
|
+
// when a transaction is started, take a snapshot of the current state of removed roots
|
|
422
|
+
branch.on("transactionStarted", () => {
|
|
423
|
+
this.removedRootsSnapshots.push(this.removedRoots.clone());
|
|
424
|
+
});
|
|
425
|
+
// when a transaction is committed, the latest snapshot of removed roots can be discarded
|
|
426
|
+
branch.on("transactionCommitted", () => {
|
|
427
|
+
this.removedRootsSnapshots.pop();
|
|
428
|
+
});
|
|
429
|
+
// after a transaction is rolled back, revert removed roots back to the latest snapshot
|
|
430
|
+
branch.on("transactionRolledBack", () => {
|
|
431
|
+
const snapshot = this.removedRootsSnapshots.pop();
|
|
432
|
+
assert(snapshot !== undefined, 0x9ae /* a snapshot for removed roots does not exist */);
|
|
433
|
+
this.removedRoots = snapshot;
|
|
434
|
+
});
|
|
435
|
+
|
|
416
436
|
// We subscribe to `beforeChange` rather than `afterChange` here because it's possible that the change is invalid WRT our forest.
|
|
417
437
|
// For example, a bug in the editor might produce a malformed change object and thus applying the change to the forest will throw an error.
|
|
418
438
|
// In such a case we will crash here, preventing the change from being added to the commit graph, and preventing `afterChange` from firing.
|
|
419
439
|
// One important consequence of this is that we will not submit the op containing the invalid change, since op submissions happens in response to `afterChange`.
|
|
420
440
|
branch.on("beforeChange", (event) => {
|
|
421
441
|
if (event.change !== undefined) {
|
|
442
|
+
const revision =
|
|
443
|
+
event.type === "replace"
|
|
444
|
+
? event.newCommits[event.newCommits.length - 1].revision
|
|
445
|
+
: event.change.revision;
|
|
446
|
+
|
|
422
447
|
// Conflicts due to schema will be empty and thus are not applied.
|
|
423
448
|
for (const change of event.change.change.changes) {
|
|
424
449
|
if (change.type === "data") {
|
|
425
|
-
const delta = intoDelta(tagChange(change.innerChange,
|
|
450
|
+
const delta = intoDelta(tagChange(change.innerChange, revision));
|
|
426
451
|
this.withCombinedVisitor((visitor) => {
|
|
427
|
-
visitDelta(delta, visitor, this.removedRoots);
|
|
452
|
+
visitDelta(delta, visitor, this.removedRoots, revision);
|
|
428
453
|
});
|
|
429
454
|
} else if (change.type === "schema") {
|
|
430
455
|
// Schema changes from a current to a new schema are expected to be backwards compatible.
|
|
@@ -518,6 +543,24 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
518
543
|
this.events.emit("commitApplied", data, getRevertible);
|
|
519
544
|
withinEventContext = false;
|
|
520
545
|
});
|
|
546
|
+
|
|
547
|
+
// When the branch is trimmed, we can garbage collect any repair data whose latest relevant revision is one of the
|
|
548
|
+
// trimmed revisions.
|
|
549
|
+
branch.on("ancestryTrimmed", (revisions) => {
|
|
550
|
+
this.withCombinedVisitor((visitor) => {
|
|
551
|
+
revisions.forEach((revision) => {
|
|
552
|
+
// get all the roots last created or used by the revision
|
|
553
|
+
const roots = this.removedRoots.getRootsLastTouchedByRevision(revision);
|
|
554
|
+
|
|
555
|
+
// get the detached field for the root and delete it from the removed roots
|
|
556
|
+
for (const root of roots) {
|
|
557
|
+
visitor.destroy(this.removedRoots.toFieldKey(root), 1);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
this.removedRoots.deleteRootsLastTouchedByRevision(revision);
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
});
|
|
521
564
|
}
|
|
522
565
|
|
|
523
566
|
private withCombinedVisitor(fn: (visitor: DeltaVisitor) => void): void {
|
|
@@ -574,7 +617,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
574
617
|
this.checkNotDisposed();
|
|
575
618
|
assert(
|
|
576
619
|
!view.transaction.inProgress(),
|
|
577
|
-
|
|
620
|
+
0x9af /* A view cannot be rebased while it has a pending transaction */,
|
|
578
621
|
);
|
|
579
622
|
view.branch.rebaseOnto(this.branch);
|
|
580
623
|
}
|
|
@@ -590,7 +633,7 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
590
633
|
this.checkNotDisposed();
|
|
591
634
|
assert(
|
|
592
635
|
!this.transaction.inProgress(),
|
|
593
|
-
|
|
636
|
+
0x9b0 /* Views cannot be merged into a view while it has a pending transaction */,
|
|
594
637
|
);
|
|
595
638
|
while (view.transaction.inProgress()) {
|
|
596
639
|
view.transaction.commit();
|
|
@@ -629,6 +672,14 @@ export class TreeCheckout implements ITreeCheckoutFork {
|
|
|
629
672
|
return trees;
|
|
630
673
|
}
|
|
631
674
|
|
|
675
|
+
/**
|
|
676
|
+
* This sets the tip revision as the latest relevant revision for any removed roots that are loaded from a summary.
|
|
677
|
+
* This needs to be called right after loading {@link this.removedRoots} from a summary to allow loaded data to be garbage collected.
|
|
678
|
+
*/
|
|
679
|
+
public setTipRevisionForLoadedData(revision: RevisionTag): void {
|
|
680
|
+
this.removedRoots.setRevisionsForLoadedData(revision);
|
|
681
|
+
}
|
|
682
|
+
|
|
632
683
|
private purgeRevertibles(): void {
|
|
633
684
|
for (const revertible of this.revertibles) {
|
|
634
685
|
revertible.dispose();
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type NodeKeyManager,
|
|
13
13
|
getTreeContext,
|
|
14
14
|
} from "../feature-libraries/index.js";
|
|
15
|
+
import { tryDisposeTreeNode } from "../simple-tree/index.js";
|
|
15
16
|
import { type IDisposable, disposeSymbol } from "../util/index.js";
|
|
16
17
|
|
|
17
18
|
import type { ITreeCheckout, ITreeCheckoutFork, TreeCheckout } from "./treeCheckout.js";
|
|
@@ -94,6 +95,10 @@ export class CheckoutFlexTreeView<
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
public [disposeSymbol](): void {
|
|
98
|
+
for (const anchorNode of this.checkout.forest.anchors) {
|
|
99
|
+
tryDisposeTreeNode(anchorNode);
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
this.context[disposeSymbol]();
|
|
98
103
|
this.onDispose?.();
|
|
99
104
|
}
|
|
@@ -137,6 +137,13 @@ export interface SharedTreeBranchEvents<TEditor extends ChangeFamilyEditor, TCha
|
|
|
137
137
|
*/
|
|
138
138
|
transactionAborted(isOuterTransaction: boolean): void;
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Fired after the current transaction is completely rolled back.
|
|
142
|
+
* @param isOuterTransaction - true iff the transaction being aborted is the outermost transaction
|
|
143
|
+
* as opposed to a nested transaction.
|
|
144
|
+
*/
|
|
145
|
+
transactionRolledBack(isOuterTransaction: boolean): void;
|
|
146
|
+
|
|
140
147
|
/**
|
|
141
148
|
* Fired after the current transaction is committed.
|
|
142
149
|
* @param isOuterTransaction - true iff the transaction being committed is the outermost transaction
|
|
@@ -361,6 +368,7 @@ export class SharedTreeBranch<
|
|
|
361
368
|
|
|
362
369
|
this.emit("transactionAborted", this.transactions.size === 0);
|
|
363
370
|
if (commits.length === 0) {
|
|
371
|
+
this.emit("transactionRolledBack", this.transactions.size === 0);
|
|
364
372
|
return [undefined, []];
|
|
365
373
|
}
|
|
366
374
|
|
|
@@ -387,6 +395,7 @@ export class SharedTreeBranch<
|
|
|
387
395
|
this.emit("beforeChange", changeEvent);
|
|
388
396
|
this.head = startCommit;
|
|
389
397
|
this.emit("afterChange", changeEvent);
|
|
398
|
+
this.emit("transactionRolledBack", this.transactions.size === 0);
|
|
390
399
|
return [change, commits];
|
|
391
400
|
}
|
|
392
401
|
|
|
@@ -95,6 +95,13 @@ export class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>
|
|
|
95
95
|
return this.getLocalBranch().editor;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Gets the revision at the head of the trunk.
|
|
100
|
+
*/
|
|
101
|
+
protected get trunkHeadRevision(): RevisionTag {
|
|
102
|
+
return this.editManager.getTrunkHead().revision;
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
/**
|
|
99
106
|
* Used to encode/decode messages sent to/received from the Fluid runtime.
|
|
100
107
|
*
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
markContentType,
|
|
28
28
|
prepareContentForHydration,
|
|
29
29
|
} from "./proxies.js";
|
|
30
|
-
import { getFlexNode } from "./proxyBinding.js";
|
|
30
|
+
import { getFlexNode, getKernel } from "./proxyBinding.js";
|
|
31
31
|
import {
|
|
32
32
|
NodeKind,
|
|
33
33
|
type ImplicitAllowedTypes,
|
|
@@ -683,7 +683,7 @@ abstract class CustomArrayNodeBase<const T extends ImplicitAllowedTypes>
|
|
|
683
683
|
input: Iterable<InsertableTreeNodeFromImplicitAllowedTypes<T>> | InternalTreeNode,
|
|
684
684
|
) {
|
|
685
685
|
super(input);
|
|
686
|
-
|
|
686
|
+
getKernel(this).on("nodeChanged", () => {
|
|
687
687
|
this.#generationNumber += 1;
|
|
688
688
|
});
|
|
689
689
|
}
|
package/src/simple-tree/index.ts
CHANGED
|
@@ -39,8 +39,8 @@ export {
|
|
|
39
39
|
type ApplyKind,
|
|
40
40
|
} from "./schemaTypes.js";
|
|
41
41
|
export { SchemaFactory, type ScopedSchemaName } from "./schemaFactory.js";
|
|
42
|
-
export { getFlexNode } from "./proxyBinding.js";
|
|
43
|
-
export { treeNodeApi, type TreeNodeApi
|
|
42
|
+
export { getFlexNode, tryDisposeTreeNode } from "./proxyBinding.js";
|
|
43
|
+
export { treeNodeApi, type TreeNodeApi } from "./treeNodeApi.js";
|
|
44
44
|
export { toFlexSchema, cursorFromUnhydratedRoot } from "./toFlexSchema.js";
|
|
45
45
|
export type {
|
|
46
46
|
FieldHasDefaultUnsafe,
|
|
@@ -82,7 +82,7 @@ export {
|
|
|
82
82
|
|
|
83
83
|
// TreeNode is only type exported, which prevents use of the class object for unsupported use-cases like direct sub-classing and instancof.
|
|
84
84
|
// See docs on TreeNode for more details.
|
|
85
|
-
export type { TreeNode, Unhydrated, InternalTreeNode } from "./types.js";
|
|
85
|
+
export type { TreeChangeEvents, TreeNode, Unhydrated, InternalTreeNode } from "./types.js";
|
|
86
86
|
export {
|
|
87
87
|
TreeArrayNode,
|
|
88
88
|
IterableTreeArrayContent,
|
|
@@ -20,9 +20,7 @@ import {
|
|
|
20
20
|
type FlexFieldSchema,
|
|
21
21
|
type FlexTreeField,
|
|
22
22
|
type FlexTreeNode,
|
|
23
|
-
type FlexTreeNodeEvents,
|
|
24
23
|
type FlexTreeTypedField,
|
|
25
|
-
type MapTreeNode,
|
|
26
24
|
tryGetMapTreeNode,
|
|
27
25
|
typeNameSymbol,
|
|
28
26
|
isFlexTreeNode,
|
|
@@ -32,7 +30,6 @@ import { type Mutable, fail, isReadonlyArray } from "../util/index.js";
|
|
|
32
30
|
import { anchorProxy, tryGetFlexNode, tryGetProxy } from "./proxyBinding.js";
|
|
33
31
|
import { tryGetSimpleNodeSchema } from "./schemaCaching.js";
|
|
34
32
|
import type { TreeNode, Unhydrated } from "./types.js";
|
|
35
|
-
import type { Off } from "../events/index.js";
|
|
36
33
|
|
|
37
34
|
/**
|
|
38
35
|
* Detects if the given 'candidate' is a TreeNode.
|
|
@@ -116,7 +113,6 @@ export function getOrCreateNodeProxy(flexNode: FlexTreeNode): TreeNode | TreeVal
|
|
|
116
113
|
/** The path of a proxy, relative to the root of the content tree that the proxy belongs to */
|
|
117
114
|
interface RelativeProxyPath {
|
|
118
115
|
readonly path: UpPath;
|
|
119
|
-
readonly mapTreeNode: MapTreeNode;
|
|
120
116
|
readonly proxy: TreeNode;
|
|
121
117
|
}
|
|
122
118
|
|
|
@@ -150,8 +146,8 @@ export function prepareContentForHydration(
|
|
|
150
146
|
proxyPaths: [],
|
|
151
147
|
};
|
|
152
148
|
|
|
153
|
-
walkMapTree(content, proxies.rootPath, (p,
|
|
154
|
-
proxies.proxyPaths.push({ path: p,
|
|
149
|
+
walkMapTree(content, proxies.rootPath, (p, proxy) => {
|
|
150
|
+
proxies.proxyPaths.push({ path: p, proxy });
|
|
155
151
|
});
|
|
156
152
|
|
|
157
153
|
bindProxies([proxies], forest);
|
|
@@ -172,8 +168,8 @@ function prepareArrayContentForHydration(
|
|
|
172
168
|
},
|
|
173
169
|
proxyPaths: [],
|
|
174
170
|
});
|
|
175
|
-
walkMapTree(content[i], proxies[i].rootPath, (p,
|
|
176
|
-
proxies[i].proxyPaths.push({ path: p,
|
|
171
|
+
walkMapTree(content[i], proxies[i].rootPath, (p, proxy) => {
|
|
172
|
+
proxies[i].proxyPaths.push({ path: p, proxy });
|
|
177
173
|
});
|
|
178
174
|
}
|
|
179
175
|
|
|
@@ -183,13 +179,13 @@ function prepareArrayContentForHydration(
|
|
|
183
179
|
function walkMapTree(
|
|
184
180
|
mapTree: MapTree,
|
|
185
181
|
path: UpPath,
|
|
186
|
-
onVisitTreeNode: (path: UpPath,
|
|
182
|
+
onVisitTreeNode: (path: UpPath, treeNode: TreeNode) => void,
|
|
187
183
|
): void {
|
|
188
184
|
const mapTreeNode = tryGetMapTreeNode(mapTree);
|
|
189
185
|
if (mapTreeNode !== undefined) {
|
|
190
186
|
const treeNode = tryGetProxy(mapTreeNode);
|
|
191
187
|
if (treeNode !== undefined) {
|
|
192
|
-
onVisitTreeNode(path,
|
|
188
|
+
onVisitTreeNode(path, treeNode);
|
|
193
189
|
}
|
|
194
190
|
}
|
|
195
191
|
|
|
@@ -215,25 +211,8 @@ function bindProxies(proxies: RootedProxyPaths[], forest: IForestSubscription):
|
|
|
215
211
|
let i = 0;
|
|
216
212
|
const off = forest.on("afterRootFieldCreated", (fieldKey) => {
|
|
217
213
|
(proxies[i].rootPath as Mutable<UpPath>).parentField = fieldKey;
|
|
218
|
-
for (const { path,
|
|
219
|
-
|
|
220
|
-
mapTreeNode.forwardEvents({
|
|
221
|
-
on<K extends keyof FlexTreeNodeEvents>(
|
|
222
|
-
eventName: K,
|
|
223
|
-
listener: FlexTreeNodeEvents[K],
|
|
224
|
-
): Off {
|
|
225
|
-
switch (eventName) {
|
|
226
|
-
case "nodeChanged": {
|
|
227
|
-
return anchorNode.on("childrenChangedAfterBatch", listener);
|
|
228
|
-
}
|
|
229
|
-
case "treeChanged": {
|
|
230
|
-
return anchorNode.on("subtreeChangedAfterBatch", listener);
|
|
231
|
-
}
|
|
232
|
-
default:
|
|
233
|
-
fail("Unexpected event subscription");
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
});
|
|
214
|
+
for (const { path, proxy } of proxies[i].proxyPaths) {
|
|
215
|
+
anchorProxy(forest.anchors, path, proxy);
|
|
237
216
|
}
|
|
238
217
|
if (++i === proxies.length) {
|
|
239
218
|
off();
|
|
@@ -29,6 +29,7 @@ import type { TreeNode } from "./types.js";
|
|
|
29
29
|
// eslint-disable-next-line import/no-internal-modules
|
|
30
30
|
import { makeTree } from "../feature-libraries/flex-tree/lazyNode.js";
|
|
31
31
|
import type { TreeMapNode } from "./mapNode.js";
|
|
32
|
+
import { TreeNodeKernel } from "./treeNodeKernel.js";
|
|
32
33
|
|
|
33
34
|
// This file contains various maps and helpers for supporting proxy binding (a.k.a. proxy hydration).
|
|
34
35
|
// See ./ProxyBinding.md for a high-level overview of the process.
|
|
@@ -185,9 +186,31 @@ function bindProxyToAnchorNode(proxy: TreeNode, anchorNode: AnchorNode): void {
|
|
|
185
186
|
proxyToAnchorNode.set(proxy, anchorNode);
|
|
186
187
|
// However, it's fine for an anchor node to rotate through different proxies when the content at that place in the tree is replaced.
|
|
187
188
|
anchorNode.slots.set(proxySlot, proxy);
|
|
189
|
+
getKernel(proxy).hydrate(anchorNode);
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
/**
|
|
191
193
|
* Given a node's schema, return the corresponding object in the proxy-based API.
|
|
192
194
|
*/
|
|
193
195
|
type TypedNode<TSchema extends FlexTreeNodeSchema> = TreeNode & WithType<TSchema["name"]>;
|
|
196
|
+
|
|
197
|
+
export function createKernel(node: TreeNode): void {
|
|
198
|
+
treeNodeToKernel.set(node, new TreeNodeKernel(node));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getKernel(node: TreeNode): TreeNodeKernel {
|
|
202
|
+
const kernel = treeNodeToKernel.get(node);
|
|
203
|
+
assert(kernel !== undefined, 0x9b1 /* Expected tree node to have kernel */);
|
|
204
|
+
return kernel;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function tryDisposeTreeNode(anchorNode: AnchorNode): void {
|
|
208
|
+
const treeNode = anchorNode.slots.get(proxySlot);
|
|
209
|
+
if (treeNode !== undefined) {
|
|
210
|
+
const kernel = treeNodeToKernel.get(treeNode);
|
|
211
|
+
assert(kernel !== undefined, 0x9b2 /* Expected tree node to have kernel */);
|
|
212
|
+
kernel.dispose();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const treeNodeToKernel = new WeakMap<TreeNode, TreeNodeKernel>();
|
package/src/simple-tree/tree.ts
CHANGED
|
@@ -165,7 +165,10 @@ export function walkNodeSchema(
|
|
|
165
165
|
walkAllowedTypes(field.allowedTypeSet, visitor, visitedSet);
|
|
166
166
|
}
|
|
167
167
|
} else {
|
|
168
|
-
assert(
|
|
168
|
+
assert(
|
|
169
|
+
schema.kind === NodeKind.Array || schema.kind === NodeKind.Map,
|
|
170
|
+
0x9b3 /* invalid schema */,
|
|
171
|
+
);
|
|
169
172
|
const childTypes = schema.info as ImplicitAllowedTypes;
|
|
170
173
|
walkFieldSchema(childTypes, visitor, visitedSet);
|
|
171
174
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { assert
|
|
6
|
+
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
7
|
|
|
8
8
|
import { Multiplicity, rootFieldKey } from "../core/index.js";
|
|
9
9
|
import {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { fail, extractFromOpaque, isReadonlyArray } from "../util/index.js";
|
|
17
17
|
|
|
18
18
|
import { getOrCreateNodeProxy, isTreeNode } from "./proxies.js";
|
|
19
|
-
import { getFlexNode } from "./proxyBinding.js";
|
|
19
|
+
import { getFlexNode, getKernel } from "./proxyBinding.js";
|
|
20
20
|
import { tryGetSimpleNodeSchema } from "./schemaCaching.js";
|
|
21
21
|
import {
|
|
22
22
|
NodeKind,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
type ImplicitAllowedTypes,
|
|
28
28
|
type TreeNodeFromImplicitAllowedTypes,
|
|
29
29
|
} from "./schemaTypes.js";
|
|
30
|
-
import type { TreeNode } from "./types.js";
|
|
30
|
+
import type { TreeNode, TreeChangeEvents } from "./types.js";
|
|
31
31
|
import {
|
|
32
32
|
booleanSchema,
|
|
33
33
|
handleSchema,
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from "./leafNodeSchema.js";
|
|
38
38
|
import { isFluidHandle } from "@fluidframework/runtime-utils/internal";
|
|
39
39
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
40
|
+
import type { Off } from "../events/index.js";
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Provides various functions for analyzing {@link TreeNode}s.
|
|
@@ -102,7 +103,7 @@ export interface TreeNodeApi {
|
|
|
102
103
|
/**
|
|
103
104
|
* Returns the {@link TreeStatus} of the given node.
|
|
104
105
|
*/
|
|
105
|
-
|
|
106
|
+
status(node: TreeNode): TreeStatus;
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
109
|
* Returns the {@link SchemaFactory.identifier | identifier} of the given node in the most compressed form possible.
|
|
@@ -127,7 +128,7 @@ export interface TreeNodeApi {
|
|
|
127
128
|
* The `Tree` object holds various functions for analyzing {@link TreeNode}s.
|
|
128
129
|
*/
|
|
129
130
|
export const treeNodeApi: TreeNodeApi = {
|
|
130
|
-
parent
|
|
131
|
+
parent(node: TreeNode): TreeNode | undefined {
|
|
131
132
|
const editNode = getFlexNode(node).parentField.parent.parent;
|
|
132
133
|
if (editNode === undefined) {
|
|
133
134
|
return undefined;
|
|
@@ -140,7 +141,7 @@ export const treeNodeApi: TreeNodeApi = {
|
|
|
140
141
|
);
|
|
141
142
|
return output;
|
|
142
143
|
},
|
|
143
|
-
key
|
|
144
|
+
key(node: TreeNode): string | number {
|
|
144
145
|
// If the parent is undefined, then this node is under the root field,
|
|
145
146
|
// so we know its key is the special root one.
|
|
146
147
|
const parent = treeNodeApi.parent(node);
|
|
@@ -156,28 +157,20 @@ export const treeNodeApi: TreeNodeApi = {
|
|
|
156
157
|
const viewKey = getViewKeyFromStoredKey(parentSchema, storedKey);
|
|
157
158
|
return viewKey;
|
|
158
159
|
},
|
|
159
|
-
on
|
|
160
|
+
on<K extends keyof TreeChangeEvents>(
|
|
160
161
|
node: TreeNode,
|
|
161
162
|
eventName: K,
|
|
162
163
|
listener: TreeChangeEvents[K],
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
switch (eventName) {
|
|
166
|
-
case "nodeChanged":
|
|
167
|
-
return flex.on("nodeChanged", listener);
|
|
168
|
-
case "treeChanged":
|
|
169
|
-
return flex.on("treeChanged", listener);
|
|
170
|
-
default:
|
|
171
|
-
return unreachableCase(eventName);
|
|
172
|
-
}
|
|
164
|
+
): Off {
|
|
165
|
+
return getKernel(node).on(eventName, listener);
|
|
173
166
|
},
|
|
174
|
-
status
|
|
175
|
-
return
|
|
167
|
+
status(node: TreeNode): TreeStatus {
|
|
168
|
+
return getKernel(node).getStatus();
|
|
176
169
|
},
|
|
177
|
-
is
|
|
170
|
+
is<TSchema extends ImplicitAllowedTypes>(
|
|
178
171
|
value: unknown,
|
|
179
172
|
schema: TSchema,
|
|
180
|
-
): value is TreeNodeFromImplicitAllowedTypes<TSchema>
|
|
173
|
+
): value is TreeNodeFromImplicitAllowedTypes<TSchema> {
|
|
181
174
|
const actualSchema = tryGetSchema(value);
|
|
182
175
|
if (actualSchema === undefined) {
|
|
183
176
|
return false;
|
|
@@ -304,78 +297,3 @@ function getViewKeyFromStoredKey(
|
|
|
304
297
|
|
|
305
298
|
return storedKey;
|
|
306
299
|
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* A collection of events that can be emitted by a {@link TreeNode}.
|
|
310
|
-
*
|
|
311
|
-
* @privateRemarks
|
|
312
|
-
* TODO: add a way to subscribe to a specific field (for nodeChanged and treeChanged).
|
|
313
|
-
* Probably have object node and map node specific APIs for this.
|
|
314
|
-
*
|
|
315
|
-
* TODO: ensure that subscription API for fields aligns with API for subscribing to the root.
|
|
316
|
-
*
|
|
317
|
-
* TODO: add more wider area (avoid needing tons of nodeChanged registration) events for use-cases other than treeChanged.
|
|
318
|
-
* Some ideas:
|
|
319
|
-
*
|
|
320
|
-
* - treeChanged, but with some subtrees/fields/paths excluded
|
|
321
|
-
* - helper to batch several nodeChanged calls to a treeChanged scope
|
|
322
|
-
* - parent change (ex: registration on the parent field for a specific index: maybe allow it for a range. Ex: node event takes optional field and optional index range?)
|
|
323
|
-
* - new content inserted into subtree. Either provide event for this and/or enough info to treeChanged to find and search the new sub-trees.
|
|
324
|
-
* Add separate (non event related) API to efficiently scan tree for given set of types (using low level cursor and schema based filtering)
|
|
325
|
-
* to allow efficiently searching for new content (and initial content) of a given type.
|
|
326
|
-
*
|
|
327
|
-
* @sealed @public
|
|
328
|
-
*/
|
|
329
|
-
export interface TreeChangeEvents {
|
|
330
|
-
/**
|
|
331
|
-
* Emitted by a node after a batch of changes has been applied to the tree, if a change affected the node, where a
|
|
332
|
-
* change is:
|
|
333
|
-
*
|
|
334
|
-
* - For an object node, when the value of one of its properties changes (i.e., the property's value is set
|
|
335
|
-
* to something else, including `undefined`).
|
|
336
|
-
*
|
|
337
|
-
* - For an array node, when an element is added, removed, or moved.
|
|
338
|
-
*
|
|
339
|
-
* - For a map node, when an entry is added, updated, or removed.
|
|
340
|
-
*
|
|
341
|
-
* @remarks
|
|
342
|
-
* This event is not emitted when:
|
|
343
|
-
*
|
|
344
|
-
* - Properties of a child node change. Notably, updates to an array node or a map node (like adding or removing
|
|
345
|
-
* elements/entries) will emit this event on the array/map node itself, but not on the node that contains the
|
|
346
|
-
* array/map node as one of its properties.
|
|
347
|
-
*
|
|
348
|
-
* - The node is moved to a different location in the tree or removed from the tree.
|
|
349
|
-
* In this case the event is emitted on the _parent_ node, not the node itself.
|
|
350
|
-
*
|
|
351
|
-
* For remote edits, this event is not guaranteed to occur in the same order or quantity that it did in
|
|
352
|
-
* the client that made the original edit.
|
|
353
|
-
*
|
|
354
|
-
* When it is emitted, the tree is guaranteed to be in-schema.
|
|
355
|
-
*
|
|
356
|
-
* @privateRemarks
|
|
357
|
-
* This event occurs whenever the apparent contents of the node instance change, regardless of what caused the change.
|
|
358
|
-
* For example, it will fire when the local client reassigns a child, when part of a remote edit is applied to the
|
|
359
|
-
* node, or when the node has to be updated due to resolution of a merge conflict
|
|
360
|
-
* (for example a previously applied local change might be undone, then reapplied differently or not at all).
|
|
361
|
-
*/
|
|
362
|
-
nodeChanged(): void;
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Emitted by a node after a batch of changes has been applied to the tree, when something changed anywhere in the
|
|
366
|
-
* subtree rooted at it.
|
|
367
|
-
*
|
|
368
|
-
* @remarks
|
|
369
|
-
* This event is not emitted when the node itself is moved to a different location in the tree or removed from the tree.
|
|
370
|
-
* In that case it is emitted on the _parent_ node, not the node itself.
|
|
371
|
-
*
|
|
372
|
-
* The node itself is part of the subtree, so this event will be emitted even if the only changes are to the properties
|
|
373
|
-
* of the node itself.
|
|
374
|
-
*
|
|
375
|
-
* For remote edits, this event is not guaranteed to occur in the same order or quantity that it did in
|
|
376
|
-
* the client that made the original edit.
|
|
377
|
-
*
|
|
378
|
-
* When it is emitted, the tree is guaranteed to be in-schema.
|
|
379
|
-
*/
|
|
380
|
-
treeChanged(): void;
|
|
381
|
-
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
+
import { createEmitter, type Listenable, type Off } from "../events/index.js";
|
|
8
|
+
import type { TreeChangeEvents, TreeNode } from "./types.js";
|
|
9
|
+
import type { AnchorNode } from "../core/index.js";
|
|
10
|
+
import {
|
|
11
|
+
flexTreeSlot,
|
|
12
|
+
isFreedSymbol,
|
|
13
|
+
LazyEntity,
|
|
14
|
+
TreeStatus,
|
|
15
|
+
treeStatusFromAnchorCache,
|
|
16
|
+
} from "../feature-libraries/index.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Contains state and an internal API for managing {@link TreeNode}s.
|
|
20
|
+
* @remarks All {@link TreeNode}s have an associated kernel object.
|
|
21
|
+
* The kernel has the same lifetime as the node and spans both its unhydrated and hydrated states.
|
|
22
|
+
* When hydration occurs, the kernel is notified via the {@link TreeNodeKernel.hydrate | hydrate} method.
|
|
23
|
+
*/
|
|
24
|
+
export class TreeNodeKernel implements Listenable<TreeChangeEvents> {
|
|
25
|
+
#hydrated?: {
|
|
26
|
+
anchorNode: AnchorNode;
|
|
27
|
+
offAnchorNode: Off;
|
|
28
|
+
};
|
|
29
|
+
#events = createEmitter<TreeChangeEvents>();
|
|
30
|
+
|
|
31
|
+
public constructor(public readonly node: TreeNode) {}
|
|
32
|
+
|
|
33
|
+
public hydrate(anchorNode: AnchorNode): void {
|
|
34
|
+
const offChildrenChanged = anchorNode.on("childrenChangedAfterBatch", () => {
|
|
35
|
+
this.#events.emit("nodeChanged");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const offSubtreeChanged = anchorNode.on("subtreeChangedAfterBatch", () => {
|
|
39
|
+
this.#events.emit("treeChanged");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const offAfterDestroy = anchorNode.on("afterDestroy", () => this.dispose());
|
|
43
|
+
|
|
44
|
+
this.#hydrated = {
|
|
45
|
+
anchorNode,
|
|
46
|
+
offAnchorNode: () => {
|
|
47
|
+
offChildrenChanged();
|
|
48
|
+
offSubtreeChanged();
|
|
49
|
+
offAfterDestroy();
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public dehydrate(): void {
|
|
55
|
+
this.#hydrated?.offAnchorNode?.();
|
|
56
|
+
this.#hydrated = undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public isHydrated(): boolean {
|
|
60
|
+
return this.#hydrated !== undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public getStatus(): TreeStatus {
|
|
64
|
+
if (this.#hydrated?.anchorNode === undefined) {
|
|
65
|
+
return TreeStatus.New;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TODO: Replace this check with the proper check against the cursor state when the cursor becomes part of the kernel
|
|
69
|
+
const flex = this.#hydrated.anchorNode.slots.get(flexTreeSlot);
|
|
70
|
+
if (flex !== undefined) {
|
|
71
|
+
assert(flex instanceof LazyEntity, 0x9b4 /* Unexpected flex node implementation */);
|
|
72
|
+
if (flex[isFreedSymbol]()) {
|
|
73
|
+
return TreeStatus.Deleted;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return treeStatusFromAnchorCache(this.#hydrated.anchorNode);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public on<K extends keyof TreeChangeEvents>(
|
|
81
|
+
eventName: K,
|
|
82
|
+
listener: TreeChangeEvents[K],
|
|
83
|
+
): Off {
|
|
84
|
+
return this.#events.on(eventName, listener);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public dispose(): void {
|
|
88
|
+
this.dehydrate();
|
|
89
|
+
// TODO: go to the context and remove myself from withAnchors
|
|
90
|
+
}
|
|
91
|
+
}
|