@fluidframework/tree 2.61.0-355656 → 2.61.0-355781
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/index.d.ts +1 -0
- package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/index.js +4 -1
- package/dist/feature-libraries/flex-tree/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +15 -8
- package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/observer.d.ts +19 -0
- package/dist/feature-libraries/flex-tree/observer.d.ts.map +1 -0
- package/dist/feature-libraries/flex-tree/observer.js +30 -0
- package/dist/feature-libraries/flex-tree/observer.js.map +1 -0
- 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 +3 -1
- package/dist/feature-libraries/index.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 +8 -1
- package/dist/shared-tree/treeAlpha.d.ts.map +1 -1
- package/dist/shared-tree/treeAlpha.js +95 -0
- package/dist/shared-tree/treeAlpha.js.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/dist/simple-tree/core/unhydratedFlexTree.js +7 -1
- package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/lib/feature-libraries/flex-tree/index.d.ts +1 -0
- package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/index.js +1 -0
- package/lib/feature-libraries/flex-tree/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +15 -8
- package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/observer.d.ts +19 -0
- package/lib/feature-libraries/flex-tree/observer.d.ts.map +1 -0
- package/lib/feature-libraries/flex-tree/observer.js +32 -0
- package/lib/feature-libraries/flex-tree/observer.js.map +1 -0
- 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/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 +8 -1
- package/lib/shared-tree/treeAlpha.d.ts.map +1 -1
- package/lib/shared-tree/treeAlpha.js +97 -2
- package/lib/shared-tree/treeAlpha.js.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
- package/lib/simple-tree/core/unhydratedFlexTree.js +8 -2
- package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
- package/package.json +20 -20
- package/src/feature-libraries/flex-tree/index.ts +2 -0
- package/src/feature-libraries/flex-tree/lazyNode.ts +13 -3
- package/src/feature-libraries/flex-tree/observer.ts +41 -0
- package/src/feature-libraries/index.ts +3 -0
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/treeAlpha.ts +125 -2
- package/src/simple-tree/core/unhydratedFlexTree.ts +11 -2
package/src/packageVersion.ts
CHANGED
|
@@ -17,13 +17,13 @@ import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
|
17
17
|
import {
|
|
18
18
|
asIndex,
|
|
19
19
|
getKernel,
|
|
20
|
-
type TreeNode,
|
|
21
20
|
type Unhydrated,
|
|
22
21
|
TreeBeta,
|
|
23
22
|
tryGetSchema,
|
|
24
23
|
createFromCursor,
|
|
25
24
|
FieldKind,
|
|
26
25
|
normalizeFieldSchema,
|
|
26
|
+
TreeNode,
|
|
27
27
|
type ImplicitFieldSchema,
|
|
28
28
|
type InsertableField,
|
|
29
29
|
type TreeFieldFromImplicitField,
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
convertField,
|
|
56
56
|
toUnhydratedSchema,
|
|
57
57
|
type TreeParsingOptions,
|
|
58
|
+
type NodeChangedData,
|
|
58
59
|
} from "../simple-tree/index.js";
|
|
59
60
|
import { brand, extractFromOpaque, type JsonCompatible } from "../util/index.js";
|
|
60
61
|
import {
|
|
@@ -63,7 +64,7 @@ import {
|
|
|
63
64
|
type ICodecOptions,
|
|
64
65
|
type CodecWriteOptions,
|
|
65
66
|
} from "../codec/index.js";
|
|
66
|
-
import { EmptyKey, type ITreeCursorSynchronous } from "../core/index.js";
|
|
67
|
+
import { EmptyKey, type FieldKey, type ITreeCursorSynchronous } from "../core/index.js";
|
|
67
68
|
import {
|
|
68
69
|
cursorForMapTreeField,
|
|
69
70
|
defaultSchemaPolicy,
|
|
@@ -76,6 +77,9 @@ import {
|
|
|
76
77
|
fluidVersionToFieldBatchCodecWriteVersion,
|
|
77
78
|
type LocalNodeIdentifier,
|
|
78
79
|
type FlexTreeSequenceField,
|
|
80
|
+
type FlexTreeNode,
|
|
81
|
+
type Observer,
|
|
82
|
+
withObservation,
|
|
79
83
|
} from "../feature-libraries/index.js";
|
|
80
84
|
import { independentInitializedView, type ViewContent } from "./independentView.js";
|
|
81
85
|
import { SchematizingSimpleTreeView, ViewSlot } from "./schematizingTreeView.js";
|
|
@@ -419,6 +423,54 @@ export interface TreeAlpha {
|
|
|
419
423
|
children(
|
|
420
424
|
node: TreeNode,
|
|
421
425
|
): Iterable<[propertyKey: string | number, child: TreeNode | TreeLeafValue]>;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Track observations of any TreeNode content.
|
|
429
|
+
*/
|
|
430
|
+
trackObservations<TResult>(
|
|
431
|
+
onInvalidation: () => void,
|
|
432
|
+
trackDuring: () => TResult,
|
|
433
|
+
): { result: TResult; unsubscribe: () => void };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
class NodeSubscription {
|
|
437
|
+
/**
|
|
438
|
+
* If undefined, subscribes to all keys.
|
|
439
|
+
* Otherwise only subscribes to the keys in the set.
|
|
440
|
+
*/
|
|
441
|
+
public keys: Set<FieldKey> | undefined;
|
|
442
|
+
public readonly unsubscribe: () => void;
|
|
443
|
+
public constructor(
|
|
444
|
+
public readonly onInvalidation: () => void,
|
|
445
|
+
flexNode: FlexTreeNode,
|
|
446
|
+
) {
|
|
447
|
+
// TODO:Performance: It is possible to optimize this to not use the public TreeNode API.
|
|
448
|
+
const node = getOrCreateNodeFromInnerNode(flexNode);
|
|
449
|
+
assert(node instanceof TreeNode, "Unexpected leaf value");
|
|
450
|
+
|
|
451
|
+
const handler = (data: NodeChangedData): void => {
|
|
452
|
+
if (this.keys === undefined || data.changedProperties === undefined) {
|
|
453
|
+
this.onInvalidation();
|
|
454
|
+
} else {
|
|
455
|
+
let keyMap: ReadonlyMap<FieldKey, string> | undefined;
|
|
456
|
+
const schema = treeNodeApi.schema(node);
|
|
457
|
+
if (isObjectNodeSchema(schema)) {
|
|
458
|
+
keyMap = schema.storedKeyToPropertyKey;
|
|
459
|
+
}
|
|
460
|
+
// TODO:Performance: Ideally this would use Set.prototype.isDisjointFrom when available.
|
|
461
|
+
for (const flexKey of this.keys) {
|
|
462
|
+
// TODO:Performance: doing everything at the flex tree layer could avoid this translation
|
|
463
|
+
const key = keyMap?.get(flexKey) ?? flexKey;
|
|
464
|
+
|
|
465
|
+
if (data.changedProperties.has(key)) {
|
|
466
|
+
this.onInvalidation();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
this.unsubscribe = TreeBeta.on(node, "nodeChanged", handler);
|
|
473
|
+
}
|
|
422
474
|
}
|
|
423
475
|
|
|
424
476
|
/**
|
|
@@ -427,6 +479,77 @@ export interface TreeAlpha {
|
|
|
427
479
|
* @alpha
|
|
428
480
|
*/
|
|
429
481
|
export const TreeAlpha: TreeAlpha = {
|
|
482
|
+
trackObservations<TResult>(
|
|
483
|
+
onInvalidation: () => void,
|
|
484
|
+
trackDuring: () => TResult,
|
|
485
|
+
): { result: TResult; unsubscribe: () => void } {
|
|
486
|
+
let observing = true;
|
|
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;
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
result,
|
|
541
|
+
unsubscribe: () => {
|
|
542
|
+
if (!subscribed) {
|
|
543
|
+
throw new UsageError("Already unsubscribed");
|
|
544
|
+
}
|
|
545
|
+
subscribed = false;
|
|
546
|
+
for (const subscription of subscriptions.values()) {
|
|
547
|
+
subscription.unsubscribe();
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
|
|
430
553
|
branch(node: TreeNode): TreeBranch | undefined {
|
|
431
554
|
const kernel = getKernel(node);
|
|
432
555
|
if (!kernel.isHydrated()) {
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
type HydratedFlexTreeNode,
|
|
51
51
|
cursorForMapTreeField,
|
|
52
52
|
type MinimalFieldMap,
|
|
53
|
+
currentObserver,
|
|
53
54
|
} from "../../feature-libraries/index.js";
|
|
54
55
|
import { brand, filterIterable, getOrCreate, mapIterable } from "../../util/index.js";
|
|
55
56
|
|
|
@@ -144,8 +145,10 @@ export class UnhydratedFlexTreeNode
|
|
|
144
145
|
*/
|
|
145
146
|
public readonly fields: MinimalFieldMap<UnhydratedFlexTreeField> = {
|
|
146
147
|
get: (key: FieldKey): UnhydratedFlexTreeField | undefined => this.tryGetField(key),
|
|
147
|
-
[Symbol.iterator]: (): IterableIterator<[FieldKey, UnhydratedFlexTreeField]> =>
|
|
148
|
-
|
|
148
|
+
[Symbol.iterator]: (): IterableIterator<[FieldKey, UnhydratedFlexTreeField]> => {
|
|
149
|
+
currentObserver?.observeNodeFields(this);
|
|
150
|
+
return filterIterable(this.fieldsAll, ([, field]) => field.length > 0);
|
|
151
|
+
},
|
|
149
152
|
};
|
|
150
153
|
|
|
151
154
|
public [Symbol.iterator](): IterableIterator<UnhydratedFlexTreeField> {
|
|
@@ -218,6 +221,7 @@ export class UnhydratedFlexTreeNode
|
|
|
218
221
|
* @remarks If this node is unparented, this method will return the special {@link unparentedLocation} as the parent.
|
|
219
222
|
*/
|
|
220
223
|
public get parentField(): LocationInField {
|
|
224
|
+
currentObserver?.observeParentOf(this);
|
|
221
225
|
return this.location;
|
|
222
226
|
}
|
|
223
227
|
|
|
@@ -226,6 +230,8 @@ export class UnhydratedFlexTreeNode
|
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
public tryGetField(key: FieldKey): UnhydratedFlexTreeField | undefined {
|
|
233
|
+
currentObserver?.observeNodeField(this, key);
|
|
234
|
+
|
|
229
235
|
const field = this.fieldsAll.get(key);
|
|
230
236
|
// Only return the field if it is not empty, in order to fulfill the contract of `tryGetField`.
|
|
231
237
|
if (field !== undefined && field.length > 0) {
|
|
@@ -235,6 +241,9 @@ export class UnhydratedFlexTreeNode
|
|
|
235
241
|
|
|
236
242
|
public getBoxed(key: string): UnhydratedFlexTreeField {
|
|
237
243
|
const fieldKey: FieldKey = brand(key);
|
|
244
|
+
|
|
245
|
+
currentObserver?.observeNodeField(this, fieldKey);
|
|
246
|
+
|
|
238
247
|
return this.getOrCreateField(fieldKey);
|
|
239
248
|
}
|
|
240
249
|
|