Skip to content

Shared Tree: Schema FormatV2 and codecs #24812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 75 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
bd8406a
Initial change.
TommyBrosman May 12, 2025
3165fe3
persistedMetadata is now backed by an object instead of a string.
TommyBrosman May 13, 2025
9709ede
Updated codec.spec.ts to cover FormatV2.
TommyBrosman May 13, 2025
ba058e1
Removed extraneous whitespace change.
TommyBrosman May 13, 2025
b819742
- Changed TreeNodeStoredSchema implementations to take the write vers…
TommyBrosman May 13, 2025
0b5c842
- Changed the stored schema implementation to handle multiple schema …
TommyBrosman May 13, 2025
ecdca7a
Merge branch 'main' into metadata-schema-3
TommyBrosman May 13, 2025
1117c2c
Apply suggestions from code review
TommyBrosman May 14, 2025
94a5c39
- Switched to using the SchemaCodecVersion in public APIs. Eventually…
TommyBrosman May 14, 2025
302cb21
Merge branch 'metadata-schema-3' of https://github.com/TommyBrosman/F…
TommyBrosman May 14, 2025
0ba34d1
- Removed the schema version constant and replaced it with SchemaCode…
TommyBrosman May 14, 2025
3275ee0
Minor: reverted changes to FluidClientVersion utils.
TommyBrosman May 15, 2025
7c48b9d
Switched back to using min client version for importCompressed.
TommyBrosman May 15, 2025
a2f4195
Minor: reverted accidental change.
TommyBrosman May 15, 2025
4786dcc
Apply suggestions from code review
TommyBrosman May 15, 2025
6fa8836
Made persisted metadata schema field naming consistent.
TommyBrosman May 15, 2025
e4064c7
- Created a separate storedSchemaDecodeDispatcher for v2 schemas.
TommyBrosman May 16, 2025
ab1b882
- Refactored the node kind dispatch.
TommyBrosman May 19, 2025
1027daa
Updated snapshots.
TommyBrosman May 19, 2025
e527eba
- Removed minimum client version changes. Equivalent changes will be …
TommyBrosman May 19, 2025
3eae1f6
Removed more minimumClientVersion glue.
TommyBrosman May 19, 2025
98e1b76
Updated API files.
TommyBrosman May 19, 2025
ecc77c9
Rename: SchemaCodecVersion -> SchemaVersion.
TommyBrosman May 20, 2025
37e900f
Merge branch 'main' into metadata-schema-3
TommyBrosman May 20, 2025
737633b
Removed top-level persisted metadata.
TommyBrosman May 21, 2025
4bb136c
Fixed comments.
TommyBrosman May 22, 2025
0bcb90f
Exposed persistedMetadata as a JsonCompatibleReadOnlyObject.
TommyBrosman May 22, 2025
a675338
Changeset.
TommyBrosman May 22, 2025
1d1ef5f
Wired up toStoredSchema. Still needs tests.
TommyBrosman May 22, 2025
b9898e8
- Updated an old snapshot that didn't include the metadata field.
TommyBrosman May 22, 2025
6ca189a
Refactor: persistedMetadata -> metadata on persisted types.
TommyBrosman May 27, 2025
85240dc
Apply suggestions from code review
TommyBrosman May 27, 2025
8883d45
- Updated the changeset description
TommyBrosman May 28, 2025
9c2c13f
Reverted unnecessary change.
TommyBrosman May 28, 2025
c62f7ec
Wired up node schema metadata persistence. Currently errors on tests …
TommyBrosman May 29, 2025
2f6b519
Reverted unneeded change.
TommyBrosman May 29, 2025
ae1779b
Removed tests for the alpha API I removed in a previous revision.
TommyBrosman May 29, 2025
7eff8ab
This revision adds simple-tree persistence for node and field schema …
TommyBrosman May 30, 2025
f3f572c
- Fixed missing persistedMetadata on fields. toStoredSchema.ts and sh…
TommyBrosman Jun 2, 2025
faaf2f7
Merge branch 'main' into metadata-schema-3
TommyBrosman Jun 2, 2025
f80d027
Removed changes for persisted format, codecs. A few tests are failing.
TommyBrosman Jun 3, 2025
a7e72f0
Minor cleanup.
TommyBrosman Jun 3, 2025
3dcfb10
Updated changeset description.
TommyBrosman Jun 4, 2025
dcf4e0e
Merge branch 'main' into metadata-in-memory
TommyBrosman Jun 4, 2025
bca4c8c
Regenerated API Extractor output.
TommyBrosman Jun 4, 2025
b66c928
Renamed the in-memory version of the persistedMetadata field to persi…
TommyBrosman Jun 6, 2025
98c853e
Merge branch 'main' into metadata-in-memory
TommyBrosman Jun 6, 2025
155b320
- Added a simple default comparison for persistedMetadata.
TommyBrosman Jun 9, 2025
4ff48d8
Minor: clarified use of persistedMetadata in tests.
TommyBrosman Jun 9, 2025
6400590
- Removed redundant alpha APIs. For now they are exposed only as static
TommyBrosman Jun 10, 2025
b329851
Reverted changes that belong in a different PR.
TommyBrosman Jun 10, 2025
6b425b2
Removed more changes that shouldn't be in this PR.
TommyBrosman Jun 10, 2025
c0eb6dc
Apply suggestions from code review
TommyBrosman Jun 10, 2025
c9a1346
Reverted another unrelated change.
TommyBrosman Jun 10, 2025
783d752
Revert "Removed changes for persisted format, codecs. A few tests are…
TommyBrosman Jun 10, 2025
f1b0c06
Revert "Reverted changes that belong in a different PR."
TommyBrosman Jun 10, 2025
0d43ee7
Revert "Removed more changes that shouldn't be in this PR."
TommyBrosman Jun 10, 2025
1a06f89
Added a changeset with code examples.
TommyBrosman Jun 12, 2025
b788c06
Merge branch 'main' into formatv2
TommyBrosman Jun 13, 2025
ac58d05
Merge branch 'main' into formatv2
TommyBrosman Jun 13, 2025
bfa3c57
- End-to-end test for enabling ST format v5
TommyBrosman Jun 13, 2025
03bce3f
Updated docs. The explanation of how to turn on persisted metadata is…
TommyBrosman Jun 13, 2025
51a6c12
Fixed indentation.
TommyBrosman Jun 13, 2025
9d42ebb
Deleted old changeset.
TommyBrosman Jun 13, 2025
52bbe1a
Merge branch 'main' into formatv2
TommyBrosman Jun 13, 2025
5abf135
Refactored fieldsObject encoding so the code is no longer duplicated.
TommyBrosman Jun 16, 2025
59db6ac
Fixed a test.
TommyBrosman Jun 16, 2025
8454d80
Apply suggestions from code review
TommyBrosman Jun 16, 2025
ef2fb5e
Refactored node schema encoding to reduce duplication.
TommyBrosman Jun 16, 2025
633c551
Merge branch 'main' into formatv2
TommyBrosman Jun 16, 2025
b145898
Small refactor.
TommyBrosman Jun 16, 2025
8a64c0a
Update .changeset/spotty-bottles-rest.md
TommyBrosman Jun 17, 2025
2b2dc5f
Tweaked documentation.
TommyBrosman Jun 17, 2025
05f00bd
Merge branch 'formatv2' of https://github.com/TommyBrosman/FluidFrame…
TommyBrosman Jun 17, 2025
cd49ae1
Merge branch 'main' into formatv2
TommyBrosman Jun 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .changeset/spotty-bottles-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
"fluid-framework": minor
"@fluidframework/tree": minor
"__section": feature
---
Persisted metadata for Shared Tree schemas (Alpha)

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.

#### Using the persisted metadata feature

As of now, persisted metadata support is available via the SchemaFactoryAlpha API:

```ts
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");
```

Persisted metadata can take the shape of any JSON-serializable object, e.g.:

```ts
const persistedMetadata = { a: 2 };
```

#### Feature flag

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:

```ts
const tree = configuredSharedTree({
formatVersion: SharedTreeFormatVersion.v5,
}).create(runtime);

export const MyContainerSchema = {
initialObjects: {
appData: tree,
},
} satisfies ContainerSchema;
```

#### Examples

##### Field schemas with persisted metadata

```ts
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { "a": 2 };

// Foo is an object type with metadata
class Foo extends schemaFactory.objectAlpha("Foo", {
// Metadata for a required number field
bar: schemaFactory.required(schemaFactory.number, { persistedMetadata }),

// Metadata for an optional string field   
baz: schemaFactory.optional(schemaFactory.string, { persistedMetadata }),
// Metadata for the object type Foo       
}, { persistedMetadata }) {}
```

##### Recursive field schemas

```ts
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { "a": 2 };

// Recursive object schema with persisted metadata
class RecursiveObject extends schemaFactory.objectRecursive("RecursiveObject", {
x: [() => RecursiveObject, schemaFactory.number],
}, { persistedMetadata }) {}

// Recursive field schema with metadata
const recursiveField = schemaFactory.optionalRecursive(
[() => RecursiveObject, schemaFactory.number],
{ persistedMetadata });
```

##### Recursive object schemas

```ts
// Construct a schema factory with alpha APIs
const schemaFactory = new SchemaFactoryAlpha("com.example");

// Define metadata. This can take the shape of any JSON-serializable object.
const persistedMetadata = { "a": 2 };

// Recursive array schema
class Foos extends schemaFactory.arrayRecursive(
"FooList",
[() => Foo],
{ persistedMetadata }) {}

// Recursive object schema
class Foo extends schemaFactory.objectRecursive(
"Foo",
{ fooList: Foos },
{ persistedMetadata }) {}

// Recursive map schema
class FooMap extends schemaFactory.mapRecursive(
"FooMap",
[() => Foo],
{ persistedMetadata }) {}
```
11 changes: 0 additions & 11 deletions .changeset/whole-days-argue.md

This file was deleted.

1 change: 1 addition & 0 deletions packages/dds/tree/api-report/tree.alpha.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,7 @@ export const SharedTreeFormatVersion: {
readonly v1: 1;
readonly v2: 2;
readonly v3: 3;
readonly v5: 5;
};

// @alpha
Expand Down
4 changes: 3 additions & 1 deletion packages/dds/tree/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,13 @@ export {
storedEmptyFieldSchema,
type StoredSchemaCollection,
schemaFormatV1,
schemaFormatV2,
LeafNodeStoredSchema,
ObjectNodeStoredSchema,
MapNodeStoredSchema,
decodeFieldSchema,
encodeFieldSchema,
encodeFieldSchemaV1,
encodeFieldSchemaV2,
storedSchemaDecodeDispatcher,
type SchemaAndPolicy,
Multiplicity,
Expand Down
78 changes: 78 additions & 0 deletions packages/dds/tree/src/core/schema-stored/formatV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import { type ObjectOptions, type Static, Type } from "@sinclair/typebox";

import { JsonCompatibleReadOnlySchema } from "../../util/index.js";
import {
FieldKindIdentifierSchema,
PersistedValueSchema,
TreeNodeSchemaIdentifierSchema,
} from "./formatV1.js";
import { unionOptions } from "../../codec/index.js";

export const PersistedMetadataFormat = Type.Optional(JsonCompatibleReadOnlySchema);

const FieldSchemaFormatBase = Type.Object({
kind: FieldKindIdentifierSchema,
types: Type.Array(TreeNodeSchemaIdentifierSchema),
metadata: PersistedMetadataFormat,
});

const noAdditionalProps: ObjectOptions = { additionalProperties: false };

export const FieldSchemaFormat = Type.Composite([FieldSchemaFormatBase], noAdditionalProps);

/**
* Format for the content of a {@link TreeNodeStoredSchema}.
*
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
*/
export const TreeNodeSchemaUnionFormat = Type.Object(
{
/**
* Object node union member.
*/
object: Type.Optional(Type.Record(Type.String(), FieldSchemaFormat)),
/**
* Map node union member.
*/
map: Type.Optional(FieldSchemaFormat),
/**
* Leaf node union member.
*/
leaf: Type.Optional(Type.Enum(PersistedValueSchema)),
},
unionOptions,
);

export type TreeNodeSchemaUnionFormat = Static<typeof TreeNodeSchemaUnionFormat>;

/**
* Format for {@link TreeNodeStoredSchema}.
*
* See {@link DiscriminatedUnionDispatcher} for more information on this pattern.
*/
export const TreeNodeSchemaDataFormat = Type.Object(
{
/**
* Node kind specific data.
*/
kind: TreeNodeSchemaUnionFormat,

// Data in common for all TreeNode schemas:
/**
* Leaf node union member.
*/
metadata: PersistedMetadataFormat,
},
noAdditionalProps,
);

export type TreeNodeSchemaDataFormat = Static<typeof TreeNodeSchemaDataFormat>;

export type FieldSchemaFormat = Static<typeof FieldSchemaFormat>;

export type PersistedMetadataFormat = Static<typeof PersistedMetadataFormat>;
5 changes: 4 additions & 1 deletion packages/dds/tree/src/core/schema-stored/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export {
ObjectNodeStoredSchema,
MapNodeStoredSchema,
decodeFieldSchema,
encodeFieldSchema,
encodeFieldSchemaV1,
encodeFieldSchemaV2,
storedSchemaDecodeDispatcher,
type SchemaAndPolicy,
type SchemaPolicy,
Expand All @@ -37,3 +38,5 @@ export type { TreeNodeSchemaIdentifier, FieldKey, FieldKindIdentifier } from "./

import * as schemaFormatV1 from "./formatV1.js";
export { schemaFormatV1 };
import * as schemaFormatV2 from "./formatV2.js";
export { schemaFormatV2 };
Loading
Loading