@fluidframework/tree 2.31.1 → 2.33.0-333010

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 (280) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/CHANGELOG.md +44 -0
  3. package/api-extractor/api-extractor.current.json +5 -1
  4. package/api-report/tree.alpha.api.md +44 -20
  5. package/dist/alpha.d.ts +6 -2
  6. package/dist/feature-libraries/flex-tree/context.d.ts +6 -1
  7. package/dist/feature-libraries/flex-tree/context.d.ts.map +1 -1
  8. package/dist/feature-libraries/flex-tree/context.js +5 -2
  9. package/dist/feature-libraries/flex-tree/context.js.map +1 -1
  10. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts +8 -0
  11. package/dist/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  12. package/dist/feature-libraries/flex-tree/flexTreeTypes.js +8 -0
  13. package/dist/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  14. package/dist/feature-libraries/flex-tree/index.d.ts +1 -1
  15. package/dist/feature-libraries/flex-tree/index.d.ts.map +1 -1
  16. package/dist/feature-libraries/flex-tree/index.js +1 -2
  17. package/dist/feature-libraries/flex-tree/index.js.map +1 -1
  18. package/dist/feature-libraries/flex-tree/lazyEntity.d.ts +6 -15
  19. package/dist/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
  20. package/dist/feature-libraries/flex-tree/lazyEntity.js +9 -18
  21. package/dist/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
  22. package/dist/feature-libraries/flex-tree/lazyField.d.ts +3 -3
  23. package/dist/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  24. package/dist/feature-libraries/flex-tree/lazyField.js +14 -14
  25. package/dist/feature-libraries/flex-tree/lazyField.js.map +1 -1
  26. package/dist/feature-libraries/flex-tree/lazyNode.d.ts +4 -4
  27. package/dist/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  28. package/dist/feature-libraries/flex-tree/lazyNode.js +12 -12
  29. package/dist/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  30. package/dist/feature-libraries/index.d.ts +1 -1
  31. package/dist/feature-libraries/index.d.ts.map +1 -1
  32. package/dist/feature-libraries/index.js +1 -2
  33. package/dist/feature-libraries/index.js.map +1 -1
  34. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.d.ts.map +1 -1
  35. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.js +29 -25
  36. package/dist/feature-libraries/node-identifier/nodeIdentifierManager.js.map +1 -1
  37. package/dist/index.d.ts +2 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +5 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/packageVersion.d.ts +1 -1
  42. package/dist/packageVersion.d.ts.map +1 -1
  43. package/dist/packageVersion.js +1 -1
  44. package/dist/packageVersion.js.map +1 -1
  45. package/dist/shared-tree/checkoutFlexTreeView.d.ts +1 -0
  46. package/dist/shared-tree/checkoutFlexTreeView.d.ts.map +1 -1
  47. package/dist/shared-tree/checkoutFlexTreeView.js +4 -0
  48. package/dist/shared-tree/checkoutFlexTreeView.js.map +1 -1
  49. package/dist/shared-tree/sharedTree.js +2 -2
  50. package/dist/shared-tree/sharedTree.js.map +1 -1
  51. package/dist/shared-tree/treeApiAlpha.d.ts +6 -6
  52. package/dist/shared-tree/treeApiAlpha.d.ts.map +1 -1
  53. package/dist/shared-tree/treeApiAlpha.js.map +1 -1
  54. package/dist/shared-tree-core/sharedTreeCore.d.ts +0 -6
  55. package/dist/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  56. package/dist/shared-tree-core/sharedTreeCore.js +0 -17
  57. package/dist/shared-tree-core/sharedTreeCore.js.map +1 -1
  58. package/dist/simple-tree/api/conciseTree.d.ts +2 -2
  59. package/dist/simple-tree/api/conciseTree.d.ts.map +1 -1
  60. package/dist/simple-tree/api/conciseTree.js.map +1 -1
  61. package/dist/simple-tree/api/customTree.d.ts +14 -12
  62. package/dist/simple-tree/api/customTree.d.ts.map +1 -1
  63. package/dist/simple-tree/api/customTree.js.map +1 -1
  64. package/dist/simple-tree/api/getJsonSchema.d.ts +21 -7
  65. package/dist/simple-tree/api/getJsonSchema.d.ts.map +1 -1
  66. package/dist/simple-tree/api/getJsonSchema.js +8 -16
  67. package/dist/simple-tree/api/getJsonSchema.js.map +1 -1
  68. package/dist/simple-tree/api/getSimpleSchema.d.ts +3 -10
  69. package/dist/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  70. package/dist/simple-tree/api/getSimpleSchema.js +4 -16
  71. package/dist/simple-tree/api/getSimpleSchema.js.map +1 -1
  72. package/dist/simple-tree/api/index.d.ts +4 -3
  73. package/dist/simple-tree/api/index.d.ts.map +1 -1
  74. package/dist/simple-tree/api/index.js +2 -1
  75. package/dist/simple-tree/api/index.js.map +1 -1
  76. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts +17 -1
  77. package/dist/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  78. package/dist/simple-tree/api/schemaFactoryAlpha.js +4 -0
  79. package/dist/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  80. package/dist/simple-tree/api/schemaFromSimple.d.ts +5 -2
  81. package/dist/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  82. package/dist/simple-tree/api/schemaFromSimple.js +24 -8
  83. package/dist/simple-tree/api/schemaFromSimple.js.map +1 -1
  84. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts +11 -3
  85. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  86. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +40 -28
  87. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  88. package/dist/simple-tree/api/tree.d.ts +32 -5
  89. package/dist/simple-tree/api/tree.d.ts.map +1 -1
  90. package/dist/simple-tree/api/tree.js +17 -1
  91. package/dist/simple-tree/api/tree.js.map +1 -1
  92. package/dist/simple-tree/api/verboseTree.d.ts +4 -28
  93. package/dist/simple-tree/api/verboseTree.d.ts.map +1 -1
  94. package/dist/simple-tree/api/verboseTree.js.map +1 -1
  95. package/dist/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  96. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js +7 -3
  97. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  98. package/dist/simple-tree/core/treeNodeKernel.d.ts +9 -2
  99. package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  100. package/dist/simple-tree/core/treeNodeKernel.js +46 -30
  101. package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
  102. package/dist/simple-tree/core/unhydratedFlexTree.d.ts +2 -1
  103. package/dist/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  104. package/dist/simple-tree/core/unhydratedFlexTree.js +15 -1
  105. package/dist/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  106. package/dist/simple-tree/index.d.ts +1 -1
  107. package/dist/simple-tree/index.d.ts.map +1 -1
  108. package/dist/simple-tree/index.js +3 -2
  109. package/dist/simple-tree/index.js.map +1 -1
  110. package/dist/simple-tree/objectNode.d.ts.map +1 -1
  111. package/dist/simple-tree/objectNode.js +3 -0
  112. package/dist/simple-tree/objectNode.js.map +1 -1
  113. package/dist/simple-tree/simpleSchema.d.ts +4 -11
  114. package/dist/simple-tree/simpleSchema.d.ts.map +1 -1
  115. package/dist/simple-tree/simpleSchema.js.map +1 -1
  116. package/dist/tableSchema.d.ts +241 -0
  117. package/dist/tableSchema.d.ts.map +1 -0
  118. package/dist/tableSchema.js +238 -0
  119. package/dist/tableSchema.js.map +1 -0
  120. package/dist/treeFactory.d.ts +4 -4
  121. package/dist/treeFactory.d.ts.map +1 -1
  122. package/dist/treeFactory.js.map +1 -1
  123. package/dist/util/breakable.d.ts.map +1 -1
  124. package/dist/util/breakable.js +9 -7
  125. package/dist/util/breakable.js.map +1 -1
  126. package/lib/alpha.d.ts +6 -2
  127. package/lib/feature-libraries/flex-tree/context.d.ts +6 -1
  128. package/lib/feature-libraries/flex-tree/context.d.ts.map +1 -1
  129. package/lib/feature-libraries/flex-tree/context.js +6 -3
  130. package/lib/feature-libraries/flex-tree/context.js.map +1 -1
  131. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts +8 -0
  132. package/lib/feature-libraries/flex-tree/flexTreeTypes.d.ts.map +1 -1
  133. package/lib/feature-libraries/flex-tree/flexTreeTypes.js +8 -0
  134. package/lib/feature-libraries/flex-tree/flexTreeTypes.js.map +1 -1
  135. package/lib/feature-libraries/flex-tree/index.d.ts +1 -1
  136. package/lib/feature-libraries/flex-tree/index.d.ts.map +1 -1
  137. package/lib/feature-libraries/flex-tree/index.js +1 -1
  138. package/lib/feature-libraries/flex-tree/index.js.map +1 -1
  139. package/lib/feature-libraries/flex-tree/lazyEntity.d.ts +6 -15
  140. package/lib/feature-libraries/flex-tree/lazyEntity.d.ts.map +1 -1
  141. package/lib/feature-libraries/flex-tree/lazyEntity.js +8 -17
  142. package/lib/feature-libraries/flex-tree/lazyEntity.js.map +1 -1
  143. package/lib/feature-libraries/flex-tree/lazyField.d.ts +3 -3
  144. package/lib/feature-libraries/flex-tree/lazyField.d.ts.map +1 -1
  145. package/lib/feature-libraries/flex-tree/lazyField.js +15 -15
  146. package/lib/feature-libraries/flex-tree/lazyField.js.map +1 -1
  147. package/lib/feature-libraries/flex-tree/lazyNode.d.ts +4 -4
  148. package/lib/feature-libraries/flex-tree/lazyNode.d.ts.map +1 -1
  149. package/lib/feature-libraries/flex-tree/lazyNode.js +13 -13
  150. package/lib/feature-libraries/flex-tree/lazyNode.js.map +1 -1
  151. package/lib/feature-libraries/index.d.ts +1 -1
  152. package/lib/feature-libraries/index.d.ts.map +1 -1
  153. package/lib/feature-libraries/index.js +1 -1
  154. package/lib/feature-libraries/index.js.map +1 -1
  155. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.d.ts.map +1 -1
  156. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.js +28 -24
  157. package/lib/feature-libraries/node-identifier/nodeIdentifierManager.js.map +1 -1
  158. package/lib/index.d.ts +2 -1
  159. package/lib/index.d.ts.map +1 -1
  160. package/lib/index.js +2 -1
  161. package/lib/index.js.map +1 -1
  162. package/lib/packageVersion.d.ts +1 -1
  163. package/lib/packageVersion.d.ts.map +1 -1
  164. package/lib/packageVersion.js +1 -1
  165. package/lib/packageVersion.js.map +1 -1
  166. package/lib/shared-tree/checkoutFlexTreeView.d.ts +1 -0
  167. package/lib/shared-tree/checkoutFlexTreeView.d.ts.map +1 -1
  168. package/lib/shared-tree/checkoutFlexTreeView.js +4 -0
  169. package/lib/shared-tree/checkoutFlexTreeView.js.map +1 -1
  170. package/lib/shared-tree/sharedTree.js +2 -2
  171. package/lib/shared-tree/sharedTree.js.map +1 -1
  172. package/lib/shared-tree/treeApiAlpha.d.ts +6 -6
  173. package/lib/shared-tree/treeApiAlpha.d.ts.map +1 -1
  174. package/lib/shared-tree/treeApiAlpha.js.map +1 -1
  175. package/lib/shared-tree-core/sharedTreeCore.d.ts +0 -6
  176. package/lib/shared-tree-core/sharedTreeCore.d.ts.map +1 -1
  177. package/lib/shared-tree-core/sharedTreeCore.js +0 -17
  178. package/lib/shared-tree-core/sharedTreeCore.js.map +1 -1
  179. package/lib/simple-tree/api/conciseTree.d.ts +2 -2
  180. package/lib/simple-tree/api/conciseTree.d.ts.map +1 -1
  181. package/lib/simple-tree/api/conciseTree.js.map +1 -1
  182. package/lib/simple-tree/api/customTree.d.ts +14 -12
  183. package/lib/simple-tree/api/customTree.d.ts.map +1 -1
  184. package/lib/simple-tree/api/customTree.js.map +1 -1
  185. package/lib/simple-tree/api/getJsonSchema.d.ts +21 -7
  186. package/lib/simple-tree/api/getJsonSchema.d.ts.map +1 -1
  187. package/lib/simple-tree/api/getJsonSchema.js +8 -16
  188. package/lib/simple-tree/api/getJsonSchema.js.map +1 -1
  189. package/lib/simple-tree/api/getSimpleSchema.d.ts +3 -10
  190. package/lib/simple-tree/api/getSimpleSchema.d.ts.map +1 -1
  191. package/lib/simple-tree/api/getSimpleSchema.js +4 -16
  192. package/lib/simple-tree/api/getSimpleSchema.js.map +1 -1
  193. package/lib/simple-tree/api/index.d.ts +4 -3
  194. package/lib/simple-tree/api/index.d.ts.map +1 -1
  195. package/lib/simple-tree/api/index.js +1 -1
  196. package/lib/simple-tree/api/index.js.map +1 -1
  197. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts +17 -1
  198. package/lib/simple-tree/api/schemaFactoryAlpha.d.ts.map +1 -1
  199. package/lib/simple-tree/api/schemaFactoryAlpha.js +4 -0
  200. package/lib/simple-tree/api/schemaFactoryAlpha.js.map +1 -1
  201. package/lib/simple-tree/api/schemaFromSimple.d.ts +5 -2
  202. package/lib/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  203. package/lib/simple-tree/api/schemaFromSimple.js +24 -8
  204. package/lib/simple-tree/api/schemaFromSimple.js.map +1 -1
  205. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts +11 -3
  206. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  207. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +41 -29
  208. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  209. package/lib/simple-tree/api/tree.d.ts +32 -5
  210. package/lib/simple-tree/api/tree.d.ts.map +1 -1
  211. package/lib/simple-tree/api/tree.js +16 -1
  212. package/lib/simple-tree/api/tree.js.map +1 -1
  213. package/lib/simple-tree/api/verboseTree.d.ts +4 -28
  214. package/lib/simple-tree/api/verboseTree.d.ts.map +1 -1
  215. package/lib/simple-tree/api/verboseTree.js.map +1 -1
  216. package/lib/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  217. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js +7 -3
  218. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  219. package/lib/simple-tree/core/treeNodeKernel.d.ts +9 -2
  220. package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
  221. package/lib/simple-tree/core/treeNodeKernel.js +48 -32
  222. package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
  223. package/lib/simple-tree/core/unhydratedFlexTree.d.ts +2 -1
  224. package/lib/simple-tree/core/unhydratedFlexTree.d.ts.map +1 -1
  225. package/lib/simple-tree/core/unhydratedFlexTree.js +15 -1
  226. package/lib/simple-tree/core/unhydratedFlexTree.js.map +1 -1
  227. package/lib/simple-tree/index.d.ts +1 -1
  228. package/lib/simple-tree/index.d.ts.map +1 -1
  229. package/lib/simple-tree/index.js +1 -1
  230. package/lib/simple-tree/index.js.map +1 -1
  231. package/lib/simple-tree/objectNode.d.ts.map +1 -1
  232. package/lib/simple-tree/objectNode.js +4 -1
  233. package/lib/simple-tree/objectNode.js.map +1 -1
  234. package/lib/simple-tree/simpleSchema.d.ts +4 -11
  235. package/lib/simple-tree/simpleSchema.d.ts.map +1 -1
  236. package/lib/simple-tree/simpleSchema.js.map +1 -1
  237. package/lib/tableSchema.d.ts +241 -0
  238. package/lib/tableSchema.d.ts.map +1 -0
  239. package/lib/tableSchema.js +235 -0
  240. package/lib/tableSchema.js.map +1 -0
  241. package/lib/treeFactory.d.ts +4 -4
  242. package/lib/treeFactory.d.ts.map +1 -1
  243. package/lib/treeFactory.js.map +1 -1
  244. package/lib/util/breakable.d.ts.map +1 -1
  245. package/lib/util/breakable.js +9 -7
  246. package/lib/util/breakable.js.map +1 -1
  247. package/package.json +23 -22
  248. package/src/feature-libraries/flex-tree/context.ts +13 -3
  249. package/src/feature-libraries/flex-tree/flexTreeTypes.ts +8 -0
  250. package/src/feature-libraries/flex-tree/index.ts +0 -1
  251. package/src/feature-libraries/flex-tree/lazyEntity.ts +11 -21
  252. package/src/feature-libraries/flex-tree/lazyField.ts +17 -26
  253. package/src/feature-libraries/flex-tree/lazyNode.ts +13 -19
  254. package/src/feature-libraries/index.ts +0 -1
  255. package/src/feature-libraries/node-identifier/nodeIdentifierManager.ts +39 -38
  256. package/src/index.ts +6 -2
  257. package/src/packageVersion.ts +1 -1
  258. package/src/shared-tree/checkoutFlexTreeView.ts +6 -0
  259. package/src/shared-tree/sharedTree.ts +2 -2
  260. package/src/shared-tree/treeApiAlpha.ts +17 -15
  261. package/src/shared-tree-core/sharedTreeCore.ts +0 -23
  262. package/src/simple-tree/api/conciseTree.ts +4 -4
  263. package/src/simple-tree/api/customTree.ts +16 -14
  264. package/src/simple-tree/api/getJsonSchema.ts +25 -16
  265. package/src/simple-tree/api/getSimpleSchema.ts +4 -18
  266. package/src/simple-tree/api/index.ts +4 -2
  267. package/src/simple-tree/api/schemaFactoryAlpha.ts +18 -1
  268. package/src/simple-tree/api/schemaFromSimple.ts +45 -16
  269. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +53 -34
  270. package/src/simple-tree/api/tree.ts +51 -4
  271. package/src/simple-tree/api/verboseTree.ts +7 -32
  272. package/src/simple-tree/api/viewSchemaToSimpleSchema.ts +8 -3
  273. package/src/simple-tree/core/treeNodeKernel.ts +57 -35
  274. package/src/simple-tree/core/unhydratedFlexTree.ts +22 -5
  275. package/src/simple-tree/index.ts +4 -2
  276. package/src/simple-tree/objectNode.ts +4 -2
  277. package/src/simple-tree/simpleSchema.ts +4 -12
  278. package/src/tableSchema.ts +668 -0
  279. package/src/treeFactory.ts +4 -3
  280. package/src/util/breakable.ts +9 -6
@@ -10,14 +10,15 @@ import {
10
10
  type AllowedTypes,
11
11
  type FieldSchemaAlpha,
12
12
  FieldKind,
13
+ type FieldProps,
13
14
  } from "../schemaTypes.js";
14
- import { SchemaFactory } from "./schemaFactory.js";
15
15
  import type {
16
16
  SimpleFieldSchema,
17
17
  SimpleNodeSchema,
18
18
  SimpleTreeSchema,
19
19
  } from "../simpleSchema.js";
20
20
  import { SchemaFactoryAlpha } from "./schemaFactoryAlpha.js";
21
+ import type { TreeSchema } from "./tree.js";
21
22
 
22
23
  const factory = new SchemaFactoryAlpha(undefined);
23
24
 
@@ -30,31 +31,53 @@ const factory = new SchemaFactoryAlpha(undefined);
30
31
  * will produce a poor TypeScript typing experience which is subject to change.
31
32
  *
32
33
  * Editing through a view produced using this schema can easily violate invariants other users of the document might expect and must be done with great care.
34
+ *
35
+ * This API bakes in some arbitrary policy choices for how to handle data that is not included in the SimpleTreeSchema API, for example the value of `allowUnknownOptionalFields`.
36
+ * If any particular choice is required for such cases, this API should not be used.
33
37
  * @alpha
34
38
  */
35
- export function generateSchemaFromSimpleSchema(simple: SimpleTreeSchema): FieldSchemaAlpha {
39
+ export function generateSchemaFromSimpleSchema(simple: SimpleTreeSchema): TreeSchema {
36
40
  const context: Context = new Map(
37
- [...simple.definitions].map(([id, schema]): [string, () => TreeNodeSchema] => [
38
- id,
39
- // This relies on the caching in evaluateLazySchema so that it only runs once.
40
- () => generateNode(id, schema, context),
41
- ]),
41
+ [...simple.definitions].map(
42
+ ([id, schema]): [string, () => TreeNodeSchema & SimpleNodeSchema] => [
43
+ id,
44
+ // This relies on the caching in evaluateLazySchema so that it only runs once.
45
+ () => generateNode(id, schema, context),
46
+ ],
47
+ ),
42
48
  );
43
- return generateFieldSchema(simple, context);
49
+ const root = generateFieldSchema(simple.root, context, undefined);
50
+ const definitions = new Map<string, TreeNodeSchema & SimpleNodeSchema>();
51
+ for (const [id, lazy] of context) {
52
+ definitions.set(id, lazy());
53
+ }
54
+ return {
55
+ root,
56
+ definitions,
57
+ };
44
58
  }
45
59
 
46
- type Context = ReadonlyMap<string, () => TreeNodeSchema>;
60
+ type Context = ReadonlyMap<string, () => TreeNodeSchema & SimpleNodeSchema>;
47
61
 
48
- function generateFieldSchema(simple: SimpleFieldSchema, context: Context): FieldSchemaAlpha {
62
+ function generateFieldSchema(
63
+ simple: SimpleFieldSchema,
64
+ context: Context,
65
+ storedKey: string | undefined,
66
+ ): FieldSchemaAlpha {
49
67
  const allowed = generateAllowedTypes(simple.allowedTypesIdentifiers, context);
68
+ const props: Omit<FieldProps, "defaultProvider"> = {
69
+ metadata: simple.metadata,
70
+ key: storedKey,
71
+ };
72
+
50
73
  // Using createFieldSchema could work, but would require setting up the default providers.
51
74
  switch (simple.kind) {
52
75
  case FieldKind.Identifier:
53
- return SchemaFactoryAlpha.identifier({ metadata: simple.metadata });
76
+ return SchemaFactoryAlpha.identifier(props);
54
77
  case FieldKind.Optional:
55
- return SchemaFactoryAlpha.optional(allowed, { metadata: simple.metadata });
78
+ return SchemaFactoryAlpha.optional(allowed, props);
56
79
  case FieldKind.Required:
57
- return SchemaFactoryAlpha.required(allowed, { metadata: simple.metadata });
80
+ return SchemaFactoryAlpha.required(allowed, props);
58
81
  default:
59
82
  return unreachableCase(simple.kind);
60
83
  }
@@ -64,13 +87,19 @@ function generateAllowedTypes(allowed: ReadonlySet<string>, context: Context): A
64
87
  return [...allowed].map((id) => context.get(id) ?? fail(0xb5a /* Missing schema */));
65
88
  }
66
89
 
67
- function generateNode(id: string, schema: SimpleNodeSchema, context: Context): TreeNodeSchema {
90
+ function generateNode(
91
+ id: string,
92
+ schema: SimpleNodeSchema,
93
+ context: Context,
94
+ ): TreeNodeSchema & SimpleNodeSchema {
68
95
  switch (schema.kind) {
69
96
  case NodeKind.Object: {
70
97
  const fields: Record<string, FieldSchema> = {};
71
98
  for (const [key, field] of schema.fields) {
72
- fields[key] = generateFieldSchema(field, context);
99
+ fields[key] = generateFieldSchema(field, context, field.storedKey);
73
100
  }
101
+ // Here allowUnknownOptionalFields is implicitly defaulting. This is a subjective policy choice:
102
+ // users of this code are expected to handle what ever choice this code makes for cases like this.
74
103
  return factory.object(id, fields, { metadata: schema.metadata });
75
104
  }
76
105
  case NodeKind.Array:
@@ -87,7 +116,7 @@ function generateNode(id: string, schema: SimpleNodeSchema, context: Context): T
87
116
  );
88
117
  case NodeKind.Leaf:
89
118
  return (
90
- SchemaFactory.leaves.find((leaf) => leaf.identifier === id) ??
119
+ SchemaFactoryAlpha.leaves.find((leaf) => leaf.identifier === id) ??
91
120
  fail(0xb5b /* Missing schema */)
92
121
  );
93
122
  default:
@@ -6,7 +6,7 @@
6
6
  import { unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
8
  import { ValueSchema } from "../../core/index.js";
9
- import { copyProperty, getOrCreate, hasSingle, type Mutable } from "../../util/index.js";
9
+ import { copyProperty, hasSingle, type Mutable } from "../../util/index.js";
10
10
  import type {
11
11
  JsonArrayNodeSchema,
12
12
  JsonFieldSchema,
@@ -24,24 +24,39 @@ import type {
24
24
  SimpleArrayNodeSchema,
25
25
  SimpleLeafNodeSchema,
26
26
  SimpleMapNodeSchema,
27
- SimpleNodeSchema,
28
- SimpleObjectNodeSchema,
29
- SimpleTreeSchema,
30
27
  } from "../simpleSchema.js";
31
- import { NodeKind } from "../core/index.js";
28
+ import { NodeKind, type TreeNodeSchema } from "../core/index.js";
29
+ import type { TreeSchema } from "./tree.js";
30
+ import type { TreeSchemaEncodingOptions } from "./getJsonSchema.js";
31
+ import { ObjectNodeSchema } from "../objectNodeTypes.js";
32
+ import { ArrayNodeSchema } from "../arrayNodeTypes.js";
33
+ import { MapNodeSchema } from "../mapNodeTypes.js";
34
+ import { LeafNodeSchema } from "../leafNodeSchema.js";
32
35
 
33
36
  /**
34
37
  * Generates a JSON Schema representation from a simple tree schema.
38
+ * @remarks
39
+ * This expects the data to be in the {@link ConciseTree} format.
40
+ *
41
+ * This cannot handle the case where the root is undefined since undefined is not a concept in JSON.
42
+ * This also cannot handle {@link SchemaStatics.handle} since they also are not supported in JSON.
43
+ *
35
44
  * @internal
36
45
  */
37
- export function toJsonSchema(schema: SimpleTreeSchema): JsonTreeSchema {
38
- const definitions = convertDefinitions(schema.definitions);
46
+ export function toJsonSchema(
47
+ schema: TreeSchema,
48
+ options: Required<TreeSchemaEncodingOptions>,
49
+ ): JsonTreeSchema {
50
+ const definitions = convertDefinitions(schema.definitions, options);
39
51
 
40
52
  const allowedTypes: JsonSchemaRef[] = [];
41
- for (const allowedType of schema.allowedTypesIdentifiers) {
53
+ for (const allowedType of schema.root.allowedTypesIdentifiers) {
42
54
  allowedTypes.push(createSchemaRef(allowedType));
43
55
  }
44
56
 
57
+ // TODO: deduplicate field handling logic from convertObjectNodeSchema: at least include metadata's description.
58
+ // TODO: maybe account for consider schema.kind, or just take in ImplicitAllowedTypes
59
+ // TODO: handle case where allowedTypes is empty.
45
60
  return hasSingle(allowedTypes)
46
61
  ? {
47
62
  ...allowedTypes[0],
@@ -54,40 +69,35 @@ export function toJsonSchema(schema: SimpleTreeSchema): JsonTreeSchema {
54
69
  }
55
70
 
56
71
  function convertDefinitions(
57
- definitions: ReadonlyMap<string, SimpleNodeSchema>,
72
+ definitions: ReadonlyMap<string, TreeNodeSchema>,
73
+ options: Required<TreeSchemaEncodingOptions>,
58
74
  ): Record<string, JsonNodeSchema> {
59
75
  const result: Record<string, JsonNodeSchema> = {};
60
76
  for (const [key, value] of definitions) {
61
- result[key] = convertNodeSchema(value);
77
+ result[key] = convertNodeSchema(value, options);
62
78
  }
63
79
  return result;
64
80
  }
65
81
 
66
- /**
67
- * Private symbol under which the results of {@link convertNodeSchema} are cached on an input {@link SimpleNodeSchema}.
68
- */
69
- const nodeJsonSchemaCache = new WeakMap<SimpleNodeSchema, JsonNodeSchema>();
70
-
71
82
  /**
72
83
  * Converts an input {@link SimpleNodeSchema} to a {@link JsonNodeSchema}.
73
84
  *
74
85
  * @remarks Caches the result on the input schema for future calls.
75
86
  */
76
- function convertNodeSchema(schema: SimpleNodeSchema): JsonNodeSchema {
77
- return getOrCreate(nodeJsonSchemaCache, schema, () => {
78
- switch (schema.kind) {
79
- case NodeKind.Array:
80
- return convertArrayNodeSchema(schema);
81
- case NodeKind.Leaf:
82
- return convertLeafNodeSchema(schema);
83
- case NodeKind.Map:
84
- return convertMapNodeSchema(schema);
85
- case NodeKind.Object:
86
- return convertObjectNodeSchema(schema);
87
- default:
88
- throw new TypeError(`Unknown node schema kind: ${(schema as SimpleNodeSchema).kind}`);
89
- }
90
- });
87
+ function convertNodeSchema(
88
+ schema: TreeNodeSchema,
89
+ options: Required<TreeSchemaEncodingOptions>,
90
+ ): JsonNodeSchema {
91
+ if (schema instanceof ArrayNodeSchema) {
92
+ return convertArrayNodeSchema(schema);
93
+ } else if (schema instanceof MapNodeSchema) {
94
+ return convertMapNodeSchema(schema);
95
+ } else if (schema instanceof ObjectNodeSchema) {
96
+ return convertObjectNodeSchema(schema, options);
97
+ } else if (schema instanceof LeafNodeSchema) {
98
+ return convertLeafNodeSchema(schema);
99
+ }
100
+ throw new TypeError(`Unknown node schema kind: ${schema.kind}`);
91
101
  }
92
102
 
93
103
  function convertArrayNodeSchema(schema: SimpleArrayNodeSchema): JsonArrayNodeSchema {
@@ -138,10 +148,14 @@ function convertLeafNodeSchema(schema: SimpleLeafNodeSchema): JsonLeafNodeSchema
138
148
  };
139
149
  }
140
150
 
141
- export function convertObjectNodeSchema(schema: SimpleObjectNodeSchema): JsonObjectNodeSchema {
151
+ export function convertObjectNodeSchema(
152
+ schema: ObjectNodeSchema,
153
+ options: Required<TreeSchemaEncodingOptions>,
154
+ ): JsonObjectNodeSchema {
142
155
  const properties: Record<string, JsonFieldSchema> = {};
143
156
  const required: string[] = [];
144
- for (const [key, fieldSchema] of schema.fields) {
157
+ for (const [propertyKey, fieldSchema] of schema.fields) {
158
+ const key = options.useStoredKeys ? fieldSchema.storedKey : propertyKey;
145
159
  const allowedTypes: JsonSchemaRef[] = [];
146
160
  for (const allowedType of fieldSchema.allowedTypesIdentifiers) {
147
161
  allowedTypes.push(createSchemaRef(allowedType));
@@ -156,8 +170,13 @@ export function convertObjectNodeSchema(schema: SimpleObjectNodeSchema): JsonObj
156
170
  copyProperty(fieldSchema.metadata, "description", output);
157
171
  properties[key] = output;
158
172
 
159
- if (fieldSchema.kind === FieldKind.Required) {
160
- required.push(key);
173
+ if (fieldSchema.kind !== FieldKind.Optional) {
174
+ if (
175
+ options.requireFieldsWithDefaults ||
176
+ fieldSchema.props?.defaultProvider === undefined
177
+ ) {
178
+ required.push(key);
179
+ }
161
180
  }
162
181
  }
163
182
 
@@ -20,6 +20,7 @@ import type {
20
20
  } from "../../shared-tree/index.js";
21
21
 
22
22
  import {
23
+ type FieldSchemaAlpha,
23
24
  type ImplicitFieldSchema,
24
25
  type InsertableField,
25
26
  type InsertableTreeFieldFromImplicitField,
@@ -29,6 +30,7 @@ import {
29
30
  type UnsafeUnknownSchema,
30
31
  FieldKind,
31
32
  markSchemaMostDerived,
33
+ normalizeFieldSchema,
32
34
  } from "../schemaTypes.js";
33
35
  import { NodeKind, type TreeNodeSchema } from "../core/index.js";
34
36
  import { toStoredSchema } from "../toStoredSchema.js";
@@ -38,7 +40,7 @@ import { getOrCreate } from "../../util/index.js";
38
40
  import type { MakeNominal } from "../../util/index.js";
39
41
  import { walkFieldSchema } from "../walkFieldSchema.js";
40
42
  import type { VerboseTree } from "./verboseTree.js";
41
- import type { SimpleTreeSchema } from "../simpleSchema.js";
43
+ import type { SimpleNodeSchema, SimpleTreeSchema } from "../simpleSchema.js";
42
44
  import type {
43
45
  RunTransactionParams,
44
46
  TransactionCallbackStatus,
@@ -106,9 +108,7 @@ export interface ITree extends ViewableTree, IFluidLoadable {}
106
108
 
107
109
  /**
108
110
  * {@link ITree} extended with some alpha APIs.
109
- * @privateRemarks
110
- * TODO: Promote this to alpha.
111
- * @internal
111
+ * @sealed @alpha
112
112
  */
113
113
  export interface ITreeAlpha extends ITree {
114
114
  /**
@@ -309,6 +309,53 @@ export class TreeViewConfiguration<
309
309
  }
310
310
  }
311
311
 
312
+ /**
313
+ * {@link TreeViewConfiguration} extended with some alpha APIs.
314
+ * @sealed @alpha
315
+ */
316
+ export class TreeViewConfigurationAlpha<
317
+ const TSchema extends ImplicitFieldSchema = ImplicitFieldSchema,
318
+ >
319
+ extends TreeViewConfiguration<TSchema>
320
+ implements TreeSchema
321
+ {
322
+ /**
323
+ * {@inheritDoc TreeSchema.root}
324
+ */
325
+ public readonly root: FieldSchemaAlpha;
326
+ /**
327
+ * {@inheritDoc TreeSchema.definitions}
328
+ */
329
+ public readonly definitions: ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;
330
+
331
+ public constructor(props: ITreeViewConfiguration<TSchema>) {
332
+ super(props);
333
+ this.root = normalizeFieldSchema(props.schema);
334
+ const definitions = new Map<string, SimpleNodeSchema & TreeNodeSchema>();
335
+ walkFieldSchema(props.schema, {
336
+ node: (schema) =>
337
+ definitions.set(schema.identifier, schema as SimpleNodeSchema & TreeNodeSchema),
338
+ });
339
+ this.definitions = definitions;
340
+ }
341
+ }
342
+
343
+ /**
344
+ * {@link TreeViewConfigurationAlpha}
345
+ * @sealed @alpha
346
+ */
347
+ export interface TreeSchema extends SimpleTreeSchema {
348
+ /**
349
+ * {@inheritDoc SimpleTreeSchema.root}
350
+ */
351
+ readonly root: FieldSchemaAlpha;
352
+
353
+ /**
354
+ * {@inheritDoc SimpleTreeSchema.definitions}
355
+ */
356
+ readonly definitions: ReadonlyMap<string, SimpleNodeSchema & TreeNodeSchema>;
357
+ }
358
+
312
359
  /**
313
360
  * Pretty print a set of types for use in error messages.
314
361
  */
@@ -41,8 +41,9 @@ import {
41
41
  customFromCursor,
42
42
  replaceHandles,
43
43
  type CustomTreeNode,
44
- type EncodeOptions,
45
44
  type HandleConverter,
45
+ type SchemalessParseOptions,
46
+ type TreeEncodingOptions,
46
47
  } from "./customTree.js";
47
48
  import { getUnhydratedContext } from "../createContext.js";
48
49
 
@@ -110,40 +111,14 @@ export interface VerboseTreeNode<THandle = IFluidHandle> {
110
111
  };
111
112
  }
112
113
 
113
- /**
114
- * Options for how to interpret a `VerboseTree` when schema information is available.
115
- * @alpha
116
- */
117
- export interface ParseOptions {
118
- /**
119
- * If true, interpret the input keys of object nodes as stored keys.
120
- * If false, interpret them as property keys.
121
- * @defaultValue false.
122
- */
123
- readonly useStoredKeys?: boolean;
124
- }
125
-
126
- /**
127
- * Options for how to interpret a `VerboseTree` without relying on schema.
128
- */
129
- export interface SchemalessParseOptions {
130
- /**
131
- * Converts stored keys into whatever key the tree is using in its encoding.
132
- */
133
- keyConverter?: {
134
- parse(type: string, inputKey: string): FieldKey;
135
- encode(type: string, key: FieldKey): string;
136
- };
137
- }
138
-
139
114
  /**
140
115
  * Use info from `schema` to convert `options` to {@link SchemalessParseOptions}.
141
116
  */
142
117
  export function applySchemaToParserOptions(
143
118
  schema: ImplicitFieldSchema,
144
- options: ParseOptions,
119
+ options: TreeEncodingOptions,
145
120
  ): SchemalessParseOptions {
146
- const config: Required<ParseOptions> = {
121
+ const config: Required<TreeEncodingOptions> = {
147
122
  useStoredKeys: false,
148
123
  ...options,
149
124
  };
@@ -305,9 +280,9 @@ function verboseTreeAdapter(options: SchemalessParseOptions): CursorAdapter<Verb
305
280
  export function verboseFromCursor(
306
281
  reader: ITreeCursor,
307
282
  rootSchema: ImplicitAllowedTypes,
308
- options: EncodeOptions,
283
+ options: TreeEncodingOptions,
309
284
  ): VerboseTree {
310
- const config: Required<EncodeOptions> = {
285
+ const config: Required<TreeEncodingOptions> = {
311
286
  useStoredKeys: false,
312
287
  ...options,
313
288
  };
@@ -319,7 +294,7 @@ export function verboseFromCursor(
319
294
 
320
295
  function verboseFromCursorInner(
321
296
  reader: ITreeCursor,
322
- options: Required<EncodeOptions>,
297
+ options: Required<TreeEncodingOptions>,
323
298
  schema: ReadonlyMap<string, TreeNodeSchema>,
324
299
  ): VerboseTree {
325
300
  const fields = customFromCursor(reader, options, schema, verboseFromCursorInner);
@@ -7,6 +7,7 @@ import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { normalizeFieldSchema, type ImplicitFieldSchema } from "../schemaTypes.js";
8
8
  import type {
9
9
  SimpleArrayNodeSchema,
10
+ SimpleFieldSchema,
10
11
  SimpleLeafNodeSchema,
11
12
  SimpleMapNodeSchema,
12
13
  SimpleNodeSchema,
@@ -59,10 +60,14 @@ export function toSimpleTreeSchema(
59
60
  });
60
61
 
61
62
  return {
62
- kind: normalizedSchema.kind,
63
- allowedTypesIdentifiers: normalizedSchema.allowedTypesIdentifiers,
63
+ root: copySchemaObjects
64
+ ? ({
65
+ allowedTypesIdentifiers: normalizedSchema.allowedTypesIdentifiers,
66
+ kind: normalizedSchema.kind,
67
+ metadata: normalizedSchema.metadata,
68
+ } satisfies SimpleFieldSchema)
69
+ : normalizedSchema,
64
70
  definitions,
65
- metadata: normalizedSchema.metadata,
66
71
  };
67
72
  }
68
73
 
@@ -3,7 +3,8 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, Lazy, fail } from "@fluidframework/core-utils/internal";
6
+ import { assert, Lazy, fail, debugAssert } from "@fluidframework/core-utils/internal";
7
+ import { UsageError } from "@fluidframework/telemetry-utils/internal";
7
8
  import { createEmitter } from "@fluid-internal/client-utils";
8
9
  import type { Listenable, Off } from "@fluidframework/core-interfaces";
9
10
  import type { InternalTreeNode, TreeNode, Unhydrated } from "./types.js";
@@ -19,7 +20,6 @@ import {
19
20
  assertFlexTreeEntityNotFreed,
20
21
  ContextSlot,
21
22
  flexTreeSlot,
22
- isFreedSymbol,
23
23
  LazyEntity,
24
24
  TreeStatus,
25
25
  treeStatusFromAnchorCache,
@@ -73,7 +73,7 @@ export function tryGetTreeNodeSchema(value: unknown): undefined | TreeNodeSchema
73
73
  /** The {@link HydrationState} of a {@link TreeNodeKernel} before the kernel is hydrated */
74
74
  interface UnhydratedState {
75
75
  off: Off;
76
- innerNode: UnhydratedFlexTreeNode;
76
+ readonly innerNode: UnhydratedFlexTreeNode;
77
77
  }
78
78
 
79
79
  /** The {@link HydrationState} of a {@link TreeNodeKernel} after the kernel is hydrated */
@@ -81,9 +81,9 @@ interface HydratedState {
81
81
  /** The flex node for this kernel (lazy - undefined if it has not yet been demanded) */
82
82
  innerNode?: FlexTreeNode;
83
83
  /** The {@link AnchorNode} that this node is associated with. */
84
- anchorNode: AnchorNode;
84
+ readonly anchorNode: AnchorNode;
85
85
  /** All {@link Off | event deregistration functions} that should be run when the kernel is disposed. */
86
- offAnchorNode: Set<Off>;
86
+ readonly offAnchorNode: Set<Off>;
87
87
  }
88
88
 
89
89
  /** State within a {@link TreeNodeKernel} that is related to the hydration process */
@@ -155,15 +155,20 @@ export class TreeNodeKernel {
155
155
  changedFields,
156
156
  });
157
157
 
158
- let n: UnhydratedFlexTreeNode | undefined = innerNode;
159
- while (n !== undefined) {
160
- const treeNode = unhydratedFlexTreeNodeToTreeNodeInternal.get(n);
158
+ let unhydratedNode: UnhydratedFlexTreeNode | undefined = innerNode;
159
+ while (unhydratedNode !== undefined) {
160
+ const treeNode = unhydratedFlexTreeNodeToTreeNodeInternal.get(unhydratedNode);
161
161
  if (treeNode !== undefined) {
162
162
  const kernel = getKernel(treeNode);
163
163
  kernel.#unhydratedEvents.value.emit("subtreeChangedAfterBatch");
164
164
  }
165
- // This cast is safe because the parent (if it exists) of an unhydrated flex node is always another unhydrated flex node.
166
- n = n.parentField.parent.parent as UnhydratedFlexTreeNode | undefined;
165
+ const parentNode: FlexTreeNode | undefined =
166
+ unhydratedNode.parentField.parent.parent;
167
+ assert(
168
+ parentNode === undefined || parentNode instanceof UnhydratedFlexTreeNode,
169
+ 0xb76 /* Unhydrated node's parent should be an unhydrated node */,
170
+ );
171
+ unhydratedNode = parentNode;
167
172
  }
168
173
  }),
169
174
  };
@@ -243,7 +248,7 @@ export class TreeNodeKernel {
243
248
  const flex = this.#hydrationState.anchorNode.slots.get(flexTreeSlot);
244
249
  if (flex !== undefined) {
245
250
  assert(flex instanceof LazyEntity, 0x9b4 /* Unexpected flex node implementation */);
246
- if (flex[isFreedSymbol]()) {
251
+ if (flex.isFreed()) {
247
252
  return TreeStatus.Deleted;
248
253
  }
249
254
  }
@@ -283,35 +288,46 @@ export class TreeNodeKernel {
283
288
  *
284
289
  * For hydrated nodes it returns a FlexTreeNode backed by the forest.
285
290
  * Note that for "marinated" nodes, this FlexTreeNode exists and returns it: it does not return the MapTreeNode which is the current InnerNode.
291
+ *
292
+ * If `allowDeleted` is false, this will throw a UsageError if the node is deleted.
286
293
  */
287
- public getOrCreateInnerNode(allowFreed = false): InnerNode {
294
+ public getOrCreateInnerNode(allowDeleted = false): InnerNode {
288
295
  if (!isHydrated(this.#hydrationState)) {
296
+ debugAssert(
297
+ () =>
298
+ this.#hydrationState.innerNode?.context.isDisposed() === false ||
299
+ "Unhydrated node should never be disposed",
300
+ );
289
301
  return this.#hydrationState.innerNode; // Unhydrated case
290
302
  }
291
303
 
292
- if (this.#hydrationState.innerNode !== undefined) {
293
- return this.#hydrationState.innerNode; // Cooked case
304
+ if (this.#hydrationState.innerNode === undefined) {
305
+ // Marinated case -> cooked
306
+ const anchorNode = this.#hydrationState.anchorNode;
307
+ // The proxy is bound to an anchor node, but it may or may not have an actual flex node yet
308
+ const flexNode = anchorNode.slots.get(flexTreeSlot);
309
+ if (flexNode !== undefined) {
310
+ // If the flex node already exists, use it...
311
+ this.#hydrationState.innerNode = flexNode;
312
+ } else {
313
+ // ...otherwise, the flex node must be created
314
+ const context =
315
+ anchorNode.anchorSet.slots.get(ContextSlot) ?? fail(0xb41 /* missing context */);
316
+ const cursor = context.checkout.forest.allocateCursor("getFlexNode");
317
+ context.checkout.forest.moveCursorToPath(anchorNode, cursor);
318
+ this.#hydrationState.innerNode = makeTree(context, cursor);
319
+ cursor.free();
320
+ // Calling this is a performance improvement, however, do this only after demand to avoid momentarily having no anchors to anchorNode
321
+ anchorForgetters?.get(this.node)?.();
322
+ if (!allowDeleted) {
323
+ assertFlexTreeEntityNotFreed(this.#hydrationState.innerNode);
324
+ }
325
+ }
294
326
  }
295
327
 
296
- // Marinated case -> cooked
297
- const anchorNode = this.#hydrationState.anchorNode;
298
- // The proxy is bound to an anchor node, but it may or may not have an actual flex node yet
299
- const flexNode = anchorNode.slots.get(flexTreeSlot);
300
- if (flexNode !== undefined) {
301
- // If the flex node already exists, use it...
302
- this.#hydrationState.innerNode = flexNode;
303
- } else {
304
- // ...otherwise, the flex node must be created
305
- const context =
306
- anchorNode.anchorSet.slots.get(ContextSlot) ?? fail(0xb41 /* missing context */);
307
- const cursor = context.checkout.forest.allocateCursor("getFlexNode");
308
- context.checkout.forest.moveCursorToPath(anchorNode, cursor);
309
- this.#hydrationState.innerNode = makeTree(context, cursor);
310
- cursor.free();
311
- // Calling this is a performance improvement, however, do this only after demand to avoid momentarily having no anchors to anchorNode
312
- anchorForgetters?.get(this.node)?.();
313
- if (!allowFreed) {
314
- assertFlexTreeEntityNotFreed(this.#hydrationState.innerNode);
328
+ if (!allowDeleted) {
329
+ if (this.#hydrationState.innerNode.context.isDisposed()) {
330
+ throw new UsageError("Cannot access a Deleted node.");
315
331
  }
316
332
  }
317
333
 
@@ -413,11 +429,15 @@ export const unhydratedFlexTreeNodeToTreeNode =
413
429
  */
414
430
  export const proxySlot = anchorSlot<TreeNode>();
415
431
 
432
+ /**
433
+ * Dispose a TreeNode (if any) for an existing anchor without disposing the anchor.
434
+ */
416
435
  export function tryDisposeTreeNode(anchorNode: AnchorNode): void {
417
436
  const treeNode = anchorNode.slots.get(proxySlot);
418
437
  if (treeNode !== undefined) {
419
438
  const kernel = getKernel(treeNode);
420
439
  kernel.dispose();
440
+ anchorNode.slots.delete(proxySlot);
421
441
  }
422
442
  }
423
443
 
@@ -450,10 +470,12 @@ export function getSimpleContextFromInnerNode(innerNode: InnerNode): Context {
450
470
  *
451
471
  * For hydrated nodes it returns a FlexTreeNode backed by the forest.
452
472
  * Note that for "marinated" nodes, this FlexTreeNode exists and returns it: it does not return the MapTreeNode which is the current InnerNode.
473
+ *
474
+ * If `allowDeleted` is false, this will throw a UsageError if the node is deleted.
453
475
  */
454
- export function getOrCreateInnerNode(treeNode: TreeNode, allowFreed = false): InnerNode {
476
+ export function getOrCreateInnerNode(treeNode: TreeNode, allowDeleted = false): InnerNode {
455
477
  const kernel = getKernel(treeNode);
456
- return kernel.getOrCreateInnerNode(allowFreed);
478
+ return kernel.getOrCreateInnerNode(allowDeleted);
457
479
  }
458
480
 
459
481
  /**
@@ -73,7 +73,7 @@ interface LocationInField {
73
73
  *
74
74
  * Create a `UnhydratedFlexTreeNode` by calling {@link getOrCreate}.
75
75
  */
76
- export class UnhydratedFlexTreeNode implements UnhydratedFlexTreeNode {
76
+ export class UnhydratedFlexTreeNode implements FlexTreeNode {
77
77
  public get schema(): TreeNodeSchemaIdentifier {
78
78
  return this.mapTree.type;
79
79
  }
@@ -150,11 +150,24 @@ export class UnhydratedFlexTreeNode implements UnhydratedFlexTreeNode {
150
150
  public adoptBy(parent: UnhydratedFlexTreeField, index: number): void;
151
151
  public adoptBy(parent: UnhydratedFlexTreeField | undefined, index?: number): void {
152
152
  if (parent !== undefined) {
153
- assert(
154
- this.location === unparentedLocation,
155
- 0x98c /* Node may not be adopted if it already has a parent */,
156
- );
157
153
  assert(index !== undefined, 0xa08 /* Expected index */);
154
+ if (this.location !== unparentedLocation) {
155
+ throw new UsageError("A node may not be inserted if it's already in a tree");
156
+ }
157
+ let unhydratedNode: UnhydratedFlexTreeNode | undefined = parent.parent;
158
+ while (unhydratedNode !== undefined) {
159
+ if (unhydratedNode === this) {
160
+ throw new UsageError(
161
+ "A node may not be inserted into a location that is under itself",
162
+ );
163
+ }
164
+ const parentNode: FlexTreeNode | undefined = unhydratedNode.parentField.parent.parent;
165
+ assert(
166
+ parentNode === undefined || parentNode instanceof UnhydratedFlexTreeNode,
167
+ 0xb77 /* Unhydrated node's parent should be an unhydrated node */,
168
+ );
169
+ unhydratedNode = parentNode;
170
+ }
158
171
  this.location = { parent, index };
159
172
  } else {
160
173
  assert(
@@ -264,6 +277,10 @@ export class UnhydratedContext implements FlexTreeContext {
264
277
  public readonly schema: TreeStoredSchema,
265
278
  ) {}
266
279
 
280
+ public isDisposed(): boolean {
281
+ return false;
282
+ }
283
+
267
284
  public isHydrated(): this is FlexTreeHydratedContext {
268
285
  return false;
269
286
  }