@fluidframework/tree 2.61.0-355990 → 2.61.0-356132

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.
Files changed (33) hide show
  1. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +2 -0
  2. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  3. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  4. package/dist/feature-libraries/flex-tree/observer.d.ts +8 -0
  5. package/dist/feature-libraries/flex-tree/observer.d.ts.map +1 -1
  6. package/dist/feature-libraries/flex-tree/observer.js +3 -0
  7. package/dist/feature-libraries/flex-tree/observer.js.map +1 -1
  8. package/dist/packageVersion.d.ts +1 -1
  9. package/dist/packageVersion.js +1 -1
  10. package/dist/packageVersion.js.map +1 -1
  11. package/dist/shared-tree/treeAlpha.d.ts +1 -1
  12. package/dist/shared-tree/treeAlpha.d.ts.map +1 -1
  13. package/dist/shared-tree/treeAlpha.js +85 -65
  14. package/dist/shared-tree/treeAlpha.js.map +1 -1
  15. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +2 -0
  16. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  17. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  18. package/lib/feature-libraries/flex-tree/observer.d.ts +8 -0
  19. package/lib/feature-libraries/flex-tree/observer.d.ts.map +1 -1
  20. package/lib/feature-libraries/flex-tree/observer.js +3 -0
  21. package/lib/feature-libraries/flex-tree/observer.js.map +1 -1
  22. package/lib/packageVersion.d.ts +1 -1
  23. package/lib/packageVersion.js +1 -1
  24. package/lib/packageVersion.js.map +1 -1
  25. package/lib/shared-tree/treeAlpha.d.ts +1 -1
  26. package/lib/shared-tree/treeAlpha.d.ts.map +1 -1
  27. package/lib/shared-tree/treeAlpha.js +86 -66
  28. package/lib/shared-tree/treeAlpha.js.map +1 -1
  29. package/package.json +23 -23
  30. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +2 -0
  31. package/src/feature-libraries/flex-tree/observer.ts +18 -1
  32. package/src/packageVersion.ts +1 -1
  33. package/src/shared-tree/treeAlpha.ts +99 -74
@@ -17,13 +17,13 @@ import type { IIdCompressor } from "@fluidframework/id-compressor";
17
17
  import {
18
18
  asIndex,
19
19
  getKernel,
20
+ TreeNode,
20
21
  type Unhydrated,
21
22
  TreeBeta,
22
23
  tryGetSchema,
23
24
  createFromCursor,
24
25
  FieldKind,
25
26
  normalizeFieldSchema,
26
- TreeNode,
27
27
  type ImplicitFieldSchema,
28
28
  type InsertableField,
29
29
  type TreeFieldFromImplicitField,
@@ -454,15 +454,20 @@ export interface TreeAlpha {
454
454
  ): { result: TResult; unsubscribe: () => void };
455
455
  }
456
456
 
457
+ /**
458
+ * Subscription to changes on a single node.
459
+ * @remarks
460
+ * Either tracks some set of fields, or all fields and can be updated to track more fields.
461
+ */
457
462
  class NodeSubscription {
458
463
  /**
459
464
  * If undefined, subscribes to all keys.
460
465
  * Otherwise only subscribes to the keys in the set.
461
466
  */
462
- public keys: Set<FieldKey> | undefined;
463
- public readonly unsubscribe: () => void;
464
- public constructor(
465
- public readonly onInvalidation: () => void,
467
+ private keys: Set<FieldKey> | undefined;
468
+ private readonly unsubscribe: () => void;
469
+ private constructor(
470
+ private readonly onInvalidation: () => void,
466
471
  flexNode: FlexTreeNode,
467
472
  ) {
468
473
  // TODO:Performance: It is possible to optimize this to not use the public TreeNode API.
@@ -492,8 +497,93 @@ class NodeSubscription {
492
497
  };
493
498
  this.unsubscribe = TreeBeta.on(node, "nodeChanged", handler);
494
499
  }
500
+
501
+ /**
502
+ * Create an {@link Observer} which subscribes to what was observed in {@link NodeSubscription}s.
503
+ */
504
+ public static createObserver(
505
+ invalidate: () => void,
506
+ onlyOnce = false,
507
+ ): { observer: Observer; unsubscribe: () => void } {
508
+ const subscriptions = new Map<FlexTreeNode, NodeSubscription>();
509
+ const observer: Observer = {
510
+ observeNodeFields(flexNode: FlexTreeNode): void {
511
+ if (flexNode.value !== undefined) {
512
+ // Leaf value, nothing to observe.
513
+ return;
514
+ }
515
+ const subscription = subscriptions.get(flexNode);
516
+ if (subscription !== undefined) {
517
+ // Already subscribed to this node.
518
+ subscription.keys = undefined; // Now subscribed to all keys.
519
+ } else {
520
+ const newSubscription = new NodeSubscription(invalidate, flexNode);
521
+ subscriptions.set(flexNode, newSubscription);
522
+ }
523
+ },
524
+ observeNodeField(flexNode: FlexTreeNode, key: FieldKey): void {
525
+ if (flexNode.value !== undefined) {
526
+ // Leaf value, nothing to observe.
527
+ return;
528
+ }
529
+ const subscription = subscriptions.get(flexNode);
530
+ if (subscription !== undefined) {
531
+ // Already subscribed to this node: if not subscribed to all keys, subscribe to this one.
532
+ // TODO:Performance: due to how JavaScript set ordering works,
533
+ // it might be faster to check `has` and only add if not present in case the same field is viewed many times.
534
+ subscription.keys?.add(key);
535
+ } else {
536
+ const newSubscription = new NodeSubscription(invalidate, flexNode);
537
+ newSubscription.keys = new Set([key]);
538
+ subscriptions.set(flexNode, newSubscription);
539
+ }
540
+ },
541
+ observeParentOf(node: FlexTreeNode): void {
542
+ // Supporting parent tracking is more difficult that it might seem at first.
543
+ // There are two main complicating factors:
544
+ // 1. The parent may be undefined (the node is a root).
545
+ // 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.
546
+ //
547
+ // 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,
548
+ // and un-parent it, could then throw a usage error.
549
+
550
+ if (!onlyOnce) {
551
+ // TODO: better APIS should be provided which make handling this case practical.
552
+ throw new UsageError("Observation tracking for parents is currently not supported.");
553
+ }
554
+
555
+ const parent = withObservation(undefined, () => node.parentField.parent);
556
+
557
+ if (parent.parent === undefined) {
558
+ // TODO: better APIS should be provided which make handling this case practical.
559
+ throw new UsageError(
560
+ "Observation tracking for parents is currently not supported when parent is undefined.",
561
+ );
562
+ }
563
+ observer.observeNodeField(parent.parent, parent.key);
564
+ },
565
+ };
566
+
567
+ let subscribed = true;
568
+
569
+ return {
570
+ observer,
571
+ unsubscribe: () => {
572
+ if (!subscribed) {
573
+ throw new UsageError("Already unsubscribed");
574
+ }
575
+ subscribed = false;
576
+ for (const subscription of subscriptions.values()) {
577
+ subscription.unsubscribe();
578
+ }
579
+ },
580
+ };
581
+ }
495
582
  }
496
583
 
584
+ /**
585
+ * Handles both {@link (TreeAlpha:interface).trackObservations} and {@link (TreeAlpha:interface).trackObservationsOnce}.
586
+ */
497
587
  function trackObservations<TResult>(
498
588
  onInvalidation: () => void,
499
589
  trackDuring: () => TResult,
@@ -508,80 +598,13 @@ function trackObservations<TResult>(
508
598
  onInvalidation();
509
599
  };
510
600
 
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
- };
601
+ const { observer, unsubscribe } = NodeSubscription.createObserver(invalidate, onlyOnce);
569
602
  const result = withObservation(observer, trackDuring);
570
603
  observing = false;
571
604
 
572
- let subscribed = true;
573
-
574
605
  return {
575
606
  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
- },
607
+ unsubscribe,
585
608
  };
586
609
  }
587
610
 
@@ -604,6 +627,8 @@ export const TreeAlpha: TreeAlpha = {
604
627
  ): { result: TResult; unsubscribe: () => void } {
605
628
  const result = trackObservations(
606
629
  () => {
630
+ // trackObservations ensures no invalidation occurs while its running,
631
+ // so this callback can only run after trackObservations has returns and thus result is defined.
607
632
  result.unsubscribe();
608
633
  onInvalidation();
609
634
  },