Skip to content

Commit 3f81ab5

Browse files
TommyBrosmanCraigMacomberalexvy86Abe27342
authored
Shared Tree: Schema FormatV2 and codecs (#24812)
Adds support for persisting metadata along with the schema. # Changes - Add schema FormatV2 - Node schema persisted metadata - Field schema persisted metadata - Schema codec for FormatV2 - Add encodeV2 method to TreeNodeStoredSchema hierarchy - Persist node schema metadata in TreeNodeStoredSchema - Add support for encoding v2 fields - Update the storedSchemaDecodeDispatcher to dispatch on the inner kind union in FormatV2 and pass node schema metadata - Create and register schema change codec handling FormatV2 - Create and register schema codec handling FormatV2 - Generate new snapshots for schema tests - Add a new Shared Tree format version - Update enums - Generate new snapshots for existing Shared Tree format tests - simple-tree changes - Persist node metadata in arraySchema, mapSchema, objectSchema - Pass metadata to NodeStoredSchema types in toStoredSchema.ts - Other tests - Update codec.spec to cover FormatV2 as well as FormatV1 - FormatV2-specific tests - API documentation - Changeset covering the new APIs - Feature flag for enabling Shared Tree Format v5 --------- Co-authored-by: Craig Macomber (Microsoft) <42876482+CraigMacomber@users.noreply.github.com> Co-authored-by: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Co-authored-by: Abram Sanderson <Abram.sanderson@gmail.com>
1 parent 68b7133 commit 3f81ab5

File tree

85 files changed

+14991
-120
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+14991
-120
lines changed

.changeset/spotty-bottles-rest.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
"fluid-framework": minor
3+
"@fluidframework/tree": minor
4+
"__section": feature
5+
---
6+
Persisted metadata for Shared Tree schemas (Alpha)
7+
8+
The persisted metadata feature for Shared Tree allows an application author to write document-persisted metadata along with the schema. This feature is supported for both node and field schemas.
9+
10+
#### Using the persisted metadata feature
11+
12+
As of now, persisted metadata support is available via the SchemaFactoryAlpha API:
13+
14+
```ts
15+
// Construct a schema factory with alpha APIs
16+
const schemaFactory = new SchemaFactoryAlpha("com.example");
17+
```
18+
19+
Persisted metadata can take the shape of any JSON-serializable object, e.g.:
20+
21+
```ts
22+
const persistedMetadata = { a: 2 };
23+
```
24+
25+
#### Feature flag
26+
27+
To enable persisted metadata, use `configuredSharedTree` to specify the format version. The tree that is returned can be substituted in place of the default `SharedTree` object exported by the Fluid Framework. For example:
28+
29+
```ts
30+
const tree = configuredSharedTree({
31+
formatVersion: SharedTreeFormatVersion.v5,
32+
}).create(runtime);
33+
34+
export const MyContainerSchema = {
35+
initialObjects: {
36+
appData: tree,
37+
},
38+
} satisfies ContainerSchema;
39+
```
40+
41+
#### Examples
42+
43+
##### Field schemas with persisted metadata
44+
45+
```ts
46+
// Construct a schema factory with alpha APIs
47+
const schemaFactory = new SchemaFactoryAlpha("com.example");
48+
49+
// Define metadata. This can take the shape of any JSON-serializable object.
50+
const persistedMetadata = { "a": 2 };
51+
52+
// Foo is an object type with metadata
53+
class Foo extends schemaFactory.objectAlpha("Foo", {
54+
// Metadata for a required number field
55+
bar: schemaFactory.required(schemaFactory.number, { persistedMetadata }),
56+
57+
// Metadata for an optional string field   
58+
baz: schemaFactory.optional(schemaFactory.string, { persistedMetadata }),
59+
// Metadata for the object type Foo       
60+
}, { persistedMetadata }) {}
61+
```
62+
63+
##### Recursive field schemas
64+
65+
```ts
66+
// Construct a schema factory with alpha APIs
67+
const schemaFactory = new SchemaFactoryAlpha("com.example");
68+
69+
// Define metadata. This can take the shape of any JSON-serializable object.
70+
const persistedMetadata = { "a": 2 };
71+
72+
// Recursive object schema with persisted metadata
73+
class RecursiveObject extends schemaFactory.objectRecursive("RecursiveObject", {
74+
x: [() => RecursiveObject, schemaFactory.number],
75+
}, { persistedMetadata }) {}
76+
77+
// Recursive field schema with metadata
78+
const recursiveField = schemaFactory.optionalRecursive(
79+
[() => RecursiveObject, schemaFactory.number],
80+
{ persistedMetadata });
81+
```
82+
83+
##### Recursive object schemas
84+
85+
```ts
86+
// Construct a schema factory with alpha APIs
87+
const schemaFactory = new SchemaFactoryAlpha("com.example");
88+
89+
// Define metadata. This can take the shape of any JSON-serializable object.
90+
const persistedMetadata = { "a": 2 };
91+
92+
// Recursive array schema
93+
class Foos extends schemaFactory.arrayRecursive(
94+
"FooList",
95+
[() => Foo],
96+
{ persistedMetadata }) {}
97+
98+
// Recursive object schema
99+
class Foo extends schemaFactory.objectRecursive(
100+
"Foo",
101+
{ fooList: Foos },
102+
{ persistedMetadata }) {}
103+
104+
// Recursive map schema
105+
class FooMap extends schemaFactory.mapRecursive(
106+
"FooMap",
107+
[() => Foo],
108+
{ persistedMetadata }) {}
109+
```

.changeset/whole-days-argue.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ export const SharedTreeFormatVersion: {
858858
readonly v1: 1;
859859
readonly v2: 2;
860860
readonly v3: 3;
861+
readonly v5: 5;
861862
};
862863

863864
// @alpha

packages/dds/tree/src/core/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,13 @@ export {
138138
storedEmptyFieldSchema,
139139
type StoredSchemaCollection,
140140
schemaFormatV1,
141+
schemaFormatV2,
141142
LeafNodeStoredSchema,
142143
ObjectNodeStoredSchema,
143144
MapNodeStoredSchema,
144145
decodeFieldSchema,
145-
encodeFieldSchema,
146+
encodeFieldSchemaV1,
147+
encodeFieldSchemaV2,
146148
storedSchemaDecodeDispatcher,
147149
type SchemaAndPolicy,
148150
Multiplicity,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { type ObjectOptions, type Static, Type } from "@sinclair/typebox";
7+
8+
import { JsonCompatibleReadOnlySchema } from "../../util/index.js";
9+
import {
10+
FieldKindIdentifierSchema,
11+
PersistedValueSchema,
12+
TreeNodeSchemaIdentifierSchema,
13+
} from "./formatV1.js";
14+
import { unionOptions } from "../../codec/index.js";
15+
16+
export const PersistedMetadataFormat = Type.Optional(JsonCompatibleReadOnlySchema);
17+
18+
const FieldSchemaFormatBase = Type.Object({
19+
kind: FieldKindIdentifierSchema,
20+
types: Type.Array(TreeNodeSchemaIdentifierSchema),
21+
metadata: PersistedMetadataFormat,
22+
});
23+
24+
const noAdditionalProps: ObjectOptions = { additionalProperties: false };
25+
26+
export const FieldSchemaFormat = Type.Composite([FieldSchemaFormatBase], noAdditionalProps);
27+
28+
/**
29+
* Format for the content of a {@link TreeNodeStoredSchema}.
30+
*
31+
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
32+
*/
33+
export const TreeNodeSchemaUnionFormat = Type.Object(
34+
{
35+
/**
36+
* Object node union member.
37+
*/
38+
object: Type.Optional(Type.Record(Type.String(), FieldSchemaFormat)),
39+
/**
40+
* Map node union member.
41+
*/
42+
map: Type.Optional(FieldSchemaFormat),
43+
/**
44+
* Leaf node union member.
45+
*/
46+
leaf: Type.Optional(Type.Enum(PersistedValueSchema)),
47+
},
48+
unionOptions,
49+
);
50+
51+
export type TreeNodeSchemaUnionFormat = Static<typeof TreeNodeSchemaUnionFormat>;
52+
53+
/**
54+
* Format for {@link TreeNodeStoredSchema}.
55+
*
56+
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
57+
*/
58+
export const TreeNodeSchemaDataFormat = Type.Object(
59+
{
60+
/**
61+
* Node kind specific data.
62+
*/
63+
kind: TreeNodeSchemaUnionFormat,
64+
65+
// Data in common for all TreeNode schemas:
66+
/**
67+
* Leaf node union member.
68+
*/
69+
metadata: PersistedMetadataFormat,
70+
},
71+
noAdditionalProps,
72+
);
73+
74+
export type TreeNodeSchemaDataFormat = Static<typeof TreeNodeSchemaDataFormat>;
75+
76+
export type FieldSchemaFormat = Static<typeof FieldSchemaFormat>;
77+
78+
export type PersistedMetadataFormat = Static<typeof PersistedMetadataFormat>;

packages/dds/tree/src/core/schema-stored/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export {
1818
ObjectNodeStoredSchema,
1919
MapNodeStoredSchema,
2020
decodeFieldSchema,
21-
encodeFieldSchema,
21+
encodeFieldSchemaV1,
22+
encodeFieldSchemaV2,
2223
storedSchemaDecodeDispatcher,
2324
type SchemaAndPolicy,
2425
type SchemaPolicy,
@@ -37,3 +38,5 @@ export type { TreeNodeSchemaIdentifier, FieldKey, FieldKindIdentifier } from "./
3738

3839
import * as schemaFormatV1 from "./formatV1.js";
3940
export { schemaFormatV1 };
41+
import * as schemaFormatV2 from "./formatV2.js";
42+
export { schemaFormatV2 };

0 commit comments

Comments
 (0)