@fluidframework/tree 2.4.0-299707 → 2.4.0

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 (78) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/api-report/tree.alpha.api.md +2 -1
  3. package/api-report/tree.beta.api.md +2 -1
  4. package/dist/feature-libraries/flex-tree/lazyField.js +1 -1
  5. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  6. package/dist/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  7. package/dist/feature-libraries/modular-schema/modularChangeFamily.js +3 -3
  8. package/dist/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  9. package/dist/packageVersion.d.ts +1 -1
  10. package/dist/packageVersion.d.ts.map +1 -1
  11. package/dist/packageVersion.js +1 -1
  12. package/dist/packageVersion.js.map +1 -1
  13. package/dist/shared-tree/schematizingTreeView.js +1 -1
  14. package/dist/shared-tree/schematizingTreeView.js.map +1 -1
  15. package/dist/shared-tree/sharedTree.js +1 -1
  16. package/dist/shared-tree/sharedTree.js.map +1 -1
  17. package/dist/shared-tree-core/branch.js +1 -1
  18. package/dist/shared-tree-core/branch.js.map +1 -1
  19. package/dist/simple-tree/api/customTree.js +5 -5
  20. package/dist/simple-tree/api/customTree.js.map +1 -1
  21. package/dist/simple-tree/api/treeApiBeta.d.ts +14 -2
  22. package/dist/simple-tree/api/treeApiBeta.d.ts.map +1 -1
  23. package/dist/simple-tree/api/treeApiBeta.js +23 -8
  24. package/dist/simple-tree/api/treeApiBeta.js.map +1 -1
  25. package/dist/simple-tree/core/schemaCaching.js +1 -1
  26. package/dist/simple-tree/core/schemaCaching.js.map +1 -1
  27. package/dist/simple-tree/core/treeNodeKernel.d.ts +6 -1
  28. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  29. package/dist/simple-tree/core/treeNodeKernel.js +7 -1
  30. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  31. package/dist/simple-tree/core/unhydratedFlexTree.js +1 -1
  32. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  33. package/dist/simple-tree/toFlexSchema.js +2 -2
  34. package/dist/simple-tree/toFlexSchema.js.map +1 -1
  35. package/lib/feature-libraries/flex-tree/lazyField.js +1 -1
  36. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  37. package/lib/feature-libraries/modular-schema/modularChangeFamily.d.ts.map +1 -1
  38. package/lib/feature-libraries/modular-schema/modularChangeFamily.js +3 -3
  39. package/lib/feature-libraries/modular-schema/modularChangeFamily.js.map +1 -1
  40. package/lib/packageVersion.d.ts +1 -1
  41. package/lib/packageVersion.d.ts.map +1 -1
  42. package/lib/packageVersion.js +1 -1
  43. package/lib/packageVersion.js.map +1 -1
  44. package/lib/shared-tree/schematizingTreeView.js +1 -1
  45. package/lib/shared-tree/schematizingTreeView.js.map +1 -1
  46. package/lib/shared-tree/sharedTree.js +1 -1
  47. package/lib/shared-tree/sharedTree.js.map +1 -1
  48. package/lib/shared-tree-core/branch.js +1 -1
  49. package/lib/shared-tree-core/branch.js.map +1 -1
  50. package/lib/simple-tree/api/customTree.js +5 -5
  51. package/lib/simple-tree/api/customTree.js.map +1 -1
  52. package/lib/simple-tree/api/treeApiBeta.d.ts +14 -2
  53. package/lib/simple-tree/api/treeApiBeta.d.ts.map +1 -1
  54. package/lib/simple-tree/api/treeApiBeta.js +23 -8
  55. package/lib/simple-tree/api/treeApiBeta.js.map +1 -1
  56. package/lib/simple-tree/core/schemaCaching.js +1 -1
  57. package/lib/simple-tree/core/schemaCaching.js.map +1 -1
  58. package/lib/simple-tree/core/treeNodeKernel.d.ts +6 -1
  59. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  60. package/lib/simple-tree/core/treeNodeKernel.js +7 -1
  61. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  62. package/lib/simple-tree/core/unhydratedFlexTree.js +1 -1
  63. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  64. package/lib/simple-tree/toFlexSchema.js +2 -2
  65. package/lib/simple-tree/toFlexSchema.js.map +1 -1
  66. package/package.json +20 -20
  67. package/src/feature-libraries/flex-tree/lazyField.ts +1 -1
  68. package/src/feature-libraries/modular-schema/modularChangeFamily.ts +9 -3
  69. package/src/packageVersion.ts +1 -1
  70. package/src/shared-tree/schematizingTreeView.ts +1 -1
  71. package/src/shared-tree/sharedTree.ts +1 -1
  72. package/src/shared-tree-core/branch.ts +1 -1
  73. package/src/simple-tree/api/customTree.ts +5 -5
  74. package/src/simple-tree/api/treeApiBeta.ts +65 -3
  75. package/src/simple-tree/core/schemaCaching.ts +1 -1
  76. package/src/simple-tree/core/treeNodeKernel.ts +10 -2
  77. package/src/simple-tree/core/unhydratedFlexTree.ts +1 -1
  78. package/src/simple-tree/toFlexSchema.ts +2 -2
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/tree";
9
- export const pkgVersion = "2.4.0-299707";
9
+ export const pkgVersion = "2.4.0";
@@ -273,7 +273,7 @@ export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitField
273
273
  this.view = view;
274
274
  assert(
275
275
  !this.checkout.forest.anchors.slots.has(SimpleContextSlot),
276
- "extra simple tree context",
276
+ 0xa47 /* extra simple tree context */,
277
277
  );
278
278
  this.checkout.forest.anchors.slots.set(
279
279
  SimpleContextSlot,
@@ -357,7 +357,7 @@ export function getBranch(view: TreeView<ImplicitFieldSchema>): TreeBranch;
357
357
  export function getBranch(treeOrView: ITree | TreeView<ImplicitFieldSchema>): TreeBranch {
358
358
  assert(
359
359
  treeOrView instanceof SharedTree || treeOrView instanceof SchematizingSimpleTreeView,
360
- "Unsupported implementation",
360
+ 0xa48 /* Unsupported implementation */,
361
361
  );
362
362
  const checkout: TreeCheckout = treeOrView.checkout;
363
363
  // This cast is safe so long as TreeCheckout supports all the operations on the branch interface.
@@ -253,7 +253,7 @@ export class SharedTreeBranch<
253
253
  this.assertNotDisposed();
254
254
 
255
255
  const revisionTag = taggedChange.revision;
256
- assert(revisionTag !== undefined, "Revision tag must be provided");
256
+ assert(revisionTag !== undefined, 0xa49 /* Revision tag must be provided */);
257
257
 
258
258
  const newHead = mintCommit(this.head, {
259
259
  revision: revisionTag,
@@ -80,15 +80,15 @@ export function customFromCursorInner<TChild, THandle>(
80
80
  case booleanSchema.identifier:
81
81
  case nullSchema.identifier:
82
82
  case stringSchema.identifier:
83
- assert(reader.value !== undefined, "out of schema: missing value");
84
- assert(!isFluidHandle(reader.value), "out of schema: unexpected FluidHandle");
83
+ assert(reader.value !== undefined, 0xa50 /* out of schema: missing value */);
84
+ assert(!isFluidHandle(reader.value), 0xa51 /* out of schema: unexpected FluidHandle */);
85
85
  return reader.value;
86
86
  case handleSchema.identifier:
87
- assert(reader.value !== undefined, "out of schema: missing value");
88
- assert(isFluidHandle(reader.value), "out of schema: expected FluidHandle");
87
+ assert(reader.value !== undefined, 0xa52 /* out of schema: missing value */);
88
+ assert(isFluidHandle(reader.value), 0xa53 /* out of schema: expected FluidHandle */);
89
89
  return options.valueConverter(reader.value);
90
90
  default: {
91
- assert(reader.value === undefined, "out of schema: unexpected value");
91
+ assert(reader.value === undefined, 0xa54 /* out of schema: unexpected value */);
92
92
  if (nodeSchema.kind === NodeKind.Array) {
93
93
  const fields = inCursorField(reader, EmptyKey, () =>
94
94
  mapCursorField(reader, () => childHandler(reader, options, schema)),
@@ -3,8 +3,19 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import type { NodeKind, TreeChangeEvents, TreeNode, WithType } from "../core/index.js";
6
+ import {
7
+ getKernel,
8
+ isTreeNode,
9
+ type NodeKind,
10
+ type TreeChangeEvents,
11
+ type TreeNode,
12
+ type Unhydrated,
13
+ type WithType,
14
+ } from "../core/index.js";
15
+ import type { ImplicitFieldSchema, TreeFieldFromImplicitField } from "../schemaTypes.js";
7
16
  import { treeNodeApi } from "./treeNodeApi.js";
17
+ import { createFromCursor, cursorFromInsertable } from "./create.js";
18
+ import type { ITreeCursorSynchronous } from "../../core/index.js";
8
19
 
9
20
  /**
10
21
  * Data included for {@link TreeChangeEventsBeta.nodeChanged}.
@@ -82,7 +93,7 @@ export interface TreeChangeEventsBeta<TNode extends TreeNode = TreeNode>
82
93
  * Extensions to {@link Tree} which are not yet stable.
83
94
  * @sealed @beta
84
95
  */
85
- export const TreeBeta = {
96
+ export const TreeBeta: {
86
97
  /**
87
98
  * Register an event listener on the given node.
88
99
  * @param node - The node whose events should be subscribed to.
@@ -91,6 +102,26 @@ export const TreeBeta = {
91
102
  * @returns A callback function which will deregister the event.
92
103
  * This callback should be called only once.
93
104
  */
105
+ on<K extends keyof TreeChangeEventsBeta<TNode>, TNode extends TreeNode>(
106
+ node: TNode,
107
+ eventName: K,
108
+ listener: NoInfer<TreeChangeEventsBeta<TNode>[K]>,
109
+ ): () => void;
110
+
111
+ /**
112
+ * Clones the persisted data associated with a node. Some key things to note:
113
+ * - Local state, such as properties added to customized schema classes, will not be cloned. However, they will be
114
+ * initialized to their default state just as if the node had been created via its constructor.
115
+ * - Value node types (i.e., numbers, strings, booleans, nulls and Fluid handles) will be returned as is.
116
+ * - The identifiers in the node's subtree will be preserved, i.e., they are not replaced with new values.
117
+ *
118
+ * @param node - The node to clone.
119
+ * @returns A new unhydrated node with the same persisted data as the original node.
120
+ */
121
+ clone<TSchema extends ImplicitFieldSchema>(
122
+ node: TreeFieldFromImplicitField<TSchema>,
123
+ ): TreeFieldFromImplicitField<TSchema>;
124
+ } = {
94
125
  on<K extends keyof TreeChangeEventsBeta<TNode>, TNode extends TreeNode>(
95
126
  node: TNode,
96
127
  eventName: K,
@@ -98,4 +129,35 @@ export const TreeBeta = {
98
129
  ): () => void {
99
130
  return treeNodeApi.on(node, eventName, listener);
100
131
  },
101
- } as const;
132
+ clone<TSchema extends ImplicitFieldSchema>(
133
+ node: TreeFieldFromImplicitField<TSchema>,
134
+ ): Unhydrated<TreeFieldFromImplicitField<TSchema>> {
135
+ /* The only non-TreeNode cases are {@link Value} (for an empty optional field) which can be returned as is. */
136
+ if (!isTreeNode(node)) {
137
+ return node;
138
+ }
139
+
140
+ const kernel = getKernel(node);
141
+ /*
142
+ * For unhydrated nodes, we can create a cursor by calling `cursorFromInsertable` because the node
143
+ * hasn't been inserted yet. We can then create a new node from the cursor.
144
+ */
145
+ if (!kernel.isHydrated()) {
146
+ return createFromCursor(
147
+ kernel.schema,
148
+ cursorFromInsertable(kernel.schema, node),
149
+ ) as Unhydrated<TreeFieldFromImplicitField<TSchema>>;
150
+ }
151
+
152
+ // For hydrated nodes, create a new cursor in the forest and then create a new node from the cursor.
153
+ const forest = kernel.context.flexContext.checkout.forest;
154
+ const cursor = forest.allocateCursor("tree.clone");
155
+ forest.moveCursorToPath(kernel.anchorNode, cursor);
156
+ const clonedNode = createFromCursor(
157
+ kernel.schema,
158
+ cursor as ITreeCursorSynchronous,
159
+ ) as Unhydrated<TreeFieldFromImplicitField<TSchema>>;
160
+ cursor.free();
161
+ return clonedNode;
162
+ },
163
+ };
@@ -29,7 +29,7 @@ export function getSimpleContextFromInnerNode(innerNode: InnerNode): Context {
29
29
  }
30
30
 
31
31
  const context = innerNode.anchorNode.anchorSet.slots.get(SimpleContextSlot);
32
- assert(context !== undefined, "missing simple tree context");
32
+ assert(context !== undefined, 0xa55 /* missing simple tree context */);
33
33
 
34
34
  return context;
35
35
  }
@@ -29,7 +29,7 @@ import { fail } from "../../util/index.js";
29
29
  // TODO: decide how to deal with dependencies on flex-tree implementation.
30
30
  // eslint-disable-next-line import/no-internal-modules
31
31
  import { makeTree } from "../../feature-libraries/flex-tree/lazyNode.js";
32
- import { SimpleContextSlot, type Context } from "./context.js";
32
+ import { SimpleContextSlot, type Context, type HydratedContext } from "./context.js";
33
33
  import { UnhydratedFlexTreeNode } from "./unhydratedFlexTree.js";
34
34
 
35
35
  const treeNodeToKernel = new WeakMap<TreeNode, TreeNodeKernel>();
@@ -267,6 +267,14 @@ export class TreeNodeKernel implements Listenable<KernelEvents> {
267
267
  // TODO: go to the context and remove myself from withAnchors
268
268
  }
269
269
 
270
+ public isHydrated(): this is { anchorNode: AnchorNode; context: HydratedContext } {
271
+ return isHydrated(this.#hydrationState);
272
+ }
273
+
274
+ public get anchorNode(): AnchorNode | undefined {
275
+ return isHydrated(this.#hydrationState) ? this.#hydrationState.anchorNode : undefined;
276
+ }
277
+
270
278
  /**
271
279
  * Retrieves the flex node associated with the given target via {@link setInnerNode}.
272
280
  * @remarks
@@ -421,7 +429,7 @@ export function tryDisposeTreeNode(anchorNode: AnchorNode): void {
421
429
  export function getTreeNodeSchemaFromHydratedFlexNode(flexNode: FlexTreeNode): TreeNodeSchema {
422
430
  assert(
423
431
  flexNode.context.isHydrated(),
424
- "getTreeNodeSchemaFromHydratedFlexNode only allows hydrated flex tree nodes",
432
+ 0xa56 /* getTreeNodeSchemaFromHydratedFlexNode only allows hydrated flex tree nodes */,
425
433
  );
426
434
 
427
435
  const context =
@@ -445,7 +445,7 @@ class EagerMapTreeRequiredField
445
445
  // This cannot use ?? since null is a legal value here.
446
446
  assert(
447
447
  super.content !== undefined,
448
- "Expected EagerMapTree required field to have a value",
448
+ 0xa57 /* Expected EagerMapTree required field to have a value */,
449
449
  );
450
450
  return super.content;
451
451
  }
@@ -98,7 +98,7 @@ export function getStoredSchema(schema: TreeNodeSchema): TreeNodeStoredSchema {
98
98
  const kind = schema.kind;
99
99
  switch (kind) {
100
100
  case NodeKind.Leaf: {
101
- assert(schema instanceof LeafNodeSchema, "invalid kind");
101
+ assert(schema instanceof LeafNodeSchema, 0xa4a /* invalid kind */);
102
102
  return new LeafNodeStoredSchema(schema.info);
103
103
  }
104
104
  case NodeKind.Map: {
@@ -116,7 +116,7 @@ export function getStoredSchema(schema: TreeNodeSchema): TreeNodeStoredSchema {
116
116
  return new ObjectNodeStoredSchema(fields);
117
117
  }
118
118
  case NodeKind.Object: {
119
- assert(isObjectNodeSchema(schema), "invalid kind");
119
+ assert(isObjectNodeSchema(schema), 0xa4b /* invalid kind */);
120
120
  const fields: Map<FieldKey, TreeFieldStoredSchema> = new Map();
121
121
  for (const field of schema.flexKeyMap.values()) {
122
122
  fields.set(field.storedKey, convertField(field.schema));