@fluidframework/tree 2.61.0-355781 → 2.61.0-355990
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/api-report/tree.alpha.api.md +4 -0
- package/dist/feature-libraries/flex-tree/observer.d.ts +1 -1
- package/dist/feature-libraries/flex-tree/observer.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/observer.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/treeAlpha.d.ts +20 -0
- package/dist/shared-tree/treeAlpha.d.ts.map +1 -1
- package/dist/shared-tree/treeAlpha.js +86 -62
- package/dist/shared-tree/treeAlpha.js.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/dist/shared-tree-core/sharedTreeCore.js +8 -12
- package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/lib/feature-libraries/flex-tree/observer.d.ts +1 -1
- package/lib/feature-libraries/flex-tree/observer.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/observer.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/treeAlpha.d.ts +20 -0
- package/lib/shared-tree/treeAlpha.d.ts.map +1 -1
- package/lib/shared-tree/treeAlpha.js +86 -62
- package/lib/shared-tree/treeAlpha.js.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
- package/lib/shared-tree-core/sharedTreeCore.js +9 -13
- package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
- package/package.json +20 -20
- package/src/feature-libraries/flex-tree/observer.ts +3 -3
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/treeAlpha.ts +126 -63
- package/src/shared-tree-core/sharedTreeCore.ts +9 -13
|
@@ -426,11 +426,32 @@ export interface TreeAlpha {
|
|
|
426
426
|
|
|
427
427
|
/**
|
|
428
428
|
* Track observations of any TreeNode content.
|
|
429
|
+
* @remarks
|
|
430
|
+
* This subscribes to changes to any nodes content observed during `trackDuring`.
|
|
431
|
+
*
|
|
432
|
+
* Currently this does not support tracking parentage (see {@link (TreeAlpha:interface).trackObservationsOnce} for a version which does):
|
|
433
|
+
* if accessing parentage during `trackDuring`, this will throw a usage error.
|
|
434
|
+
*
|
|
435
|
+
* This also does not track node status changes (e.g. whether a node is attached to a view or not).
|
|
436
|
+
* The current behavior of checking status is unspecified: future versions may track it, error, or ignore it.
|
|
437
|
+
*
|
|
438
|
+
* Even after onInvalidation is called, these subscriptions remain active until `unsubscribe` is called.
|
|
439
|
+
* See {@link (TreeAlpha:interface).trackObservationsOnce} for a version which automatically unsubscribes on the first invalidation.
|
|
429
440
|
*/
|
|
430
441
|
trackObservations<TResult>(
|
|
431
442
|
onInvalidation: () => void,
|
|
432
443
|
trackDuring: () => TResult,
|
|
433
444
|
): { result: TResult; unsubscribe: () => void };
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* {@link (TreeAlpha:interface).trackObservations} except automatically unsubscribes when the first invalidation occurs.
|
|
448
|
+
* @remarks
|
|
449
|
+
* This also supports tracking parentage, unlike {@link (TreeAlpha:interface).trackObservations}, as long as the parent is not undefined.
|
|
450
|
+
*/
|
|
451
|
+
trackObservationsOnce<TResult>(
|
|
452
|
+
onInvalidation: () => void,
|
|
453
|
+
trackDuring: () => TResult,
|
|
454
|
+
): { result: TResult; unsubscribe: () => void };
|
|
434
455
|
}
|
|
435
456
|
|
|
436
457
|
class NodeSubscription {
|
|
@@ -473,6 +494,97 @@ class NodeSubscription {
|
|
|
473
494
|
}
|
|
474
495
|
}
|
|
475
496
|
|
|
497
|
+
function trackObservations<TResult>(
|
|
498
|
+
onInvalidation: () => void,
|
|
499
|
+
trackDuring: () => TResult,
|
|
500
|
+
onlyOnce = false,
|
|
501
|
+
): { result: TResult; unsubscribe: () => void } {
|
|
502
|
+
let observing = true;
|
|
503
|
+
|
|
504
|
+
const invalidate = (): void => {
|
|
505
|
+
if (observing) {
|
|
506
|
+
throw new UsageError("Cannot invalidate while tracking observations");
|
|
507
|
+
}
|
|
508
|
+
onInvalidation();
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const subscriptions = new Map<FlexTreeNode, NodeSubscription>();
|
|
512
|
+
const observer: Observer = {
|
|
513
|
+
observeNodeFields(flexNode: FlexTreeNode): void {
|
|
514
|
+
if (flexNode.value !== undefined) {
|
|
515
|
+
// Leaf value, nothing to observe.
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const subscription = subscriptions.get(flexNode);
|
|
519
|
+
if (subscription !== undefined) {
|
|
520
|
+
// Already subscribed to this node.
|
|
521
|
+
subscription.keys = undefined; // Now subscribed to all keys.
|
|
522
|
+
} else {
|
|
523
|
+
const newSubscription = new NodeSubscription(invalidate, flexNode);
|
|
524
|
+
subscriptions.set(flexNode, newSubscription);
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
observeNodeField(flexNode: FlexTreeNode, key: FieldKey): void {
|
|
528
|
+
if (flexNode.value !== undefined) {
|
|
529
|
+
// Leaf value, nothing to observe.
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const subscription = subscriptions.get(flexNode);
|
|
533
|
+
if (subscription !== undefined) {
|
|
534
|
+
// Already subscribed to this node: if not subscribed to all keys, subscribe to this one.
|
|
535
|
+
// TODO:Performance: due to how JavaScript set ordering works,
|
|
536
|
+
// it might be faster to check `has` and only add if not present in case the same field is viewed many times.
|
|
537
|
+
subscription.keys?.add(key);
|
|
538
|
+
} else {
|
|
539
|
+
const newSubscription = new NodeSubscription(invalidate, flexNode);
|
|
540
|
+
newSubscription.keys = new Set([key]);
|
|
541
|
+
subscriptions.set(flexNode, newSubscription);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
observeParentOf(node: FlexTreeNode): void {
|
|
545
|
+
// Supporting parent tracking is more difficult that it might seem at first.
|
|
546
|
+
// There are two main complicating factors:
|
|
547
|
+
// 1. The parent may be undefined (the node is a root).
|
|
548
|
+
// 2. If tracking this by subscribing to the parent's changes, then which events are subscribed to needs to be updated after the parent changes.
|
|
549
|
+
//
|
|
550
|
+
// If not supporting the first case (undefined parents), the second case gets problematic since it would result in edits which take a node who's parent was observed,
|
|
551
|
+
// and un-parent it, could then throw a usage error.
|
|
552
|
+
|
|
553
|
+
if (!onlyOnce) {
|
|
554
|
+
// TODO: better APIS should be provided which make handling this case practical.
|
|
555
|
+
throw new UsageError("Observation tracking for parents is currently not supported.");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const parent = withObservation(undefined, () => node.parentField.parent);
|
|
559
|
+
|
|
560
|
+
if (parent.parent === undefined) {
|
|
561
|
+
// TODO: better APIS should be provided which make handling this case practical.
|
|
562
|
+
throw new UsageError(
|
|
563
|
+
"Observation tracking for parents is currently not supported when parent is undefined.",
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
observer.observeNodeField(parent.parent, parent.key);
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
const result = withObservation(observer, trackDuring);
|
|
570
|
+
observing = false;
|
|
571
|
+
|
|
572
|
+
let subscribed = true;
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
result,
|
|
576
|
+
unsubscribe: () => {
|
|
577
|
+
if (!subscribed) {
|
|
578
|
+
throw new UsageError("Already unsubscribed");
|
|
579
|
+
}
|
|
580
|
+
subscribed = false;
|
|
581
|
+
for (const subscription of subscriptions.values()) {
|
|
582
|
+
subscription.unsubscribe();
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
476
588
|
/**
|
|
477
589
|
* Extensions to {@link (Tree:variable)} and {@link (TreeBeta:variable)} which are not yet stable.
|
|
478
590
|
* @see {@link (TreeAlpha:interface)}.
|
|
@@ -483,71 +595,22 @@ export const TreeAlpha: TreeAlpha = {
|
|
|
483
595
|
onInvalidation: () => void,
|
|
484
596
|
trackDuring: () => TResult,
|
|
485
597
|
): { result: TResult; unsubscribe: () => void } {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const invalidate = (): void => {
|
|
489
|
-
if (observing) {
|
|
490
|
-
throw new UsageError("Cannot invalidate while tracking observations");
|
|
491
|
-
}
|
|
492
|
-
onInvalidation();
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
const subscriptions = new Map<FlexTreeNode, NodeSubscription>();
|
|
496
|
-
const observer: Observer = {
|
|
497
|
-
observeNodeFields(flexNode: FlexTreeNode): void {
|
|
498
|
-
if (flexNode.value !== undefined) {
|
|
499
|
-
// Leaf value, nothing to observe.
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
const subscription = subscriptions.get(flexNode);
|
|
503
|
-
if (subscription !== undefined) {
|
|
504
|
-
// Already subscribed to this node.
|
|
505
|
-
subscription.keys = undefined; // Now subscribed to all keys.
|
|
506
|
-
} else {
|
|
507
|
-
const newSubscription = new NodeSubscription(invalidate, flexNode);
|
|
508
|
-
subscriptions.set(flexNode, newSubscription);
|
|
509
|
-
}
|
|
510
|
-
},
|
|
511
|
-
observeNodeField(flexNode: FlexTreeNode, key: FieldKey): void {
|
|
512
|
-
if (flexNode.value !== undefined) {
|
|
513
|
-
// Leaf value, nothing to observe.
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
const subscription = subscriptions.get(flexNode);
|
|
517
|
-
if (subscription !== undefined) {
|
|
518
|
-
// Already subscribed to this node: if not subscribed to all keys, subscribe to this one.
|
|
519
|
-
// TODO:Performance: due to how JavaScript set ordering works,
|
|
520
|
-
// it might be faster to check `has` and only add if not present in case the same field is viewed many times.
|
|
521
|
-
subscription.keys?.add(key);
|
|
522
|
-
} else {
|
|
523
|
-
const newSubscription = new NodeSubscription(invalidate, flexNode);
|
|
524
|
-
newSubscription.keys = new Set([key]);
|
|
525
|
-
subscriptions.set(flexNode, newSubscription);
|
|
526
|
-
}
|
|
527
|
-
},
|
|
528
|
-
observeParentOf(node: FlexTreeNode): void {
|
|
529
|
-
throw new UsageError("Observation tracking for parents is currently not supported.");
|
|
530
|
-
// This is conservatively correct, but could be optimized.
|
|
531
|
-
// observer.observeNodeContent(parent);
|
|
532
|
-
},
|
|
533
|
-
};
|
|
534
|
-
const result = withObservation(observer, trackDuring);
|
|
535
|
-
observing = false;
|
|
536
|
-
|
|
537
|
-
let subscribed = true;
|
|
598
|
+
return trackObservations(onInvalidation, trackDuring);
|
|
599
|
+
},
|
|
538
600
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
subscription.unsubscribe();
|
|
548
|
-
}
|
|
601
|
+
trackObservationsOnce<TResult>(
|
|
602
|
+
onInvalidation: () => void,
|
|
603
|
+
trackDuring: () => TResult,
|
|
604
|
+
): { result: TResult; unsubscribe: () => void } {
|
|
605
|
+
const result = trackObservations(
|
|
606
|
+
() => {
|
|
607
|
+
result.unsubscribe();
|
|
608
|
+
onInvalidation();
|
|
549
609
|
},
|
|
550
|
-
|
|
610
|
+
trackDuring,
|
|
611
|
+
true,
|
|
612
|
+
);
|
|
613
|
+
return result;
|
|
551
614
|
},
|
|
552
615
|
|
|
553
616
|
branch(node: TreeNode): TreeBranch | undefined {
|
|
@@ -25,7 +25,6 @@ import type { ICodecOptions, IJsonCodec } from "../codec/index.js";
|
|
|
25
25
|
import {
|
|
26
26
|
type ChangeFamily,
|
|
27
27
|
type ChangeFamilyEditor,
|
|
28
|
-
findAncestor,
|
|
29
28
|
type GraphCommit,
|
|
30
29
|
type RevisionTag,
|
|
31
30
|
RevisionTagCodec,
|
|
@@ -259,18 +258,15 @@ export class SharedTreeCore<TEditor extends ChangeFamilyEditor, TChange>
|
|
|
259
258
|
// If we are detached but loading from a summary, then we need to update our detached revision to ensure that it is ahead of all detached revisions in the summary.
|
|
260
259
|
// First, finish loading the edit manager so that we can inspect the sequence numbers of the commits on the trunk.
|
|
261
260
|
await loadEditManager;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
});
|
|
272
|
-
// ...and set our detached revision to be as it would be if we had been already created that revision.
|
|
273
|
-
this.detachedRevision = latestDetachedSequenceNumber ?? this.detachedRevision;
|
|
261
|
+
|
|
262
|
+
const head = this.editManager.getTrunkHead();
|
|
263
|
+
const latestDetachedSequenceNumber = this.editManager.getSequenceNumber(head);
|
|
264
|
+
// When we load a summary for a tree that was never attached,
|
|
265
|
+
// latestDetachedSequenceNumber is either undefined (no commits in summary) or negative (all commits in summary were made while detached).
|
|
266
|
+
// We only need to update `this.detachedRevision` in the latter case.
|
|
267
|
+
if (latestDetachedSequenceNumber !== undefined && latestDetachedSequenceNumber < 0) {
|
|
268
|
+
this.detachedRevision = latestDetachedSequenceNumber;
|
|
269
|
+
}
|
|
274
270
|
await Promise.all(loadSummarizables);
|
|
275
271
|
} else {
|
|
276
272
|
await Promise.all([loadEditManager, ...loadSummarizables]);
|