Skip to content

Commit fdfff43

Browse files
Shared Tree: Simple Schema codec (#25741)
Introduces new APIs for encoding and decoding view schemas. Also adds new fields to Simple Schemas to support compatibility checking scenarios. ## Description New APIs (in alpha): - `encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleReadOnly` - Encodes a Simple Schema into a JSON-compatible object format. - `decodeSimpleSchema(encodedSchema: JsonCompatibleReadOnly, validator?: FormatValidator): SimpleTreeSchema` - Decodes a Simple Schema from a JSON-compatible object format. Other changes: - Updated `toSimpleTreeSchema` to include `compatibility-related fields. These include: - On SimpleAllowedTypeAttributes: `isStaged` - On object schemas: `allowUnknownOptionalTypes` --------- Co-authored-by: Noah Encke <[email protected]>
1 parent b85efc0 commit fdfff43

28 files changed

+1621
-342
lines changed

packages/common/core-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ export { PromiseTimer, setLongTimeout, Timer } from "./timer.js";
3333
export { unreachableCase } from "./unreachable.js";
3434
export { isObject, isPromiseLike } from "./typesGuards.js";
3535
export { oob } from "./oob.js";
36+
export { transformMapValues } from "./map.js";
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
/**
7+
* Transform the values of a Map using the provided transform function.
8+
* @param map - The map to transform.
9+
* @param transformValue - A method for transforming values in the map.
10+
* @returns A new map with the transformed values.
11+
*
12+
* @internal
13+
*/
14+
export function transformMapValues<Key, InputValue, OutputValue>(
15+
map: ReadonlyMap<Key, InputValue>,
16+
transformValue: (value: InputValue, key: Key) => OutputValue,
17+
): Map<Key, OutputValue> {
18+
return new Map([...map.entries()].map(([key, value]) => [key, transformValue(value, key)]));
19+
}

packages/dds/tree/api-report/tree.alpha.api.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ export function createSimpleTreeIndex<TFieldSchema extends ImplicitFieldSchema,
198198
// @alpha
199199
export function createSimpleTreeIndex<TFieldSchema extends ImplicitFieldSchema, TKey extends TreeIndexKey, TValue, TSchema extends TreeNodeSchema>(view: TreeView<TFieldSchema>, indexer: Map<TreeNodeSchema, string>, getValue: (nodes: TreeIndexNodes<NodeFromSchema<TSchema>>) => TValue, isKeyValid: (key: TreeIndexKey) => key is TKey, indexableSchema: readonly TSchema[]): SimpleTreeIndex<TKey, TValue>;
200200

201+
// @alpha
202+
export function decodeSimpleSchema(encodedSchema: JsonCompatibleReadOnly, validator?: FormatValidator): SimpleTreeSchema;
203+
201204
// @public @sealed @system
202205
interface DefaultProvider extends ErasedType<"@fluidframework/tree.FieldProvider"> {
203206
}
@@ -213,6 +216,9 @@ export interface DirtyTreeMap {
213216
// @alpha
214217
export type DirtyTreeStatus = "new" | "changed" | "moved";
215218

219+
// @alpha
220+
export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleReadOnly;
221+
216222
// @beta
217223
export function enumFromStrings<TScope extends string, const Members extends readonly string[]>(factory: SchemaFactory<TScope>, members: Members): (<TValue extends Members[number]>(value: TValue) => TValue extends unknown ? TreeNode & {
218224
readonly value: TValue;
@@ -1071,6 +1077,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema {
10711077

10721078
// @alpha @sealed
10731079
export interface SimpleObjectNodeSchema<out TCustomMetadata = unknown> extends SimpleNodeSchemaBaseAlpha<NodeKind.Object, TCustomMetadata> {
1080+
readonly allowUnknownOptionalFields: boolean | undefined;
10741081
readonly fields: ReadonlyMap<string, SimpleObjectFieldSchema>;
10751082
}
10761083

packages/dds/tree/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ export {
283283
type SchemaFactory_base,
284284
type NumberKeys,
285285
type SimpleAllowedTypeAttributes,
286+
encodeSimpleSchema,
287+
decodeSimpleSchema,
286288
} from "./simple-tree/index.js";
287289
export {
288290
SharedTree,

packages/dds/tree/src/shared-tree/sharedTree.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,8 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie
935935
* Export a {@link SimpleNodeSchema} from a {@link TreeNodeStoredSchema}.
936936
* @privateRemarks
937937
* TODO: Persist node metadata once schema FormatV2 is supported.
938+
* Note on SimpleNodeSchema construction: In the persisted format `persistedMetadata` is just called `metadata` whereas the `metadata`
939+
* field on SimpleNodeSchema is not persisted.
938940
*/
939941
function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeSchema {
940942
const arrayTypes = tryStoredSchemaAsArray(schema);
@@ -951,7 +953,13 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS
951953
for (const [storedKey, field] of schema.objectNodeFields) {
952954
fields.set(storedKey, { ...exportSimpleFieldSchemaStored(field), storedKey });
953955
}
954-
return { kind: NodeKind.Object, fields, metadata: {}, persistedMetadata: schema.metadata };
956+
return {
957+
kind: NodeKind.Object,
958+
fields,
959+
allowUnknownOptionalFields: undefined,
960+
metadata: {},
961+
persistedMetadata: schema.metadata,
962+
};
955963
}
956964
if (schema instanceof MapNodeStoredSchema) {
957965
assert(

packages/dds/tree/src/simple-tree/api/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,7 @@ export {
167167
getShouldIncrementallySummarizeAllowedTypes,
168168
incrementalSummaryHint,
169169
} from "./incrementalAllowedTypes.js";
170+
export {
171+
encodeSimpleSchema,
172+
decodeSimpleSchema,
173+
} from "./simpleSchemaCodec.js";

packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ function generateNode(
106106
for (const [key, field] of schema.fields) {
107107
fields[key] = generateFieldSchema(field, context, field.storedKey);
108108
}
109-
// Here allowUnknownOptionalFields is implicitly defaulting. This is a subjective policy choice:
110-
// users of this code are expected to handle what ever choice this code makes for cases like this.
111-
return factory.objectAlpha(id, fields, { metadata: schema.metadata });
109+
// Here allowUnknownOptionalFields is implicitly defaulting in the case where the input schema does not explicitly specify the value.
110+
// This is a subjective policy choice: users of this code are expected to handle what ever choice this code makes for cases like this.
111+
return factory.objectAlpha(id, fields, {
112+
metadata: schema.metadata,
113+
allowUnknownOptionalFields: schema.allowUnknownOptionalFields ?? false,
114+
});
112115
}
113116
case NodeKind.Array:
114117
return factory.arrayAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), {

0 commit comments

Comments
 (0)