@fluidframework/tree 2.10.0-306579 → 2.10.0-307060
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api-report/tree.alpha.api.md +11 -9
- package/api-report/tree.beta.api.md +2 -0
- package/api-report/tree.legacy.alpha.api.md +2 -0
- package/api-report/tree.legacy.public.api.md +2 -0
- package/api-report/tree.public.api.md +2 -0
- package/dist/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
- package/dist/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/basicChunk.js +15 -5
- package/dist/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/dist/feature-libraries/chunked-forest/chunkedForest.js +5 -0
- package/dist/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/dist/feature-libraries/index.d.ts +1 -1
- package/dist/feature-libraries/index.d.ts.map +1 -1
- package/dist/feature-libraries/index.js +2 -2
- package/dist/feature-libraries/index.js.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts +27 -27
- package/dist/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/discrepancies.js +33 -33
- package/dist/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts +1 -1
- package/dist/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/dist/feature-libraries/modular-schema/index.js +2 -2
- package/dist/feature-libraries/modular-schema/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/shared-tree/treeApi.js +4 -1
- package/dist/shared-tree/treeApi.js.map +1 -1
- package/dist/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
- package/dist/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaCreationUtilities.js +9 -7
- package/dist/simple-tree/api/schemaCreationUtilities.js.map +1 -1
- package/dist/simple-tree/api/schemaFactory.d.ts +2 -0
- package/dist/simple-tree/api/schemaFactory.d.ts.map +1 -1
- package/dist/simple-tree/api/schemaFactory.js +4 -1
- package/dist/simple-tree/api/schemaFactory.js.map +1 -1
- package/dist/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.d.ts +4 -5
- package/dist/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/dist/simple-tree/core/treeNodeKernel.js +63 -67
- package/dist/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/dist/simple-tree/objectNode.d.ts +1 -1
- package/dist/simple-tree/objectNode.js.map +1 -1
- package/dist/simple-tree/objectNodeTypes.d.ts +3 -0
- package/dist/simple-tree/objectNodeTypes.d.ts.map +1 -1
- package/dist/simple-tree/objectNodeTypes.js +3 -1
- package/dist/simple-tree/objectNodeTypes.js.map +1 -1
- package/docs/.attachments/object-merge-semantics.drawio +145 -0
- package/docs/user-facing/array-merge-semantics.md +344 -0
- package/docs/user-facing/map-merge-semantics.md +128 -0
- package/docs/user-facing/merge-semantics.md +7 -3
- package/docs/user-facing/object-merge-semantics.md +77 -0
- package/lib/feature-libraries/chunked-forest/basicChunk.d.ts +26 -5
- package/lib/feature-libraries/chunked-forest/basicChunk.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/basicChunk.js +15 -5
- package/lib/feature-libraries/chunked-forest/basicChunk.js.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.d.ts.map +1 -1
- package/lib/feature-libraries/chunked-forest/chunkedForest.js +5 -0
- package/lib/feature-libraries/chunked-forest/chunkedForest.js.map +1 -1
- package/lib/feature-libraries/index.d.ts +1 -1
- package/lib/feature-libraries/index.d.ts.map +1 -1
- package/lib/feature-libraries/index.js +1 -1
- package/lib/feature-libraries/index.js.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts +27 -27
- package/lib/feature-libraries/modular-schema/discrepancies.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/discrepancies.js +31 -31
- package/lib/feature-libraries/modular-schema/discrepancies.js.map +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts +1 -1
- package/lib/feature-libraries/modular-schema/index.d.ts.map +1 -1
- package/lib/feature-libraries/modular-schema/index.js +1 -1
- package/lib/feature-libraries/modular-schema/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/shared-tree/treeApi.js +5 -2
- package/lib/shared-tree/treeApi.js.map +1 -1
- package/lib/simple-tree/api/schemaCreationUtilities.d.ts +12 -14
- package/lib/simple-tree/api/schemaCreationUtilities.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaCreationUtilities.js +9 -7
- package/lib/simple-tree/api/schemaCreationUtilities.js.map +1 -1
- package/lib/simple-tree/api/schemaFactory.d.ts +2 -0
- package/lib/simple-tree/api/schemaFactory.d.ts.map +1 -1
- package/lib/simple-tree/api/schemaFactory.js +4 -1
- package/lib/simple-tree/api/schemaFactory.js.map +1 -1
- package/lib/simple-tree/api/schemaFactoryRecursive.js.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.d.ts +4 -5
- package/lib/simple-tree/core/treeNodeKernel.d.ts.map +1 -1
- package/lib/simple-tree/core/treeNodeKernel.js +64 -68
- package/lib/simple-tree/core/treeNodeKernel.js.map +1 -1
- package/lib/simple-tree/objectNode.d.ts +1 -1
- package/lib/simple-tree/objectNode.js.map +1 -1
- package/lib/simple-tree/objectNodeTypes.d.ts +3 -0
- package/lib/simple-tree/objectNodeTypes.d.ts.map +1 -1
- package/lib/simple-tree/objectNodeTypes.js +3 -1
- package/lib/simple-tree/objectNodeTypes.js.map +1 -1
- package/package.json +20 -20
- package/src/feature-libraries/chunked-forest/basicChunk.ts +12 -4
- package/src/feature-libraries/chunked-forest/chunkedForest.ts +5 -0
- package/src/feature-libraries/index.ts +1 -1
- package/src/feature-libraries/modular-schema/discrepancies.ts +75 -77
- package/src/feature-libraries/modular-schema/index.ts +4 -1
- package/src/packageVersion.ts +1 -1
- package/src/shared-tree/treeApi.ts +7 -5
- package/src/simple-tree/api/schemaCreationUtilities.ts +29 -17
- package/src/simple-tree/api/schemaFactory.ts +25 -18
- package/src/simple-tree/api/schemaFactoryRecursive.ts +1 -1
- package/src/simple-tree/core/treeNodeKernel.ts +62 -64
- package/src/simple-tree/objectNode.ts +1 -1
- package/src/simple-tree/objectNodeTypes.ts +3 -1
|
@@ -27,45 +27,45 @@ import { brand } from "../../util/index.js";
|
|
|
27
27
|
/**
|
|
28
28
|
* @remarks
|
|
29
29
|
*
|
|
30
|
-
* 1.
|
|
30
|
+
* 1. FieldDiscrepancy
|
|
31
31
|
*
|
|
32
|
-
* `
|
|
32
|
+
* `FieldDiscrepancy` represents the differences between two `TreeFieldStoredSchema` objects. It consists of
|
|
33
33
|
* three types of incompatibilities:
|
|
34
34
|
*
|
|
35
|
-
* -
|
|
35
|
+
* - FieldKindDiscrepancy: Indicates the differences in `FieldKindIdentifier` between two `TreeFieldStoredSchema`
|
|
36
36
|
* objects (e.g., optional, required, sequence, etc.).
|
|
37
|
-
* -
|
|
38
|
-
* -
|
|
37
|
+
* - AllowedTypesDiscrepancy: Indicates the differences in the allowed child types between the two schemas.
|
|
38
|
+
* - ValueSchemaDiscrepancy: Specifically indicates the differences in the `ValueSchema` of two
|
|
39
39
|
* `LeafNodeStoredSchema` objects.
|
|
40
40
|
*
|
|
41
|
-
* 2.
|
|
41
|
+
* 2. NodeDiscrepancy
|
|
42
42
|
*
|
|
43
|
-
* `
|
|
43
|
+
* `NodeDiscrepancy` represents the differences between two `TreeNodeStoredSchema` objects and includes:
|
|
44
44
|
*
|
|
45
|
-
* -
|
|
45
|
+
* - NodeKindDiscrepancy: Indicates the differences in the types of `TreeNodeStoredSchema` (currently supports
|
|
46
46
|
* `ObjectNodeStoredSchema`, `MapNodeStoredSchema`, and `LeafNodeStoredSchema`).
|
|
47
|
-
* -
|
|
48
|
-
* `TreeNodeStoredSchema`. It includes an array of `
|
|
47
|
+
* - NodeFieldsDiscrepancy: Indicates the `FieldDiscrepancy` of `TreeFieldStoredSchema` within two
|
|
48
|
+
* `TreeNodeStoredSchema`. It includes an array of `FieldDiscrepancy` instances in the `differences` field.
|
|
49
49
|
*
|
|
50
50
|
* When comparing two nodes for compatibility, it only makes sense to compare their fields if the nodes are of
|
|
51
51
|
* the same kind (map, object, leaf).
|
|
52
52
|
*
|
|
53
|
-
* 3.
|
|
53
|
+
* 3. Discrepancy
|
|
54
54
|
*
|
|
55
|
-
*
|
|
56
|
-
* schema differences. See {@link
|
|
55
|
+
* Discrepancy consists of both `NodeDiscrepancy` and `FieldDiscrepancy`, representing any kind of
|
|
56
|
+
* schema differences. See {@link getAllowedContentDiscrepancies} for more details about how we process it
|
|
57
57
|
* and the ordering.
|
|
58
58
|
*/
|
|
59
|
-
export type
|
|
59
|
+
export type Discrepancy = FieldDiscrepancy | NodeDiscrepancy;
|
|
60
60
|
|
|
61
|
-
export type
|
|
61
|
+
export type NodeDiscrepancy = NodeKindDiscrepancy | NodeFieldsDiscrepancy;
|
|
62
62
|
|
|
63
|
-
export type
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
63
|
+
export type FieldDiscrepancy =
|
|
64
|
+
| AllowedTypeDiscrepancy
|
|
65
|
+
| FieldKindDiscrepancy
|
|
66
|
+
| ValueSchemaDiscrepancy;
|
|
67
67
|
|
|
68
|
-
export interface
|
|
68
|
+
export interface AllowedTypeDiscrepancy {
|
|
69
69
|
identifier: string | undefined; // undefined indicates root field schema
|
|
70
70
|
mismatch: "allowedTypes";
|
|
71
71
|
/**
|
|
@@ -78,62 +78,60 @@ export interface AllowedTypeIncompatibility {
|
|
|
78
78
|
stored: string[];
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export interface
|
|
81
|
+
export interface FieldKindDiscrepancy {
|
|
82
82
|
identifier: string | undefined; // undefined indicates root field schema
|
|
83
83
|
mismatch: "fieldKind";
|
|
84
84
|
view: FieldKindIdentifier;
|
|
85
85
|
stored: FieldKindIdentifier;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
export interface
|
|
88
|
+
export interface ValueSchemaDiscrepancy {
|
|
89
89
|
identifier: string;
|
|
90
90
|
mismatch: "valueSchema";
|
|
91
91
|
view: ValueSchema | undefined;
|
|
92
92
|
stored: ValueSchema | undefined;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
export interface
|
|
95
|
+
export interface NodeKindDiscrepancy {
|
|
96
96
|
identifier: string;
|
|
97
97
|
mismatch: "nodeKind";
|
|
98
98
|
view: SchemaFactoryNodeKind | undefined;
|
|
99
99
|
stored: SchemaFactoryNodeKind | undefined;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
export interface
|
|
102
|
+
export interface NodeFieldsDiscrepancy {
|
|
103
103
|
identifier: string;
|
|
104
104
|
mismatch: "fields";
|
|
105
|
-
differences:
|
|
105
|
+
differences: FieldDiscrepancy[];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
type SchemaFactoryNodeKind = "object" | "leaf" | "map";
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
111
|
+
* Finds and reports discrepancies between a view schema and a stored schema.
|
|
112
112
|
*
|
|
113
113
|
* The workflow for finding schema incompatibilities:
|
|
114
|
-
* 1. Compare the two root schemas to identify any `
|
|
114
|
+
* 1. Compare the two root schemas to identify any `FieldDiscrepancy`.
|
|
115
115
|
*
|
|
116
116
|
* 2. For each node schema in the `view`:
|
|
117
117
|
* - Verify if the node schema exists in the stored. If it does, ensure that the `SchemaFactoryNodeKind` are
|
|
118
|
-
* consistent. Otherwise this difference is treated as `
|
|
118
|
+
* consistent. Otherwise this difference is treated as `NodeKindDiscrepancy`
|
|
119
119
|
* - If a node schema with the same identifier exists in both view and stored, and their `SchemaFactoryNodeKind`
|
|
120
|
-
* are consistent, perform a exhaustive validation to identify all `
|
|
120
|
+
* are consistent, perform a exhaustive validation to identify all `FieldDiscrepancy`.
|
|
121
121
|
*
|
|
122
122
|
* 3. For each node schema in the stored, verify if it exists in the view. The overlapping parts were already
|
|
123
123
|
* addressed in the previous step.
|
|
124
124
|
*
|
|
125
125
|
* @returns the discrepancies between two TreeStoredSchema objects
|
|
126
126
|
*/
|
|
127
|
-
export function
|
|
127
|
+
export function getAllowedContentDiscrepancies(
|
|
128
128
|
view: TreeStoredSchema,
|
|
129
129
|
stored: TreeStoredSchema,
|
|
130
|
-
):
|
|
131
|
-
const
|
|
130
|
+
): Discrepancy[] {
|
|
131
|
+
const discrepancies: Discrepancy[] = [];
|
|
132
132
|
|
|
133
133
|
// check root schema discrepancies
|
|
134
|
-
|
|
135
|
-
...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema),
|
|
136
|
-
);
|
|
134
|
+
discrepancies.push(...trackFieldDiscrepancies(view.rootFieldSchema, stored.rootFieldSchema));
|
|
137
135
|
|
|
138
136
|
// Verify the existence and type of a node schema given its identifier (key), then determine if
|
|
139
137
|
// an exhaustive search is necessary.
|
|
@@ -143,7 +141,7 @@ export function getAllowedContentIncompatibilities(
|
|
|
143
141
|
|
|
144
142
|
if (viewNodeSchema instanceof ObjectNodeStoredSchema) {
|
|
145
143
|
if (!stored.nodeSchema.has(key)) {
|
|
146
|
-
|
|
144
|
+
discrepancies.push({
|
|
147
145
|
identifier: key,
|
|
148
146
|
mismatch: "nodeKind",
|
|
149
147
|
view: "object",
|
|
@@ -156,27 +154,27 @@ export function getAllowedContentIncompatibilities(
|
|
|
156
154
|
0x9be /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
|
|
157
155
|
);
|
|
158
156
|
if (storedNodeSchema instanceof MapNodeStoredSchema) {
|
|
159
|
-
|
|
157
|
+
discrepancies.push({
|
|
160
158
|
identifier: key,
|
|
161
159
|
mismatch: "nodeKind",
|
|
162
160
|
view: "object",
|
|
163
161
|
stored: "map",
|
|
164
|
-
} satisfies
|
|
162
|
+
} satisfies NodeKindDiscrepancy);
|
|
165
163
|
} else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
|
|
166
|
-
|
|
164
|
+
discrepancies.push({
|
|
167
165
|
identifier: key,
|
|
168
166
|
mismatch: "nodeKind",
|
|
169
167
|
view: "object",
|
|
170
168
|
stored: "leaf",
|
|
171
|
-
} satisfies
|
|
169
|
+
} satisfies NodeKindDiscrepancy);
|
|
172
170
|
} else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
|
|
173
171
|
const differences = trackObjectNodeDiscrepancies(viewNodeSchema, storedNodeSchema);
|
|
174
172
|
if (differences.length > 0) {
|
|
175
|
-
|
|
173
|
+
discrepancies.push({
|
|
176
174
|
identifier: key,
|
|
177
175
|
mismatch: "fields",
|
|
178
176
|
differences,
|
|
179
|
-
} satisfies
|
|
177
|
+
} satisfies NodeFieldsDiscrepancy);
|
|
180
178
|
}
|
|
181
179
|
} else {
|
|
182
180
|
throwUnsupportedNodeType(storedNodeSchema.constructor.name);
|
|
@@ -184,12 +182,12 @@ export function getAllowedContentIncompatibilities(
|
|
|
184
182
|
}
|
|
185
183
|
} else if (viewNodeSchema instanceof MapNodeStoredSchema) {
|
|
186
184
|
if (!stored.nodeSchema.has(key)) {
|
|
187
|
-
|
|
185
|
+
discrepancies.push({
|
|
188
186
|
identifier: key,
|
|
189
187
|
mismatch: "nodeKind",
|
|
190
188
|
view: "map",
|
|
191
189
|
stored: undefined,
|
|
192
|
-
} satisfies
|
|
190
|
+
} satisfies NodeKindDiscrepancy);
|
|
193
191
|
} else {
|
|
194
192
|
const storedNodeSchema = stored.nodeSchema.get(key);
|
|
195
193
|
assert(
|
|
@@ -197,21 +195,21 @@ export function getAllowedContentIncompatibilities(
|
|
|
197
195
|
0x9bf /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
|
|
198
196
|
);
|
|
199
197
|
if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
|
|
200
|
-
|
|
198
|
+
discrepancies.push({
|
|
201
199
|
identifier: key,
|
|
202
200
|
mismatch: "nodeKind",
|
|
203
201
|
view: "map",
|
|
204
202
|
stored: "object",
|
|
205
|
-
} satisfies
|
|
203
|
+
} satisfies NodeKindDiscrepancy);
|
|
206
204
|
} else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
|
|
207
|
-
|
|
205
|
+
discrepancies.push({
|
|
208
206
|
identifier: key,
|
|
209
207
|
mismatch: "nodeKind",
|
|
210
208
|
view: "map",
|
|
211
209
|
stored: "leaf",
|
|
212
|
-
} satisfies
|
|
210
|
+
} satisfies NodeKindDiscrepancy);
|
|
213
211
|
} else if (storedNodeSchema instanceof MapNodeStoredSchema) {
|
|
214
|
-
|
|
212
|
+
discrepancies.push(
|
|
215
213
|
...trackFieldDiscrepancies(
|
|
216
214
|
viewNodeSchema.mapFields,
|
|
217
215
|
storedNodeSchema.mapFields,
|
|
@@ -224,7 +222,7 @@ export function getAllowedContentIncompatibilities(
|
|
|
224
222
|
}
|
|
225
223
|
} else if (viewNodeSchema instanceof LeafNodeStoredSchema) {
|
|
226
224
|
if (!stored.nodeSchema.has(key)) {
|
|
227
|
-
|
|
225
|
+
discrepancies.push({
|
|
228
226
|
identifier: key,
|
|
229
227
|
mismatch: "nodeKind",
|
|
230
228
|
view: "leaf",
|
|
@@ -237,27 +235,27 @@ export function getAllowedContentIncompatibilities(
|
|
|
237
235
|
0x9c0 /* The storedNodeSchema in stored.nodeSchema should not be undefined */,
|
|
238
236
|
);
|
|
239
237
|
if (storedNodeSchema instanceof MapNodeStoredSchema) {
|
|
240
|
-
|
|
238
|
+
discrepancies.push({
|
|
241
239
|
identifier: key,
|
|
242
240
|
mismatch: "nodeKind",
|
|
243
241
|
view: "leaf",
|
|
244
242
|
stored: "map",
|
|
245
|
-
} satisfies
|
|
243
|
+
} satisfies NodeKindDiscrepancy);
|
|
246
244
|
} else if (storedNodeSchema instanceof ObjectNodeStoredSchema) {
|
|
247
|
-
|
|
245
|
+
discrepancies.push({
|
|
248
246
|
identifier: key,
|
|
249
247
|
mismatch: "nodeKind",
|
|
250
248
|
view: "leaf",
|
|
251
249
|
stored: "object",
|
|
252
|
-
} satisfies
|
|
250
|
+
} satisfies NodeKindDiscrepancy);
|
|
253
251
|
} else if (storedNodeSchema instanceof LeafNodeStoredSchema) {
|
|
254
252
|
if (viewNodeSchema.leafValue !== storedNodeSchema.leafValue) {
|
|
255
|
-
|
|
253
|
+
discrepancies.push({
|
|
256
254
|
identifier: key,
|
|
257
255
|
mismatch: "valueSchema",
|
|
258
256
|
view: viewNodeSchema.leafValue,
|
|
259
257
|
stored: storedNodeSchema.leafValue,
|
|
260
|
-
} satisfies
|
|
258
|
+
} satisfies ValueSchemaDiscrepancy);
|
|
261
259
|
}
|
|
262
260
|
} else {
|
|
263
261
|
throwUnsupportedNodeType(storedNodeSchema.constructor.name);
|
|
@@ -270,7 +268,7 @@ export function getAllowedContentIncompatibilities(
|
|
|
270
268
|
|
|
271
269
|
for (const [key, storedNodeSchema] of stored.nodeSchema) {
|
|
272
270
|
if (!viewNodeKeys.has(key)) {
|
|
273
|
-
|
|
271
|
+
discrepancies.push({
|
|
274
272
|
identifier: key,
|
|
275
273
|
mismatch: "nodeKind",
|
|
276
274
|
view: undefined,
|
|
@@ -280,11 +278,11 @@ export function getAllowedContentIncompatibilities(
|
|
|
280
278
|
: storedNodeSchema instanceof ObjectNodeStoredSchema
|
|
281
279
|
? "object"
|
|
282
280
|
: "leaf",
|
|
283
|
-
} satisfies
|
|
281
|
+
} satisfies NodeKindDiscrepancy);
|
|
284
282
|
}
|
|
285
283
|
}
|
|
286
284
|
|
|
287
|
-
return
|
|
285
|
+
return discrepancies;
|
|
288
286
|
}
|
|
289
287
|
|
|
290
288
|
/**
|
|
@@ -296,8 +294,8 @@ function trackFieldDiscrepancies(
|
|
|
296
294
|
view: TreeFieldStoredSchema,
|
|
297
295
|
stored: TreeFieldStoredSchema,
|
|
298
296
|
keyOrRoot?: string,
|
|
299
|
-
):
|
|
300
|
-
const differences:
|
|
297
|
+
): FieldDiscrepancy[] {
|
|
298
|
+
const differences: FieldDiscrepancy[] = [];
|
|
301
299
|
|
|
302
300
|
// Only track the symmetric differences of two sets.
|
|
303
301
|
const findSetDiscrepancies = (
|
|
@@ -316,7 +314,7 @@ function trackFieldDiscrepancies(
|
|
|
316
314
|
mismatch: "allowedTypes",
|
|
317
315
|
view: allowedTypesDiscrepancies[0],
|
|
318
316
|
stored: allowedTypesDiscrepancies[1],
|
|
319
|
-
} satisfies
|
|
317
|
+
} satisfies AllowedTypeDiscrepancy);
|
|
320
318
|
}
|
|
321
319
|
|
|
322
320
|
if (view.kind !== stored.kind) {
|
|
@@ -325,7 +323,7 @@ function trackFieldDiscrepancies(
|
|
|
325
323
|
mismatch: "fieldKind",
|
|
326
324
|
view: view.kind,
|
|
327
325
|
stored: stored.kind,
|
|
328
|
-
} satisfies
|
|
326
|
+
} satisfies FieldKindDiscrepancy);
|
|
329
327
|
}
|
|
330
328
|
|
|
331
329
|
return differences;
|
|
@@ -334,8 +332,8 @@ function trackFieldDiscrepancies(
|
|
|
334
332
|
function trackObjectNodeDiscrepancies(
|
|
335
333
|
view: ObjectNodeStoredSchema,
|
|
336
334
|
stored: ObjectNodeStoredSchema,
|
|
337
|
-
):
|
|
338
|
-
const differences:
|
|
335
|
+
): FieldDiscrepancy[] {
|
|
336
|
+
const differences: FieldDiscrepancy[] = [];
|
|
339
337
|
const viewFieldKeys = new Set<FieldKey>();
|
|
340
338
|
/**
|
|
341
339
|
* Similar to the logic used for tracking discrepancies between two node schemas, we will identify
|
|
@@ -359,7 +357,7 @@ function trackObjectNodeDiscrepancies(
|
|
|
359
357
|
mismatch: "fieldKind",
|
|
360
358
|
view: fieldStoredSchema.kind,
|
|
361
359
|
stored: storedEmptyFieldSchema.kind,
|
|
362
|
-
} satisfies
|
|
360
|
+
} satisfies FieldKindDiscrepancy);
|
|
363
361
|
} else {
|
|
364
362
|
differences.push(
|
|
365
363
|
...trackFieldDiscrepancies(
|
|
@@ -382,7 +380,7 @@ function trackObjectNodeDiscrepancies(
|
|
|
382
380
|
mismatch: "fieldKind",
|
|
383
381
|
view: storedEmptyFieldSchema.kind,
|
|
384
382
|
stored: fieldStoredSchema.kind,
|
|
385
|
-
} satisfies
|
|
383
|
+
} satisfies FieldKindDiscrepancy);
|
|
386
384
|
}
|
|
387
385
|
}
|
|
388
386
|
|
|
@@ -407,12 +405,12 @@ function trackObjectNodeDiscrepancies(
|
|
|
407
405
|
* validating internal fields.
|
|
408
406
|
*/
|
|
409
407
|
export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema): boolean {
|
|
410
|
-
const
|
|
408
|
+
const discrepancies = getAllowedContentDiscrepancies(view, stored);
|
|
411
409
|
|
|
412
|
-
for (const
|
|
413
|
-
switch (
|
|
410
|
+
for (const discrepancy of discrepancies) {
|
|
411
|
+
switch (discrepancy.mismatch) {
|
|
414
412
|
case "nodeKind": {
|
|
415
|
-
if (
|
|
413
|
+
if (discrepancy.stored !== undefined) {
|
|
416
414
|
// It's fine for the view schema to know of a node type that the stored schema doesn't know about.
|
|
417
415
|
return false;
|
|
418
416
|
}
|
|
@@ -421,14 +419,14 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
|
|
|
421
419
|
case "valueSchema":
|
|
422
420
|
case "allowedTypes":
|
|
423
421
|
case "fieldKind": {
|
|
424
|
-
if (!validateFieldIncompatibility(
|
|
422
|
+
if (!validateFieldIncompatibility(discrepancy)) {
|
|
425
423
|
return false;
|
|
426
424
|
}
|
|
427
425
|
break;
|
|
428
426
|
}
|
|
429
427
|
case "fields": {
|
|
430
428
|
if (
|
|
431
|
-
|
|
429
|
+
discrepancy.differences.some(
|
|
432
430
|
(difference) => !validateFieldIncompatibility(difference),
|
|
433
431
|
)
|
|
434
432
|
) {
|
|
@@ -442,16 +440,16 @@ export function isRepoSuperset(view: TreeStoredSchema, stored: TreeStoredSchema)
|
|
|
442
440
|
return true;
|
|
443
441
|
}
|
|
444
442
|
|
|
445
|
-
function validateFieldIncompatibility(
|
|
446
|
-
switch (
|
|
443
|
+
function validateFieldIncompatibility(discrepancy: FieldDiscrepancy): boolean {
|
|
444
|
+
switch (discrepancy.mismatch) {
|
|
447
445
|
case "allowedTypes": {
|
|
448
446
|
// Since we only track the symmetric difference between the allowed types in the view and
|
|
449
447
|
// stored schemas, it's sufficient to check if any extra allowed types still exist in the
|
|
450
448
|
// stored schema.
|
|
451
|
-
return
|
|
449
|
+
return discrepancy.stored.length === 0;
|
|
452
450
|
}
|
|
453
451
|
case "fieldKind": {
|
|
454
|
-
return posetLte(
|
|
452
|
+
return posetLte(discrepancy.stored, discrepancy.view, fieldRealizer);
|
|
455
453
|
}
|
|
456
454
|
case "valueSchema": {
|
|
457
455
|
return false;
|
|
@@ -74,4 +74,7 @@ export type {
|
|
|
74
74
|
FieldKindConfiguration,
|
|
75
75
|
FieldKindConfigurationEntry,
|
|
76
76
|
} from "./fieldKindConfiguration.js";
|
|
77
|
-
export {
|
|
77
|
+
export {
|
|
78
|
+
getAllowedContentDiscrepancies,
|
|
79
|
+
isRepoSuperset,
|
|
80
|
+
} from "./discrepancies.js";
|
package/src/packageVersion.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { unreachableCase } from "@fluidframework/core-utils/internal";
|
|
7
7
|
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
|
|
9
9
|
import { TreeStatus } from "../feature-libraries/index.js";
|
|
@@ -464,10 +464,12 @@ function runTransactionInCheckout<TResult>(
|
|
|
464
464
|
switch (constraint.type) {
|
|
465
465
|
case "nodeInDocument": {
|
|
466
466
|
const node = getOrCreateInnerNode(constraint.node);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
467
|
+
const nodeStatus = treeApi.status(constraint.node);
|
|
468
|
+
if (nodeStatus !== TreeStatus.InDocument) {
|
|
469
|
+
throw new UsageError(
|
|
470
|
+
`Attempted to add a "nodeInDocument" constraint, but the node is not currently in the document. Node status: ${nodeStatus}`,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
471
473
|
checkout.editor.addNodeExistsConstraint(node.anchorNode);
|
|
472
474
|
break;
|
|
473
475
|
}
|
|
@@ -73,8 +73,8 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
73
73
|
/**
|
|
74
74
|
* Converts an enum into a collection of schema which can be used in a union.
|
|
75
75
|
* @remarks
|
|
76
|
-
* Currently only supports `string` enums.
|
|
77
76
|
* The string value of the enum is used as the name of the schema: callers must ensure that it is stable and unique.
|
|
77
|
+
* Numeric enums values have the value implicitly converted into a string.
|
|
78
78
|
* Consider making a dedicated schema factory with a nested scope to avoid the enum members colliding with other schema.
|
|
79
79
|
* @example
|
|
80
80
|
* ```typescript
|
|
@@ -87,7 +87,7 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
87
87
|
* // Define the schema for each member of the enum using a nested scope to group them together.
|
|
88
88
|
* const ModeNodes = adaptEnum(new SchemaFactory(`${schemaFactory.scope}.Mode`), Mode);
|
|
89
89
|
* // Defined the types of the nodes which correspond to this the schema.
|
|
90
|
-
* type ModeNodes =
|
|
90
|
+
* type ModeNodes = TreeNodeFromImplicitAllowedTypes<(typeof ModeNodes.schema)>;
|
|
91
91
|
* // An example schema which has an enum as a child.
|
|
92
92
|
* class Parent extends schemaFactory.object("Parent", {
|
|
93
93
|
* // adaptEnum's return value has a ".schema" property can be use as an `AllowedTypes` array allowing any of the members of the enum.
|
|
@@ -106,8 +106,6 @@ export function singletonSchema<TScope extends string, TName extends string | nu
|
|
|
106
106
|
* }
|
|
107
107
|
* ```
|
|
108
108
|
* @privateRemarks
|
|
109
|
-
* TODO:
|
|
110
|
-
* Extend this to support numeric enums.
|
|
111
109
|
* Maybe provide `SchemaFactory.nested` to ease creating nested scopes?
|
|
112
110
|
* @see {@link enumFromStrings} for a similar function that works on arrays of strings instead of an enum.
|
|
113
111
|
* @alpha
|
|
@@ -139,9 +137,12 @@ export function adaptEnum<
|
|
|
139
137
|
|
|
140
138
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
141
139
|
const factoryOut = <TValue extends Values>(value: TValue) => {
|
|
142
|
-
return new out[
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
return new out[
|
|
141
|
+
inverse.get(value) ?? fail("missing enum value")
|
|
142
|
+
// "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
|
|
143
|
+
]() as TValue extends unknown
|
|
144
|
+
? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
|
|
145
|
+
: never;
|
|
145
146
|
};
|
|
146
147
|
const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
|
|
147
148
|
for (const [key, value] of Object.entries(members)) {
|
|
@@ -176,7 +177,7 @@ export function adaptEnum<
|
|
|
176
177
|
* ```typescript
|
|
177
178
|
* const schemaFactory = new SchemaFactory("com.myApp");
|
|
178
179
|
* const Mode = enumFromStrings(schemaFactory, ["Fun", "Cool"]);
|
|
179
|
-
* type Mode =
|
|
180
|
+
* type Mode = TreeNodeFromImplicitAllowedTypes<typeof Mode.schema>;
|
|
180
181
|
* const nodeFromString: Mode = Mode("Fun");
|
|
181
182
|
* const nodeFromSchema: Mode = new Mode.Fun();
|
|
182
183
|
*
|
|
@@ -198,21 +199,32 @@ export function enumFromStrings<
|
|
|
198
199
|
throw new UsageError("All members of enums must have distinct names");
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
type
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
type MembersUnion = Members[number];
|
|
203
|
+
|
|
204
|
+
// Get all keys of the Members tuple which are numeric strings as union of numbers:
|
|
205
|
+
type Indexes = Extract<keyof Members, `${number}`> extends `${infer N extends number}`
|
|
206
|
+
? N
|
|
207
|
+
: never;
|
|
208
|
+
|
|
209
|
+
type TOut = {
|
|
210
|
+
[Index in Indexes as Members[Index]]: ReturnType<
|
|
211
|
+
typeof singletonSchema<TScope, Members[Index] & string>
|
|
209
212
|
>;
|
|
210
213
|
};
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
216
|
+
const factoryOut = <TValue extends MembersUnion>(value: TValue) => {
|
|
217
|
+
// "extends unknown" is required here to handle when TValue is an union: each member of the union should be processed independently.
|
|
218
|
+
return new recordOut[value]() as TValue extends unknown
|
|
219
|
+
? NodeFromSchema<ReturnType<typeof singletonSchema<TScope, TValue>>>
|
|
220
|
+
: never;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
type SchemaArray = UnionToTuple<MembersUnion extends unknown ? TOut[MembersUnion] : never>;
|
|
213
224
|
const schemaArray: TreeNodeSchema[] = [];
|
|
214
225
|
|
|
215
226
|
const out = factoryOut as typeof factoryOut & TOut & { readonly schema: SchemaArray };
|
|
227
|
+
const recordOut = out as Record<MembersUnion, new () => unknown>;
|
|
216
228
|
for (const name of members) {
|
|
217
229
|
const schema = singletonSchema(factory, name);
|
|
218
230
|
schemaArray.push(schema);
|
|
@@ -804,6 +804,8 @@ export class SchemaFactory<
|
|
|
804
804
|
name,
|
|
805
805
|
allowedTypes as T & ImplicitAllowedTypes,
|
|
806
806
|
true,
|
|
807
|
+
// Setting this (implicitlyConstructable) to true seems to work ok currently, but not for other node kinds.
|
|
808
|
+
// Supporting this could be fragile and might break other future changes, so it's being kept as false for now.
|
|
807
809
|
false,
|
|
808
810
|
);
|
|
809
811
|
|
|
@@ -811,24 +813,29 @@ export class SchemaFactory<
|
|
|
811
813
|
ScopedSchemaName<TScope, Name>,
|
|
812
814
|
NodeKind.Map,
|
|
813
815
|
TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Map>,
|
|
814
|
-
{
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
// Ideally this would be
|
|
831
|
-
//
|
|
816
|
+
| {
|
|
817
|
+
/**
|
|
818
|
+
* Iterator for the iterable of content for this node.
|
|
819
|
+
* @privateRemarks
|
|
820
|
+
* Wrapping the constructor parameter for recursive arrays and maps in an inlined object type avoids (for unknown reasons)
|
|
821
|
+
* the following compile error when declaring the recursive schema:
|
|
822
|
+
* `Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.`
|
|
823
|
+
* To benefit from this without impacting the API, the definition of `Iterable` has been inlined as such an object.
|
|
824
|
+
*
|
|
825
|
+
* If this workaround is kept, ideally this comment would be deduplicated with the other instance of it.
|
|
826
|
+
* Unfortunately attempts to do this failed to avoid the compile error this was introduced to solve.
|
|
827
|
+
*/
|
|
828
|
+
[Symbol.iterator](): Iterator<
|
|
829
|
+
[string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>]
|
|
830
|
+
>;
|
|
831
|
+
}
|
|
832
|
+
// Ideally this would be
|
|
833
|
+
// RestrictiveStringRecord<InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>>,
|
|
834
|
+
// but doing so breaks recursive types.
|
|
835
|
+
// Instead we do a less nice version:
|
|
836
|
+
| {
|
|
837
|
+
readonly [P in string]: InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
|
|
838
|
+
},
|
|
832
839
|
false,
|
|
833
840
|
T,
|
|
834
841
|
undefined
|
|
@@ -140,7 +140,7 @@ export type ValidateRecursiveSchema<
|
|
|
140
140
|
? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes<T["info"]>]>
|
|
141
141
|
: unknown;
|
|
142
142
|
}[T["kind"]],
|
|
143
|
-
// ImplicitlyConstructable: recursive types are not implicitly constructable.
|
|
143
|
+
// ImplicitlyConstructable: recursive types are currently not implicitly constructable.
|
|
144
144
|
false,
|
|
145
145
|
// Info: What's passed to the method to create the schema. Constraining these here should be about as effective as if the actual constraints existed on the actual method itself.
|
|
146
146
|
{
|