@fluidframework/tree 2.70.0 → 2.71.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 (179) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/api-report/tree.alpha.api.md +25 -4
  3. package/api-report/tree.beta.api.md +3 -0
  4. package/api-report/tree.legacy.beta.api.md +3 -0
  5. package/dist/alpha.d.ts +3 -0
  6. package/dist/beta.d.ts +1 -0
  7. package/dist/codec/discriminatedUnions.d.ts +1 -1
  8. package/dist/codec/discriminatedUnions.js +1 -1
  9. package/dist/codec/discriminatedUnions.js.map +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/legacy.d.ts +1 -0
  15. package/dist/packageVersion.d.ts +1 -1
  16. package/dist/packageVersion.js +1 -1
  17. package/dist/packageVersion.js.map +1 -1
  18. package/dist/shared-tree/independentView.d.ts +74 -5
  19. package/dist/shared-tree/independentView.d.ts.map +1 -1
  20. package/dist/shared-tree/independentView.js +104 -38
  21. package/dist/shared-tree/independentView.js.map +1 -1
  22. package/dist/shared-tree/index.d.ts +2 -1
  23. package/dist/shared-tree/index.d.ts.map +1 -1
  24. package/dist/shared-tree/index.js +6 -1
  25. package/dist/shared-tree/index.js.map +1 -1
  26. package/dist/shared-tree/sharedTree.d.ts.map +1 -1
  27. package/dist/shared-tree/sharedTree.js +18 -3
  28. package/dist/shared-tree/sharedTree.js.map +1 -1
  29. package/dist/simple-tree/api/schemaFactory.d.ts +2 -2
  30. package/dist/simple-tree/api/schemaFactory.js +2 -2
  31. package/dist/simple-tree/api/schemaFactory.js.map +1 -1
  32. package/dist/simple-tree/api/schemaFactoryRecursive.d.ts.map +1 -1
  33. package/dist/simple-tree/api/schemaFactoryRecursive.js +1 -0
  34. package/dist/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  35. package/dist/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  36. package/dist/simple-tree/api/schemaFromSimple.js +9 -5
  37. package/dist/simple-tree/api/schemaFromSimple.js.map +1 -1
  38. package/dist/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  39. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js +4 -2
  40. package/dist/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  41. package/dist/simple-tree/api/viewSchemaToSimpleSchema.d.ts +2 -1
  42. package/dist/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  43. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js +31 -10
  44. package/dist/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  45. package/dist/simple-tree/core/allowedTypes.d.ts +9 -4
  46. package/dist/simple-tree/core/allowedTypes.d.ts.map +1 -1
  47. package/dist/simple-tree/core/allowedTypes.js +17 -4
  48. package/dist/simple-tree/core/allowedTypes.js.map +1 -1
  49. package/dist/simple-tree/fieldSchema.d.ts +2 -1
  50. package/dist/simple-tree/fieldSchema.d.ts.map +1 -1
  51. package/dist/simple-tree/fieldSchema.js +10 -0
  52. package/dist/simple-tree/fieldSchema.js.map +1 -1
  53. package/dist/simple-tree/index.d.ts +1 -1
  54. package/dist/simple-tree/index.d.ts.map +1 -1
  55. package/dist/simple-tree/index.js.map +1 -1
  56. package/dist/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  57. package/dist/simple-tree/node-kinds/array/arrayNode.js +6 -0
  58. package/dist/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  59. package/dist/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
  60. package/dist/simple-tree/node-kinds/map/mapNode.js +6 -0
  61. package/dist/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  62. package/dist/simple-tree/node-kinds/object/objectNode.d.ts +17 -0
  63. package/dist/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  64. package/dist/simple-tree/node-kinds/object/objectNode.js +19 -6
  65. package/dist/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  66. package/dist/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
  67. package/dist/simple-tree/node-kinds/record/recordNode.js +6 -0
  68. package/dist/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  69. package/dist/simple-tree/node-kinds/record/recordNodeTypes.d.ts +16 -0
  70. package/dist/simple-tree/node-kinds/record/recordNodeTypes.d.ts.map +1 -1
  71. package/dist/simple-tree/node-kinds/record/recordNodeTypes.js.map +1 -1
  72. package/dist/simple-tree/simpleSchema.d.ts +21 -5
  73. package/dist/simple-tree/simpleSchema.d.ts.map +1 -1
  74. package/dist/simple-tree/simpleSchema.js.map +1 -1
  75. package/dist/simple-tree/toStoredSchema.d.ts.map +1 -1
  76. package/dist/simple-tree/toStoredSchema.js +6 -3
  77. package/dist/simple-tree/toStoredSchema.js.map +1 -1
  78. package/dist/util/referenceCounting.d.ts.map +1 -1
  79. package/dist/util/referenceCounting.js +1 -0
  80. package/dist/util/referenceCounting.js.map +1 -1
  81. package/lib/alpha.d.ts +3 -0
  82. package/lib/beta.d.ts +1 -0
  83. package/lib/codec/discriminatedUnions.d.ts +1 -1
  84. package/lib/codec/discriminatedUnions.js +1 -1
  85. package/lib/codec/discriminatedUnions.js.map +1 -1
  86. package/lib/index.d.ts +2 -2
  87. package/lib/index.d.ts.map +1 -1
  88. package/lib/index.js +1 -1
  89. package/lib/index.js.map +1 -1
  90. package/lib/legacy.d.ts +1 -0
  91. package/lib/packageVersion.d.ts +1 -1
  92. package/lib/packageVersion.js +1 -1
  93. package/lib/packageVersion.js.map +1 -1
  94. package/lib/shared-tree/independentView.d.ts +74 -5
  95. package/lib/shared-tree/independentView.d.ts.map +1 -1
  96. package/lib/shared-tree/independentView.js +103 -38
  97. package/lib/shared-tree/independentView.js.map +1 -1
  98. package/lib/shared-tree/index.d.ts +2 -1
  99. package/lib/shared-tree/index.d.ts.map +1 -1
  100. package/lib/shared-tree/index.js +2 -1
  101. package/lib/shared-tree/index.js.map +1 -1
  102. package/lib/shared-tree/sharedTree.d.ts.map +1 -1
  103. package/lib/shared-tree/sharedTree.js +18 -3
  104. package/lib/shared-tree/sharedTree.js.map +1 -1
  105. package/lib/simple-tree/api/schemaFactory.d.ts +2 -2
  106. package/lib/simple-tree/api/schemaFactory.js +2 -2
  107. package/lib/simple-tree/api/schemaFactory.js.map +1 -1
  108. package/lib/simple-tree/api/schemaFactoryRecursive.d.ts.map +1 -1
  109. package/lib/simple-tree/api/schemaFactoryRecursive.js +1 -0
  110. package/lib/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
  111. package/lib/simple-tree/api/schemaFromSimple.d.ts.map +1 -1
  112. package/lib/simple-tree/api/schemaFromSimple.js +9 -5
  113. package/lib/simple-tree/api/schemaFromSimple.js.map +1 -1
  114. package/lib/simple-tree/api/simpleSchemaToJsonSchema.d.ts.map +1 -1
  115. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js +4 -2
  116. package/lib/simple-tree/api/simpleSchemaToJsonSchema.js.map +1 -1
  117. package/lib/simple-tree/api/viewSchemaToSimpleSchema.d.ts +2 -1
  118. package/lib/simple-tree/api/viewSchemaToSimpleSchema.d.ts.map +1 -1
  119. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js +31 -10
  120. package/lib/simple-tree/api/viewSchemaToSimpleSchema.js.map +1 -1
  121. package/lib/simple-tree/core/allowedTypes.d.ts +9 -4
  122. package/lib/simple-tree/core/allowedTypes.d.ts.map +1 -1
  123. package/lib/simple-tree/core/allowedTypes.js +18 -5
  124. package/lib/simple-tree/core/allowedTypes.js.map +1 -1
  125. package/lib/simple-tree/fieldSchema.d.ts +2 -1
  126. package/lib/simple-tree/fieldSchema.d.ts.map +1 -1
  127. package/lib/simple-tree/fieldSchema.js +10 -0
  128. package/lib/simple-tree/fieldSchema.js.map +1 -1
  129. package/lib/simple-tree/index.d.ts +1 -1
  130. package/lib/simple-tree/index.d.ts.map +1 -1
  131. package/lib/simple-tree/index.js.map +1 -1
  132. package/lib/simple-tree/node-kinds/array/arrayNode.d.ts.map +1 -1
  133. package/lib/simple-tree/node-kinds/array/arrayNode.js +7 -1
  134. package/lib/simple-tree/node-kinds/array/arrayNode.js.map +1 -1
  135. package/lib/simple-tree/node-kinds/map/mapNode.d.ts.map +1 -1
  136. package/lib/simple-tree/node-kinds/map/mapNode.js +7 -1
  137. package/lib/simple-tree/node-kinds/map/mapNode.js.map +1 -1
  138. package/lib/simple-tree/node-kinds/object/objectNode.d.ts +17 -0
  139. package/lib/simple-tree/node-kinds/object/objectNode.d.ts.map +1 -1
  140. package/lib/simple-tree/node-kinds/object/objectNode.js +19 -6
  141. package/lib/simple-tree/node-kinds/object/objectNode.js.map +1 -1
  142. package/lib/simple-tree/node-kinds/record/recordNode.d.ts.map +1 -1
  143. package/lib/simple-tree/node-kinds/record/recordNode.js +7 -1
  144. package/lib/simple-tree/node-kinds/record/recordNode.js.map +1 -1
  145. package/lib/simple-tree/node-kinds/record/recordNodeTypes.d.ts +16 -0
  146. package/lib/simple-tree/node-kinds/record/recordNodeTypes.d.ts.map +1 -1
  147. package/lib/simple-tree/node-kinds/record/recordNodeTypes.js.map +1 -1
  148. package/lib/simple-tree/simpleSchema.d.ts +21 -5
  149. package/lib/simple-tree/simpleSchema.d.ts.map +1 -1
  150. package/lib/simple-tree/simpleSchema.js.map +1 -1
  151. package/lib/simple-tree/toStoredSchema.d.ts.map +1 -1
  152. package/lib/simple-tree/toStoredSchema.js +6 -3
  153. package/lib/simple-tree/toStoredSchema.js.map +1 -1
  154. package/lib/util/referenceCounting.d.ts.map +1 -1
  155. package/lib/util/referenceCounting.js +1 -0
  156. package/lib/util/referenceCounting.js.map +1 -1
  157. package/package.json +24 -24
  158. package/src/codec/discriminatedUnions.ts +1 -1
  159. package/src/index.ts +3 -0
  160. package/src/packageVersion.ts +1 -1
  161. package/src/shared-tree/independentView.ts +147 -72
  162. package/src/shared-tree/index.ts +4 -0
  163. package/src/shared-tree/sharedTree.ts +25 -3
  164. package/src/simple-tree/api/schemaFactory.ts +2 -2
  165. package/src/simple-tree/api/schemaFactoryRecursive.ts +2 -0
  166. package/src/simple-tree/api/schemaFromSimple.ts +17 -14
  167. package/src/simple-tree/api/simpleSchemaToJsonSchema.ts +8 -2
  168. package/src/simple-tree/api/viewSchemaToSimpleSchema.ts +44 -8
  169. package/src/simple-tree/core/allowedTypes.ts +26 -13
  170. package/src/simple-tree/fieldSchema.ts +14 -1
  171. package/src/simple-tree/index.ts +1 -0
  172. package/src/simple-tree/node-kinds/array/arrayNode.ts +9 -0
  173. package/src/simple-tree/node-kinds/map/mapNode.ts +9 -0
  174. package/src/simple-tree/node-kinds/object/objectNode.ts +54 -18
  175. package/src/simple-tree/node-kinds/record/recordNode.ts +9 -0
  176. package/src/simple-tree/node-kinds/record/recordNodeTypes.ts +16 -0
  177. package/src/simple-tree/simpleSchema.ts +22 -5
  178. package/src/simple-tree/toStoredSchema.ts +12 -3
  179. package/src/util/referenceCounting.ts +1 -0
@@ -13,6 +13,7 @@ import {
13
13
  type FieldProps,
14
14
  } from "../fieldSchema.js";
15
15
  import type {
16
+ SimpleAllowedTypeAttributes,
16
17
  SimpleFieldSchema,
17
18
  SimpleNodeSchema,
18
19
  SimpleTreeSchema,
@@ -65,7 +66,7 @@ function generateFieldSchema(
65
66
  context: Context,
66
67
  storedKey: string | undefined,
67
68
  ): FieldSchemaAlpha {
68
- const allowed = generateAllowedTypes(simple.allowedTypesIdentifiers, context);
69
+ const allowed = generateAllowedTypes(simple.simpleAllowedTypes, context);
69
70
  const props: Omit<FieldProps, "defaultProvider"> = {
70
71
  metadata: simple.metadata,
71
72
  key: storedKey,
@@ -84,8 +85,14 @@ function generateFieldSchema(
84
85
  }
85
86
  }
86
87
 
87
- function generateAllowedTypes(allowed: ReadonlySet<string>, context: Context): AllowedTypes {
88
- return [...allowed].map((id) => context.get(id) ?? fail(0xb5a /* Missing schema */));
88
+ function generateAllowedTypes(
89
+ allowed: ReadonlyMap<string, SimpleAllowedTypeAttributes>,
90
+ context: Context,
91
+ ): AllowedTypes {
92
+ return Array.from(
93
+ allowed.keys(),
94
+ (id) => context.get(id) ?? fail(0xb5a /* Missing schema */),
95
+ );
89
96
  }
90
97
 
91
98
  function generateNode(
@@ -104,21 +111,17 @@ function generateNode(
104
111
  return factory.objectAlpha(id, fields, { metadata: schema.metadata });
105
112
  }
106
113
  case NodeKind.Array:
107
- return factory.arrayAlpha(
108
- id,
109
- generateAllowedTypes(schema.allowedTypesIdentifiers, context),
110
- { metadata: schema.metadata },
111
- );
114
+ return factory.arrayAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), {
115
+ metadata: schema.metadata,
116
+ });
112
117
  case NodeKind.Map:
113
- return factory.mapAlpha(
114
- id,
115
- generateAllowedTypes(schema.allowedTypesIdentifiers, context),
116
- { metadata: schema.metadata },
117
- );
118
+ return factory.mapAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), {
119
+ metadata: schema.metadata,
120
+ });
118
121
  case NodeKind.Record:
119
122
  return factory.recordAlpha(
120
123
  id,
121
- generateAllowedTypes(schema.allowedTypesIdentifiers, context),
124
+ generateAllowedTypes(schema.simpleAllowedTypes, context),
122
125
  { metadata: schema.metadata },
123
126
  );
124
127
  case NodeKind.Leaf:
@@ -107,7 +107,10 @@ function convertNodeSchema(
107
107
 
108
108
  function convertArrayNodeSchema(schema: SimpleArrayNodeSchema): JsonArrayNodeSchema {
109
109
  const allowedTypes: JsonSchemaRef[] = [];
110
- schema.allowedTypesIdentifiers.forEach((type) => {
110
+ const allowedTypesIdentifiers: ReadonlySet<string> = new Set(
111
+ schema.simpleAllowedTypes.keys(),
112
+ );
113
+ allowedTypesIdentifiers.forEach((type) => {
111
114
  allowedTypes.push(createSchemaRef(type));
112
115
  });
113
116
 
@@ -206,7 +209,10 @@ function convertRecordLikeNodeSchema(
206
209
  schema: SimpleRecordNodeSchema | SimpleMapNodeSchema,
207
210
  ): JsonMapNodeSchema | JsonRecordNodeSchema {
208
211
  const allowedTypes: JsonSchemaRef[] = [];
209
- schema.allowedTypesIdentifiers.forEach((type) => {
212
+ const allowedTypesIdentifiers: ReadonlySet<string> = new Set(
213
+ schema.simpleAllowedTypes.keys(),
214
+ );
215
+ allowedTypesIdentifiers.forEach((type) => {
210
216
  allowedTypes.push(createSchemaRef(type));
211
217
  });
212
218
 
@@ -6,6 +6,7 @@
6
6
  import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
7
7
  import { normalizeFieldSchema, type ImplicitFieldSchema } from "../fieldSchema.js";
8
8
  import type {
9
+ SimpleAllowedTypeAttributes,
9
10
  SimpleArrayNodeSchema,
10
11
  SimpleFieldSchema,
11
12
  SimpleLeafNodeSchema,
@@ -31,6 +32,7 @@ import { LeafNodeSchema } from "../leafNodeSchema.js";
31
32
  *
32
33
  * @param schema - The schema to convert
33
34
  * @param copySchemaObjects - If true, TreeNodeSchema and FieldSchema are copied into plain JavaScript objects. Either way, custom metadata is referenced and not copied.
35
+ * @param isViewSchema - If true (default), properties used by view schema but not part of stored schema (for example, `isStaged` on allowed types) are preserved in the output.
34
36
  *
35
37
  * @remarks
36
38
  * Given that the Schema types used in {@link ImplicitFieldSchema} already implement the {@link SimpleNodeSchema} interfaces, there are limited use-cases for this function.
@@ -45,6 +47,7 @@ import { LeafNodeSchema } from "../leafNodeSchema.js";
45
47
  export function toSimpleTreeSchema(
46
48
  schema: ImplicitFieldSchema,
47
49
  copySchemaObjects: boolean,
50
+ isViewSchema: boolean = true,
48
51
  ): SimpleTreeSchema {
49
52
  const normalizedSchema = normalizeFieldSchema(schema);
50
53
  const definitions = new Map<string, SimpleNodeSchema>();
@@ -59,7 +62,9 @@ export function toSimpleTreeSchema(
59
62
  nodeSchema instanceof RecordNodeSchema,
60
63
  0xb60 /* Invalid schema */,
61
64
  );
62
- const outSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema) : nodeSchema;
65
+ const outSchema = copySchemaObjects
66
+ ? copySimpleNodeSchema(nodeSchema, isViewSchema)
67
+ : nodeSchema;
63
68
  definitions.set(nodeSchema.identifier, outSchema);
64
69
  },
65
70
  });
@@ -67,7 +72,10 @@ export function toSimpleTreeSchema(
67
72
  return {
68
73
  root: copySchemaObjects
69
74
  ? ({
70
- allowedTypesIdentifiers: normalizedSchema.allowedTypesIdentifiers,
75
+ simpleAllowedTypes: normalizeSimpleAllowedTypes(
76
+ normalizedSchema.simpleAllowedTypes,
77
+ isViewSchema,
78
+ ),
71
79
  kind: normalizedSchema.kind,
72
80
  metadata: normalizedSchema.metadata,
73
81
  persistedMetadata: normalizedSchema.persistedMetadata,
@@ -77,12 +85,36 @@ export function toSimpleTreeSchema(
77
85
  };
78
86
  }
79
87
 
88
+ /**
89
+ * Normalizes the {@link SimpleAllowedTypeAttributes} by either preserving or omitting view-specific schema properties.
90
+ * @param simpleAllowedTypes - The simple allowed types to normalize.
91
+ * @param isViewSchema - If true, properties used by view schema but not part of stored schema (for example, `isStaged` on allowed types) are preserved in the output.
92
+ * @returns The normalized simple allowed types.
93
+ */
94
+ function normalizeSimpleAllowedTypes(
95
+ simpleAllowedTypes: ReadonlyMap<string, SimpleAllowedTypeAttributes>,
96
+ isViewSchema: boolean,
97
+ ): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
98
+ if (isViewSchema) {
99
+ return simpleAllowedTypes;
100
+ } else {
101
+ const normalized = new Map<string, SimpleAllowedTypeAttributes>();
102
+ for (const [identifier, attributes] of simpleAllowedTypes.entries()) {
103
+ normalized.set(identifier, { ...attributes, isStaged: undefined });
104
+ }
105
+ return normalized;
106
+ }
107
+ }
108
+
80
109
  /**
81
110
  * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object.
82
111
  *
83
112
  * @remarks Caches the result on the input schema for future calls.
84
113
  */
85
- function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema {
114
+ function copySimpleNodeSchema(
115
+ schema: SimpleNodeSchema,
116
+ isViewSchema: boolean,
117
+ ): SimpleNodeSchema {
86
118
  const kind = schema.kind;
87
119
  switch (kind) {
88
120
  case NodeKind.Leaf:
@@ -90,9 +122,9 @@ function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema {
90
122
  case NodeKind.Array:
91
123
  case NodeKind.Map:
92
124
  case NodeKind.Record:
93
- return copySimpleSchemaWithAllowedTypes(schema);
125
+ return copySimpleSchemaWithAllowedTypes(schema, isViewSchema);
94
126
  case NodeKind.Object:
95
- return copySimpleObjectSchema(schema);
127
+ return copySimpleObjectSchema(schema, isViewSchema);
96
128
  default:
97
129
  unreachableCase(kind);
98
130
  }
@@ -109,22 +141,26 @@ function copySimpleLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchem
109
141
 
110
142
  function copySimpleSchemaWithAllowedTypes(
111
143
  schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema,
144
+ isViewSchema: boolean,
112
145
  ): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema {
113
146
  return {
114
147
  kind: schema.kind,
115
- allowedTypesIdentifiers: schema.allowedTypesIdentifiers,
148
+ simpleAllowedTypes: normalizeSimpleAllowedTypes(schema.simpleAllowedTypes, isViewSchema),
116
149
  metadata: schema.metadata,
117
150
  persistedMetadata: schema.persistedMetadata,
118
151
  };
119
152
  }
120
153
 
121
- function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema {
154
+ function copySimpleObjectSchema(
155
+ schema: SimpleObjectNodeSchema,
156
+ isViewSchema: boolean,
157
+ ): SimpleObjectNodeSchema {
122
158
  const fields: Map<string, SimpleObjectFieldSchema> = new Map();
123
159
  for (const [propertyKey, field] of schema.fields) {
124
160
  // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object.
125
161
  fields.set(propertyKey, {
126
162
  kind: field.kind,
127
- allowedTypesIdentifiers: field.allowedTypesIdentifiers,
163
+ simpleAllowedTypes: normalizeSimpleAllowedTypes(field.simpleAllowedTypes, isViewSchema),
128
164
  metadata: field.metadata,
129
165
  persistedMetadata: field.persistedMetadata,
130
166
  storedKey: field.storedKey,
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
7
- import { assert, Lazy } from "@fluidframework/core-utils/internal";
7
+ import { Lazy } from "@fluidframework/core-utils/internal";
8
8
 
9
9
  import {
10
10
  type ErasedBaseType,
@@ -25,6 +25,7 @@ import {
25
25
  type TreeNodeSchema,
26
26
  } from "./treeNodeSchema.js";
27
27
  import { schemaAsTreeNodeValid } from "./treeNodeValid.js";
28
+ import type { SimpleAllowedTypeAttributes } from "../simpleSchema.js";
28
29
 
29
30
  /**
30
31
  * Schema for types allowed in some location in a tree (like a field, map entry or array).
@@ -252,6 +253,21 @@ export class AnnotatedAllowedTypesInternal<
252
253
  return this.lazyEvaluate.value.identifiers;
253
254
  }
254
255
 
256
+ /**
257
+ * Get the {@link SimpleAllowedTypeAttributes} version of the allowed types set.
258
+ */
259
+ public static evaluateSimpleAllowedTypes(
260
+ annotatedAllowedTypes: AnnotatedAllowedTypes,
261
+ ): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
262
+ const simpleAllowedTypes = new Map<string, SimpleAllowedTypeAttributes>();
263
+ for (const type of annotatedAllowedTypes.evaluate().types) {
264
+ simpleAllowedTypes.set(type.type.identifier, {
265
+ isStaged: type.metadata.stagedSchemaUpgrade !== undefined,
266
+ });
267
+ }
268
+ return simpleAllowedTypes;
269
+ }
270
+
255
271
  public static override [Symbol.hasInstance]<TThis extends { prototype: object }>(
256
272
  this: TThis,
257
273
  value: unknown,
@@ -332,7 +348,7 @@ export class AnnotatedAllowedTypesInternal<
332
348
  public static create<const T extends readonly AnnotatedAllowedType[]>(
333
349
  types: T,
334
350
  metadata: AllowedTypesMetadata = {},
335
- ): AnnotatedAllowedTypesInternal<T> & AllowedTypesFull<T> {
351
+ ): AnnotatedAllowedTypesInternal<Readonly<T>> & AllowedTypesFull<Readonly<T>> {
336
352
  const result = new AnnotatedAllowedTypesInternal(types, metadata);
337
353
  return result as typeof result & UnannotateAllowedTypesList<T>;
338
354
  }
@@ -340,7 +356,8 @@ export class AnnotatedAllowedTypesInternal<
340
356
  public static createUnannotated<const T extends AllowedTypes>(
341
357
  types: T,
342
358
  metadata: AllowedTypesMetadata = {},
343
- ): AnnotatedAllowedTypesInternal & T {
359
+ ): AnnotatedAllowedTypesInternal & Readonly<T> {
360
+ Object.freeze(types);
344
361
  const annotatedTypes: AnnotatedAllowedType[] = types.map(normalizeToAnnotatedAllowedType);
345
362
  const result = AnnotatedAllowedTypesInternal.create(annotatedTypes, metadata);
346
363
  return result as typeof result & T;
@@ -349,6 +366,7 @@ export class AnnotatedAllowedTypesInternal<
349
366
  public static createMixed<
350
367
  const T extends readonly (AnnotatedAllowedType | LazyItem<TreeNodeSchema>)[],
351
368
  >(types: T, metadata: AllowedTypesMetadata = {}): AllowedTypesFullFromMixed<T> {
369
+ Object.freeze(types);
352
370
  const annotatedTypes: AnnotatedAllowedType[] = types.map(normalizeToAnnotatedAllowedType);
353
371
  const result = AnnotatedAllowedTypesInternal.create(annotatedTypes, metadata);
354
372
  return result as AllowedTypesFullFromMixed<T>;
@@ -524,14 +542,9 @@ export function normalizeAllowedTypesInternal(
524
542
  // Adding this cache improved the performance of the "large recursive union" test (which mostly just constructs a TreeConfiguration) by ~5 times.
525
543
  // This cache is strictly a performance optimization: it is not required for correctness.
526
544
  return getOrCreate(cachedNormalize, type, () => {
527
- // Due to more specific internal type, the above does not narrow sufficiently, so more narrowing is needed.
528
- // It is possible this will give a false error if a TreeNodeSchema which matches this check is used.
529
- assert(
530
- !("types" in type && "metadata" in type),
531
- 0xc7d /* invalid AnnotatedAllowedTypes */,
532
- );
533
-
534
- const annotatedTypes: AnnotatedAllowedType[] = (isReadonlyArray(type) ? type : [type]).map(
545
+ const inputArray = isReadonlyArray(type) ? type : [type];
546
+ Object.freeze(inputArray);
547
+ const annotatedTypes: AnnotatedAllowedType[] = inputArray.map(
535
548
  normalizeToAnnotatedAllowedType,
536
549
  );
537
550
 
@@ -637,8 +650,8 @@ export type TreeNodeFromImplicitAllowedTypes<
637
650
  * This type exists only to be linked from documentation to provide a single linkable place to document some details of
638
651
  * "Input" types and how they handle schema.
639
652
  *
640
- * When a schema is used to describe data which is an input into an API, the API is [contravariant](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) over the schema.
641
- * (See also, [TypeScript Variance Annotations](https://www.typescriptlang.org/docs/handbook/2/generics.html#variance-annotations)).
653
+ * When a schema is used to describe data which is an input into an API, the API is {@link https://en.wikipedia.org/wiki/Type_variance | contravariant}) over the schema.
654
+ * (See also {@link https://www.typescriptlang.org/docs/handbook/2/generics.html#variance-annotations | TypeScript Variance Annotations}).
642
655
  *
643
656
  * Since these schema are expressed using TypeScript types, it is possible for the user of the API to provide non-exact values of these types which has implications that depended on the variance.
644
657
  *
@@ -31,7 +31,7 @@ import type {
31
31
  } from "./core/index.js";
32
32
  import { normalizeAllowedTypes } from "./core/index.js";
33
33
 
34
- import type { SimpleFieldSchema } from "./simpleSchema.js";
34
+ import type { SimpleAllowedTypeAttributes, SimpleFieldSchema } from "./simpleSchema.js";
35
35
  import type { UnsafeUnknownSchema } from "./unsafeUnknownSchema.js";
36
36
  import type { InsertableContent } from "./unhydratedFlexTreeFromInsertable.js";
37
37
 
@@ -412,6 +412,19 @@ export class FieldSchemaAlpha<
412
412
  return this.allowedTypesFull.evaluateIdentifiers();
413
413
  }
414
414
 
415
+ public get simpleAllowedTypes(): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
416
+ const types = this.allowedTypesFull.evaluate().types;
417
+ const info = new Map<string, SimpleAllowedTypeAttributes>();
418
+
419
+ for (const type of types) {
420
+ info.set(type.type.identifier, {
421
+ isStaged: type.metadata.stagedSchemaUpgrade !== undefined,
422
+ });
423
+ }
424
+
425
+ return info;
426
+ }
427
+
415
428
  protected constructor(
416
429
  kind: Kind,
417
430
  types: Types,
@@ -198,6 +198,7 @@ export type {
198
198
  SimpleNodeSchemaBaseAlpha,
199
199
  SimpleObjectFieldSchema,
200
200
  SimpleRecordNodeSchema,
201
+ SimpleAllowedTypeAttributes,
201
202
  } from "./simpleSchema.js";
202
203
  export {
203
204
  type ImplicitFieldSchema,
@@ -47,6 +47,7 @@ import {
47
47
  type TreeNodeSchemaPrivateData,
48
48
  convertAllowedTypes,
49
49
  withBufferedTreeEvents,
50
+ AnnotatedAllowedTypesInternal,
50
51
  } from "../../core/index.js";
51
52
  import {
52
53
  type FactoryContent,
@@ -68,6 +69,7 @@ import type {
68
69
  import { brand, type JsonCompatibleReadOnlyObject } from "../../../util/index.js";
69
70
  import { nullSchema } from "../../leafNodeSchema.js";
70
71
  import { arrayNodeStoredSchema } from "../../toStoredSchema.js";
72
+ import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js";
71
73
 
72
74
  /**
73
75
  * A covariant base type for {@link (TreeArrayNode:interface)}.
@@ -1168,6 +1170,9 @@ export function arraySchema<
1168
1170
  const lazyAllowedTypesIdentifiers = new Lazy(
1169
1171
  () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)),
1170
1172
  );
1173
+ const lazySimpleAllowedTypes = new Lazy(() => {
1174
+ return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes);
1175
+ });
1171
1176
 
1172
1177
  let privateData: TreeNodeSchemaPrivateData | undefined;
1173
1178
 
@@ -1206,6 +1211,10 @@ export function arraySchema<
1206
1211
  return lazyAllowedTypesIdentifiers.value;
1207
1212
  }
1208
1213
 
1214
+ public static get simpleAllowedTypes(): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
1215
+ return lazySimpleAllowedTypes.value;
1216
+ }
1217
+
1209
1218
  protected static override constructorCached: MostDerivedData | undefined = undefined;
1210
1219
 
1211
1220
  protected static override oneTimeSetup(): TreeNodeSchemaInitializedData {
@@ -43,6 +43,7 @@ import {
43
43
  type FlexContent,
44
44
  type TreeNodeSchemaPrivateData,
45
45
  convertAllowedTypes,
46
+ AnnotatedAllowedTypesInternal,
46
47
  } from "../../core/index.js";
47
48
  import {
48
49
  unhydratedFlexTreeFromInsertable,
@@ -66,6 +67,7 @@ import type {
66
67
  import { recordLikeDataToFlexContent } from "../common.js";
67
68
  import { MapNodeStoredSchema } from "../../../core/index.js";
68
69
  import type { NodeSchemaOptionsAlpha } from "../../api/index.js";
70
+ import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js";
69
71
 
70
72
  /**
71
73
  * A map of string keys to tree objects.
@@ -275,6 +277,9 @@ export function mapSchema<
275
277
  const lazyAllowedTypesIdentifiers = new Lazy(
276
278
  () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)),
277
279
  );
280
+ const lazySimpleAllowedTypes = new Lazy(() => {
281
+ return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes);
282
+ });
278
283
 
279
284
  let privateData: TreeNodeSchemaPrivateData | undefined;
280
285
  const persistedMetadata = nodeOptions.persistedMetadata;
@@ -303,6 +308,10 @@ export function mapSchema<
303
308
  return lazyAllowedTypesIdentifiers.value;
304
309
  }
305
310
 
311
+ public static get simpleAllowedTypes(): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
312
+ return lazySimpleAllowedTypes.value;
313
+ }
314
+
306
315
  protected static override constructorCached: MostDerivedData | undefined = undefined;
307
316
 
308
317
  protected static override oneTimeSetup(): TreeNodeSchemaInitializedData {
@@ -118,6 +118,23 @@ export type ObjectFromSchemaRecord<T extends RestrictiveStringRecord<ImplicitFie
118
118
  * No other own `own` or `enumerable` properties are included on object nodes unless the user of the node manually adds custom session only state.
119
119
  * This allows a majority of general purpose JavaScript object processing operations (like `for...in`, `Reflect.ownKeys()` and `Object.entries()`) to enumerate all the children.
120
120
  *
121
+ * Field Assignment and Deletion
122
+ * Assigning to a field updates the tree value as long as it is still valid based on the schema.
123
+ * - For required fields, assigning `undefined` is invalid, and will throw.
124
+ * - For optional fields, assigning `undefined` removes the field's contents, and marks it as empty (non-enumerable and value set to undefined).
125
+ *
126
+ * Example:
127
+ * ```ts
128
+ * const Foo = schemaFactory.object("Foo", {bar: schemaFactory.optional(schemaFactory.number)});
129
+ * const node = new Foo({bar: 1})
130
+ *
131
+ * // This clears the field, is non-enumerable, and value is undefined.
132
+ * delete node.bar;
133
+ *
134
+ * // This is equivalent to the delete example.
135
+ * node.bar = undefined
136
+ * ```
137
+ *
121
138
  * The API for fields is defined by {@link ObjectFromSchemaRecord}.
122
139
  * @public
123
140
  */
@@ -275,27 +292,17 @@ function createProxyHandler(
275
292
  : false;
276
293
  }
277
294
 
278
- const innerNode = getInnerNode(proxy);
279
-
280
- const innerSchema = innerNode.context.schema.nodeSchema.get(brand(schema.identifier));
281
- assert(
282
- innerSchema instanceof ObjectNodeStoredSchema,
283
- 0xc18 /* Expected ObjectNodeStoredSchema */,
284
- );
285
-
286
- setField(
287
- innerNode.getBoxed(fieldInfo.storedKey),
288
- fieldInfo.schema,
289
- value,
290
- innerSchema.getFieldSchema(fieldInfo.storedKey),
291
- );
295
+ applyFieldChange(schema, { kind: "proxy", node: proxy }, fieldInfo, value);
292
296
  return true;
293
297
  },
294
298
  deleteProperty(target, propertyKey): boolean {
295
- // TODO: supporting delete when it makes sense (custom local fields, and optional field) could be added as a feature in the future.
296
- throw new UsageError(
297
- `Object nodes do not support the delete operator. Optional fields can be assigned to undefined instead.`,
298
- );
299
+ const fieldInfo = schema.flexKeyMap.get(propertyKey);
300
+ if (fieldInfo === undefined) {
301
+ return allowAdditionalProperties ? Reflect.deleteProperty(target, propertyKey) : false;
302
+ }
303
+
304
+ applyFieldChange(schema, { kind: "target", node: target }, fieldInfo, undefined);
305
+ return true;
299
306
  },
300
307
  has: (target, propertyKey) => {
301
308
  return (
@@ -750,3 +757,32 @@ function getFieldProperty(
750
757
  }
751
758
  return undefined;
752
759
  }
760
+
761
+ function applyFieldChange(
762
+ schema: ObjectNodeSchemaPrivate,
763
+ from: { kind: "proxy"; node: TreeNode } | { kind: "target"; node: object },
764
+ fieldInfo: { storedKey: FieldKey; schema: FieldSchema },
765
+ value: InsertableContent | undefined,
766
+ ): void {
767
+ const proxy =
768
+ from.kind === "proxy"
769
+ ? from.node
770
+ : (targetToProxy.get(from.node) ?? fail(0xc95 /* missing proxy */));
771
+ const inner = getInnerNode(proxy);
772
+ const storedSchema = inner.context.schema.nodeSchema.get(brand(schema.identifier));
773
+ assert(
774
+ storedSchema instanceof ObjectNodeStoredSchema,
775
+ 0xc96 /* Expected ObjectNodeStoredSchema */,
776
+ );
777
+
778
+ if (value === undefined && inner.tryGetField(fieldInfo.storedKey) === undefined) {
779
+ return;
780
+ }
781
+
782
+ setField(
783
+ inner.getBoxed(fieldInfo.storedKey),
784
+ fieldInfo.schema,
785
+ value,
786
+ storedSchema.getFieldSchema(fieldInfo.storedKey),
787
+ );
788
+ }
@@ -32,6 +32,7 @@ import {
32
32
  CompatibilityLevel,
33
33
  type TreeNodeSchemaPrivateData,
34
34
  convertAllowedTypes,
35
+ AnnotatedAllowedTypesInternal,
35
36
  } from "../../core/index.js";
36
37
  import { getTreeNodeSchemaInitializedData } from "../../createContext.js";
37
38
  import { tryGetTreeNodeForField } from "../../getTreeNodeForField.js";
@@ -58,6 +59,7 @@ import { prepareForInsertion } from "../../prepareForInsertion.js";
58
59
  import { recordLikeDataToFlexContent } from "../common.js";
59
60
  import { MapNodeStoredSchema } from "../../../core/index.js";
60
61
  import type { NodeSchemaOptionsAlpha } from "../../api/index.js";
62
+ import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js";
61
63
 
62
64
  /**
63
65
  * Create a proxy which implements the {@link TreeRecordNode} API.
@@ -258,6 +260,9 @@ export function recordSchema<
258
260
  const lazyAllowedTypesIdentifiers = new Lazy(
259
261
  () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)),
260
262
  );
263
+ const lazySimpleAllowedTypes = new Lazy(() => {
264
+ return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes);
265
+ });
261
266
 
262
267
  let privateData: TreeNodeSchemaPrivateData | undefined;
263
268
 
@@ -347,6 +352,10 @@ export function recordSchema<
347
352
  return lazyAllowedTypesIdentifiers.value;
348
353
  }
349
354
 
355
+ public static get simpleAllowedTypes(): ReadonlyMap<string, SimpleAllowedTypeAttributes> {
356
+ return lazySimpleAllowedTypes.value;
357
+ }
358
+
350
359
  protected static override constructorCached: MostDerivedData | undefined = undefined;
351
360
 
352
361
  public static readonly identifier = identifier;
@@ -26,6 +26,22 @@ import type { RestrictiveStringRecord } from "../../../util/index.js";
26
26
  * Therefore code assigning to these fields must explicitly construct nodes using the schema's constructor or create method,
27
27
  * or using some other method like {@link (TreeAlpha:interface).create}.
28
28
  *
29
+ * Field Assignment and Deletion
30
+ *
31
+ * Unlike JavaScript's `Record` type, assigning `undefined` to a key in {@link TreeRecordNode} behaves like a `delete` operation,
32
+ * removing the field's contents.
33
+ *
34
+ * This is to stay consistent with {@link TreeObjectNode} behavior.
35
+ *
36
+ * Example:
37
+ * ```ts
38
+ * const Numbers = schemaFactory.record("Numbers", schemaFactory.number);
39
+ * const record = new Numbers({ a:1, b:2 });
40
+ *
41
+ * // This is equivalent to delete record.a, and removes the entry.
42
+ * record.a = undefined
43
+ * ```
44
+ *
29
45
  * @beta
30
46
  */
31
47
  export interface TreeRecordNode<
@@ -91,7 +91,7 @@ export interface SimpleArrayNodeSchema<out TCustomMetadata = unknown>
91
91
  * @remarks Refers to the types by identifier.
92
92
  * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}.
93
93
  */
94
- readonly allowedTypesIdentifiers: ReadonlySet<string>;
94
+ readonly simpleAllowedTypes: ReadonlyMap<string, SimpleAllowedTypeAttributes>;
95
95
  }
96
96
 
97
97
  /**
@@ -108,7 +108,7 @@ export interface SimpleMapNodeSchema<out TCustomMetadata = unknown>
108
108
  * @remarks Refers to the types by identifier.
109
109
  * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}.
110
110
  */
111
- readonly allowedTypesIdentifiers: ReadonlySet<string>;
111
+ readonly simpleAllowedTypes: ReadonlyMap<string, SimpleAllowedTypeAttributes>;
112
112
  }
113
113
 
114
114
  /**
@@ -125,7 +125,7 @@ export interface SimpleRecordNodeSchema<out TCustomMetadata = unknown>
125
125
  * @remarks Refers to the types by identifier.
126
126
  * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}.
127
127
  */
128
- readonly allowedTypesIdentifiers: ReadonlySet<string>;
128
+ readonly simpleAllowedTypes: ReadonlyMap<string, SimpleAllowedTypeAttributes>;
129
129
  }
130
130
 
131
131
  /**
@@ -162,6 +162,23 @@ export type SimpleNodeSchema =
162
162
  | SimpleObjectNodeSchema
163
163
  | SimpleRecordNodeSchema;
164
164
 
165
+ /**
166
+ * Information about allowed types.
167
+ *
168
+ * @alpha
169
+ * @sealed
170
+ */
171
+ export interface SimpleAllowedTypeAttributes {
172
+ /**
173
+ * True if this schema is included as a {@link SchemaStaticsAlpha.staged | staged} schema upgrade,
174
+ * allowing the view schema be compatible with stored schema with (post upgrade) or without it (pre-upgrade).
175
+ * New documents and schema upgrades will omit any staged schema.
176
+ *
177
+ * Undefined if derived from a stored schema.
178
+ */
179
+ readonly isStaged: boolean | undefined;
180
+ }
181
+
165
182
  /**
166
183
  * A simple, shallow representation of a schema for a field.
167
184
  *
@@ -179,12 +196,12 @@ export interface SimpleFieldSchema {
179
196
  readonly kind: FieldKind;
180
197
 
181
198
  /**
182
- * The types allowed under the field.
199
+ * Information about the allowed types under this field.
183
200
  *
184
201
  * @remarks Refers to the types by identifier.
185
202
  * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}.
186
203
  */
187
- readonly allowedTypesIdentifiers: ReadonlySet<string>;
204
+ readonly simpleAllowedTypes: ReadonlyMap<string, SimpleAllowedTypeAttributes>;
188
205
 
189
206
  /**
190
207
  * {@inheritDoc FieldSchemaMetadata}
@@ -139,7 +139,10 @@ export function convertField(
139
139
  if (schema instanceof FieldSchemaAlpha) {
140
140
  types = convertAllowedTypes(schema.allowedTypes, options);
141
141
  } else {
142
- types = schema.allowedTypesIdentifiers as TreeTypeSet;
142
+ const allowedTypesIdentifiers: ReadonlySet<string> = new Set(
143
+ schema.simpleAllowedTypes.keys(),
144
+ );
145
+ types = allowedTypesIdentifiers as TreeTypeSet;
143
146
  }
144
147
  return { kind, types, persistedMetadata: schema.persistedMetadata };
145
148
  }
@@ -175,7 +178,10 @@ export function getStoredSchema(
175
178
  }
176
179
  case NodeKind.Map:
177
180
  case NodeKind.Record: {
178
- const types = schema.allowedTypesIdentifiers as TreeTypeSet;
181
+ const allowedTypesIdentifiers: ReadonlySet<string> = new Set(
182
+ schema.simpleAllowedTypes.keys(),
183
+ );
184
+ const types = allowedTypesIdentifiers as TreeTypeSet;
179
185
  return new MapNodeStoredSchema(
180
186
  {
181
187
  kind: FieldKinds.optional.identifier,
@@ -187,7 +193,10 @@ export function getStoredSchema(
187
193
  );
188
194
  }
189
195
  case NodeKind.Array: {
190
- const types = schema.allowedTypesIdentifiers as TreeTypeSet;
196
+ const allowedTypesIdentifiers: ReadonlySet<string> = new Set(
197
+ schema.simpleAllowedTypes.keys(),
198
+ );
199
+ const types = allowedTypesIdentifiers as TreeTypeSet;
191
200
  return arrayNodeStoredSchema(types, schema.persistedMetadata);
192
201
  }
193
202
  case NodeKind.Object: {