@fluidframework/tree 2.2.0 → 2.3.0-288113
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 +39 -3
- package/api-report/tree.beta.api.md +8 -3
- package/api-report/tree.public.api.md +8 -3
- package/dist/beta.d.ts +1 -0
- package/dist/core/tree/anchorSet.d.ts +4 -6
- package/dist/core/tree/anchorSet.d.ts.map +1 -1
- package/dist/core/tree/anchorSet.js +11 -1
- package/dist/core/tree/anchorSet.js.map +1 -1
- package/dist/events/events.d.ts +7 -1
- package/dist/events/events.d.ts.map +1 -1
- package/dist/events/events.js +5 -2
- package/dist/events/events.js.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts +0 -2
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js +0 -12
- package/dist/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +4 -95
- package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js +1 -30
- package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/dist/feature-libraries/flex-tree/index.d.ts +2 -2
- package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/index.js +1 -3
- package/dist/feature-libraries/flex-tree/index.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts +0 -1
- package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyField.js +0 -3
- package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts +3 -10
- package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/dist/feature-libraries/flex-tree/lazyNode.js +2 -87
- package/dist/feature-libraries/flex-tree/lazyNode.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 +2 -4
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -31
- package/dist/index.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/public.d.ts +1 -0
- package/dist/simple-tree/api/treeNodeApi.d.ts +1 -1
- package/dist/simple-tree/api/treeNodeApi.d.ts.map +1 -1
- package/dist/simple-tree/api/treeNodeApi.js +30 -2
- package/dist/simple-tree/api/treeNodeApi.js.map +1 -1
- package/dist/simple-tree/arrayNode.d.ts.map +1 -1
- package/dist/simple-tree/arrayNode.js +5 -19
- package/dist/simple-tree/arrayNode.js.map +1 -1
- package/dist/simple-tree/core/index.d.ts +1 -1
- package/dist/simple-tree/core/index.d.ts.map +1 -1
- package/dist/simple-tree/core/index.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts +18 -5
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +57 -21
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/simple-tree/core/types.d.ts +28 -10
- package/dist/simple-tree/core/types.d.ts.map +1 -1
- package/dist/simple-tree/core/types.js.map +1 -1
- package/dist/simple-tree/index.d.ts +1 -1
- package/dist/simple-tree/index.d.ts.map +1 -1
- package/dist/simple-tree/index.js.map +1 -1
- package/lib/beta.d.ts +1 -0
- package/lib/core/tree/anchorSet.d.ts +4 -6
- package/lib/core/tree/anchorSet.d.ts.map +1 -1
- package/lib/core/tree/anchorSet.js +11 -1
- package/lib/core/tree/anchorSet.js.map +1 -1
- package/lib/events/events.d.ts +7 -1
- package/lib/events/events.d.ts.map +1 -1
- package/lib/events/events.js +5 -2
- package/lib/events/events.js.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts +0 -2
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js +0 -12
- package/lib/feature-libraries/flex-map-tree/mapTreeNode.js.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +4 -95
- package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js +0 -29
- package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
- package/lib/feature-libraries/flex-tree/index.d.ts +2 -2
- package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/index.js +1 -1
- package/lib/feature-libraries/flex-tree/index.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts +0 -1
- package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyField.js +0 -3
- package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts +3 -10
- package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
- package/lib/feature-libraries/flex-tree/lazyNode.js +3 -86
- package/lib/feature-libraries/flex-tree/lazyNode.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/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -0
- package/lib/index.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/public.d.ts +1 -0
- package/lib/simple-tree/api/treeNodeApi.d.ts +1 -1
- package/lib/simple-tree/api/treeNodeApi.d.ts.map +1 -1
- package/lib/simple-tree/api/treeNodeApi.js +30 -2
- package/lib/simple-tree/api/treeNodeApi.js.map +1 -1
- package/lib/simple-tree/arrayNode.d.ts.map +1 -1
- package/lib/simple-tree/arrayNode.js +5 -19
- package/lib/simple-tree/arrayNode.js.map +1 -1
- package/lib/simple-tree/core/index.d.ts +1 -1
- package/lib/simple-tree/core/index.d.ts.map +1 -1
- package/lib/simple-tree/core/index.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts +18 -5
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +58 -22
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/simple-tree/core/types.d.ts +28 -10
- package/lib/simple-tree/core/types.d.ts.map +1 -1
- package/lib/simple-tree/core/types.js.map +1 -1
- package/lib/simple-tree/index.d.ts +1 -1
- package/lib/simple-tree/index.d.ts.map +1 -1
- package/lib/simple-tree/index.js.map +1 -1
- package/package.json +22 -32
- package/src/core/tree/anchorSet.ts +20 -9
- package/src/events/events.ts +10 -2
- package/src/feature-libraries/flex-map-tree/mapTreeNode.ts +0 -21
- package/src/feature-libraries/flex-tree/flexTreeTypes.ts +3 -170
- package/src/feature-libraries/flex-tree/index.ts +1 -17
- package/src/feature-libraries/flex-tree/lazyField.ts +0 -6
- package/src/feature-libraries/flex-tree/lazyNode.ts +3 -154
- package/src/feature-libraries/index.ts +0 -14
- package/src/index.ts +8 -0
- package/src/packageVersion.ts +1 -1
- package/src/simple-tree/api/treeNodeApi.ts +37 -5
- package/src/simple-tree/arrayNode.ts +3 -12
- package/src/simple-tree/core/index.ts +1 -0
- package/src/simple-tree/core/treeNodeKernel.ts +88 -29
- package/src/simple-tree/core/types.ts +35 -9
- package/src/simple-tree/index.ts +1 -0
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
createEmitter,
|
|
9
|
+
type HasListeners,
|
|
10
|
+
type IEmitter,
|
|
11
|
+
type Listenable,
|
|
12
|
+
type Off,
|
|
13
|
+
} from "../../events/index.js";
|
|
14
|
+
import type { TreeNode, Unhydrated } from "./types.js";
|
|
15
|
+
import type { AnchorEvents, AnchorNode } from "../../core/index.js";
|
|
10
16
|
import {
|
|
11
17
|
flexTreeSlot,
|
|
12
18
|
isFreedSymbol,
|
|
@@ -60,12 +66,48 @@ export function tryGetTreeNodeSchema(value: unknown): undefined | TreeNodeSchema
|
|
|
60
66
|
* The kernel has the same lifetime as the node and spans both its unhydrated and hydrated states.
|
|
61
67
|
* When hydration occurs, the kernel is notified via the {@link TreeNodeKernel.hydrate | hydrate} method.
|
|
62
68
|
*/
|
|
63
|
-
export class TreeNodeKernel implements Listenable<
|
|
69
|
+
export class TreeNodeKernel implements Listenable<KernelEvents> {
|
|
70
|
+
private disposed = false;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generation number which is incremented any time we have an edit on the node.
|
|
74
|
+
* Used during iteration to make sure there has been no edits that were concurrently made.
|
|
75
|
+
*/
|
|
76
|
+
public generationNumber: number = 0;
|
|
77
|
+
|
|
64
78
|
#hydrated?: {
|
|
65
79
|
anchorNode: AnchorNode;
|
|
66
|
-
offAnchorNode: Off
|
|
80
|
+
offAnchorNode: Set<Off>;
|
|
67
81
|
};
|
|
68
|
-
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Events registered before hydration.
|
|
85
|
+
* @remarks
|
|
86
|
+
*
|
|
87
|
+
*/
|
|
88
|
+
#preHydrationEvents?: Listenable<KernelEvents> &
|
|
89
|
+
IEmitter<KernelEvents> &
|
|
90
|
+
HasListeners<KernelEvents>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the listener.
|
|
94
|
+
* @remarks
|
|
95
|
+
* If before hydration, allocates and uses `#preHydrationEvents`, otherwise the anchorNode.
|
|
96
|
+
* This design avoids allocating `#preHydrationEvents` if unneeded.
|
|
97
|
+
*
|
|
98
|
+
* This design also avoids extra forwarding overhead for events from anchorNode and also
|
|
99
|
+
* avoids registering for events that the are unneeded.
|
|
100
|
+
* This means optimizations like skipping processing data in subtrees where no subtreeChanged events are subscribed to would be able to work,
|
|
101
|
+
* since this code does not unconditionally subscribe to those events (like a design simply forwarding all events would).
|
|
102
|
+
*/
|
|
103
|
+
get #events(): Listenable<KernelEvents> {
|
|
104
|
+
if (this.#hydrated === undefined) {
|
|
105
|
+
this.#preHydrationEvents ??= createEmitter<KernelEvents>();
|
|
106
|
+
return this.#preHydrationEvents;
|
|
107
|
+
} else {
|
|
108
|
+
return this.#hydrated.anchorNode;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
69
111
|
|
|
70
112
|
/**
|
|
71
113
|
* Create a TreeNodeKernel which can be looked up with {@link getKernel}.
|
|
@@ -80,37 +122,50 @@ export class TreeNodeKernel implements Listenable<TreeChangeEvents> {
|
|
|
80
122
|
treeNodeToKernel.set(node, this);
|
|
81
123
|
}
|
|
82
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Transition from {@link Unhydrated} to hydrated.
|
|
127
|
+
* @remarks
|
|
128
|
+
* Happens at most once for any given node.
|
|
129
|
+
*/
|
|
83
130
|
public hydrate(anchorNode: AnchorNode): void {
|
|
84
|
-
|
|
85
|
-
this.#events.emit("nodeChanged");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const offSubtreeChanged = anchorNode.on("subtreeChangedAfterBatch", () => {
|
|
89
|
-
this.#events.emit("treeChanged");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const offAfterDestroy = anchorNode.on("afterDestroy", () => this.dispose());
|
|
131
|
+
assert(!this.disposed, "cannot use a disposed node");
|
|
93
132
|
|
|
94
133
|
this.#hydrated = {
|
|
95
134
|
anchorNode,
|
|
96
|
-
offAnchorNode: (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
135
|
+
offAnchorNode: new Set([
|
|
136
|
+
anchorNode.on("afterDestroy", () => this.dispose()),
|
|
137
|
+
// TODO: this should be triggered on change even for unhydrated nodes.
|
|
138
|
+
anchorNode.on("childrenChanging", () => {
|
|
139
|
+
this.generationNumber += 1;
|
|
140
|
+
}),
|
|
141
|
+
]),
|
|
101
142
|
};
|
|
102
|
-
}
|
|
103
143
|
|
|
104
|
-
|
|
105
|
-
this.#
|
|
106
|
-
|
|
144
|
+
// If needed, register forwarding emitters for events from before hydration
|
|
145
|
+
if (this.#preHydrationEvents !== undefined) {
|
|
146
|
+
for (const eventName of kernelEvents) {
|
|
147
|
+
if (this.#preHydrationEvents.hasListeners(eventName)) {
|
|
148
|
+
this.#hydrated.offAnchorNode.add(
|
|
149
|
+
// Argument is forwarded between matching events, so the type should be correct.
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
151
|
+
anchorNode.on(eventName, (arg: any) =>
|
|
152
|
+
this.#preHydrationEvents?.emit(eventName, arg),
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
107
158
|
}
|
|
108
159
|
|
|
109
160
|
public isHydrated(): boolean {
|
|
161
|
+
assert(!this.disposed, "cannot use a disposed node");
|
|
110
162
|
return this.#hydrated !== undefined;
|
|
111
163
|
}
|
|
112
164
|
|
|
113
165
|
public getStatus(): TreeStatus {
|
|
166
|
+
if (this.disposed) {
|
|
167
|
+
return TreeStatus.Deleted;
|
|
168
|
+
}
|
|
114
169
|
if (this.#hydrated?.anchorNode === undefined) {
|
|
115
170
|
return TreeStatus.New;
|
|
116
171
|
}
|
|
@@ -127,15 +182,19 @@ export class TreeNodeKernel implements Listenable<TreeChangeEvents> {
|
|
|
127
182
|
return treeStatusFromAnchorCache(this.#hydrated.anchorNode);
|
|
128
183
|
}
|
|
129
184
|
|
|
130
|
-
public on<K extends keyof
|
|
131
|
-
eventName: K,
|
|
132
|
-
listener: TreeChangeEvents[K],
|
|
133
|
-
): Off {
|
|
185
|
+
public on<K extends keyof KernelEvents>(eventName: K, listener: KernelEvents[K]): Off {
|
|
134
186
|
return this.#events.on(eventName, listener);
|
|
135
187
|
}
|
|
136
188
|
|
|
137
189
|
public dispose(): void {
|
|
138
|
-
this.
|
|
190
|
+
this.disposed = true;
|
|
191
|
+
for (const off of this.#hydrated?.offAnchorNode ?? []) {
|
|
192
|
+
off();
|
|
193
|
+
}
|
|
139
194
|
// TODO: go to the context and remove myself from withAnchors
|
|
140
195
|
}
|
|
141
196
|
}
|
|
197
|
+
|
|
198
|
+
const kernelEvents = ["childrenChangedAfterBatch", "subtreeChangedAfterBatch"] as const;
|
|
199
|
+
|
|
200
|
+
type KernelEvents = Pick<AnchorEvents, (typeof kernelEvents)[number]>;
|
|
@@ -24,9 +24,27 @@ import { isFlexTreeNode, type FlexTreeNode } from "../../feature-libraries/index
|
|
|
24
24
|
*/
|
|
25
25
|
export type Unhydrated<T> = T;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Data included for {@link TreeChangeEvents.nodeChanged}.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export interface NodeChangedData {
|
|
32
|
+
/**
|
|
33
|
+
* When the node changed is an object or Map node, this lists all the properties which changed.
|
|
34
|
+
* @remarks
|
|
35
|
+
* This only includes changes to the node itself (which would trigger {@link TreeChangeEvents.nodeChanged}).
|
|
36
|
+
*/
|
|
37
|
+
readonly changedProperties?: ReadonlySet<string>;
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
/**
|
|
28
41
|
* A collection of events that can be emitted by a {@link TreeNode}.
|
|
29
42
|
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* Currently events can be subscribed to for {@link Unhydrated} nodes, however no events will be triggered for the nodes until after they are hydrated.
|
|
45
|
+
* This is considered a known issue, and should be fixed in future versions.
|
|
46
|
+
* Do not rely on the fact that editing unhydrated nodes does not trigger their events.
|
|
47
|
+
*
|
|
30
48
|
* @privateRemarks
|
|
31
49
|
* TODO: add a way to subscribe to a specific field (for nodeChanged and treeChanged).
|
|
32
50
|
* Probably have object node and map node specific APIs for this.
|
|
@@ -45,17 +63,15 @@ export type Unhydrated<T> = T;
|
|
|
45
63
|
*
|
|
46
64
|
* @sealed @public
|
|
47
65
|
*/
|
|
48
|
-
export interface TreeChangeEvents {
|
|
66
|
+
export interface TreeChangeEvents<TNode = TreeNode> {
|
|
49
67
|
/**
|
|
50
|
-
* Emitted by a node after a batch of changes has been applied to the tree, if
|
|
51
|
-
* change is:
|
|
68
|
+
* Emitted by a node after a batch of changes has been applied to the tree, if any of the changes affected the node.
|
|
52
69
|
*
|
|
53
|
-
* -
|
|
54
|
-
* to something else, including `undefined`).
|
|
70
|
+
* - Object nodes define a change as the value of one of its properties changing (i.e., the property's value is set, including when set to undefined).
|
|
55
71
|
*
|
|
56
|
-
* -
|
|
72
|
+
* - Array node define a change as when an element is added, removed, moved or replaced.
|
|
57
73
|
*
|
|
58
|
-
* -
|
|
74
|
+
* - Map nodes define a change as when an entry is added, updated, or removed.
|
|
59
75
|
*
|
|
60
76
|
* @remarks
|
|
61
77
|
* This event is not emitted when:
|
|
@@ -70,15 +86,23 @@ export interface TreeChangeEvents {
|
|
|
70
86
|
* For remote edits, this event is not guaranteed to occur in the same order or quantity that it did in
|
|
71
87
|
* the client that made the original edit.
|
|
72
88
|
*
|
|
73
|
-
* When
|
|
89
|
+
* When the event is emitted, the tree is guaranteed to be in-schema.
|
|
74
90
|
*
|
|
75
91
|
* @privateRemarks
|
|
76
92
|
* This event occurs whenever the apparent contents of the node instance change, regardless of what caused the change.
|
|
77
93
|
* For example, it will fire when the local client reassigns a child, when part of a remote edit is applied to the
|
|
78
94
|
* node, or when the node has to be updated due to resolution of a merge conflict
|
|
79
95
|
* (for example a previously applied local change might be undone, then reapplied differently or not at all).
|
|
96
|
+
*
|
|
97
|
+
* TODO: defined and document event ordering (ex: bottom up, with nodeChanged before treeCHange on each level).
|
|
80
98
|
*/
|
|
81
|
-
nodeChanged(
|
|
99
|
+
nodeChanged(
|
|
100
|
+
data: NodeChangedData &
|
|
101
|
+
// For object and Map nodes, make properties specific to them required instead of optional:
|
|
102
|
+
(TNode extends WithType<string, NodeKind.Map | NodeKind.Object>
|
|
103
|
+
? Required<Pick<NodeChangedData, "changedProperties">>
|
|
104
|
+
: unknown),
|
|
105
|
+
): void;
|
|
82
106
|
|
|
83
107
|
/**
|
|
84
108
|
* Emitted by a node after a batch of changes has been applied to the tree, when something changed anywhere in the
|
|
@@ -99,6 +123,8 @@ export interface TreeChangeEvents {
|
|
|
99
123
|
treeChanged(): void;
|
|
100
124
|
}
|
|
101
125
|
|
|
126
|
+
export type IsListener2<TListener> = TListener extends (...args: any[]) => void ? true : false;
|
|
127
|
+
|
|
102
128
|
/**
|
|
103
129
|
* A non-{@link NodeKind.Leaf|leaf} SharedTree node. Includes objects, arrays, and maps.
|
|
104
130
|
*
|