From 45ed7f7a06336bae735215b62664b430c5cbfcd0 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 22 Oct 2025 17:11:22 -0700 Subject: [PATCH 01/47] Initial change. Implemented viewSchemaToViewCompatibilitySchema. No tests yet and I might be missing members. --- .../dds/tree/api-report/tree.alpha.api.md | 11 +- packages/dds/tree/src/index.ts | 1 + .../dds/tree/src/simple-tree/api/index.ts | 1 + .../api/viewSchemaToSimpleSchema.ts | 2 + .../viewSchemaToViewCompatibilitySchema.ts | 143 ++++++++++++++++++ .../dds/tree/src/simple-tree/fieldSchema.ts | 13 ++ packages/dds/tree/src/simple-tree/index.ts | 1 + .../dds/tree/src/simple-tree/simpleSchema.ts | 16 +- .../api-report/fluid-framework.alpha.api.md | 4 + 9 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index f812b7e7cc9f..92d8a0e27883 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -268,6 +268,8 @@ export class FieldSchemaAlpha; // (undocumented) get persistedMetadata(): JsonCompatibleReadOnlyObject | undefined; + // (undocumented) + get stagedSchemaUpgrades(): SchemaUpgrade[]; } // @alpha @sealed @system @@ -310,7 +312,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -510,7 +512,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; + readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system @@ -1007,6 +1009,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; + readonly stagedSchemaUpgrades?: SchemaUpgrade[]; } // @alpha @sealed @@ -1040,6 +1043,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { + readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } @@ -1294,6 +1298,9 @@ export namespace TableSchema { }): System_TableSchema.TableSchemaBase; } +// @alpha +export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; + // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 8b79e34740c6..56a5c8e34da3 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -338,3 +338,4 @@ export { JsonAsTree } from "./jsonDomainSchema.js"; export { FluidSerializableAsTree } from "./serializableDomainSchema.js"; export { TableSchema, type System_TableSchema } from "./tableSchema.js"; export { asAlpha } from "./api.js"; +export { toViewCompatibilityTreeSchema } from "./simple-tree/index.js"; diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index c42426cec935..63a9e4a0e32e 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -160,4 +160,5 @@ export { export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; export { toSimpleTreeSchema } from "./viewSchemaToSimpleSchema.js"; +export { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; export type { TreeChangeEvents } from "./treeChangeEvents.js"; diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index b0e25b1dbea4..31450d1814f1 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -128,6 +128,7 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod metadata: field.metadata, persistedMetadata: field.persistedMetadata, storedKey: field.storedKey, + stagedSchemaUpgrades: field.stagedSchemaUpgrades, }); } @@ -136,5 +137,6 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod fields, metadata: schema.metadata, persistedMetadata: schema.persistedMetadata, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts new file mode 100644 index 000000000000..54f1454aeaec --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -0,0 +1,143 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { assert, unreachableCase } from "@fluidframework/core-utils/internal"; +import { NodeKind } from "../core/index.js"; +import type { + SimpleArrayNodeSchema, + SimpleLeafNodeSchema, + SimpleMapNodeSchema, + SimpleNodeSchema, + SimpleObjectFieldSchema, + SimpleObjectNodeSchema, + SimpleRecordNodeSchema, + SimpleTreeSchema, +} from "../simpleSchema.js"; +import type { TreeSchema } from "./configuration.js"; +import { LeafNodeSchema } from "../leafNodeSchema.js"; +import { + ArrayNodeSchema, + MapNodeSchema, + ObjectNodeSchema, + RecordNodeSchema, +} from "../node-kinds/index.js"; + +/** + * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. + * + * @param schema - The stored schema to convert. + * @param copySchemaObjects - If true, copies the contents of the schema into new objects. + * @returns The converted SimpleTreeSchema. + * + * @alpha + */ +export function toViewCompatibilityTreeSchema( + schema: TreeSchema, + copySchemaObjects: boolean, +): SimpleTreeSchema { + const definitions = new Map(); + + // Walk the node definitions and convert them one by one. Recurse into fields used in compatibility checks. + for (const nodeSchema of schema.definitions.values()) { + // TODO: Move this assert to a common location so it can be used from both SimpleSchema builders. + // The set of node kinds is extensible, but the typing of SimpleNodeSchema is not, so we need to check that the schema is one of the known kinds. + assert( + nodeSchema instanceof ArrayNodeSchema || + nodeSchema instanceof MapNodeSchema || + nodeSchema instanceof LeafNodeSchema || + nodeSchema instanceof ObjectNodeSchema || + nodeSchema instanceof RecordNodeSchema, + // TODO: New error code. + 0xb60 /* Invalid schema */, + ); + + // TODO: Do we need walkNodeSchema to recurse into fields in this context? + // Probably not: we are walking all schema definitions unconditionally without worrying about tree order. + // It is probably enough to just walk the fields of object schema here. + + // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. + // TODO: Refactor copy methods to avoid duplication with viewSchemaToSimpleSchema. + // TODO: Does anything need to be done outside of `copyNodeSchema`? Are there any properties on TreeNodeSchema or the root field that need to be handled? + const simpleNodeSchema = copySchemaObjects ? copyNodeSchema(nodeSchema) : nodeSchema; + definitions.set(nodeSchema.identifier, simpleNodeSchema); + } + + return { + root: copySchemaObjects + ? { + kind: schema.root.kind, + allowedTypesIdentifiers: schema.root.allowedTypesIdentifiers, + metadata: schema.root.metadata, + persistedMetadata: schema.root.persistedMetadata, + stagedSchemaUpgrades: schema.root.stagedSchemaUpgrades, + } + : schema.root, // TODO: Convert the root field + definitions, + }; +} + +/** + * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. + * + * @remarks Caches the result on the input schema for future calls. + */ +function copyNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { + const kind = schema.kind; + switch (kind) { + case NodeKind.Leaf: + return copyLeafSchema(schema); + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return copySchemaWithAllowedTypes(schema); + case NodeKind.Object: + return copyObjectSchema(schema); + default: + unreachableCase(kind); + } +} + +function copyLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; +} + +function copySchemaWithAllowedTypes( + schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, +): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { + return { + kind: schema.kind, + allowedTypesIdentifiers: schema.allowedTypesIdentifiers, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; +} + +function copyObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { + const fields: Map = new Map(); + for (const [propertyKey, field] of schema.fields) { + // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. + fields.set(propertyKey, { + kind: field.kind, + allowedTypesIdentifiers: field.allowedTypesIdentifiers, + metadata: field.metadata, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + stagedSchemaUpgrades: field.stagedSchemaUpgrades, + }); + } + + return { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; +} diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index 668ea5abc30f..c02736b63c1d 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -28,6 +28,7 @@ import type { TreeLeafValue, InsertableTreeNodeFromImplicitAllowedTypes, AllowedTypesFull, + SchemaUpgrade, } from "./core/index.js"; import { normalizeAllowedTypes } from "./core/index.js"; @@ -412,6 +413,18 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } + public get stagedSchemaUpgrades(): SchemaUpgrade[] { + const annotatedAllowedTypes = this.allowedTypesFull.evaluate().types; + const upgrades: SchemaUpgrade[] = []; + for (const type of annotatedAllowedTypes) { + if (type.metadata.stagedSchemaUpgrade !== undefined) { + upgrades.push(type.metadata.stagedSchemaUpgrade); + } + } + + return upgrades; + } + protected constructor( kind: Kind, types: Types, diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 2ffaef453983..61358fb9b8fa 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -274,3 +274,4 @@ export { nullSchema, } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; +export { toViewCompatibilityTreeSchema } from "./api/index.js"; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 11f186e55507..62788e40c4df 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -5,7 +5,7 @@ import type { ValueSchema } from "../core/index.js"; import type { JsonCompatibleReadOnlyObject } from "../util/index.js"; -import type { NodeKind, SimpleNodeSchemaBase } from "./core/index.js"; +import type { NodeKind, SchemaUpgrade, SimpleNodeSchemaBase } from "./core/index.js"; import type { FieldKind, FieldSchemaMetadata } from "./fieldSchema.js"; /* @@ -58,6 +58,13 @@ export interface SimpleObjectNodeSchema * especially if/when TreeNodeSchema for objects provide more maps. */ readonly fields: ReadonlyMap; + + /** + * Whether the object node allows unknown optional fields. + * + * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + */ + readonly allowUnknownOptionalFields?: boolean; } /** @@ -186,6 +193,13 @@ export interface SimpleFieldSchema { */ readonly allowedTypesIdentifiers: ReadonlySet; + /** + * Staged schema upgrades for this field indexed by the allowed type identifier they apply to. + * + * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + */ + readonly stagedSchemaUpgrades?: SchemaUpgrade[]; + /** * {@inheritDoc FieldSchemaMetadata} */ diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index abe60272d3a4..4b40cd8bc394 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -322,6 +322,8 @@ export class FieldSchemaAlpha; // (undocumented) get persistedMetadata(): JsonCompatibleReadOnlyObject | undefined; + // (undocumented) + get stagedSchemaUpgrades(): SchemaUpgrade[]; } // @alpha @sealed @system @@ -1385,6 +1387,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; + readonly stagedSchemaUpgrades?: SchemaUpgrade[]; } // @alpha @sealed @@ -1418,6 +1421,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { + readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } From f76e1ebebe2b51c44845cba5e1a343c02cd20a77 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 23 Oct 2025 12:37:09 -0700 Subject: [PATCH 02/47] Added test coverage using the existing toSimpleSchema tests as a starting point. --- .../api/viewSchemaToSimpleSchema.ts | 2 - .../simple-tree/api/getSimpleSchema.spec.ts | 925 +++++++++++++----- 2 files changed, 654 insertions(+), 273 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 31450d1814f1..b0e25b1dbea4 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -128,7 +128,6 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod metadata: field.metadata, persistedMetadata: field.persistedMetadata, storedKey: field.storedKey, - stagedSchemaUpgrades: field.stagedSchemaUpgrades, }); } @@ -137,6 +136,5 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod fields, metadata: schema.metadata, persistedMetadata: schema.persistedMetadata, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, }; } diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index ffc1dd055cd3..b2b5a0755ac6 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -10,6 +10,8 @@ import { SchemaFactory, SchemaFactoryAlpha, stringSchema, + toViewCompatibilityTreeSchema, + TreeViewConfigurationAlpha, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -35,352 +37,733 @@ const simpleNumber: SimpleLeafNodeSchema = { }; describe("getSimpleSchema", () => { - it("non-copying", () => { + describe("non-copying", () => { const Schema = stringSchema; const root = SchemaFactoryAlpha.optional(Schema); - const actual = toSimpleTreeSchema(root, false); - const expected: SimpleTreeSchema = { root, definitions: new Map([[Schema.identifier, Schema]]), }; - assert.deepEqual(actual, expected); - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(root, false); + + assert.deepEqual(actual, expected); + + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toViewCompatibilityTreeSchema(treeView, false); + + assert.deepEqual(actual, expected); + + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); + }); }); - it("Field Schema", () => { + describe("Field Schema", () => { const schemaFactory = new SchemaFactory("test"); const Schema = schemaFactory.optional(schemaFactory.string, { metadata: { description: "An optional string." }, }); - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - metadata: { description: "An optional string." }, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - assert.deepEqual(actual, expected); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + metadata: { description: "An optional string." }, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + metadata: { description: "An optional string." }, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + persistedMetadata: undefined, + stagedSchemaUpgrades: [], + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Leaf node", () => { + describe("Leaf node", () => { const Schema = SchemaFactory.string; - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - assert.deepEqual(actual, expected); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + persistedMetadata: undefined, + stagedSchemaUpgrades: [], + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Union root", () => { + describe("Union root", () => { const Schema = [SchemaFactory.number, SchemaFactory.string]; - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string", + ]), + }, + definitions: new Map([ + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string", + ]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], ]), - }, - definitions: new Map([ - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + }; + + assert.deepEqual(actual, expected); + }); }); - it("Array schema", () => { + describe("Array schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.array("array", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.array"]), + }, + definitions: new Map([ + [ + "test.array", + { + kind: NodeKind.Array, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + metadata: {}, + persistedMetadata: undefined, + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.array"]), - }, - definitions: new Map([ - [ - "test.array", - { - kind: NodeKind.Array, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - metadata: {}, - persistedMetadata: undefined, - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.array"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.array", + { + kind: NodeKind.Array, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + metadata: {}, + persistedMetadata: undefined, + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Map schema", () => { + describe("Map schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.map("map", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.map"]), - }, - definitions: new Map([ - [ - "test.map", - { - kind: NodeKind.Map, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.map"]), + }, + definitions: new Map([ + [ + "test.map", + { + kind: NodeKind.Map, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.map"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.map", + { + kind: NodeKind.Map, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Record schema", () => { + describe("Record schema", () => { const schemaFactory = new SchemaFactoryAlpha("test"); class Schema extends schemaFactory.record("record", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.record"]), - }, - definitions: new Map([ - [ - "test.record", - { - kind: NodeKind.Record, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.record"]), + }, + definitions: new Map([ + [ + "test.record", + { + kind: NodeKind.Record, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.record"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.record", + { + kind: NodeKind.Record, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Object schema", () => { + describe("Object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.optional(schemaFactory.number), bar: schemaFactory.required(schemaFactory.string), }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), + storedKey: "foo", + }, + ], + [ + "bar", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + storedKey: "bar", + }, + ], + ]), + } satisfies SimpleObjectNodeSchema, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), - storedKey: "foo", - }, - ], - [ - "bar", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - storedKey: "bar", - }, - ], - ]), - } satisfies SimpleObjectNodeSchema, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), + storedKey: "foo", + stagedSchemaUpgrades: [], + }, + ], + [ + "bar", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + storedKey: "bar", + stagedSchemaUpgrades: [], + }, + ], + ]), + } satisfies SimpleObjectNodeSchema, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Object schema including an identifier field", () => { + describe("Object schema including an identifier field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { id: schemaFactory.identifier, }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "id", + { + kind: FieldKind.Identifier, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + storedKey: "id", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "id", - { - kind: FieldKind.Identifier, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - storedKey: "id", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "id", + { + kind: FieldKind.Identifier, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + storedKey: "id", + stagedSchemaUpgrades: [], + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + const actual = toViewCompatibilityTreeSchema(treeView, true); + assert.deepEqual(actual, expected); + }); }); - it("Object schema including a union field", () => { + describe("Object schema including a union field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.required([schemaFactory.number, schemaFactory.string]), }) {} - // Must enable copy so deep equality passes. - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + // Must enable copy so deep equality passes. + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string", + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + + // Must enable copy so deep equality passes. + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.object"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string", + ]), + storedKey: "foo", + stagedSchemaUpgrades: [], + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); - it("Recursive object schema", () => { + describe("Recursive object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.objectRecursive("recursive-object", { foo: schemaFactory.optionalRecursive([schemaFactory.string, () => Schema]), }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.recursive-object"]), + }, + definitions: new Map([ + [ + "test.recursive-object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.string", + "test.recursive-object", + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.recursive-object"]), - }, - definitions: new Map([ - [ - "test.recursive-object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.string", - "test.recursive-object", - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set(["test.recursive-object"]), + stagedSchemaUpgrades: [], + }, + definitions: new Map([ + [ + "test.recursive-object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + allowedTypesIdentifiers: new Set([ + "com.fluidframework.leaf.string", + "test.recursive-object", + ]), + storedKey: "foo", + stagedSchemaUpgrades: [], + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); }); }); From 491c2edabef2dc887fe5424c71e715d981133ee2 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 23 Oct 2025 13:29:56 -0700 Subject: [PATCH 03/47] Refactored the schema copying code. --- .../dds/tree/api-report/tree.alpha.api.md | 4 +- .../api/viewSchemaToSimpleSchema.ts | 94 +++++++++++++++---- .../viewSchemaToViewCompatibilitySchema.ts | 85 ++--------------- 3 files changed, 85 insertions(+), 98 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 92d8a0e27883..5cefeb2e47fd 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -312,7 +312,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -512,7 +512,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index b0e25b1dbea4..d43366071219 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -59,7 +59,9 @@ export function toSimpleTreeSchema( nodeSchema instanceof RecordNodeSchema, 0xb60 /* Invalid schema */, ); - const outSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema) : nodeSchema; + const outSchema = copySchemaObjects + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyKind.SimpleSchema) + : nodeSchema; definitions.set(nodeSchema.identifier, outSchema); }, }); @@ -77,12 +79,24 @@ export function toSimpleTreeSchema( }; } +/** + * Specifies which fields are included when copying a Simple Schema. + */ +export enum SimpleSchemaCopyKind { + // TODO: Rename + SimpleSchema, + ViewCompatibilitySchema, +} + /** * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. * * @remarks Caches the result on the input schema for future calls. */ -function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { +export function copySimpleNodeSchema( + schema: SimpleNodeSchema, + copyKind: SimpleSchemaCopyKind, +): SimpleNodeSchema { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: @@ -92,7 +106,7 @@ function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { case NodeKind.Record: return copySimpleSchemaWithAllowedTypes(schema); case NodeKind.Object: - return copySimpleObjectSchema(schema); + return copySimpleObjectSchema(schema, copyKind); default: unreachableCase(kind); } @@ -118,23 +132,69 @@ function copySimpleSchemaWithAllowedTypes( }; } -function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { +function copySimpleObjectSchema( + schema: SimpleObjectNodeSchema, + copyKind: SimpleSchemaCopyKind, +): SimpleObjectNodeSchema { const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - fields.set(propertyKey, { - kind: field.kind, - allowedTypesIdentifiers: field.allowedTypesIdentifiers, - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, - storedKey: field.storedKey, - }); + let simpleField: SimpleObjectFieldSchema | undefined; + + switch (copyKind) { + case SimpleSchemaCopyKind.SimpleSchema: + simpleField = { + kind: field.kind, + allowedTypesIdentifiers: field.allowedTypesIdentifiers, + metadata: field.metadata, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + }; + break; + + case SimpleSchemaCopyKind.ViewCompatibilitySchema: + simpleField = { + kind: field.kind, + allowedTypesIdentifiers: field.allowedTypesIdentifiers, + metadata: field.metadata, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + stagedSchemaUpgrades: field.stagedSchemaUpgrades, + }; + break; + + default: + unreachableCase(copyKind); + } + + fields.set(propertyKey, simpleField); } - return { - kind: NodeKind.Object, - fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; + let simpleObject: SimpleObjectNodeSchema | undefined; + + switch (copyKind) { + case SimpleSchemaCopyKind.SimpleSchema: + simpleObject = { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + break; + + case SimpleSchemaCopyKind.ViewCompatibilitySchema: + simpleObject = { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; + break; + + default: + unreachableCase(copyKind); + } + + return simpleObject; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts index 54f1454aeaec..09acd4d697e3 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -3,18 +3,8 @@ * Licensed under the MIT License. */ -import { assert, unreachableCase } from "@fluidframework/core-utils/internal"; -import { NodeKind } from "../core/index.js"; -import type { - SimpleArrayNodeSchema, - SimpleLeafNodeSchema, - SimpleMapNodeSchema, - SimpleNodeSchema, - SimpleObjectFieldSchema, - SimpleObjectNodeSchema, - SimpleRecordNodeSchema, - SimpleTreeSchema, -} from "../simpleSchema.js"; +import { assert } from "@fluidframework/core-utils/internal"; +import type { SimpleNodeSchema, SimpleTreeSchema } from "../simpleSchema.js"; import type { TreeSchema } from "./configuration.js"; import { LeafNodeSchema } from "../leafNodeSchema.js"; import { @@ -23,6 +13,7 @@ import { ObjectNodeSchema, RecordNodeSchema, } from "../node-kinds/index.js"; +import { copySimpleNodeSchema, SimpleSchemaCopyKind } from "./viewSchemaToSimpleSchema.js"; /** * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. @@ -58,9 +49,9 @@ export function toViewCompatibilityTreeSchema( // It is probably enough to just walk the fields of object schema here. // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. - // TODO: Refactor copy methods to avoid duplication with viewSchemaToSimpleSchema. - // TODO: Does anything need to be done outside of `copyNodeSchema`? Are there any properties on TreeNodeSchema or the root field that need to be handled? - const simpleNodeSchema = copySchemaObjects ? copyNodeSchema(nodeSchema) : nodeSchema; + const simpleNodeSchema = copySchemaObjects + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyKind.ViewCompatibilitySchema) + : nodeSchema; definitions.set(nodeSchema.identifier, simpleNodeSchema); } @@ -77,67 +68,3 @@ export function toViewCompatibilityTreeSchema( definitions, }; } - -/** - * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. - * - * @remarks Caches the result on the input schema for future calls. - */ -function copyNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { - const kind = schema.kind; - switch (kind) { - case NodeKind.Leaf: - return copyLeafSchema(schema); - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return copySchemaWithAllowedTypes(schema); - case NodeKind.Object: - return copyObjectSchema(schema); - default: - unreachableCase(kind); - } -} - -function copyLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; -} - -function copySchemaWithAllowedTypes( - schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, -): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { - return { - kind: schema.kind, - allowedTypesIdentifiers: schema.allowedTypesIdentifiers, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; -} - -function copyObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { - const fields: Map = new Map(); - for (const [propertyKey, field] of schema.fields) { - // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - fields.set(propertyKey, { - kind: field.kind, - allowedTypesIdentifiers: field.allowedTypesIdentifiers, - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, - storedKey: field.storedKey, - stagedSchemaUpgrades: field.stagedSchemaUpgrades, - }); - } - - return { - kind: NodeKind.Object, - fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; -} From 25eef862ab0489ebaa34f68f6eb8adad452f25f2 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 23 Oct 2025 13:39:56 -0700 Subject: [PATCH 04/47] Removed persistedMetadata and metadata from Node schemas copied in ViewCompatibility mode. --- .../api/viewSchemaToSimpleSchema.ts | 96 ++++++++++++------- .../viewSchemaToViewCompatibilitySchema.ts | 4 +- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index d43366071219..674ddfff5fb9 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -60,7 +60,7 @@ export function toSimpleTreeSchema( 0xb60 /* Invalid schema */, ); const outSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyKind.SimpleSchema) + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.SimpleSchema) : nodeSchema; definitions.set(nodeSchema.identifier, outSchema); }, @@ -82,7 +82,7 @@ export function toSimpleTreeSchema( /** * Specifies which fields are included when copying a Simple Schema. */ -export enum SimpleSchemaCopyKind { +export enum SimpleSchemaCopyMode { // TODO: Rename SimpleSchema, ViewCompatibilitySchema, @@ -95,54 +95,84 @@ export enum SimpleSchemaCopyKind { */ export function copySimpleNodeSchema( schema: SimpleNodeSchema, - copyKind: SimpleSchemaCopyKind, + copyMode: SimpleSchemaCopyMode, ): SimpleNodeSchema { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: - return copySimpleLeafSchema(schema); + return copySimpleLeafSchema(schema, copyMode); case NodeKind.Array: case NodeKind.Map: case NodeKind.Record: - return copySimpleSchemaWithAllowedTypes(schema); + return copySimpleSchemaWithAllowedTypes(schema, copyMode); case NodeKind.Object: - return copySimpleObjectSchema(schema, copyKind); + return copySimpleObjectSchema(schema, copyMode); default: unreachableCase(kind); } } -function copySimpleLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; +function copySimpleLeafSchema( + schema: SimpleLeafNodeSchema, + copyMode: SimpleSchemaCopyMode, +): SimpleLeafNodeSchema { + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + }; + default: + unreachableCase(copyMode); + } } function copySimpleSchemaWithAllowedTypes( schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, + copyMode: SimpleSchemaCopyMode, ): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { - return { - kind: schema.kind, - allowedTypesIdentifiers: schema.allowedTypesIdentifiers, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + return { + kind: schema.kind, + allowedTypesIdentifiers: schema.allowedTypesIdentifiers, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + return { + kind: schema.kind, + allowedTypesIdentifiers: schema.allowedTypesIdentifiers, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + }; + default: + unreachableCase(copyMode); + } } function copySimpleObjectSchema( schema: SimpleObjectNodeSchema, - copyKind: SimpleSchemaCopyKind, + copyMode: SimpleSchemaCopyMode, ): SimpleObjectNodeSchema { const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. let simpleField: SimpleObjectFieldSchema | undefined; - switch (copyKind) { - case SimpleSchemaCopyKind.SimpleSchema: + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: simpleField = { kind: field.kind, allowedTypesIdentifiers: field.allowedTypesIdentifiers, @@ -152,19 +182,20 @@ function copySimpleObjectSchema( }; break; - case SimpleSchemaCopyKind.ViewCompatibilitySchema: + case SimpleSchemaCopyMode.ViewCompatibilitySchema: simpleField = { kind: field.kind, allowedTypesIdentifiers: field.allowedTypesIdentifiers, - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, storedKey: field.storedKey, stagedSchemaUpgrades: field.stagedSchemaUpgrades, }; break; default: - unreachableCase(copyKind); + unreachableCase(copyMode); } fields.set(propertyKey, simpleField); @@ -172,8 +203,8 @@ function copySimpleObjectSchema( let simpleObject: SimpleObjectNodeSchema | undefined; - switch (copyKind) { - case SimpleSchemaCopyKind.SimpleSchema: + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: simpleObject = { kind: NodeKind.Object, fields, @@ -182,18 +213,19 @@ function copySimpleObjectSchema( }; break; - case SimpleSchemaCopyKind.ViewCompatibilitySchema: + case SimpleSchemaCopyMode.ViewCompatibilitySchema: simpleObject = { kind: NodeKind.Object, fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, allowUnknownOptionalFields: schema.allowUnknownOptionalFields, }; break; default: - unreachableCase(copyKind); + unreachableCase(copyMode); } return simpleObject; diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts index 09acd4d697e3..894b0ff6476b 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -13,7 +13,7 @@ import { ObjectNodeSchema, RecordNodeSchema, } from "../node-kinds/index.js"; -import { copySimpleNodeSchema, SimpleSchemaCopyKind } from "./viewSchemaToSimpleSchema.js"; +import { copySimpleNodeSchema, SimpleSchemaCopyMode } from "./viewSchemaToSimpleSchema.js"; /** * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. @@ -50,7 +50,7 @@ export function toViewCompatibilityTreeSchema( // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. const simpleNodeSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyKind.ViewCompatibilitySchema) + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.ViewCompatibilitySchema) : nodeSchema; definitions.set(nodeSchema.identifier, simpleNodeSchema); } From d80bee6910f07d17cf2573fb38b1ed902199f9ad Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 23 Oct 2025 16:21:46 -0700 Subject: [PATCH 05/47] Minor: better typing in copy methods. --- .../dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 674ddfff5fb9..deced9ee939a 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -169,7 +169,7 @@ function copySimpleObjectSchema( const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - let simpleField: SimpleObjectFieldSchema | undefined; + let simpleField: SimpleObjectFieldSchema; switch (copyMode) { case SimpleSchemaCopyMode.SimpleSchema: @@ -201,7 +201,7 @@ function copySimpleObjectSchema( fields.set(propertyKey, simpleField); } - let simpleObject: SimpleObjectNodeSchema | undefined; + let simpleObject: SimpleObjectNodeSchema; switch (copyMode) { case SimpleSchemaCopyMode.SimpleSchema: From 15fa315a664a7e430bd5b7b2dac0091082879d1e Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 23 Oct 2025 17:30:38 -0700 Subject: [PATCH 06/47] Added snapshot tests. --- .../viewSchemaToViewCompatibilitySchema.ts | 4 - .../simple-tree/api/getSimpleSchema.spec.ts | 76 +++++++++++++++++++ ...ith JSON serialization - Array schema.json | 9 +++ ...ith JSON serialization - Field Schema.json | 11 +++ ...a with JSON serialization - Leaf node.json | 9 +++ ... with JSON serialization - Map schema.json | 9 +++ ...Object schema including a union field.json | 9 +++ ... schema including an identifier field.json | 9 +++ ...th JSON serialization - Object schema.json | 9 +++ ...th JSON serialization - Record schema.json | 9 +++ ...rialization - Recursive object schema.json | 9 +++ ... with JSON serialization - Union root.json | 9 +++ 12 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts index 894b0ff6476b..fe4b2db8199f 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -44,10 +44,6 @@ export function toViewCompatibilityTreeSchema( 0xb60 /* Invalid schema */, ); - // TODO: Do we need walkNodeSchema to recurse into fields in this context? - // Probably not: we are walking all schema definitions unconditionally without worrying about tree order. - // It is probably enough to just walk the fields of object schema here. - // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. const simpleNodeSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.ViewCompatibilitySchema) diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index b2b5a0755ac6..e7b6bacfc3dc 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -12,6 +12,7 @@ import { stringSchema, toViewCompatibilityTreeSchema, TreeViewConfigurationAlpha, + type ImplicitFieldSchema, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -21,6 +22,8 @@ import { import { ValueSchema } from "../../../core/index.js"; // eslint-disable-next-line import/no-internal-modules import { toSimpleTreeSchema } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; +import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; +import type { JsonCompatible } from "../../../util/index.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -36,7 +39,20 @@ const simpleNumber: SimpleLeafNodeSchema = { persistedMetadata: undefined, }; +function toSerializableCompatibilitySchema( + treeView: TreeViewConfigurationAlpha, +): JsonCompatible { + const serializableSchema = toViewCompatibilityTreeSchema( + treeView, + // Copying is required for JSON serialization to avoid circular references in metadata/etc. + true, + ) as unknown as JsonCompatible; + return serializableSchema; +} + describe("getSimpleSchema", () => { + useSnapshotDirectory("get-simple-schema"); + describe("non-copying", () => { const Schema = stringSchema; const root = SchemaFactoryAlpha.optional(Schema); @@ -105,6 +121,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Field Schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Leaf node", () => { @@ -143,6 +165,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Leaf node", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Union root", () => { @@ -193,6 +221,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Union root", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Array schema", () => { @@ -254,6 +288,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Array schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Map schema", () => { @@ -315,6 +355,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Map schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Record schema", () => { @@ -376,6 +422,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Record schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Object schema", () => { @@ -487,6 +539,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Object schema including an identifier field", () => { @@ -574,6 +632,12 @@ describe("getSimpleSchema", () => { const actual = toViewCompatibilityTreeSchema(treeView, true); assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Object schema including a union field", () => { @@ -672,6 +736,12 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); describe("Recursive object schema", () => { @@ -765,5 +835,11 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); + + it("toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); }); diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json new file mode 100644 index 000000000000..c7f188a4293e --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json @@ -0,0 +1,11 @@ +{ + "root": { + "kind": 0, + "allowedTypesIdentifiers": {}, + "metadata": { + "description": "An optional string." + }, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json new file mode 100644 index 000000000000..cb51003abbb9 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json @@ -0,0 +1,9 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": {}, + "metadata": {}, + "stagedSchemaUpgrades": [] + }, + "definitions": {} +} \ No newline at end of file From a239d8880eb4051aa3d556168462aee040b68595 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 24 Oct 2025 14:06:17 -0700 Subject: [PATCH 07/47] Got snapshotting working correctly. It currently involves a lot of careful serialization of each type, though I think this safer and more durable than a more generic approach. --- .../dds/tree/src/simple-tree/api/index.ts | 1 + .../api/serializableCompatibilitySchema.ts | 177 ++++++++++++++++++ packages/dds/tree/src/simple-tree/index.ts | 5 +- .../simple-tree/api/getSimpleSchema.spec.ts | 34 ++-- ...ith JSON serialization - Array schema.json | 9 - ...ith JSON serialization - Field Schema.json | 11 -- ...a with JSON serialization - Leaf node.json | 9 - ... with JSON serialization - Map schema.json | 9 - ...Object schema including a union field.json | 9 - ... schema including an identifier field.json | 9 - ...th JSON serialization - Object schema.json | 9 - ...th JSON serialization - Record schema.json | 9 - ...rialization - Recursive object schema.json | 9 - ... with JSON serialization - Union root.json | 9 - ...w compatibility schema - Array schema.json | 21 +++ ...w compatibility schema - Field Schema.json | 15 ++ ...view compatibility schema - Leaf node.json | 15 ++ ...iew compatibility schema - Map schema.json | 21 +++ ...Object schema including a union field.json | 34 ++++ ... schema including an identifier field.json | 29 +++ ... compatibility schema - Object schema.json | 41 ++++ ... compatibility schema - Record schema.json | 21 +++ ...lity schema - Recursive object schema.json | 30 +++ ...iew compatibility schema - Union root.json | 20 ++ 24 files changed, 440 insertions(+), 116 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 63a9e4a0e32e..55e1ee27d04c 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -162,3 +162,4 @@ export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; export { toSimpleTreeSchema } from "./viewSchemaToSimpleSchema.js"; export { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; export type { TreeChangeEvents } from "./treeChangeEvents.js"; +export { toSerializableCompatibilitySchema } from "./serializableCompatibilitySchema.js"; diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts new file mode 100644 index 000000000000..b8dad4ce888b --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -0,0 +1,177 @@ +import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; +import type { ImplicitFieldSchema } from "../fieldSchema.js"; +import type { TreeViewConfigurationAlpha } from "./configuration.js"; +import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; +import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; +import type { + SimpleArrayNodeSchema, + SimpleFieldSchema, + SimpleLeafNodeSchema, + SimpleMapNodeSchema, + SimpleNodeSchema, + SimpleObjectFieldSchema, + SimpleObjectNodeSchema, + SimpleRecordNodeSchema, +} from "../simpleSchema.js"; +import { NodeKind } from "../core/index.js"; + +/** + * Convert a view schema to a serializable format for compatibility testing. + * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. + * It is not possible to reconstruct a full view schema from the serialized format. + * @param treeView - The tree view schema to convert. + * @returns A serializable representation of the view schema. + */ +export function toSerializableCompatibilitySchema( + treeView: TreeViewConfigurationAlpha, +): JsonCompatible { + const simpleSchema = toViewCompatibilityTreeSchema( + treeView, + // Copying is required for JSON serialization to avoid potential circular references in metadata/etc., as well as to ensure + // that the Map type serialization approach below does not modify the original schema. + true, + ); + + // Convert types to serializable forms + const serializableDefinitions = new Map(); + + for (const [identifier, schema] of simpleSchema.definitions) { + const serializableDefinition = nodeSchemaToSerializable(schema); + serializableDefinitions.set(identifier, serializableDefinition); + } + + const serializableSchema = { + root: toSerializableField(simpleSchema.root), + definitions: mapToRecord(serializableDefinitions), + } as unknown as JsonCompatible; + + return serializableSchema; +} + +/** + * Converts a node schema to a serializable object. + * @param schema - The node schema to convert. + * @returns A serializable representation of the node schema. + */ +function nodeSchemaToSerializable(schema: SimpleNodeSchema): JsonCompatibleObject { + const kind = schema.kind; + switch (kind) { + case NodeKind.Leaf: + return leafNodeToSerializable(schema); + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return containerNodeToSerializable(schema); + case NodeKind.Object: + return objectNodeToSerializable(schema); + default: { + unreachableCase(kind); + } + } +} + +/** + * Converts a leaf node schema to a serializable object. + * @param schema - The leaf node schema to convert. + * @returns A serializable representation of the leaf node schema. + */ +function leafNodeToSerializable(schema: SimpleLeafNodeSchema): JsonCompatibleObject { + return { + kind: schema.kind, + leafKind: schema.leafKind, + }; +} + +/** + * Converts a container node schema to a serializable object. + * @param schema - The container node schema to convert. + * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different + * container kinds. + */ +function containerNodeToSerializable( + schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, +): JsonCompatibleObject { + return { + kind: schema.kind, + allowedTypesIdentifiers: setToArray(schema.allowedTypesIdentifiers), + }; +} + +/** + * Converts an object node schema to a serializable object. + * @param schema - The object node schema to convert. + * @returns A serializable representation of the object node schema. + */ +function objectNodeToSerializable(schema: SimpleObjectNodeSchema): JsonCompatibleObject { + const serializableFields: Record = {}; + for (const [fieldKey, fieldSchema] of schema.fields) { + serializableFields[fieldKey] = toSerializableObjectField(fieldSchema); + } + + return { + kind: schema.kind, + fields: serializableFields, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; +} + +/** + * Converts an object field schema to a serializable object. + * @param fieldSchema - The object field schema to convert. + * @returns A serializable representation of the object field schema. + */ +function toSerializableObjectField( + fieldSchema: SimpleObjectFieldSchema, +): JsonCompatibleObject { + const serializableField = toSerializableField(fieldSchema); + serializableField.storedKey = fieldSchema.storedKey; + return serializableField; +} + +/** + * Converts a field schema to a serializable object. + * @param fieldSchema - The field schema to convert. + * @returns A serializable representation of the field schema. + */ +function toSerializableField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { + return { + kind: fieldSchema.kind, + allowedTypesIdentifiers: setToArray(fieldSchema.allowedTypesIdentifiers), + + // We can't serialize SchemaUpgrades, but the presence of staged upgrades is useful information for compatibility testing. + hasStagedSchemaUpgrades: + fieldSchema.stagedSchemaUpgrades !== undefined && + fieldSchema.stagedSchemaUpgrades.length > 0, + }; +} + +/** + * Convert a Map to a Record for serialization. + * @remarks This is needed because the JSON serializer does not support Maps. + * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output + * should only be used in snapshots or debugging scenarios. + * @param map - The Map to convert. + * @returns A Record with the contents of the Map. + */ +function mapToRecord(map: ReadonlyMap): Record { + const resultObject: Record = {}; + const sortedKeys = Array.from(map.keys()).sort(); + + for (const key of sortedKeys) { + const value = + map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); + resultObject[`${key}`] = value; + } + + return resultObject; +} + +/** + * Convert a Set to a sorted Array for serialization. + * @remarks This is needed because the JSON serializer does not support Sets. + * @param set - The set to convert. + * @returns An Array with the contents of the Set sorted lexicographically. + */ +function setToArray(set: ReadonlySet): Value[] { + return Array.from(set).sort(); +} diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 61358fb9b8fa..fadbe4d77e14 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -274,4 +274,7 @@ export { nullSchema, } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; -export { toViewCompatibilityTreeSchema } from "./api/index.js"; +export { + toViewCompatibilityTreeSchema, + toSerializableCompatibilitySchema, +} from "./api/index.js"; diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index e7b6bacfc3dc..b7eb83b465a7 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -10,9 +10,9 @@ import { SchemaFactory, SchemaFactoryAlpha, stringSchema, + toSerializableCompatibilitySchema, toViewCompatibilityTreeSchema, TreeViewConfigurationAlpha, - type ImplicitFieldSchema, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -23,7 +23,6 @@ import { ValueSchema } from "../../../core/index.js"; // eslint-disable-next-line import/no-internal-modules import { toSimpleTreeSchema } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; -import type { JsonCompatible } from "../../../util/index.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -39,17 +38,6 @@ const simpleNumber: SimpleLeafNodeSchema = { persistedMetadata: undefined, }; -function toSerializableCompatibilitySchema( - treeView: TreeViewConfigurationAlpha, -): JsonCompatible { - const serializableSchema = toViewCompatibilityTreeSchema( - treeView, - // Copying is required for JSON serialization to avoid circular references in metadata/etc. - true, - ) as unknown as JsonCompatible; - return serializableSchema; -} - describe("getSimpleSchema", () => { useSnapshotDirectory("get-simple-schema"); @@ -122,7 +110,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Field Schema", () => { + it("view compatibility schema - Field Schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -166,7 +154,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Leaf node", () => { + it("view compatibility schema - Leaf node", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -222,7 +210,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Union root", () => { + it("view compatibility schema - Union root", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -289,7 +277,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Array schema", () => { + it("view compatibility schema - Array schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -356,7 +344,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Map schema", () => { + it("view compatibility schema - Map schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -423,7 +411,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Record schema", () => { + it("view compatibility schema - Record schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -540,7 +528,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Object schema", () => { + it("view compatibility schema - Object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -633,7 +621,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field", () => { + it("view compatibility schema - Object schema including an identifier field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -737,7 +725,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field", () => { + it("view compatibility schema - Object schema including a union field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); @@ -836,7 +824,7 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema", () => { + it("view compatibility schema - Recursive object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Array schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json deleted file mode 100644 index c7f188a4293e..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Field Schema.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "root": { - "kind": 0, - "allowedTypesIdentifiers": {}, - "metadata": { - "description": "An optional string." - }, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Leaf node.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Map schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including a union field.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema including an identifier field.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Object schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Record schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Recursive object schema.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json deleted file mode 100644 index cb51003abbb9..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/toViewCompatibilityTreeSchema with JSON serialization - Union root.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesIdentifiers": {}, - "metadata": {}, - "stagedSchemaUpgrades": [] - }, - "definitions": {} -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json new file mode 100644 index 000000000000..921ada79ba2b --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json @@ -0,0 +1,21 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.array" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.array": { + "kind": 1, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ] + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json new file mode 100644 index 000000000000..48e3e0243dcd --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json @@ -0,0 +1,15 @@ +{ + "root": { + "kind": 0, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json new file mode 100644 index 000000000000..c22dcc3e517d --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json @@ -0,0 +1,15 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json new file mode 100644 index 000000000000..c693f05ea2a1 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json @@ -0,0 +1,21 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.map" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.map": { + "kind": 0, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ] + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json new file mode 100644 index 000000000000..b1d0cf45ee3d --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json @@ -0,0 +1,34 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.object" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "foo": { + "kind": 1, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false, + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json new file mode 100644 index 000000000000..205c107e1713 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json @@ -0,0 +1,29 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.object" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "id": { + "kind": 2, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false, + "storedKey": "id" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json new file mode 100644 index 000000000000..b97cc5763501 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json @@ -0,0 +1,41 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.object" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.number" + ], + "hasStagedSchemaUpgrades": false, + "storedKey": "foo" + }, + "bar": { + "kind": 1, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false, + "storedKey": "bar" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json new file mode 100644 index 000000000000..b1d0c0d72715 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json @@ -0,0 +1,21 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.record" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.record": { + "kind": 4, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ] + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json new file mode 100644 index 000000000000..43025e2a5112 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json @@ -0,0 +1,30 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "test.recursive-object" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.recursive-object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string", + "test.recursive-object" + ], + "hasStagedSchemaUpgrades": false, + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json new file mode 100644 index 000000000000..2d6ae2f7b54b --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json @@ -0,0 +1,20 @@ +{ + "root": { + "kind": 1, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.number", + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": false + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file From b168cd457ef0eb51091a27f7c25d1d6616e7907f Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 24 Oct 2025 14:29:04 -0700 Subject: [PATCH 08/47] Cleanup. --- .../api/serializableCompatibilitySchema.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts index b8dad4ce888b..adfe4d48e802 100644 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -1,6 +1,5 @@ import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; -import type { ImplicitFieldSchema } from "../fieldSchema.js"; -import type { TreeViewConfigurationAlpha } from "./configuration.js"; +import type { TreeSchema } from "./configuration.js"; import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; import type { @@ -19,16 +18,13 @@ import { NodeKind } from "../core/index.js"; * Convert a view schema to a serializable format for compatibility testing. * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. * It is not possible to reconstruct a full view schema from the serialized format. - * @param treeView - The tree view schema to convert. + * @param treeSchema - The tree schema to convert. * @returns A serializable representation of the view schema. */ -export function toSerializableCompatibilitySchema( - treeView: TreeViewConfigurationAlpha, -): JsonCompatible { +export function toSerializableCompatibilitySchema(treeSchema: TreeSchema): JsonCompatible { const simpleSchema = toViewCompatibilityTreeSchema( - treeView, - // Copying is required for JSON serialization to avoid potential circular references in metadata/etc., as well as to ensure - // that the Map type serialization approach below does not modify the original schema. + treeSchema, + // Copying strips out references to metadata and other non-essential information. true, ); From 95c255a81bf00dbd97937c007d7d72f16cffd739 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 24 Oct 2025 15:49:50 -0700 Subject: [PATCH 09/47] Added a test around hasStagedSchemaUpgrades. --- .../api/serializableCompatibilitySchema.ts | 2 +- .../getViewCompatibilityTreeSchema.spec.ts | 48 +++++++++++++++++++ ...lity schema - hasStagedSchemaUpgrades.json | 15 ++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts create mode 100644 packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts index adfe4d48e802..a36ad1dd2f2c 100644 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -15,7 +15,7 @@ import type { import { NodeKind } from "../core/index.js"; /** - * Convert a view schema to a serializable format for compatibility testing. + * Converts a view schema to a serializable format for compatibility testing. * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. * It is not possible to reconstruct a full view schema from the serialized format. * @param treeSchema - The tree schema to convert. diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts new file mode 100644 index 000000000000..cab1e9096d66 --- /dev/null +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -0,0 +1,48 @@ +import { + FieldKind, + SchemaFactoryAlpha, + stringSchema, + toSerializableCompatibilitySchema, + toViewCompatibilityTreeSchema, + TreeViewConfigurationAlpha, + type SimpleTreeSchema, +} from "../../../simple-tree/index.js"; +import { strict as assert } from "node:assert"; +import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; + +describe("getViewCompatibilityTreeSchema", () => { + useSnapshotDirectory("get-view-compatibility-tree-schema"); + + describe("With staged schema upgrades", () => { + const leafSchema = stringSchema; + const schemaFactory = new SchemaFactoryAlpha("test"); + const root = schemaFactory.optional( + schemaFactory.types([schemaFactory.staged(leafSchema)]), + ); + + const stagedSchemaUpgrades = root.stagedSchemaUpgrades; + + it("Should preserve staged schema upgrades when converting to SimpleTreeSchema", () => { + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + allowedTypesIdentifiers: new Set([leafSchema.identifier]), + metadata: {}, + persistedMetadata: undefined, + stagedSchemaUpgrades, + }, + definitions: new Map([[leafSchema.identifier, leafSchema]]), + }; + + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + assert.deepEqual(actual.root.stagedSchemaUpgrades, expected.root.stagedSchemaUpgrades); + }); + + it("view compatibility schema - hasStagedSchemaUpgrades", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); + }); +}); diff --git a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json new file mode 100644 index 000000000000..8cdf4d83e361 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json @@ -0,0 +1,15 @@ +{ + "root": { + "kind": 0, + "allowedTypesIdentifiers": [ + "com.fluidframework.leaf.string" + ], + "hasStagedSchemaUpgrades": true + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file From 7559289ca970d6176b29945e3b50b21dc8d6f63e Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 28 Oct 2025 14:02:00 -0700 Subject: [PATCH 10/47] Added staged allowed type info to SimpleSchema. Started replacing allowedTypesIdentifiers with allowedTypesInfo. --- .../dds/tree/api-report/tree.alpha.api.md | 30 ++++-- packages/dds/tree/src/index.ts | 1 + .../dds/tree/src/shared-tree/sharedTree.ts | 16 ++++ .../api/serializableCompatibilitySchema.ts | 16 ++-- .../api/viewSchemaToSimpleSchema.ts | 6 +- .../viewSchemaToViewCompatibilitySchema.ts | 2 +- .../dds/tree/src/simple-tree/fieldSchema.ts | 31 +++++-- packages/dds/tree/src/simple-tree/index.ts | 1 + .../simple-tree/node-kinds/array/arrayNode.ts | 14 +++ .../src/simple-tree/node-kinds/map/mapNode.ts | 14 +++ .../node-kinds/record/recordNode.ts | 14 +++ .../dds/tree/src/simple-tree/simpleSchema.ts | 37 +++++++- .../simple-tree/api/getSimpleSchema.spec.ts | 93 ++++++++++++++++--- .../getViewCompatibilityTreeSchema.spec.ts | 11 +-- .../api/simpleSchemaToJsonSchema.spec.ts | 57 ++++++++++++ ...w compatibility schema - Array schema.json | 10 +- ...w compatibility schema - Field Schema.json | 10 +- ...view compatibility schema - Leaf node.json | 10 +- ...iew compatibility schema - Map schema.json | 10 +- ...Object schema including a union field.json | 23 +++-- ... schema including an identifier field.json | 18 ++-- ... compatibility schema - Object schema.json | 26 ++++-- ... compatibility schema - Record schema.json | 10 +- ...lity schema - Recursive object schema.json | 23 +++-- ...iew compatibility schema - Union root.json | 15 ++- ...ity schema - allowedTypesIdentifiers.json} | 10 +- 26 files changed, 393 insertions(+), 115 deletions(-) rename packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/{view compatibility schema - hasStagedSchemaUpgrades.json => view compatibility schema - allowedTypesIdentifiers.json} (51%) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 5cefeb2e47fd..0ca84653f09c 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -15,6 +15,11 @@ export function adaptEnum, true, Record, undefined>; }[keyof TEnum]>; }; +// @alpha @sealed +export interface AllowedTypeInfo { + readonly isStaged?: boolean; +} + // @alpha @input export interface AllowedTypeMetadata { readonly custom?: unknown; @@ -267,9 +272,11 @@ export class FieldSchemaAlpha; // (undocumented) + get allowedTypesInfo(): Map; + // (undocumented) get persistedMetadata(): JsonCompatibleReadOnlyObject | undefined; // (undocumented) - get stagedSchemaUpgrades(): SchemaUpgrade[]; + get readonlyAllowedTypesIdentifiers(): ReadonlySet; } // @alpha @sealed @system @@ -1001,15 +1008,16 @@ export type SharedTreeOptionsBeta = ForestOptions; // @alpha @sealed export interface SimpleArrayNodeSchema extends SimpleNodeSchemaBaseAlpha { readonly allowedTypesIdentifiers: ReadonlySet; + readonly allowedTypesInfo: ReadonlyMap; } // @alpha @sealed export interface SimpleFieldSchema { readonly allowedTypesIdentifiers: ReadonlySet; + readonly allowedTypesInfo: ReadonlyMap; readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly stagedSchemaUpgrades?: SchemaUpgrade[]; } // @alpha @sealed @@ -1020,6 +1028,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { readonly allowedTypesIdentifiers: ReadonlySet; + readonly allowedTypesInfo: ReadonlyMap; } // @alpha @@ -1050,6 +1059,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { readonly allowedTypesIdentifiers: ReadonlySet; + readonly allowedTypesInfo: ReadonlyMap; } // @alpha @@ -1100,18 +1110,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 56a5c8e34da3..4fa6c43efbe6 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -277,6 +277,7 @@ export { type TreeParsingOptions, type SchemaFactory_base, type NumberKeys, + type AllowedTypeInfo, } from "./simple-tree/index.js"; export { SharedTree, diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index d5167388efd2..70cb9ccf56ee 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -42,6 +42,7 @@ import { type TreeStoredSchema, TreeStoredSchemaRepository, type TreeStoredSchemaSubscription, + type TreeTypeSet, getCodecTreeForDetachedFieldIndexFormat, makeDetachedFieldIndex, moveToDetachedField, @@ -98,6 +99,7 @@ import { FieldKind, type ITreeAlpha, type SimpleObjectFieldSchema, + type AllowedTypeInfo, } from "../simple-tree/index.js"; import { SchematizingSimpleTreeView } from "./schematizingTreeView.js"; @@ -950,6 +952,16 @@ export const defaultSharedTreeOptions: Required = { shouldEncodeIncrementally: defaultIncrementalEncodingPolicy, }; +function buildAllowedTypesInfo(types: TreeTypeSet): ReadonlyMap { + const allowedTypesInfo = new Map(); + for (const type of types) { + allowedTypesInfo.set(type, { + isStaged: false, + }); + } + return allowedTypesInfo; +} + function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFieldSchema { let kind: FieldKind; switch (schema.kind) { @@ -971,7 +983,9 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie } return { kind, + // TODO: Refactor allowedTypesIdentifiers: schema.types, + allowedTypesInfo: buildAllowedTypesInfo(schema.types), metadata: {}, persistedMetadata: schema.persistedMetadata, }; @@ -988,6 +1002,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS return { kind: NodeKind.Array, allowedTypesIdentifiers: arrayTypes, + allowedTypesInfo: buildAllowedTypesInfo(arrayTypes), metadata: {}, persistedMetadata: schema.metadata, }; @@ -1007,6 +1022,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS return { kind: NodeKind.Map, allowedTypesIdentifiers: schema.mapFields.types, + allowedTypesInfo: buildAllowedTypesInfo(schema.mapFields.types), metadata: {}, persistedMetadata: schema.metadata, }; diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts index a36ad1dd2f2c..37f9082dc0a3 100644 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -3,6 +3,7 @@ import type { TreeSchema } from "./configuration.js"; import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; import type { + AllowedTypeInfo, SimpleArrayNodeSchema, SimpleFieldSchema, SimpleLeafNodeSchema, @@ -130,14 +131,17 @@ function toSerializableObjectField( * @returns A serializable representation of the field schema. */ function toSerializableField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { + const allowedTypesInfo: JsonCompatibleObject[] = []; + for (const [identifier, typeInfo] of fieldSchema.allowedTypesInfo ?? + new Map()) { + allowedTypesInfo.push({ + identifier, + isReadOnly: typeInfo.isStaged, + }); + } return { kind: fieldSchema.kind, - allowedTypesIdentifiers: setToArray(fieldSchema.allowedTypesIdentifiers), - - // We can't serialize SchemaUpgrades, but the presence of staged upgrades is useful information for compatibility testing. - hasStagedSchemaUpgrades: - fieldSchema.stagedSchemaUpgrades !== undefined && - fieldSchema.stagedSchemaUpgrades.length > 0, + allowedTypesInfo, }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index deced9ee939a..8a5bc1b60d53 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -70,6 +70,7 @@ export function toSimpleTreeSchema( root: copySchemaObjects ? ({ allowedTypesIdentifiers: normalizedSchema.allowedTypesIdentifiers, + allowedTypesInfo: normalizedSchema.allowedTypesInfo, kind: normalizedSchema.kind, metadata: normalizedSchema.metadata, persistedMetadata: normalizedSchema.persistedMetadata, @@ -146,6 +147,7 @@ function copySimpleSchemaWithAllowedTypes( return { kind: schema.kind, allowedTypesIdentifiers: schema.allowedTypesIdentifiers, + allowedTypesInfo: schema.allowedTypesInfo, metadata: schema.metadata, persistedMetadata: schema.persistedMetadata, }; @@ -153,6 +155,7 @@ function copySimpleSchemaWithAllowedTypes( return { kind: schema.kind, allowedTypesIdentifiers: schema.allowedTypesIdentifiers, + allowedTypesInfo: schema.allowedTypesInfo, // Don't include metadata or persistedMetadata in view compatibility schema. metadata: {}, persistedMetadata: undefined, @@ -176,6 +179,7 @@ function copySimpleObjectSchema( simpleField = { kind: field.kind, allowedTypesIdentifiers: field.allowedTypesIdentifiers, + allowedTypesInfo: field.allowedTypesInfo, metadata: field.metadata, persistedMetadata: field.persistedMetadata, storedKey: field.storedKey, @@ -186,11 +190,11 @@ function copySimpleObjectSchema( simpleField = { kind: field.kind, allowedTypesIdentifiers: field.allowedTypesIdentifiers, + allowedTypesInfo: field.allowedTypesInfo, // Don't include metadata or persistedMetadata in view compatibility schema. metadata: {}, persistedMetadata: undefined, storedKey: field.storedKey, - stagedSchemaUpgrades: field.stagedSchemaUpgrades, }; break; diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts index fe4b2db8199f..afbf9ca28376 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -56,9 +56,9 @@ export function toViewCompatibilityTreeSchema( ? { kind: schema.root.kind, allowedTypesIdentifiers: schema.root.allowedTypesIdentifiers, + allowedTypesInfo: schema.root.allowedTypesInfo, metadata: schema.root.metadata, persistedMetadata: schema.root.persistedMetadata, - stagedSchemaUpgrades: schema.root.stagedSchemaUpgrades, } : schema.root, // TODO: Convert the root field definitions, diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index c02736b63c1d..bd5f3625f1f5 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -28,11 +28,10 @@ import type { TreeLeafValue, InsertableTreeNodeFromImplicitAllowedTypes, AllowedTypesFull, - SchemaUpgrade, } from "./core/index.js"; import { normalizeAllowedTypes } from "./core/index.js"; -import type { SimpleFieldSchema } from "./simpleSchema.js"; +import type { AllowedTypeInfo, SimpleFieldSchema } from "./simpleSchema.js"; import type { UnsafeUnknownSchema } from "./unsafeUnknownSchema.js"; import type { InsertableContent } from "./unhydratedFlexTreeFromInsertable.js"; @@ -413,16 +412,30 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } - public get stagedSchemaUpgrades(): SchemaUpgrade[] { + public get allowedTypesInfo(): Map { + const types = this.allowedTypesFull.evaluate().types; + const info = new Map(); + + for (const type of types) { + info.set(type.type.identifier, { + isStaged: type.metadata.stagedSchemaUpgrade !== undefined, + }); + } + + return info; + } + + // TODO: Refactor allowedTypesIdentifiers and readonlyAllowedTypesIdentifiers so that there is an intermediate type containing + // the identifier and any other information needed for compatibility checks. + public get readonlyAllowedTypesIdentifiers(): ReadonlySet { const annotatedAllowedTypes = this.allowedTypesFull.evaluate().types; - const upgrades: SchemaUpgrade[] = []; - for (const type of annotatedAllowedTypes) { - if (type.metadata.stagedSchemaUpgrade !== undefined) { - upgrades.push(type.metadata.stagedSchemaUpgrade); + const readonlyTypes = new Set(); + for (const allowedType of annotatedAllowedTypes) { + if (allowedType.metadata.stagedSchemaUpgrade !== undefined) { + readonlyTypes.add(allowedType.type.identifier); } } - - return upgrades; + return readonlyTypes; } protected constructor( diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index fadbe4d77e14..0f83afc9ab0f 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -193,6 +193,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, + AllowedTypeInfo, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index 4573827927a8..217c427a28cb 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -68,6 +68,7 @@ import type { import { brand, type JsonCompatibleReadOnlyObject } from "../../../util/index.js"; import { nullSchema } from "../../leafNodeSchema.js"; import { arrayNodeStoredSchema } from "../../toStoredSchema.js"; +import type { AllowedTypeInfo } from "../../simpleSchema.js"; /** * A covariant base type for {@link (TreeArrayNode:interface)}. @@ -1168,6 +1169,15 @@ export function arraySchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); + const lazyAllowedTypesInfo = new Lazy(() => { + const map = new Map(); + for (const type of normalizedTypes.evaluate()) { + map.set(type.identifier, { + isStaged: false, + }); + } + return map; + }); let privateData: TreeNodeSchemaPrivateData | undefined; @@ -1206,6 +1216,10 @@ export function arraySchema< return lazyAllowedTypesIdentifiers.value; } + public static get allowedTypesInfo(): ReadonlyMap { + return lazyAllowedTypesInfo.value; + } + protected static override constructorCached: MostDerivedData | undefined = undefined; protected static override oneTimeSetup(): TreeNodeSchemaInitializedData { diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index 3129feb44657..ab0a5d37d4cd 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -66,6 +66,7 @@ import type { import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; +import type { AllowedTypeInfo } from "../../simpleSchema.js"; /** * A map of string keys to tree objects. @@ -275,6 +276,15 @@ export function mapSchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); + const lazyAllowedTypesInfo = new Lazy(() => { + const map = new Map(); + for (const type of normalizedTypes.evaluate()) { + map.set(type.identifier, { + isStaged: false, + }); + } + return map; + }); let privateData: TreeNodeSchemaPrivateData | undefined; const persistedMetadata = nodeOptions.persistedMetadata; @@ -303,6 +313,10 @@ export function mapSchema< return lazyAllowedTypesIdentifiers.value; } + public static get allowedTypesInfo(): ReadonlyMap { + return lazyAllowedTypesInfo.value; + } + protected static override constructorCached: MostDerivedData | undefined = undefined; protected static override oneTimeSetup(): TreeNodeSchemaInitializedData { diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index e257ddb56bfe..fb62d5b02d88 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -58,6 +58,7 @@ import { prepareForInsertion } from "../../prepareForInsertion.js"; import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; +import type { AllowedTypeInfo } from "../../simpleSchema.js"; /** * Create a proxy which implements the {@link TreeRecordNode} API. @@ -258,6 +259,15 @@ export function recordSchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); + const lazyAllowedTypesInfo = new Lazy(() => { + const map = new Map(); + for (const type of normalizedTypes.evaluate()) { + map.set(type.identifier, { + isStaged: false, + }); + } + return map; + }); let privateData: TreeNodeSchemaPrivateData | undefined; @@ -347,6 +357,10 @@ export function recordSchema< return lazyAllowedTypesIdentifiers.value; } + public static get allowedTypesInfo(): ReadonlyMap { + return lazyAllowedTypesInfo.value; + } + protected static override constructorCached: MostDerivedData | undefined = undefined; public static readonly identifier = identifier; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 62788e40c4df..862ac434e138 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -5,7 +5,7 @@ import type { ValueSchema } from "../core/index.js"; import type { JsonCompatibleReadOnlyObject } from "../util/index.js"; -import type { NodeKind, SchemaUpgrade, SimpleNodeSchemaBase } from "./core/index.js"; +import type { NodeKind, SimpleNodeSchemaBase } from "./core/index.js"; import type { FieldKind, FieldSchemaMetadata } from "./fieldSchema.js"; /* @@ -99,6 +99,11 @@ export interface SimpleArrayNodeSchema * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ readonly allowedTypesIdentifiers: ReadonlySet; + + /** + * Information about the allowed types. + */ + readonly allowedTypesInfo: ReadonlyMap; } /** @@ -116,6 +121,11 @@ export interface SimpleMapNodeSchema * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ readonly allowedTypesIdentifiers: ReadonlySet; + + /** + * Information about the allowed types. + */ + readonly allowedTypesInfo: ReadonlyMap; } /** @@ -133,6 +143,11 @@ export interface SimpleRecordNodeSchema * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ readonly allowedTypesIdentifiers: ReadonlySet; + + /** + * Information about the allowed types. + */ + readonly allowedTypesInfo: ReadonlyMap; } /** @@ -169,6 +184,20 @@ export type SimpleNodeSchema = | SimpleObjectNodeSchema | SimpleRecordNodeSchema; +/** + * Information about allowed types under a field. + * + * @alpha + * @sealed + */ +export interface AllowedTypeInfo { + /** + * True if there is an associated schema upgrade that makes this type read-only. + * Undefined if derived from a stored schema, which has no concept of staged allowed type upgrades. + */ + readonly isStaged?: boolean; +} + /** * A simple, shallow representation of a schema for a field. * @@ -194,11 +223,9 @@ export interface SimpleFieldSchema { readonly allowedTypesIdentifiers: ReadonlySet; /** - * Staged schema upgrades for this field indexed by the allowed type identifier they apply to. - * - * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + * Information about the allowed types under this field. */ - readonly stagedSchemaUpgrades?: SchemaUpgrade[]; + readonly allowedTypesInfo: ReadonlyMap; /** * {@inheritDoc FieldSchemaMetadata} diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index b7eb83b465a7..7d9fe1a35c1f 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -84,6 +84,7 @@ describe("getSimpleSchema", () => { kind: FieldKind.Optional, metadata: { description: "An optional string." }, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), persistedMetadata: undefined, }, definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), @@ -101,8 +102,8 @@ describe("getSimpleSchema", () => { kind: FieldKind.Optional, metadata: { description: "An optional string." }, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), persistedMetadata: undefined, - stagedSchemaUpgrades: [], }, definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), }; @@ -128,6 +129,7 @@ describe("getSimpleSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), persistedMetadata: undefined, }, definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), @@ -145,8 +147,8 @@ describe("getSimpleSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), persistedMetadata: undefined, - stagedSchemaUpgrades: [], }, definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), }; @@ -176,6 +178,10 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.number", "com.fluidframework.leaf.string", ]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, definitions: new Map([ ["com.fluidframework.leaf.number", simpleNumber], @@ -199,7 +205,10 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.number", "com.fluidframework.leaf.string", ]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, definitions: new Map([ ["com.fluidframework.leaf.number", simpleNumber], @@ -230,6 +239,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.array"]), + allowedTypesInfo: new Map([["test.array", { isStaged: false }]]), }, definitions: new Map([ [ @@ -237,6 +247,9 @@ describe("getSimpleSchema", () => { { kind: NodeKind.Array, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), metadata: {}, persistedMetadata: undefined, }, @@ -258,7 +271,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.array"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.array", { isStaged: false }]]), }, definitions: new Map([ [ @@ -266,6 +279,9 @@ describe("getSimpleSchema", () => { { kind: NodeKind.Array, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), metadata: {}, persistedMetadata: undefined, }, @@ -297,6 +313,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.map"]), + allowedTypesInfo: new Map([["test.map", { isStaged: false }]]), }, definitions: new Map([ [ @@ -306,6 +323,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, ], ["com.fluidframework.leaf.string", simpleString], @@ -325,7 +345,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.map"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.map", { isStaged: false }]]), }, definitions: new Map([ [ @@ -335,6 +355,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, ], ["com.fluidframework.leaf.string", simpleString], @@ -364,6 +387,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.record"]), + allowedTypesInfo: new Map([["test.record", { isStaged: false }]]), }, definitions: new Map([ [ @@ -373,6 +397,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, ], ["com.fluidframework.leaf.string", simpleString], @@ -392,7 +419,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.record"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.record", { isStaged: false }]]), }, definitions: new Map([ [ @@ -402,6 +429,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), }, ], ["com.fluidframework.leaf.string", simpleString], @@ -434,6 +464,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -450,6 +481,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ]), storedKey: "foo", }, ], @@ -460,6 +494,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "bar", }, ], @@ -484,7 +521,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -502,8 +539,10 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ]), storedKey: "foo", - stagedSchemaUpgrades: [], }, ], [ @@ -513,8 +552,10 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "bar", - stagedSchemaUpgrades: [], }, ], ]), @@ -550,6 +591,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -566,6 +608,9 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "id", }, ], @@ -588,7 +633,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -606,8 +651,10 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "id", - stagedSchemaUpgrades: [], }, ], ]), @@ -644,6 +691,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -663,6 +711,10 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.number", "com.fluidframework.leaf.string", ]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "foo", }, ], @@ -689,7 +741,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.object"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -710,8 +762,11 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.number", "com.fluidframework.leaf.string", ]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), storedKey: "foo", - stagedSchemaUpgrades: [], }, ], ]), @@ -747,6 +802,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.recursive-object"]), + allowedTypesInfo: new Map([["test.recursive-object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -766,6 +822,10 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.string", "test.recursive-object", ]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), storedKey: "foo", }, ], @@ -789,7 +849,7 @@ describe("getSimpleSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set(["test.recursive-object"]), - stagedSchemaUpgrades: [], + allowedTypesInfo: new Map([["test.recursive-object", { isStaged: false }]]), }, definitions: new Map([ [ @@ -810,8 +870,11 @@ describe("getSimpleSchema", () => { "com.fluidframework.leaf.string", "test.recursive-object", ]), + allowedTypesInfo: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), storedKey: "foo", - stagedSchemaUpgrades: [], }, ], ]), diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts index cab1e9096d66..4fc8d78e5719 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -17,29 +17,28 @@ describe("getViewCompatibilityTreeSchema", () => { const leafSchema = stringSchema; const schemaFactory = new SchemaFactoryAlpha("test"); const root = schemaFactory.optional( + // Staged allowed types are read-only for the sake of schema migrations schemaFactory.types([schemaFactory.staged(leafSchema)]), ); - const stagedSchemaUpgrades = root.stagedSchemaUpgrades; - - it("Should preserve staged schema upgrades when converting to SimpleTreeSchema", () => { + it("Should preserve isReadOnly when converting to SimpleTreeSchema", () => { const expected: SimpleTreeSchema = { root: { kind: FieldKind.Optional, + allowedTypesInfo: new Map([[leafSchema.identifier, { isStaged: true }]]), allowedTypesIdentifiers: new Set([leafSchema.identifier]), metadata: {}, persistedMetadata: undefined, - stagedSchemaUpgrades, }, definitions: new Map([[leafSchema.identifier, leafSchema]]), }; const treeView = new TreeViewConfigurationAlpha({ schema: root }); const actual = toViewCompatibilityTreeSchema(treeView, true); - assert.deepEqual(actual.root.stagedSchemaUpgrades, expected.root.stagedSchemaUpgrades); + assert.deepEqual(actual.root.allowedTypesInfo, expected.root.allowedTypesInfo); }); - it("view compatibility schema - hasStagedSchemaUpgrades", () => { + it("view compatibility schema - allowedTypesIdentifiers", () => { const treeView = new TreeViewConfigurationAlpha({ schema: root }); const actual = toSerializableCompatibilitySchema(treeView); takeJsonSnapshot(actual); diff --git a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts index 0083bd9ad5fc..f62fc8968d1f 100644 --- a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts @@ -21,6 +21,7 @@ import { } from "../../../simple-tree/index.js"; import { getJsonValidator } from "./jsonSchemaUtilities.js"; import type { + AllowedTypeInfo, SimpleNodeSchema, SimpleTreeSchema, // eslint-disable-next-line import/no-internal-modules @@ -50,6 +51,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), }, definitions: new Map([ [stringSchema.identifier, stringSchema], @@ -86,6 +90,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["test.handle"]), + allowedTypesInfo: new Map([ + ["test.handle", { isStaged: false }], + ]), }, definitions: new Map([ [ @@ -108,6 +115,9 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, allowedTypesIdentifiers: new Set(["test.array"]), + allowedTypesInfo: new Map([ + ["test.array", { isStaged: false }], + ]), metadata: {}, }, definitions: new Map([ @@ -118,6 +128,9 @@ describe("simpleSchemaToJsonSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), }, ], [stringSchema.identifier, stringSchema], @@ -161,6 +174,9 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, allowedTypesIdentifiers: new Set(["test.map"]), + allowedTypesInfo: new Map([ + ["test.map", { isStaged: false }], + ]), metadata: {}, }, definitions: new Map([ @@ -171,6 +187,9 @@ describe("simpleSchemaToJsonSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), }, ], [stringSchema.identifier, stringSchema], @@ -226,6 +245,9 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, allowedTypesIdentifiers: new Set(["test.record"]), + allowedTypesInfo: new Map([ + ["test.record", { isStaged: false }], + ]), metadata: {}, }, definitions: new Map([ @@ -236,6 +258,9 @@ describe("simpleSchemaToJsonSchema", () => { metadata: {}, persistedMetadata: undefined, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), }, ], [stringSchema.identifier, stringSchema], @@ -338,6 +363,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([ + ["test.object", { isStaged: false }], + ]), }, definitions: new Map([ [ @@ -352,6 +380,9 @@ describe("simpleSchemaToJsonSchema", () => { { kind: FieldKind.Optional, allowedTypesIdentifiers: new Set([numberSchema.identifier]), + allowedTypesInfo: new Map([ + [numberSchema.identifier, { isStaged: false }], + ]), metadata: { description: "A number representing the concept of Foo." }, persistedMetadata: undefined, storedKey: "foo", @@ -362,6 +393,9 @@ describe("simpleSchemaToJsonSchema", () => { { kind: FieldKind.Required, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), metadata: { description: "A string representing the concept of Bar." }, persistedMetadata: undefined, storedKey: "bar", @@ -372,6 +406,9 @@ describe("simpleSchemaToJsonSchema", () => { { kind: FieldKind.Identifier, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), metadata: { description: "Unique identifier for the test object.", }, @@ -475,6 +512,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([ + ["test.object", { isStaged: false }], + ]), }, definitions: new Map([ [ @@ -489,6 +529,9 @@ describe("simpleSchemaToJsonSchema", () => { { kind: FieldKind.Identifier, allowedTypesIdentifiers: new Set([stringSchema.identifier]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ]), storedKey: "id", metadata: {}, }, @@ -531,6 +574,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["test.object"]), + allowedTypesInfo: new Map([ + ["test.object", { isStaged: false }], + ]), }, definitions: new Map([ [ @@ -550,6 +596,10 @@ describe("simpleSchemaToJsonSchema", () => { numberSchema.identifier, stringSchema.identifier, ]), + allowedTypesInfo: new Map([ + [numberSchema.identifier, { isStaged: false }], + [stringSchema.identifier, { isStaged: false }], + ]), storedKey: "foo", }, ], @@ -599,6 +649,9 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, allowedTypesIdentifiers: new Set(["test.recursive-object"]), + allowedTypesInfo: new Map([ + ["test.recursive-object", { isStaged: false }], + ]), }, definitions: new Map([ [ @@ -618,6 +671,10 @@ describe("simpleSchemaToJsonSchema", () => { stringSchema.identifier, "test.recursive-object", ]), + allowedTypesInfo: new Map([ + [stringSchema.identifier, { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), storedKey: "foo", }, ], diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json index 921ada79ba2b..5e47334f1ab3 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.array" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.array", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json index 48e3e0243dcd..8f241c679833 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 0, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json index c22dcc3e517d..607f716b6e59 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json index c693f05ea2a1..0eb0719b8250 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.map" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.map", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json index b1d0cf45ee3d..7a1bf683304f 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.object" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.object", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.number": { @@ -20,11 +22,16 @@ "fields": { "foo": { "kind": 1, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string" + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.number", + "isReadOnly": false + }, + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } ], - "hasStagedSchemaUpgrades": false, "storedKey": "foo" } }, diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json index 205c107e1713..15e6f78db6df 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.object" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.object", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { @@ -16,10 +18,12 @@ "fields": { "id": { "kind": 2, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } ], - "hasStagedSchemaUpgrades": false, "storedKey": "id" } }, diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json index b97cc5763501..006c0ff80071 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.object" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.object", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.number": { @@ -20,18 +22,22 @@ "fields": { "foo": { "kind": 0, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.number" + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.number", + "isReadOnly": false + } ], - "hasStagedSchemaUpgrades": false, "storedKey": "foo" }, "bar": { "kind": 1, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } ], - "hasStagedSchemaUpgrades": false, "storedKey": "bar" } }, diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json index b1d0c0d72715..ac96d7074949 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.record" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.record", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json index 43025e2a5112..9569637fcc5a 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json @@ -1,10 +1,12 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "test.recursive-object" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "test.recursive-object", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.string": { @@ -16,11 +18,16 @@ "fields": { "foo": { "kind": 0, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string", - "test.recursive-object" + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + }, + { + "identifier": "test.recursive-object", + "isReadOnly": false + } ], - "hasStagedSchemaUpgrades": false, "storedKey": "foo" } }, diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json index 2d6ae2f7b54b..2a900a4635ed 100644 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json @@ -1,11 +1,16 @@ { "root": { "kind": 1, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string" - ], - "hasStagedSchemaUpgrades": false + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.number", + "isReadOnly": false + }, + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": false + } + ] }, "definitions": { "com.fluidframework.leaf.number": { diff --git a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json similarity index 51% rename from packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json rename to packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json index 8cdf4d83e361..d85b1242c0c8 100644 --- a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - hasStagedSchemaUpgrades.json +++ b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json @@ -1,10 +1,12 @@ { "root": { "kind": 0, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ], - "hasStagedSchemaUpgrades": true + "allowedTypesInfo": [ + { + "identifier": "com.fluidframework.leaf.string", + "isReadOnly": true + } + ] }, "definitions": { "com.fluidframework.leaf.string": { From 2e443d4becd85b5423b16887cee97c6a30d86bc7 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 28 Oct 2025 17:30:13 -0700 Subject: [PATCH 11/47] Replaced SimpleSchema allowedTypesIdentifiers with simpleAllowedTypes. --- .../dds/tree/api-report/tree.alpha.api.md | 19 +- packages/dds/tree/src/index.ts | 1 - .../dds/tree/src/shared-tree/sharedTree.ts | 11 +- .../dds/tree/src/simple-tree/api/index.ts | 2 - .../src/simple-tree/api/schemaFromSimple.ts | 30 +- .../api/serializableCompatibilitySchema.ts | 177 --- .../api/simpleSchemaToJsonSchema.ts | 10 +- .../api/viewSchemaToSimpleSchema.ts | 162 +-- .../viewSchemaToViewCompatibilitySchema.ts | 66 - .../dds/tree/src/simple-tree/fieldSchema.ts | 19 +- packages/dds/tree/src/simple-tree/index.ts | 6 +- .../simple-tree/node-kinds/array/arrayNode.ts | 10 +- .../src/simple-tree/node-kinds/map/mapNode.ts | 10 +- .../node-kinds/record/recordNode.ts | 10 +- .../dds/tree/src/simple-tree/simpleSchema.ts | 43 +- .../tree/src/simple-tree/toStoredSchema.ts | 15 +- .../simple-tree/api/getSimpleSchema.spec.ts | 1064 +++++------------ .../getViewCompatibilityTreeSchema.spec.ts | 47 - .../api/simpleSchemaToJsonSchema.spec.ts | 62 +- ...w compatibility schema - Array schema.json | 23 - ...w compatibility schema - Field Schema.json | 17 - ...view compatibility schema - Leaf node.json | 17 - ...iew compatibility schema - Map schema.json | 23 - ...Object schema including a union field.json | 41 - ... schema including an identifier field.json | 33 - ... compatibility schema - Object schema.json | 47 - ... compatibility schema - Record schema.json | 23 - ...lity schema - Recursive object schema.json | 37 - ...iew compatibility schema - Union root.json | 25 - ...lity schema - allowedTypesIdentifiers.json | 17 - 30 files changed, 412 insertions(+), 1655 deletions(-) delete mode 100644 packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts delete mode 100644 packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts delete mode 100644 packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json delete mode 100644 packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 0ca84653f09c..74c2f789c4df 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -272,11 +272,9 @@ export class FieldSchemaAlpha; // (undocumented) - get allowedTypesInfo(): Map; - // (undocumented) get persistedMetadata(): JsonCompatibleReadOnlyObject | undefined; // (undocumented) - get readonlyAllowedTypesIdentifiers(): ReadonlySet; + get simpleAllowedTypes(): Map; } // @alpha @sealed @system @@ -1007,17 +1005,15 @@ export type SharedTreeOptionsBeta = ForestOptions; // @alpha @sealed export interface SimpleArrayNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed export interface SimpleFieldSchema { - readonly allowedTypesIdentifiers: ReadonlySet; - readonly allowedTypesInfo: ReadonlyMap; readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1027,8 +1023,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1058,8 +1053,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1308,9 +1302,6 @@ export namespace TableSchema { }): System_TableSchema.TableSchemaBase; } -// @alpha -export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; - // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 4fa6c43efbe6..fe5b4dc865b6 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -339,4 +339,3 @@ export { JsonAsTree } from "./jsonDomainSchema.js"; export { FluidSerializableAsTree } from "./serializableDomainSchema.js"; export { TableSchema, type System_TableSchema } from "./tableSchema.js"; export { asAlpha } from "./api.js"; -export { toViewCompatibilityTreeSchema } from "./simple-tree/index.js"; diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index 70cb9ccf56ee..689c67f71fe2 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -952,7 +952,7 @@ export const defaultSharedTreeOptions: Required = { shouldEncodeIncrementally: defaultIncrementalEncodingPolicy, }; -function buildAllowedTypesInfo(types: TreeTypeSet): ReadonlyMap { +function buildSimpleAllowedTypes(types: TreeTypeSet): ReadonlyMap { const allowedTypesInfo = new Map(); for (const type of types) { allowedTypesInfo.set(type, { @@ -984,8 +984,7 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie return { kind, // TODO: Refactor - allowedTypesIdentifiers: schema.types, - allowedTypesInfo: buildAllowedTypesInfo(schema.types), + simpleAllowedTypes: buildSimpleAllowedTypes(schema.types), metadata: {}, persistedMetadata: schema.persistedMetadata, }; @@ -1001,8 +1000,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS if (arrayTypes !== undefined) { return { kind: NodeKind.Array, - allowedTypesIdentifiers: arrayTypes, - allowedTypesInfo: buildAllowedTypesInfo(arrayTypes), + simpleAllowedTypes: buildSimpleAllowedTypes(arrayTypes), metadata: {}, persistedMetadata: schema.metadata, }; @@ -1021,8 +1019,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS ); return { kind: NodeKind.Map, - allowedTypesIdentifiers: schema.mapFields.types, - allowedTypesInfo: buildAllowedTypesInfo(schema.mapFields.types), + simpleAllowedTypes: buildSimpleAllowedTypes(schema.mapFields.types), metadata: {}, persistedMetadata: schema.metadata, }; diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 55e1ee27d04c..c42426cec935 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -160,6 +160,4 @@ export { export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; export { toSimpleTreeSchema } from "./viewSchemaToSimpleSchema.js"; -export { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; export type { TreeChangeEvents } from "./treeChangeEvents.js"; -export { toSerializableCompatibilitySchema } from "./serializableCompatibilitySchema.js"; diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts index dd3ece77442b..d8f2b3e8e7ce 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -13,6 +13,7 @@ import { type FieldProps, } from "../fieldSchema.js"; import type { + SimpleAllowedTypes, SimpleFieldSchema, SimpleNodeSchema, SimpleTreeSchema, @@ -65,7 +66,7 @@ function generateFieldSchema( context: Context, storedKey: string | undefined, ): FieldSchemaAlpha { - const allowed = generateAllowedTypes(simple.allowedTypesIdentifiers, context); + const allowed = generateAllowedTypes(simple.simpleAllowedTypes, context); const props: Omit = { metadata: simple.metadata, key: storedKey, @@ -84,8 +85,13 @@ function generateFieldSchema( } } -function generateAllowedTypes(allowed: ReadonlySet, context: Context): AllowedTypes { - return [...allowed].map((id) => context.get(id) ?? fail(0xb5a /* Missing schema */)); +function generateAllowedTypes( + allowed: ReadonlyMap, + context: Context, +): AllowedTypes { + return [...new Set(allowed.keys())].map( + (id) => context.get(id) ?? fail(0xb5a /* Missing schema */), + ); } function generateNode( @@ -104,21 +110,17 @@ function generateNode( return factory.objectAlpha(id, fields, { metadata: schema.metadata }); } case NodeKind.Array: - return factory.arrayAlpha( - id, - generateAllowedTypes(schema.allowedTypesIdentifiers, context), - { metadata: schema.metadata }, - ); + return factory.arrayAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), { + metadata: schema.metadata, + }); case NodeKind.Map: - return factory.mapAlpha( - id, - generateAllowedTypes(schema.allowedTypesIdentifiers, context), - { metadata: schema.metadata }, - ); + return factory.mapAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), { + metadata: schema.metadata, + }); case NodeKind.Record: return factory.recordAlpha( id, - generateAllowedTypes(schema.allowedTypesIdentifiers, context), + generateAllowedTypes(schema.simpleAllowedTypes, context), { metadata: schema.metadata }, ); case NodeKind.Leaf: diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts deleted file mode 100644 index 37f9082dc0a3..000000000000 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ /dev/null @@ -1,177 +0,0 @@ -import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; -import type { TreeSchema } from "./configuration.js"; -import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; -import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; -import type { - AllowedTypeInfo, - SimpleArrayNodeSchema, - SimpleFieldSchema, - SimpleLeafNodeSchema, - SimpleMapNodeSchema, - SimpleNodeSchema, - SimpleObjectFieldSchema, - SimpleObjectNodeSchema, - SimpleRecordNodeSchema, -} from "../simpleSchema.js"; -import { NodeKind } from "../core/index.js"; - -/** - * Converts a view schema to a serializable format for compatibility testing. - * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. - * It is not possible to reconstruct a full view schema from the serialized format. - * @param treeSchema - The tree schema to convert. - * @returns A serializable representation of the view schema. - */ -export function toSerializableCompatibilitySchema(treeSchema: TreeSchema): JsonCompatible { - const simpleSchema = toViewCompatibilityTreeSchema( - treeSchema, - // Copying strips out references to metadata and other non-essential information. - true, - ); - - // Convert types to serializable forms - const serializableDefinitions = new Map(); - - for (const [identifier, schema] of simpleSchema.definitions) { - const serializableDefinition = nodeSchemaToSerializable(schema); - serializableDefinitions.set(identifier, serializableDefinition); - } - - const serializableSchema = { - root: toSerializableField(simpleSchema.root), - definitions: mapToRecord(serializableDefinitions), - } as unknown as JsonCompatible; - - return serializableSchema; -} - -/** - * Converts a node schema to a serializable object. - * @param schema - The node schema to convert. - * @returns A serializable representation of the node schema. - */ -function nodeSchemaToSerializable(schema: SimpleNodeSchema): JsonCompatibleObject { - const kind = schema.kind; - switch (kind) { - case NodeKind.Leaf: - return leafNodeToSerializable(schema); - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return containerNodeToSerializable(schema); - case NodeKind.Object: - return objectNodeToSerializable(schema); - default: { - unreachableCase(kind); - } - } -} - -/** - * Converts a leaf node schema to a serializable object. - * @param schema - The leaf node schema to convert. - * @returns A serializable representation of the leaf node schema. - */ -function leafNodeToSerializable(schema: SimpleLeafNodeSchema): JsonCompatibleObject { - return { - kind: schema.kind, - leafKind: schema.leafKind, - }; -} - -/** - * Converts a container node schema to a serializable object. - * @param schema - The container node schema to convert. - * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different - * container kinds. - */ -function containerNodeToSerializable( - schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, -): JsonCompatibleObject { - return { - kind: schema.kind, - allowedTypesIdentifiers: setToArray(schema.allowedTypesIdentifiers), - }; -} - -/** - * Converts an object node schema to a serializable object. - * @param schema - The object node schema to convert. - * @returns A serializable representation of the object node schema. - */ -function objectNodeToSerializable(schema: SimpleObjectNodeSchema): JsonCompatibleObject { - const serializableFields: Record = {}; - for (const [fieldKey, fieldSchema] of schema.fields) { - serializableFields[fieldKey] = toSerializableObjectField(fieldSchema); - } - - return { - kind: schema.kind, - fields: serializableFields, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; -} - -/** - * Converts an object field schema to a serializable object. - * @param fieldSchema - The object field schema to convert. - * @returns A serializable representation of the object field schema. - */ -function toSerializableObjectField( - fieldSchema: SimpleObjectFieldSchema, -): JsonCompatibleObject { - const serializableField = toSerializableField(fieldSchema); - serializableField.storedKey = fieldSchema.storedKey; - return serializableField; -} - -/** - * Converts a field schema to a serializable object. - * @param fieldSchema - The field schema to convert. - * @returns A serializable representation of the field schema. - */ -function toSerializableField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { - const allowedTypesInfo: JsonCompatibleObject[] = []; - for (const [identifier, typeInfo] of fieldSchema.allowedTypesInfo ?? - new Map()) { - allowedTypesInfo.push({ - identifier, - isReadOnly: typeInfo.isStaged, - }); - } - return { - kind: fieldSchema.kind, - allowedTypesInfo, - }; -} - -/** - * Convert a Map to a Record for serialization. - * @remarks This is needed because the JSON serializer does not support Maps. - * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output - * should only be used in snapshots or debugging scenarios. - * @param map - The Map to convert. - * @returns A Record with the contents of the Map. - */ -function mapToRecord(map: ReadonlyMap): Record { - const resultObject: Record = {}; - const sortedKeys = Array.from(map.keys()).sort(); - - for (const key of sortedKeys) { - const value = - map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); - resultObject[`${key}`] = value; - } - - return resultObject; -} - -/** - * Convert a Set to a sorted Array for serialization. - * @remarks This is needed because the JSON serializer does not support Sets. - * @param set - The set to convert. - * @returns An Array with the contents of the Set sorted lexicographically. - */ -function setToArray(set: ReadonlySet): Value[] { - return Array.from(set).sort(); -} diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaToJsonSchema.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaToJsonSchema.ts index 4fd51b0c393d..4d9ac6b8a422 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaToJsonSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaToJsonSchema.ts @@ -107,7 +107,10 @@ function convertNodeSchema( function convertArrayNodeSchema(schema: SimpleArrayNodeSchema): JsonArrayNodeSchema { const allowedTypes: JsonSchemaRef[] = []; - schema.allowedTypesIdentifiers.forEach((type) => { + const allowedTypesIdentifiers: ReadonlySet = new Set( + schema.simpleAllowedTypes.keys(), + ); + allowedTypesIdentifiers.forEach((type) => { allowedTypes.push(createSchemaRef(type)); }); @@ -206,7 +209,10 @@ function convertRecordLikeNodeSchema( schema: SimpleRecordNodeSchema | SimpleMapNodeSchema, ): JsonMapNodeSchema | JsonRecordNodeSchema { const allowedTypes: JsonSchemaRef[] = []; - schema.allowedTypesIdentifiers.forEach((type) => { + const allowedTypesIdentifiers: ReadonlySet = new Set( + schema.simpleAllowedTypes.keys(), + ); + allowedTypesIdentifiers.forEach((type) => { allowedTypes.push(createSchemaRef(type)); }); diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 8a5bc1b60d53..15a6e1d3017e 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -59,9 +59,7 @@ export function toSimpleTreeSchema( nodeSchema instanceof RecordNodeSchema, 0xb60 /* Invalid schema */, ); - const outSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.SimpleSchema) - : nodeSchema; + const outSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema) : nodeSchema; definitions.set(nodeSchema.identifier, outSchema); }, }); @@ -69,8 +67,7 @@ export function toSimpleTreeSchema( return { root: copySchemaObjects ? ({ - allowedTypesIdentifiers: normalizedSchema.allowedTypesIdentifiers, - allowedTypesInfo: normalizedSchema.allowedTypesInfo, + simpleAllowedTypes: normalizedSchema.simpleAllowedTypes, kind: normalizedSchema.kind, metadata: normalizedSchema.metadata, persistedMetadata: normalizedSchema.persistedMetadata, @@ -80,157 +77,64 @@ export function toSimpleTreeSchema( }; } -/** - * Specifies which fields are included when copying a Simple Schema. - */ -export enum SimpleSchemaCopyMode { - // TODO: Rename - SimpleSchema, - ViewCompatibilitySchema, -} - /** * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. * * @remarks Caches the result on the input schema for future calls. */ -export function copySimpleNodeSchema( - schema: SimpleNodeSchema, - copyMode: SimpleSchemaCopyMode, -): SimpleNodeSchema { +function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: - return copySimpleLeafSchema(schema, copyMode); + return copySimpleLeafSchema(schema); case NodeKind.Array: case NodeKind.Map: case NodeKind.Record: - return copySimpleSchemaWithAllowedTypes(schema, copyMode); + return copySimpleSchemaWithAllowedTypes(schema); case NodeKind.Object: - return copySimpleObjectSchema(schema, copyMode); + return copySimpleObjectSchema(schema); default: unreachableCase(kind); } } -function copySimpleLeafSchema( - schema: SimpleLeafNodeSchema, - copyMode: SimpleSchemaCopyMode, -): SimpleLeafNodeSchema { - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - }; - default: - unreachableCase(copyMode); - } +function copySimpleLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; } function copySimpleSchemaWithAllowedTypes( schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, - copyMode: SimpleSchemaCopyMode, ): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - return { - kind: schema.kind, - allowedTypesIdentifiers: schema.allowedTypesIdentifiers, - allowedTypesInfo: schema.allowedTypesInfo, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - return { - kind: schema.kind, - allowedTypesIdentifiers: schema.allowedTypesIdentifiers, - allowedTypesInfo: schema.allowedTypesInfo, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - }; - default: - unreachableCase(copyMode); - } + return { + kind: schema.kind, + simpleAllowedTypes: schema.simpleAllowedTypes, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; } -function copySimpleObjectSchema( - schema: SimpleObjectNodeSchema, - copyMode: SimpleSchemaCopyMode, -): SimpleObjectNodeSchema { +function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - let simpleField: SimpleObjectFieldSchema; - - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - simpleField = { - kind: field.kind, - allowedTypesIdentifiers: field.allowedTypesIdentifiers, - allowedTypesInfo: field.allowedTypesInfo, - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, - storedKey: field.storedKey, - }; - break; - - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - simpleField = { - kind: field.kind, - allowedTypesIdentifiers: field.allowedTypesIdentifiers, - allowedTypesInfo: field.allowedTypesInfo, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - storedKey: field.storedKey, - }; - break; - - default: - unreachableCase(copyMode); - } - - fields.set(propertyKey, simpleField); - } - - let simpleObject: SimpleObjectNodeSchema; - - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - simpleObject = { - kind: NodeKind.Object, - fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - break; - - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - simpleObject = { - kind: NodeKind.Object, - fields, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; - break; - - default: - unreachableCase(copyMode); + fields.set(propertyKey, { + kind: field.kind, + simpleAllowedTypes: field.simpleAllowedTypes, + metadata: field.metadata, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + }); } - return simpleObject; + return { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts deleted file mode 100644 index afbf9ca28376..000000000000 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*! - * Copyright (c) Microsoft Corporation and contributors. All rights reserved. - * Licensed under the MIT License. - */ - -import { assert } from "@fluidframework/core-utils/internal"; -import type { SimpleNodeSchema, SimpleTreeSchema } from "../simpleSchema.js"; -import type { TreeSchema } from "./configuration.js"; -import { LeafNodeSchema } from "../leafNodeSchema.js"; -import { - ArrayNodeSchema, - MapNodeSchema, - ObjectNodeSchema, - RecordNodeSchema, -} from "../node-kinds/index.js"; -import { copySimpleNodeSchema, SimpleSchemaCopyMode } from "./viewSchemaToSimpleSchema.js"; - -/** - * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. - * - * @param schema - The stored schema to convert. - * @param copySchemaObjects - If true, copies the contents of the schema into new objects. - * @returns The converted SimpleTreeSchema. - * - * @alpha - */ -export function toViewCompatibilityTreeSchema( - schema: TreeSchema, - copySchemaObjects: boolean, -): SimpleTreeSchema { - const definitions = new Map(); - - // Walk the node definitions and convert them one by one. Recurse into fields used in compatibility checks. - for (const nodeSchema of schema.definitions.values()) { - // TODO: Move this assert to a common location so it can be used from both SimpleSchema builders. - // The set of node kinds is extensible, but the typing of SimpleNodeSchema is not, so we need to check that the schema is one of the known kinds. - assert( - nodeSchema instanceof ArrayNodeSchema || - nodeSchema instanceof MapNodeSchema || - nodeSchema instanceof LeafNodeSchema || - nodeSchema instanceof ObjectNodeSchema || - nodeSchema instanceof RecordNodeSchema, - // TODO: New error code. - 0xb60 /* Invalid schema */, - ); - - // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. - const simpleNodeSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.ViewCompatibilitySchema) - : nodeSchema; - definitions.set(nodeSchema.identifier, simpleNodeSchema); - } - - return { - root: copySchemaObjects - ? { - kind: schema.root.kind, - allowedTypesIdentifiers: schema.root.allowedTypesIdentifiers, - allowedTypesInfo: schema.root.allowedTypesInfo, - metadata: schema.root.metadata, - persistedMetadata: schema.root.persistedMetadata, - } - : schema.root, // TODO: Convert the root field - definitions, - }; -} diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index bd5f3625f1f5..2dfd85cfe0f4 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -31,7 +31,7 @@ import type { } from "./core/index.js"; import { normalizeAllowedTypes } from "./core/index.js"; -import type { AllowedTypeInfo, SimpleFieldSchema } from "./simpleSchema.js"; +import type { SimpleAllowedTypes, SimpleFieldSchema } from "./simpleSchema.js"; import type { UnsafeUnknownSchema } from "./unsafeUnknownSchema.js"; import type { InsertableContent } from "./unhydratedFlexTreeFromInsertable.js"; @@ -412,9 +412,9 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } - public get allowedTypesInfo(): Map { + public get simpleAllowedTypes(): Map { const types = this.allowedTypesFull.evaluate().types; - const info = new Map(); + const info = new Map(); for (const type of types) { info.set(type.type.identifier, { @@ -425,19 +425,6 @@ export class FieldSchemaAlpha< return info; } - // TODO: Refactor allowedTypesIdentifiers and readonlyAllowedTypesIdentifiers so that there is an intermediate type containing - // the identifier and any other information needed for compatibility checks. - public get readonlyAllowedTypesIdentifiers(): ReadonlySet { - const annotatedAllowedTypes = this.allowedTypesFull.evaluate().types; - const readonlyTypes = new Set(); - for (const allowedType of annotatedAllowedTypes) { - if (allowedType.metadata.stagedSchemaUpgrade !== undefined) { - readonlyTypes.add(allowedType.type.identifier); - } - } - return readonlyTypes; - } - protected constructor( kind: Kind, types: Types, diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 0f83afc9ab0f..031a61717d6e 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -193,7 +193,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, - AllowedTypeInfo, + SimpleAllowedTypes as AllowedTypeInfo, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, @@ -275,7 +275,3 @@ export { nullSchema, } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; -export { - toViewCompatibilityTreeSchema, - toSerializableCompatibilitySchema, -} from "./api/index.js"; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index 217c427a28cb..087197963854 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -68,7 +68,7 @@ import type { import { brand, type JsonCompatibleReadOnlyObject } from "../../../util/index.js"; import { nullSchema } from "../../leafNodeSchema.js"; import { arrayNodeStoredSchema } from "../../toStoredSchema.js"; -import type { AllowedTypeInfo } from "../../simpleSchema.js"; +import type { SimpleAllowedTypes } from "../../simpleSchema.js"; /** * A covariant base type for {@link (TreeArrayNode:interface)}. @@ -1169,8 +1169,8 @@ export function arraySchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); - const lazyAllowedTypesInfo = new Lazy(() => { - const map = new Map(); + const lazySimpleAllowedTypes = new Lazy(() => { + const map = new Map(); for (const type of normalizedTypes.evaluate()) { map.set(type.identifier, { isStaged: false, @@ -1216,8 +1216,8 @@ export function arraySchema< return lazyAllowedTypesIdentifiers.value; } - public static get allowedTypesInfo(): ReadonlyMap { - return lazyAllowedTypesInfo.value; + public static get simpleAllowedTypes(): ReadonlyMap { + return lazySimpleAllowedTypes.value; } protected static override constructorCached: MostDerivedData | undefined = undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index ab0a5d37d4cd..c3fb433a66a4 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -66,7 +66,7 @@ import type { import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { AllowedTypeInfo } from "../../simpleSchema.js"; +import type { SimpleAllowedTypes } from "../../simpleSchema.js"; /** * A map of string keys to tree objects. @@ -276,8 +276,8 @@ export function mapSchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); - const lazyAllowedTypesInfo = new Lazy(() => { - const map = new Map(); + const lazySimpleAllowedTypes = new Lazy(() => { + const map = new Map(); for (const type of normalizedTypes.evaluate()) { map.set(type.identifier, { isStaged: false, @@ -313,8 +313,8 @@ export function mapSchema< return lazyAllowedTypesIdentifiers.value; } - public static get allowedTypesInfo(): ReadonlyMap { - return lazyAllowedTypesInfo.value; + public static get simpleAllowedTypes(): ReadonlyMap { + return lazySimpleAllowedTypes.value; } protected static override constructorCached: MostDerivedData | undefined = undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index fb62d5b02d88..fa554babfda0 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -58,7 +58,7 @@ import { prepareForInsertion } from "../../prepareForInsertion.js"; import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { AllowedTypeInfo } from "../../simpleSchema.js"; +import type { SimpleAllowedTypes } from "../../simpleSchema.js"; /** * Create a proxy which implements the {@link TreeRecordNode} API. @@ -259,8 +259,8 @@ export function recordSchema< const lazyAllowedTypesIdentifiers = new Lazy( () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); - const lazyAllowedTypesInfo = new Lazy(() => { - const map = new Map(); + const lazySimpleAllowedTypes = new Lazy(() => { + const map = new Map(); for (const type of normalizedTypes.evaluate()) { map.set(type.identifier, { isStaged: false, @@ -357,8 +357,8 @@ export function recordSchema< return lazyAllowedTypesIdentifiers.value; } - public static get allowedTypesInfo(): ReadonlyMap { - return lazyAllowedTypesInfo.value; + public static get simpleAllowedTypes(): ReadonlyMap { + return lazySimpleAllowedTypes.value; } protected static override constructorCached: MostDerivedData | undefined = undefined; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 862ac434e138..bffc698dd640 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -62,7 +62,7 @@ export interface SimpleObjectNodeSchema /** * Whether the object node allows unknown optional fields. * - * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + * @remarks Used for compatibility checks. Not available in all cases. */ readonly allowUnknownOptionalFields?: boolean; } @@ -98,12 +98,7 @@ export interface SimpleArrayNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly allowedTypesIdentifiers: ReadonlySet; - - /** - * Information about the allowed types. - */ - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -120,12 +115,7 @@ export interface SimpleMapNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly allowedTypesIdentifiers: ReadonlySet; - - /** - * Information about the allowed types. - */ - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -142,12 +132,7 @@ export interface SimpleRecordNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly allowedTypesIdentifiers: ReadonlySet; - - /** - * Information about the allowed types. - */ - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -185,15 +170,18 @@ export type SimpleNodeSchema = | SimpleRecordNodeSchema; /** - * Information about allowed types under a field. + * Information about allowed types. * * @alpha * @sealed */ -export interface AllowedTypeInfo { +export interface SimpleAllowedTypes { /** - * True if there is an associated schema upgrade that makes this type read-only. - * Undefined if derived from a stored schema, which has no concept of staged allowed type upgrades. + * True if this schema is included as a {@link SchemaStaticsAlpha.staged | staged} schema upgrade, + * allowing the view schema be compatible with stored schema with (post upgrade) or without it (pre-upgrade). + * New documents and schema upgrades will omit any staged schema. + * + * Undefined if derived from a stored schema. */ readonly isStaged?: boolean; } @@ -215,17 +203,12 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; /** - * The types allowed under the field. + * Information about the allowed types under this field. * * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly allowedTypesIdentifiers: ReadonlySet; - - /** - * Information about the allowed types under this field. - */ - readonly allowedTypesInfo: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; /** * {@inheritDoc FieldSchemaMetadata} diff --git a/packages/dds/tree/src/simple-tree/toStoredSchema.ts b/packages/dds/tree/src/simple-tree/toStoredSchema.ts index 1939435f18e7..ea785e776ff8 100644 --- a/packages/dds/tree/src/simple-tree/toStoredSchema.ts +++ b/packages/dds/tree/src/simple-tree/toStoredSchema.ts @@ -139,7 +139,10 @@ export function convertField( if (schema instanceof FieldSchemaAlpha) { types = convertAllowedTypes(schema.allowedTypes, options); } else { - types = schema.allowedTypesIdentifiers as TreeTypeSet; + const allowedTypesIdentifiers: ReadonlySet = new Set( + schema.simpleAllowedTypes.keys(), + ); + types = allowedTypesIdentifiers as TreeTypeSet; } return { kind, types, persistedMetadata: schema.persistedMetadata }; } @@ -175,7 +178,10 @@ export function getStoredSchema( } case NodeKind.Map: case NodeKind.Record: { - const types = schema.allowedTypesIdentifiers as TreeTypeSet; + const allowedTypesIdentifiers: ReadonlySet = new Set( + schema.simpleAllowedTypes.keys(), + ); + const types = allowedTypesIdentifiers as TreeTypeSet; return new MapNodeStoredSchema( { kind: FieldKinds.optional.identifier, @@ -187,7 +193,10 @@ export function getStoredSchema( ); } case NodeKind.Array: { - const types = schema.allowedTypesIdentifiers as TreeTypeSet; + const allowedTypesIdentifiers: ReadonlySet = new Set( + schema.simpleAllowedTypes.keys(), + ); + const types = allowedTypesIdentifiers as TreeTypeSet; return arrayNodeStoredSchema(types, schema.persistedMetadata); } case NodeKind.Object: { diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 7d9fe1a35c1f..0ae5c6503800 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -10,9 +10,6 @@ import { SchemaFactory, SchemaFactoryAlpha, stringSchema, - toSerializableCompatibilitySchema, - toViewCompatibilityTreeSchema, - TreeViewConfigurationAlpha, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -22,7 +19,6 @@ import { import { ValueSchema } from "../../../core/index.js"; // eslint-disable-next-line import/no-internal-modules import { toSimpleTreeSchema } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; -import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -39,858 +35,364 @@ const simpleNumber: SimpleLeafNodeSchema = { }; describe("getSimpleSchema", () => { - useSnapshotDirectory("get-simple-schema"); - - describe("non-copying", () => { + it("non-copying", () => { const Schema = stringSchema; const root = SchemaFactoryAlpha.optional(Schema); + const actual = toSimpleTreeSchema(root, false); + const expected: SimpleTreeSchema = { root, definitions: new Map([[Schema.identifier, Schema]]), }; + assert.deepEqual(actual, expected); - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(root, false); - - assert.deepEqual(actual, expected); - - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toViewCompatibilityTreeSchema(treeView, false); - - assert.deepEqual(actual, expected); - - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); - }); + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); }); - describe("Field Schema", () => { + it("Field Schema", () => { const schemaFactory = new SchemaFactory("test"); const Schema = schemaFactory.optional(schemaFactory.string, { metadata: { description: "An optional string." }, }); - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - metadata: { description: "An optional string." }, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - metadata: { description: "An optional string." }, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); + const actual = toSimpleTreeSchema(Schema, true); - it("view compatibility schema - Field Schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + metadata: { description: "An optional string." }, + simpleAllowedTypes: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + assert.deepEqual(actual, expected); }); - describe("Leaf node", () => { + it("Leaf node", () => { const Schema = SchemaFactory.string; - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); + const actual = toSimpleTreeSchema(Schema, true); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Leaf node", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + simpleAllowedTypes: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + assert.deepEqual(actual, expected); }); - describe("Union root", () => { + it("Union root", () => { const Schema = [SchemaFactory.number, SchemaFactory.string]; - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - definitions: new Map([ - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); + const actual = toSimpleTreeSchema(Schema, true); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - definitions: new Map([ - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Union root", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + }, + definitions: new Map([ + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Array schema", () => { + it("Array schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.array("array", schemaFactory.string) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.array"]), - allowedTypesInfo: new Map([["test.array", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.array", - { - kind: NodeKind.Array, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - metadata: {}, - persistedMetadata: undefined, - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); + const actual = toSimpleTreeSchema(Schema, true); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.array"]), - allowedTypesInfo: new Map([["test.array", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.array", - { - kind: NodeKind.Array, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - metadata: {}, - persistedMetadata: undefined, - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Array schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.array", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.array", + { + kind: NodeKind.Array, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + metadata: {}, + persistedMetadata: undefined, + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Map schema", () => { + it("Map schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.map("map", schemaFactory.string) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.map"]), - allowedTypesInfo: new Map([["test.map", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.map", - { - kind: NodeKind.Map, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.map"]), - allowedTypesInfo: new Map([["test.map", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.map", - { - kind: NodeKind.Map, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Map schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const actual = toSimpleTreeSchema(Schema, true); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.map", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.map", + { + kind: NodeKind.Map, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Record schema", () => { + it("Record schema", () => { const schemaFactory = new SchemaFactoryAlpha("test"); class Schema extends schemaFactory.record("record", schemaFactory.string) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.record"]), - allowedTypesInfo: new Map([["test.record", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.record", - { - kind: NodeKind.Record, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.record"]), - allowedTypesInfo: new Map([["test.record", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.record", - { - kind: NodeKind.Record, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Record schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const actual = toSimpleTreeSchema(Schema, true); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.record", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.record", + { + kind: NodeKind.Record, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Object schema", () => { + it("Object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.optional(schemaFactory.number), bar: schemaFactory.required(schemaFactory.string), }) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - [ - "bar", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "bar", - }, - ], - ]), - } satisfies SimpleObjectNodeSchema, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: false, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.number"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - [ - "bar", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "bar", - }, - ], - ]), - } satisfies SimpleObjectNodeSchema, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; + const actual = toSimpleTreeSchema(Schema, true); - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + [ + "bar", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "bar", + }, + ], + ]), + } satisfies SimpleObjectNodeSchema, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Object schema including an identifier field", () => { + it("Object schema including an identifier field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { id: schemaFactory.identifier, }) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "id", - { - kind: FieldKind.Identifier, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "id", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: false, - fields: new Map([ - [ - "id", - { - kind: FieldKind.Identifier, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["com.fluidframework.leaf.string"]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "id", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; + const actual = toSimpleTreeSchema(Schema, true); - const actual = toViewCompatibilityTreeSchema(treeView, true); - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Object schema including an identifier field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "id", + { + kind: FieldKind.Identifier, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "id", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Object schema including a union field", () => { + it("Object schema including a union field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.required([schemaFactory.number, schemaFactory.string]), }) {} - it("toSimpleTreeSchema", () => { - // Must enable copy so deep equality passes. - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; + // Must enable copy so deep equality passes. + const actual = toSimpleTreeSchema(Schema, true); - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - - // Must enable copy so deep equality passes. - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: false, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.number", - "com.fluidframework.leaf.string", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Object schema including a union field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); - describe("Recursive object schema", () => { + it("Recursive object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.objectRecursive("recursive-object", { foo: schemaFactory.optionalRecursive([schemaFactory.string, () => Schema]), }) {} - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(Schema, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.recursive-object"]), - allowedTypesInfo: new Map([["test.recursive-object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.recursive-object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.string", - "test.recursive-object", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ["test.recursive-object", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set(["test.recursive-object"]), - allowedTypesInfo: new Map([["test.recursive-object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.recursive-object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: false, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - "com.fluidframework.leaf.string", - "test.recursive-object", - ]), - allowedTypesInfo: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ["test.recursive-object", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); + const actual = toSimpleTreeSchema(Schema, true); - it("view compatibility schema - Recursive object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.recursive-object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.recursive-object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + assert.deepEqual(actual, expected); }); }); diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts deleted file mode 100644 index 4fc8d78e5719..000000000000 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - FieldKind, - SchemaFactoryAlpha, - stringSchema, - toSerializableCompatibilitySchema, - toViewCompatibilityTreeSchema, - TreeViewConfigurationAlpha, - type SimpleTreeSchema, -} from "../../../simple-tree/index.js"; -import { strict as assert } from "node:assert"; -import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; - -describe("getViewCompatibilityTreeSchema", () => { - useSnapshotDirectory("get-view-compatibility-tree-schema"); - - describe("With staged schema upgrades", () => { - const leafSchema = stringSchema; - const schemaFactory = new SchemaFactoryAlpha("test"); - const root = schemaFactory.optional( - // Staged allowed types are read-only for the sake of schema migrations - schemaFactory.types([schemaFactory.staged(leafSchema)]), - ); - - it("Should preserve isReadOnly when converting to SimpleTreeSchema", () => { - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - allowedTypesInfo: new Map([[leafSchema.identifier, { isStaged: true }]]), - allowedTypesIdentifiers: new Set([leafSchema.identifier]), - metadata: {}, - persistedMetadata: undefined, - }, - definitions: new Map([[leafSchema.identifier, leafSchema]]), - }; - - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toViewCompatibilityTreeSchema(treeView, true); - assert.deepEqual(actual.root.allowedTypesInfo, expected.root.allowedTypesInfo); - }); - - it("view compatibility schema - allowedTypesIdentifiers", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toSerializableCompatibilitySchema(treeView); - takeJsonSnapshot(actual); - }); - }); -}); diff --git a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts index f62fc8968d1f..9f67b9ecc7a8 100644 --- a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts @@ -21,7 +21,7 @@ import { } from "../../../simple-tree/index.js"; import { getJsonValidator } from "./jsonSchemaUtilities.js"; import type { - AllowedTypeInfo, + SimpleAllowedTypes, SimpleNodeSchema, SimpleTreeSchema, // eslint-disable-next-line import/no-internal-modules @@ -50,8 +50,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -89,8 +88,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set(["test.handle"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.handle", { isStaged: false }], ]), }, @@ -114,8 +112,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - allowedTypesIdentifiers: new Set(["test.array"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.array", { isStaged: false }], ]), metadata: {}, @@ -127,8 +124,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Array, metadata: {}, persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -173,8 +169,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - allowedTypesIdentifiers: new Set(["test.map"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.map", { isStaged: false }], ]), metadata: {}, @@ -186,8 +181,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Map, metadata: {}, persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -244,8 +238,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - allowedTypesIdentifiers: new Set(["test.record"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.record", { isStaged: false }], ]), metadata: {}, @@ -257,8 +250,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Record, metadata: {}, persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -362,8 +354,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -379,8 +370,7 @@ describe("simpleSchemaToJsonSchema", () => { "foo", { kind: FieldKind.Optional, - allowedTypesIdentifiers: new Set([numberSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], ]), metadata: { description: "A number representing the concept of Foo." }, @@ -392,8 +382,7 @@ describe("simpleSchemaToJsonSchema", () => { "bar", { kind: FieldKind.Required, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { description: "A string representing the concept of Bar." }, @@ -405,8 +394,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { @@ -511,8 +499,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -528,8 +515,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - allowedTypesIdentifiers: new Set([stringSchema.identifier]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), storedKey: "id", @@ -573,8 +559,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set(["test.object"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -592,11 +577,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - numberSchema.identifier, - stringSchema.identifier, - ]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], [stringSchema.identifier, { isStaged: false }], ]), @@ -648,8 +629,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - allowedTypesIdentifiers: new Set(["test.recursive-object"]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ ["test.recursive-object", { isStaged: false }], ]), }, @@ -667,11 +647,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Optional, metadata: {}, persistedMetadata: undefined, - allowedTypesIdentifiers: new Set([ - stringSchema.identifier, - "test.recursive-object", - ]), - allowedTypesInfo: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ["test.recursive-object", { isStaged: false }], ]), diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json deleted file mode 100644 index 5e47334f1ab3..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.array", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.array": { - "kind": 1, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ] - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json deleted file mode 100644 index 8f241c679833..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "root": { - "kind": 0, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json deleted file mode 100644 index 607f716b6e59..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json deleted file mode 100644 index 0eb0719b8250..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.map", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.map": { - "kind": 0, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ] - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json deleted file mode 100644 index 7a1bf683304f..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.object", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 - }, - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.object": { - "kind": 2, - "fields": { - "foo": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.number", - "isReadOnly": false - }, - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ], - "storedKey": "foo" - } - }, - "allowUnknownOptionalFields": false - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json deleted file mode 100644 index 15e6f78db6df..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.object", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.object": { - "kind": 2, - "fields": { - "id": { - "kind": 2, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ], - "storedKey": "id" - } - }, - "allowUnknownOptionalFields": false - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json deleted file mode 100644 index 006c0ff80071..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.object", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 - }, - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.object": { - "kind": 2, - "fields": { - "foo": { - "kind": 0, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.number", - "isReadOnly": false - } - ], - "storedKey": "foo" - }, - "bar": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ], - "storedKey": "bar" - } - }, - "allowUnknownOptionalFields": false - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json deleted file mode 100644 index ac96d7074949..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.record", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.record": { - "kind": 4, - "allowedTypesIdentifiers": [ - "com.fluidframework.leaf.string" - ] - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json deleted file mode 100644 index 9569637fcc5a..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "test.recursive-object", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - }, - "test.recursive-object": { - "kind": 2, - "fields": { - "foo": { - "kind": 0, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - }, - { - "identifier": "test.recursive-object", - "isReadOnly": false - } - ], - "storedKey": "foo" - } - }, - "allowUnknownOptionalFields": false - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json deleted file mode 100644 index 2a900a4635ed..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "root": { - "kind": 1, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.number", - "isReadOnly": false - }, - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": false - } - ] - }, - "definitions": { - "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 - }, - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - } - } -} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json deleted file mode 100644 index d85b1242c0c8..000000000000 --- a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - allowedTypesIdentifiers.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "root": { - "kind": 0, - "allowedTypesInfo": [ - { - "identifier": "com.fluidframework.leaf.string", - "isReadOnly": true - } - ] - }, - "definitions": { - "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 - } - } -} \ No newline at end of file From 4e970d2e4fd239d15a398331ede6cee8f586117c Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 12:34:37 -0700 Subject: [PATCH 12/47] - Fixed a couple references to allowedTypesIdentifiers. - Regenerated API files. --- .../dds/tree/api-report/tree.alpha.api.md | 4 +-- .../src/explicit-strategy/typeGeneration.ts | 12 +++---- .../api-report/fluid-framework.alpha.api.md | 36 ++++++++++--------- .../tree-agent/src/typeGeneration.ts | 8 ++--- .../data-visualization/DefaultVisualizers.ts | 2 +- .../SharedTreeVisualizer.ts | 6 ++-- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 74c2f789c4df..eefa403492bf 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -317,7 +317,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -517,7 +517,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; + readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system diff --git a/packages/framework/ai-collab/src/explicit-strategy/typeGeneration.ts b/packages/framework/ai-collab/src/explicit-strategy/typeGeneration.ts index 9b39f69387b3..d002061d2b8a 100644 --- a/packages/framework/ai-collab/src/explicit-strategy/typeGeneration.ts +++ b/packages/framework/ai-collab/src/explicit-strategy/typeGeneration.ts @@ -235,14 +235,14 @@ function getOrCreateType( // TODO: Remove when AI better if ( Array.from( - field.allowedTypesIdentifiers, + new Set(field.simpleAllowedTypes.keys()), (n) => definitionMap.get(n) ?? fail("Unknown definition"), ).some((n) => n.kind === NodeKind.Array) ) { continue; } modifyFieldSet.add(key); - for (const type of field.allowedTypesIdentifiers) { + for (const type of field.simpleAllowedTypes.keys()) { modifyTypeSet.add(type); } } @@ -270,7 +270,7 @@ function getOrCreateType( } case NodeKind.Array: { for (const [name] of Array.from( - nodeSchema.allowedTypesIdentifiers, + nodeSchema.simpleAllowedTypes.keys(), (n): [string, SimpleNodeSchema] => [ n, definitionMap.get(n) ?? fail("Unknown definition"), @@ -287,7 +287,7 @@ function getOrCreateType( insertSet, modifyFieldSet, modifyTypeSet, - nodeSchema.allowedTypesIdentifiers, + new Set(nodeSchema.simpleAllowedTypes.keys()), ), ); } @@ -332,7 +332,7 @@ function getOrCreateTypeForField( insertSet, modifyFieldSet, modifyTypeSet, - fieldSchema.allowedTypesIdentifiers, + new Set(fieldSchema.simpleAllowedTypes.keys()), ); } case FieldKind.Optional: { @@ -344,7 +344,7 @@ function getOrCreateTypeForField( insertSet, modifyFieldSet, modifyTypeSet, - fieldSchema.allowedTypesIdentifiers, + new Set(fieldSchema.simpleAllowedTypes.keys()), ), ]); } diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 4b40cd8bc394..5027d34a81be 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -15,6 +15,11 @@ export function adaptEnum, true, Record, undefined>; }[keyof TEnum]>; }; +// @alpha @sealed +export interface AllowedTypeInfo { + readonly isStaged?: boolean; +} + // @alpha @input export interface AllowedTypeMetadata { readonly custom?: unknown; @@ -323,7 +328,7 @@ export class FieldSchemaAlpha; } // @alpha @sealed @system @@ -374,7 +379,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -861,7 +866,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; + readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system @@ -1378,16 +1383,15 @@ export type SharedTreeOptionsBeta = ForestOptions; // @alpha @sealed export interface SimpleArrayNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed export interface SimpleFieldSchema { - readonly allowedTypesIdentifiers: ReadonlySet; readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly stagedSchemaUpgrades?: SchemaUpgrade[]; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1397,7 +1401,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1427,7 +1431,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowedTypesIdentifiers: ReadonlySet; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1478,18 +1482,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system diff --git a/packages/framework/tree-agent/src/typeGeneration.ts b/packages/framework/tree-agent/src/typeGeneration.ts index 9125d322cb31..8b33817a2ced 100644 --- a/packages/framework/tree-agent/src/typeGeneration.ts +++ b/packages/framework/tree-agent/src/typeGeneration.ts @@ -228,7 +228,7 @@ function getOrCreateType( z.string(), getTypeForAllowedTypes( definitionMap, - simpleNodeSchema.allowedTypesIdentifiers, + new Set(simpleNodeSchema.simpleAllowedTypes.keys()), objectCache, bindableSchemas, ), @@ -245,7 +245,7 @@ function getOrCreateType( const zodType = z.record( getTypeForAllowedTypes( definitionMap, - simpleNodeSchema.allowedTypesIdentifiers, + new Set(simpleNodeSchema.simpleAllowedTypes.keys()), objectCache, bindableSchemas, ), @@ -262,7 +262,7 @@ function getOrCreateType( const zodType = z.array( getTypeForAllowedTypes( definitionMap, - simpleNodeSchema.allowedTypesIdentifiers, + new Set(simpleNodeSchema.simpleAllowedTypes.keys()), objectCache, bindableSchemas, ), @@ -327,7 +327,7 @@ function getOrCreateTypeForField( const field = getTypeForAllowedTypes( definitionMap, - fieldSchema.allowedTypesIdentifiers, + new Set(fieldSchema.simpleAllowedTypes.keys()), objectCache, bindableSchemas, ).describe( diff --git a/packages/tools/devtools/devtools-core/src/data-visualization/DefaultVisualizers.ts b/packages/tools/devtools/devtools-core/src/data-visualization/DefaultVisualizers.ts index ab37eedccb34..ae76e4826349 100644 --- a/packages/tools/devtools/devtools-core/src/data-visualization/DefaultVisualizers.ts +++ b/packages/tools/devtools/devtools-core/src/data-visualization/DefaultVisualizers.ts @@ -318,7 +318,7 @@ export const visualizeSharedTree: VisualizeSharedObject = async ( * Since the {@link SimpleTreeSchema.allowedTypes} of each children node is only accessible at the parent field level, * each node's allowed types are computed at the parent field level. */ - const allowedTypes = treeSimpleSchema.root.allowedTypesIdentifiers; + const allowedTypes = new Set(treeSimpleSchema.root.simpleAllowedTypes.keys()); const isRequired = treeSimpleSchema.root.kind === FieldKind.Required; if (treeView === undefined) { diff --git a/packages/tools/devtools/devtools-core/src/data-visualization/SharedTreeVisualizer.ts b/packages/tools/devtools/devtools-core/src/data-visualization/SharedTreeVisualizer.ts index 1657897e9205..6e0d7d372280 100644 --- a/packages/tools/devtools/devtools-core/src/data-visualization/SharedTreeVisualizer.ts +++ b/packages/tools/devtools/devtools-core/src/data-visualization/SharedTreeVisualizer.ts @@ -223,7 +223,7 @@ async function visualizeObjectNode( const objectNodeSchemaProperties: Record = {}; for (const [fieldKey, treeFieldSimpleSchema] of schema.fields) { objectNodeSchemaProperties[fieldKey] = { - allowedTypes: treeFieldSimpleSchema.allowedTypesIdentifiers, + allowedTypes: new Set(treeFieldSimpleSchema.simpleAllowedTypes.keys()), isRequired: treeFieldSimpleSchema.kind === FieldKind.Required ? true : false, }; } @@ -257,7 +257,7 @@ async function visualizeMapNode( const mapNodeSchemaProperties: Record = {}; for (const key of Object.keys(tree.fields)) { mapNodeSchemaProperties[key] = { - allowedTypes: schema.allowedTypesIdentifiers, + allowedTypes: new Set(schema.simpleAllowedTypes.keys()), // Map values are always required. Don't display field requirement information, since that information is redundant. isRequired: undefined, }; @@ -297,7 +297,7 @@ async function visualizeArrayNode( const arrayNodeSchemaProperties: Record = {}; for (const [i] of children.entries()) { arrayNodeSchemaProperties[i] = { - allowedTypes: schema.allowedTypesIdentifiers, + allowedTypes: new Set(schema.simpleAllowedTypes.keys()), // Array values are always required. Don't display field requirement information, since that information is redundant. isRequired: undefined, }; From 55ca84b1bafc5d4e678f4863d5b3b0c53df2a021 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 13:06:11 -0700 Subject: [PATCH 13/47] Minor refactors. Addressed some PR feedback. --- .../dds/tree/api-report/tree.alpha.api.md | 20 +++++++-------- packages/dds/tree/src/index.ts | 2 +- .../dds/tree/src/shared-tree/sharedTree.ts | 25 ++++++++++++------- packages/dds/tree/src/simple-tree/index.ts | 2 +- .../api-report/fluid-framework.alpha.api.md | 20 +++++++-------- 5 files changed, 38 insertions(+), 31 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index eefa403492bf..3b6a8d4ef28e 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -15,11 +15,6 @@ export function adaptEnum, true, Record, undefined>; }[keyof TEnum]>; }; -// @alpha @sealed -export interface AllowedTypeInfo { - readonly isStaged?: boolean; -} - // @alpha @input export interface AllowedTypeMetadata { readonly custom?: unknown; @@ -274,7 +269,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): Map; } // @alpha @sealed @system @@ -1003,9 +998,14 @@ export type SharedTreeOptions = Partial & Partial extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1013,7 +1013,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1023,7 +1023,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1053,7 +1053,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index fe5b4dc865b6..3f2234a7f9c6 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -277,7 +277,7 @@ export { type TreeParsingOptions, type SchemaFactory_base, type NumberKeys, - type AllowedTypeInfo, + type SimpleAllowedTypes, } from "./simple-tree/index.js"; export { SharedTree, diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index 689c67f71fe2..875b01fdd7c3 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -99,7 +99,7 @@ import { FieldKind, type ITreeAlpha, type SimpleObjectFieldSchema, - type AllowedTypeInfo, + type SimpleAllowedTypes, } from "../simple-tree/index.js"; import { SchematizingSimpleTreeView } from "./schematizingTreeView.js"; @@ -952,12 +952,19 @@ export const defaultSharedTreeOptions: Required = { shouldEncodeIncrementally: defaultIncrementalEncodingPolicy, }; -function buildSimpleAllowedTypes(types: TreeTypeSet): ReadonlyMap { - const allowedTypesInfo = new Map(); +/** + * Build the allowed types for a Stored Schema. + * + * @remarks Staged upgrades do not apply to stored schemas, so we omit the flag when building {@link SimpleAllowedTypes}. + * @param types - The types to create allowed types for. + * @returns The allowed types. + */ +function buildSimpleAllowedTypesForStoredSchema( + types: TreeTypeSet, +): ReadonlyMap { + const allowedTypesInfo = new Map(); for (const type of types) { - allowedTypesInfo.set(type, { - isStaged: false, - }); + allowedTypesInfo.set(type, {}); } return allowedTypesInfo; } @@ -984,7 +991,7 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie return { kind, // TODO: Refactor - simpleAllowedTypes: buildSimpleAllowedTypes(schema.types), + simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(schema.types), metadata: {}, persistedMetadata: schema.persistedMetadata, }; @@ -1000,7 +1007,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS if (arrayTypes !== undefined) { return { kind: NodeKind.Array, - simpleAllowedTypes: buildSimpleAllowedTypes(arrayTypes), + simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(arrayTypes), metadata: {}, persistedMetadata: schema.metadata, }; @@ -1019,7 +1026,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS ); return { kind: NodeKind.Map, - simpleAllowedTypes: buildSimpleAllowedTypes(schema.mapFields.types), + simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(schema.mapFields.types), metadata: {}, persistedMetadata: schema.metadata, }; diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 031a61717d6e..f44069c2aaa7 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -193,7 +193,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, - SimpleAllowedTypes as AllowedTypeInfo, + SimpleAllowedTypes, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 5027d34a81be..c48b98040b7e 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -15,11 +15,6 @@ export function adaptEnum, true, Record, undefined>; }[keyof TEnum]>; }; -// @alpha @sealed -export interface AllowedTypeInfo { - readonly isStaged?: boolean; -} - // @alpha @input export interface AllowedTypeMetadata { readonly custom?: unknown; @@ -328,7 +323,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): Map; } // @alpha @sealed @system @@ -1381,9 +1376,14 @@ export type SharedTreeOptions = Partial & Partial extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1391,7 +1391,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1401,7 +1401,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1431,7 +1431,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha From f48aa8a5f3dd217340e8c61719c9a79ba1b863bd Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 13:59:32 -0700 Subject: [PATCH 14/47] - PR feedback. - Regenerated API Extractor output. --- .../dds/tree/api-report/tree.alpha.api.md | 21 ++++++++++--------- .../tree/src/simple-tree/core/allowedTypes.ts | 16 ++++++++++++++ .../simple-tree/node-kinds/array/arrayNode.ts | 8 +------ .../src/simple-tree/node-kinds/map/mapNode.ts | 8 +------ .../node-kinds/record/recordNode.ts | 8 +------ .../api-report/fluid-framework.alpha.api.md | 21 ++++++++++--------- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 3b6a8d4ef28e..981688968504 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -68,6 +68,7 @@ export interface AnnotatedAllowedTypes exte evaluate(): AllowedTypesFullEvaluated; evaluateIdentifiers(): ReadonlySet; evaluateSet(): ReadonlySet; + evaluateSimpleAllowedTypes(): ReadonlyMap; readonly metadata: AllowedTypesMetadata; readonly types: T; } @@ -312,7 +313,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -512,7 +513,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system @@ -1104,18 +1105,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system diff --git a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts index fccac8a0bb99..22932b6549de 100644 --- a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts +++ b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts @@ -25,6 +25,7 @@ import { type TreeNodeSchema, } from "./treeNodeSchema.js"; import { schemaAsTreeNodeValid } from "./treeNodeValid.js"; +import type { SimpleAllowedTypes } from "../simpleSchema.js"; /** * Schema for types allowed in some location in a tree (like a field, map entry or array). @@ -133,6 +134,11 @@ export interface AnnotatedAllowedTypes * rather than identifiers where possible since its more type safe and it is possible that two schema with the same identifier exist. */ evaluateIdentifiers(): ReadonlySet; + + /** + * Get the {@link SimpleAllowedTypes} version of the allowed types set. + */ + evaluateSimpleAllowedTypes(): ReadonlyMap; } /** @@ -253,6 +259,16 @@ export class AnnotatedAllowedTypesInternal< return this.lazyEvaluate.value.identifiers; } + public evaluateSimpleAllowedTypes(): ReadonlyMap { + const simpleAllowedTypes = new Map(); + for (const type of this.evaluate().types) { + simpleAllowedTypes.set(type.type.identifier, { + isStaged: type.metadata.stagedSchemaUpgrade !== undefined, + }); + } + return simpleAllowedTypes; + } + public static override [Symbol.hasInstance]< TThis extends | (abstract new ( diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index 087197963854..872cbea84caa 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -1170,13 +1170,7 @@ export function arraySchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - const map = new Map(); - for (const type of normalizedTypes.evaluate()) { - map.set(type.identifier, { - isStaged: false, - }); - } - return map; + return normalizedTypes.evaluateSimpleAllowedTypes(); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index c3fb433a66a4..72f795a41e97 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -277,13 +277,7 @@ export function mapSchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - const map = new Map(); - for (const type of normalizedTypes.evaluate()) { - map.set(type.identifier, { - isStaged: false, - }); - } - return map; + return normalizedTypes.evaluateSimpleAllowedTypes(); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index fa554babfda0..b30051686eda 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -260,13 +260,7 @@ export function recordSchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - const map = new Map(); - for (const type of normalizedTypes.evaluate()) { - map.set(type.identifier, { - isStaged: false, - }); - } - return map; + return normalizedTypes.evaluateSimpleAllowedTypes(); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index c48b98040b7e..9e1bb78fe46a 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -68,6 +68,7 @@ export interface AnnotatedAllowedTypes exte evaluate(): AllowedTypesFullEvaluated; evaluateIdentifiers(): ReadonlySet; evaluateSet(): ReadonlySet; + evaluateSimpleAllowedTypes(): ReadonlyMap; readonly metadata: AllowedTypesMetadata; readonly types: T; } @@ -374,7 +375,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -861,7 +862,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system @@ -1482,18 +1483,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system From ee473df21324edfdfd937baf7b0ef376ceb6f114 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 14:02:34 -0700 Subject: [PATCH 15/47] Removed change that shouldn't be in this PR. --- packages/dds/tree/src/simple-tree/simpleSchema.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index bffc698dd640..b1b8eaf4b633 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -58,13 +58,6 @@ export interface SimpleObjectNodeSchema * especially if/when TreeNodeSchema for objects provide more maps. */ readonly fields: ReadonlyMap; - - /** - * Whether the object node allows unknown optional fields. - * - * @remarks Used for compatibility checks. Not available in all cases. - */ - readonly allowUnknownOptionalFields?: boolean; } /** From 9bf18105fc95d3577f305a0603aa48e710246acc Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 15:53:05 -0700 Subject: [PATCH 16/47] - Removed evaluateSimpleAllowedTypes from AnnotatedAllowedTypes (which is beta). - PR feedback. --- .../dds/tree/api-report/tree.alpha.api.md | 22 +++++++++---------- .../dds/tree/src/shared-tree/sharedTree.ts | 4 ++-- .../src/simple-tree/api/schemaFromSimple.ts | 3 ++- .../tree/src/simple-tree/core/allowedTypes.ts | 14 ++++++------ .../dds/tree/src/simple-tree/fieldSchema.ts | 2 +- .../simple-tree/node-kinds/array/arrayNode.ts | 3 ++- .../src/simple-tree/node-kinds/map/mapNode.ts | 3 ++- .../node-kinds/record/recordNode.ts | 3 ++- .../dds/tree/src/simple-tree/simpleSchema.ts | 2 +- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 1e6d7a64b4e8..405687750927 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -68,7 +68,6 @@ export interface AnnotatedAllowedTypes exte evaluate(): AllowedTypesFullEvaluated; evaluateIdentifiers(): ReadonlySet; evaluateSet(): ReadonlySet; - evaluateSimpleAllowedTypes(): ReadonlyMap; readonly metadata: AllowedTypesMetadata; readonly types: T; } @@ -276,7 +275,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): ReadonlyMap; } // @alpha @sealed @system @@ -1009,7 +1008,7 @@ export type SharedTreeOptionsBeta = ForestOptions; // @alpha @sealed export interface SimpleAllowedTypes { - readonly isStaged?: boolean; + readonly isStaged: boolean | undefined; } // @alpha @sealed @@ -1056,7 +1055,6 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } @@ -1113,18 +1111,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index 2d4819dc7252..bfc0dbe860d9 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -966,7 +966,8 @@ function buildSimpleAllowedTypesForStoredSchema( ): ReadonlyMap { const allowedTypesInfo = new Map(); for (const type of types) { - allowedTypesInfo.set(type, {}); + // Stored schemas do not have staged upgrades + allowedTypesInfo.set(type, { isStaged: undefined }); } return allowedTypesInfo; } @@ -992,7 +993,6 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie } return { kind, - // TODO: Refactor simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(schema.types), metadata: {}, persistedMetadata: schema.persistedMetadata, diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts index d8f2b3e8e7ce..f396ae0cef75 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -89,7 +89,8 @@ function generateAllowedTypes( allowed: ReadonlyMap, context: Context, ): AllowedTypes { - return [...new Set(allowed.keys())].map( + return Array.from( + allowed.keys(), (id) => context.get(id) ?? fail(0xb5a /* Missing schema */), ); } diff --git a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts index 3e936f0cb02f..27b6dcc24bd6 100644 --- a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts +++ b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts @@ -134,11 +134,6 @@ export interface AnnotatedAllowedTypes * rather than identifiers where possible since its more type safe and it is possible that two schema with the same identifier exist. */ evaluateIdentifiers(): ReadonlySet; - - /** - * Get the {@link SimpleAllowedTypes} version of the allowed types set. - */ - evaluateSimpleAllowedTypes(): ReadonlyMap; } /** @@ -258,9 +253,14 @@ export class AnnotatedAllowedTypesInternal< return this.lazyEvaluate.value.identifiers; } - public evaluateSimpleAllowedTypes(): ReadonlyMap { + /** + * Get the {@link SimpleAllowedTypes} version of the allowed types set. + */ + public static evaluateSimpleAllowedTypes( + annotatedAllowedTypes: AnnotatedAllowedTypes, + ): ReadonlyMap { const simpleAllowedTypes = new Map(); - for (const type of this.evaluate().types) { + for (const type of annotatedAllowedTypes.evaluate().types) { simpleAllowedTypes.set(type.type.identifier, { isStaged: type.metadata.stagedSchemaUpgrade !== undefined, }); diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index 2dfd85cfe0f4..3ce6e9e44298 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -412,7 +412,7 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } - public get simpleAllowedTypes(): Map { + public get simpleAllowedTypes(): ReadonlyMap { const types = this.allowedTypesFull.evaluate().types; const info = new Map(); diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index c202df58274e..5d2d3973ba6f 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -47,6 +47,7 @@ import { type TreeNodeSchemaPrivateData, convertAllowedTypes, withBufferedTreeEvents, + AnnotatedAllowedTypesInternal, } from "../../core/index.js"; import { type FactoryContent, @@ -1170,7 +1171,7 @@ export function arraySchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - return normalizedTypes.evaluateSimpleAllowedTypes(); + return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index 99c62dd134b2..b9bfdf0d9589 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -43,6 +43,7 @@ import { type FlexContent, type TreeNodeSchemaPrivateData, convertAllowedTypes, + AnnotatedAllowedTypesInternal, } from "../../core/index.js"; import { unhydratedFlexTreeFromInsertable, @@ -277,7 +278,7 @@ export function mapSchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - return normalizedTypes.evaluateSimpleAllowedTypes(); + return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index ec9db6c90c33..e26b7427a905 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -32,6 +32,7 @@ import { CompatibilityLevel, type TreeNodeSchemaPrivateData, convertAllowedTypes, + AnnotatedAllowedTypesInternal, } from "../../core/index.js"; import { getTreeNodeSchemaInitializedData } from "../../createContext.js"; import { tryGetTreeNodeForField } from "../../getTreeNodeForField.js"; @@ -260,7 +261,7 @@ export function recordSchema< () => new Set(normalizedTypes.evaluate().map((type) => type.identifier)), ); const lazySimpleAllowedTypes = new Lazy(() => { - return normalizedTypes.evaluateSimpleAllowedTypes(); + return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes); }); let privateData: TreeNodeSchemaPrivateData | undefined; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index b1b8eaf4b633..00703432aecf 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -176,7 +176,7 @@ export interface SimpleAllowedTypes { * * Undefined if derived from a stored schema. */ - readonly isStaged?: boolean; + readonly isStaged: boolean | undefined; } /** From 97e1b12774e871086a7a5dc394d1d9fdcdca1358 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 15:54:43 -0700 Subject: [PATCH 17/47] Apply suggestion from @noencke Co-authored-by: Noah Encke <78610362+noencke@users.noreply.github.com> --- packages/dds/tree/src/shared-tree/sharedTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index bfc0dbe860d9..2d623a9cf567 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -957,7 +957,7 @@ export const defaultSharedTreeOptions: Required = { /** * Build the allowed types for a Stored Schema. * - * @remarks Staged upgrades do not apply to stored schemas, so we omit the flag when building {@link SimpleAllowedTypes}. + * @remarks Staged upgrades do not apply to stored schemas, so we omit the {@link SimpleAllowedTypes.isStaged | staging flag } when building {@link SimpleAllowedTypes}. * @param types - The types to create allowed types for. * @returns The allowed types. */ From 6eddd774ea48be02511bcaa23cf98ca1fc9998ec Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 16:04:01 -0700 Subject: [PATCH 18/47] Rename. --- .../dds/tree/api-report/tree.alpha.api.md | 16 ++++---- .../src/simple-tree/api/schemaFromSimple.ts | 4 +- .../tree/src/simple-tree/core/allowedTypes.ts | 8 ++-- .../dds/tree/src/simple-tree/fieldSchema.ts | 6 +-- packages/dds/tree/src/simple-tree/index.ts | 2 +- .../simple-tree/node-kinds/array/arrayNode.ts | 4 +- .../src/simple-tree/node-kinds/map/mapNode.ts | 4 +- .../node-kinds/record/recordNode.ts | 4 +- .../dds/tree/src/simple-tree/simpleSchema.ts | 10 ++--- .../api/simpleSchemaToJsonSchema.spec.ts | 38 +++++++++---------- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 405687750927..49849db9e795 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -1111,18 +1111,18 @@ export namespace System_TableSchema { }>; // @system export function createTableSchema, const TRowSchema extends RowSchemaBase>(inputSchemaFactory: SchemaFactoryBeta, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2, "Table">, NodeKind.Object, true, { - readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; - readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; + readonly rows: TreeNodeSchemaClass, "Table.rows">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>, Iterable>, true, TRowSchema, undefined>; + readonly columns: TreeNodeSchemaClass, "Table.columns">, NodeKind.Array, TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>, Iterable>, true, TColumnSchema, undefined>; }, object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }, unknown> & (new (data: InternalTreeNode | (object & { - readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; })) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>) & { empty, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; - readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; + readonly rows: (InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.rows">, NodeKind.Array, true, TRowSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TRowSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.rows">, NodeKind.Array, unknown>)>; + readonly columns: (InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)> | undefined) & InsertableTypedNode_2, "Table.columns">, NodeKind.Array, true, TColumnSchema, Iterable>, unknown> & (new (data?: InternalTreeNode | Iterable> | undefined) => TreeArrayNode : TreeNodeFromImplicitAllowedTypes, [TColumnSchema] extends [ImplicitAllowedTypes] ? InsertableTreeNodeFromImplicitAllowedTypes : never, ReadonlyArrayNode_2> & WithType, "Table.columns">, NodeKind.Array, unknown>)>; }) => TreeNode & TableSchema.Table & WithType, "Table">, NodeKind, unknown>>(this: TThis): InstanceType; }; // @system diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts index f396ae0cef75..0892005387b5 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -13,7 +13,7 @@ import { type FieldProps, } from "../fieldSchema.js"; import type { - SimpleAllowedTypes, + SimpleAllowedTypesAttributes, SimpleFieldSchema, SimpleNodeSchema, SimpleTreeSchema, @@ -86,7 +86,7 @@ function generateFieldSchema( } function generateAllowedTypes( - allowed: ReadonlyMap, + allowed: ReadonlyMap, context: Context, ): AllowedTypes { return Array.from( diff --git a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts index 27b6dcc24bd6..e4b667ce729d 100644 --- a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts +++ b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts @@ -25,7 +25,7 @@ import { type TreeNodeSchema, } from "./treeNodeSchema.js"; import { schemaAsTreeNodeValid } from "./treeNodeValid.js"; -import type { SimpleAllowedTypes } from "../simpleSchema.js"; +import type { SimpleAllowedTypesAttributes } from "../simpleSchema.js"; /** * Schema for types allowed in some location in a tree (like a field, map entry or array). @@ -254,12 +254,12 @@ export class AnnotatedAllowedTypesInternal< } /** - * Get the {@link SimpleAllowedTypes} version of the allowed types set. + * Get the {@link SimpleAllowedTypesAttributes} version of the allowed types set. */ public static evaluateSimpleAllowedTypes( annotatedAllowedTypes: AnnotatedAllowedTypes, - ): ReadonlyMap { - const simpleAllowedTypes = new Map(); + ): ReadonlyMap { + const simpleAllowedTypes = new Map(); for (const type of annotatedAllowedTypes.evaluate().types) { simpleAllowedTypes.set(type.type.identifier, { isStaged: type.metadata.stagedSchemaUpgrade !== undefined, diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index 3ce6e9e44298..ecaff7cb0849 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -31,7 +31,7 @@ import type { } from "./core/index.js"; import { normalizeAllowedTypes } from "./core/index.js"; -import type { SimpleAllowedTypes, SimpleFieldSchema } from "./simpleSchema.js"; +import type { SimpleAllowedTypesAttributes, SimpleFieldSchema } from "./simpleSchema.js"; import type { UnsafeUnknownSchema } from "./unsafeUnknownSchema.js"; import type { InsertableContent } from "./unhydratedFlexTreeFromInsertable.js"; @@ -412,9 +412,9 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } - public get simpleAllowedTypes(): ReadonlyMap { + public get simpleAllowedTypes(): ReadonlyMap { const types = this.allowedTypesFull.evaluate().types; - const info = new Map(); + const info = new Map(); for (const type of types) { info.set(type.type.identifier, { diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 4f5ccf858dce..8bbdd17254cd 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -198,7 +198,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, - SimpleAllowedTypes, + SimpleAllowedTypesAttributes as SimpleAllowedTypes, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index 5d2d3973ba6f..b93c36e80de8 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -69,7 +69,7 @@ import type { import { brand, type JsonCompatibleReadOnlyObject } from "../../../util/index.js"; import { nullSchema } from "../../leafNodeSchema.js"; import { arrayNodeStoredSchema } from "../../toStoredSchema.js"; -import type { SimpleAllowedTypes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; /** * A covariant base type for {@link (TreeArrayNode:interface)}. @@ -1211,7 +1211,7 @@ export function arraySchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index b9bfdf0d9589..b5df401197f7 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -67,7 +67,7 @@ import type { import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { SimpleAllowedTypes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; /** * A map of string keys to tree objects. @@ -308,7 +308,7 @@ export function mapSchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index e26b7427a905..784a99bbcad8 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -59,7 +59,7 @@ import { prepareForInsertion } from "../../prepareForInsertion.js"; import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { SimpleAllowedTypes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; /** * Create a proxy which implements the {@link TreeRecordNode} API. @@ -352,7 +352,7 @@ export function recordSchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 00703432aecf..ea73b1dff1dd 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -91,7 +91,7 @@ export interface SimpleArrayNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -108,7 +108,7 @@ export interface SimpleMapNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -125,7 +125,7 @@ export interface SimpleRecordNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -168,7 +168,7 @@ export type SimpleNodeSchema = * @alpha * @sealed */ -export interface SimpleAllowedTypes { +export interface SimpleAllowedTypesAttributes { /** * True if this schema is included as a {@link SchemaStaticsAlpha.staged | staged} schema upgrade, * allowing the view schema be compatible with stored schema with (post upgrade) or without it (pre-upgrade). @@ -201,7 +201,7 @@ export interface SimpleFieldSchema { * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; /** * {@inheritDoc FieldSchemaMetadata} diff --git a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts index 9f67b9ecc7a8..b42ed1d8551c 100644 --- a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts @@ -21,7 +21,7 @@ import { } from "../../../simple-tree/index.js"; import { getJsonValidator } from "./jsonSchemaUtilities.js"; import type { - SimpleAllowedTypes, + SimpleAllowedTypesAttributes, SimpleNodeSchema, SimpleTreeSchema, // eslint-disable-next-line import/no-internal-modules @@ -50,7 +50,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -88,7 +88,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.handle", { isStaged: false }], ]), }, @@ -112,7 +112,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.array", { isStaged: false }], ]), metadata: {}, @@ -124,7 +124,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Array, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -169,7 +169,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.map", { isStaged: false }], ]), metadata: {}, @@ -181,7 +181,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Map, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -238,7 +238,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.record", { isStaged: false }], ]), metadata: {}, @@ -250,7 +250,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Record, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -354,7 +354,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -370,7 +370,7 @@ describe("simpleSchemaToJsonSchema", () => { "foo", { kind: FieldKind.Optional, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], ]), metadata: { description: "A number representing the concept of Foo." }, @@ -382,7 +382,7 @@ describe("simpleSchemaToJsonSchema", () => { "bar", { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { description: "A string representing the concept of Bar." }, @@ -394,7 +394,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { @@ -499,7 +499,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -515,7 +515,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), storedKey: "id", @@ -559,7 +559,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -577,7 +577,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], [stringSchema.identifier, { isStaged: false }], ]), @@ -629,7 +629,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.recursive-object", { isStaged: false }], ]), }, @@ -647,7 +647,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Optional, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ["test.recursive-object", { isStaged: false }], ]), From f4e16ea69627124ec8dcc0b1fbc6ab6d81f4559c Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 16:32:24 -0700 Subject: [PATCH 19/47] API Extractor. --- .../api-report/fluid-framework.alpha.api.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 2cefa7f013db..363233850da2 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -68,7 +68,6 @@ export interface AnnotatedAllowedTypes exte evaluate(): AllowedTypesFullEvaluated; evaluateIdentifiers(): ReadonlySet; evaluateSet(): ReadonlySet; - evaluateSimpleAllowedTypes(): ReadonlyMap; readonly metadata: AllowedTypesMetadata; readonly types: T; } @@ -330,7 +329,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): ReadonlyMap; } // @alpha @sealed @system @@ -1386,13 +1385,13 @@ export type SharedTreeOptions = Partial & Partial extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1400,7 +1399,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1410,7 +1409,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1434,13 +1433,12 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha From d00a237b30ca1435a460cb75a73e07680acb8778 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 16:43:09 -0700 Subject: [PATCH 20/47] Rename. --- .../src/simple-tree/api/schemaFromSimple.ts | 4 +- .../tree/src/simple-tree/core/allowedTypes.ts | 8 ++-- .../dds/tree/src/simple-tree/fieldSchema.ts | 6 +-- packages/dds/tree/src/simple-tree/index.ts | 2 +- .../simple-tree/node-kinds/array/arrayNode.ts | 4 +- .../src/simple-tree/node-kinds/map/mapNode.ts | 4 +- .../node-kinds/record/recordNode.ts | 4 +- .../dds/tree/src/simple-tree/simpleSchema.ts | 10 ++--- .../api/simpleSchemaToJsonSchema.spec.ts | 38 +++++++++---------- .../api-report/fluid-framework.alpha.api.md | 12 +++--- 10 files changed, 46 insertions(+), 46 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts index 0892005387b5..0fe717923caa 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -13,7 +13,7 @@ import { type FieldProps, } from "../fieldSchema.js"; import type { - SimpleAllowedTypesAttributes, + SimpleAllowedTypeAttributes, SimpleFieldSchema, SimpleNodeSchema, SimpleTreeSchema, @@ -86,7 +86,7 @@ function generateFieldSchema( } function generateAllowedTypes( - allowed: ReadonlyMap, + allowed: ReadonlyMap, context: Context, ): AllowedTypes { return Array.from( diff --git a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts index e4b667ce729d..957bb57daaac 100644 --- a/packages/dds/tree/src/simple-tree/core/allowedTypes.ts +++ b/packages/dds/tree/src/simple-tree/core/allowedTypes.ts @@ -25,7 +25,7 @@ import { type TreeNodeSchema, } from "./treeNodeSchema.js"; import { schemaAsTreeNodeValid } from "./treeNodeValid.js"; -import type { SimpleAllowedTypesAttributes } from "../simpleSchema.js"; +import type { SimpleAllowedTypeAttributes } from "../simpleSchema.js"; /** * Schema for types allowed in some location in a tree (like a field, map entry or array). @@ -254,12 +254,12 @@ export class AnnotatedAllowedTypesInternal< } /** - * Get the {@link SimpleAllowedTypesAttributes} version of the allowed types set. + * Get the {@link SimpleAllowedTypeAttributes} version of the allowed types set. */ public static evaluateSimpleAllowedTypes( annotatedAllowedTypes: AnnotatedAllowedTypes, - ): ReadonlyMap { - const simpleAllowedTypes = new Map(); + ): ReadonlyMap { + const simpleAllowedTypes = new Map(); for (const type of annotatedAllowedTypes.evaluate().types) { simpleAllowedTypes.set(type.type.identifier, { isStaged: type.metadata.stagedSchemaUpgrade !== undefined, diff --git a/packages/dds/tree/src/simple-tree/fieldSchema.ts b/packages/dds/tree/src/simple-tree/fieldSchema.ts index ecaff7cb0849..994855450f85 100644 --- a/packages/dds/tree/src/simple-tree/fieldSchema.ts +++ b/packages/dds/tree/src/simple-tree/fieldSchema.ts @@ -31,7 +31,7 @@ import type { } from "./core/index.js"; import { normalizeAllowedTypes } from "./core/index.js"; -import type { SimpleAllowedTypesAttributes, SimpleFieldSchema } from "./simpleSchema.js"; +import type { SimpleAllowedTypeAttributes, SimpleFieldSchema } from "./simpleSchema.js"; import type { UnsafeUnknownSchema } from "./unsafeUnknownSchema.js"; import type { InsertableContent } from "./unhydratedFlexTreeFromInsertable.js"; @@ -412,9 +412,9 @@ export class FieldSchemaAlpha< return this.allowedTypesFull.evaluateIdentifiers(); } - public get simpleAllowedTypes(): ReadonlyMap { + public get simpleAllowedTypes(): ReadonlyMap { const types = this.allowedTypesFull.evaluate().types; - const info = new Map(); + const info = new Map(); for (const type of types) { info.set(type.type.identifier, { diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 8bbdd17254cd..d27f31165506 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -198,7 +198,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, - SimpleAllowedTypesAttributes as SimpleAllowedTypes, + SimpleAllowedTypeAttributes as SimpleAllowedTypes, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, diff --git a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts index b93c36e80de8..d01219ee6482 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/array/arrayNode.ts @@ -69,7 +69,7 @@ import type { import { brand, type JsonCompatibleReadOnlyObject } from "../../../util/index.js"; import { nullSchema } from "../../leafNodeSchema.js"; import { arrayNodeStoredSchema } from "../../toStoredSchema.js"; -import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js"; /** * A covariant base type for {@link (TreeArrayNode:interface)}. @@ -1211,7 +1211,7 @@ export function arraySchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts index b5df401197f7..f05501dad65e 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/map/mapNode.ts @@ -67,7 +67,7 @@ import type { import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js"; /** * A map of string keys to tree objects. @@ -308,7 +308,7 @@ export function mapSchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts index 784a99bbcad8..3ce793d0ad6a 100644 --- a/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts +++ b/packages/dds/tree/src/simple-tree/node-kinds/record/recordNode.ts @@ -59,7 +59,7 @@ import { prepareForInsertion } from "../../prepareForInsertion.js"; import { recordLikeDataToFlexContent } from "../common.js"; import { MapNodeStoredSchema } from "../../../core/index.js"; import type { NodeSchemaOptionsAlpha } from "../../api/index.js"; -import type { SimpleAllowedTypesAttributes } from "../../simpleSchema.js"; +import type { SimpleAllowedTypeAttributes } from "../../simpleSchema.js"; /** * Create a proxy which implements the {@link TreeRecordNode} API. @@ -352,7 +352,7 @@ export function recordSchema< return lazyAllowedTypesIdentifiers.value; } - public static get simpleAllowedTypes(): ReadonlyMap { + public static get simpleAllowedTypes(): ReadonlyMap { return lazySimpleAllowedTypes.value; } diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index ea73b1dff1dd..9d9d41bc30be 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -91,7 +91,7 @@ export interface SimpleArrayNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -108,7 +108,7 @@ export interface SimpleMapNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -125,7 +125,7 @@ export interface SimpleRecordNodeSchema * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } /** @@ -168,7 +168,7 @@ export type SimpleNodeSchema = * @alpha * @sealed */ -export interface SimpleAllowedTypesAttributes { +export interface SimpleAllowedTypeAttributes { /** * True if this schema is included as a {@link SchemaStaticsAlpha.staged | staged} schema upgrade, * allowing the view schema be compatible with stored schema with (post upgrade) or without it (pre-upgrade). @@ -201,7 +201,7 @@ export interface SimpleFieldSchema { * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. */ - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; /** * {@inheritDoc FieldSchemaMetadata} diff --git a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts index b42ed1d8551c..6bb4a8ccb03a 100644 --- a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts @@ -21,7 +21,7 @@ import { } from "../../../simple-tree/index.js"; import { getJsonValidator } from "./jsonSchemaUtilities.js"; import type { - SimpleAllowedTypesAttributes, + SimpleAllowedTypeAttributes, SimpleNodeSchema, SimpleTreeSchema, // eslint-disable-next-line import/no-internal-modules @@ -50,7 +50,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -88,7 +88,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.handle", { isStaged: false }], ]), }, @@ -112,7 +112,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.array", { isStaged: false }], ]), metadata: {}, @@ -124,7 +124,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Array, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -169,7 +169,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.map", { isStaged: false }], ]), metadata: {}, @@ -181,7 +181,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Map, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -238,7 +238,7 @@ describe("simpleSchemaToJsonSchema", () => { const input: SimpleTreeSchema = { root: { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.record", { isStaged: false }], ]), metadata: {}, @@ -250,7 +250,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Record, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), }, @@ -354,7 +354,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -370,7 +370,7 @@ describe("simpleSchemaToJsonSchema", () => { "foo", { kind: FieldKind.Optional, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], ]), metadata: { description: "A number representing the concept of Foo." }, @@ -382,7 +382,7 @@ describe("simpleSchemaToJsonSchema", () => { "bar", { kind: FieldKind.Required, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { description: "A string representing the concept of Bar." }, @@ -394,7 +394,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), metadata: { @@ -499,7 +499,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -515,7 +515,7 @@ describe("simpleSchemaToJsonSchema", () => { "id", { kind: FieldKind.Identifier, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ]), storedKey: "id", @@ -559,7 +559,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.object", { isStaged: false }], ]), }, @@ -577,7 +577,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Required, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [numberSchema.identifier, { isStaged: false }], [stringSchema.identifier, { isStaged: false }], ]), @@ -629,7 +629,7 @@ describe("simpleSchemaToJsonSchema", () => { root: { kind: FieldKind.Required, metadata: {}, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ ["test.recursive-object", { isStaged: false }], ]), }, @@ -647,7 +647,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: FieldKind.Optional, metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([ + simpleAllowedTypes: new Map([ [stringSchema.identifier, { isStaged: false }], ["test.recursive-object", { isStaged: false }], ]), diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 363233850da2..0e2137f1dfaa 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -329,7 +329,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): ReadonlyMap; } // @alpha @sealed @system @@ -1385,13 +1385,13 @@ export type SharedTreeOptions = Partial & Partial extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1399,7 +1399,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1409,7 +1409,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1438,7 +1438,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha From 550240b9aa31186a9a3ea65b8bacbb1c0e8d2c48 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 29 Oct 2025 20:41:15 -0700 Subject: [PATCH 21/47] Fixed exports. --- packages/dds/tree/api-report/tree.alpha.api.md | 12 ++++++------ packages/dds/tree/src/index.ts | 2 +- .../dds/tree/src/shared-tree/sharedTree.ts | 18 ++++++++++-------- packages/dds/tree/src/simple-tree/index.ts | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 49849db9e795..d512f1598436 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -275,7 +275,7 @@ export class FieldSchemaAlpha; + get simpleAllowedTypes(): ReadonlyMap; } // @alpha @sealed @system @@ -1007,13 +1007,13 @@ export type SharedTreeOptions = Partial & Partial extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1021,7 +1021,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; readonly metadata: FieldSchemaMetadata; readonly persistedMetadata?: JsonCompatibleReadOnlyObject | undefined; - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @sealed @@ -1031,7 +1031,7 @@ export interface SimpleLeafNodeSchema extends SimpleNodeSchemaBaseAlpha extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha @@ -1060,7 +1060,7 @@ export interface SimpleObjectNodeSchema extends S // @alpha @sealed export interface SimpleRecordNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly simpleAllowedTypes: ReadonlyMap; + readonly simpleAllowedTypes: ReadonlyMap; } // @alpha diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 77304ad20c3d..95adf131fec3 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -280,7 +280,7 @@ export { type TreeParsingOptions, type SchemaFactory_base, type NumberKeys, - type SimpleAllowedTypes, + type SimpleAllowedTypeAttributes, } from "./simple-tree/index.js"; export { SharedTree, diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index 2d623a9cf567..3e14a3434b9e 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -99,7 +99,7 @@ import { FieldKind, type ITreeAlpha, type SimpleObjectFieldSchema, - type SimpleAllowedTypes, + type SimpleAllowedTypeAttributes, } from "../simple-tree/index.js"; import { SchematizingSimpleTreeView } from "./schematizingTreeView.js"; @@ -957,14 +957,14 @@ export const defaultSharedTreeOptions: Required = { /** * Build the allowed types for a Stored Schema. * - * @remarks Staged upgrades do not apply to stored schemas, so we omit the {@link SimpleAllowedTypes.isStaged | staging flag } when building {@link SimpleAllowedTypes}. + * @remarks Staged upgrades do not apply to stored schemas, so we omit the {@link SimpleAllowedTypeAttributes.isStaged | staging flag } when building {@link SimpleAllowedTypeAttributes}. * @param types - The types to create allowed types for. * @returns The allowed types. */ -function buildSimpleAllowedTypesForStoredSchema( +function buildSimpleAllowedTypeAttributesForStoredSchema( types: TreeTypeSet, -): ReadonlyMap { - const allowedTypesInfo = new Map(); +): ReadonlyMap { + const allowedTypesInfo = new Map(); for (const type of types) { // Stored schemas do not have staged upgrades allowedTypesInfo.set(type, { isStaged: undefined }); @@ -993,7 +993,7 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie } return { kind, - simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(schema.types), + simpleAllowedTypes: buildSimpleAllowedTypeAttributesForStoredSchema(schema.types), metadata: {}, persistedMetadata: schema.persistedMetadata, }; @@ -1009,7 +1009,7 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS if (arrayTypes !== undefined) { return { kind: NodeKind.Array, - simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(arrayTypes), + simpleAllowedTypes: buildSimpleAllowedTypeAttributesForStoredSchema(arrayTypes), metadata: {}, persistedMetadata: schema.metadata, }; @@ -1028,7 +1028,9 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS ); return { kind: NodeKind.Map, - simpleAllowedTypes: buildSimpleAllowedTypesForStoredSchema(schema.mapFields.types), + simpleAllowedTypes: buildSimpleAllowedTypeAttributesForStoredSchema( + schema.mapFields.types, + ), metadata: {}, persistedMetadata: schema.metadata, }; diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index d27f31165506..5c23b174daa6 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -198,7 +198,7 @@ export type { SimpleNodeSchemaBaseAlpha, SimpleObjectFieldSchema, SimpleRecordNodeSchema, - SimpleAllowedTypeAttributes as SimpleAllowedTypes, + SimpleAllowedTypeAttributes, } from "./simpleSchema.js"; export { type ImplicitFieldSchema, From 1ca90646101280dc7b18b823cfea6a60af888010 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 30 Oct 2025 10:58:23 -0700 Subject: [PATCH 22/47] Re-implemented most of the PR on top of the new changes to SimpleSchema. One test currently fails. This reverts commit 2e443d4becd85b5423b16887cee97c6a30d86bc7. --- .../dds/tree/api-report/tree.alpha.api.md | 4 + packages/dds/tree/src/index.ts | 1 + .../dds/tree/src/simple-tree/api/index.ts | 2 + .../api/serializableCompatibilitySchema.ts | 176 +++ .../api/viewSchemaToSimpleSchema.ts | 156 ++- .../viewSchemaToViewCompatibilitySchema.ts | 65 ++ packages/dds/tree/src/simple-tree/index.ts | 4 + .../dds/tree/src/simple-tree/simpleSchema.ts | 18 +- .../simple-tree/api/getSimpleSchema.spec.ts | 1018 ++++++++++++----- .../getViewCompatibilityTreeSchema.spec.ts | 46 + ...w compatibility schema - Array schema.json | 24 + ...w compatibility schema - Field Schema.json | 16 + ...view compatibility schema - Leaf node.json | 16 + ...iew compatibility schema - Map schema.json | 24 + ...Object schema including a union field.json | 38 + ... schema including an identifier field.json | 31 + ... compatibility schema - Object schema.json | 44 + ... compatibility schema - Record schema.json | 24 + ...lity schema - Recursive object schema.json | 34 + ...iew compatibility schema - Union root.json | 23 + ...atibility schema - simpleAllowedTypes.json | 16 + .../api-report/fluid-framework.alpha.api.md | 4 + 22 files changed, 1462 insertions(+), 322 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts create mode 100644 packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts create mode 100644 packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json create mode 100644 packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index d512f1598436..8db4c9c506ca 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -1055,6 +1055,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { + readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } @@ -1311,6 +1312,9 @@ export namespace TableSchema { }): System_TableSchema.TableSchemaBase; } +// @alpha +export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; + // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 95adf131fec3..147b8aaba83e 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -342,3 +342,4 @@ export { JsonAsTree } from "./jsonDomainSchema.js"; export { FluidSerializableAsTree } from "./serializableDomainSchema.js"; export { TableSchema, type System_TableSchema } from "./tableSchema.js"; export { asAlpha, asBeta } from "./api.js"; +export { toViewCompatibilityTreeSchema } from "./simple-tree/index.js"; diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 1c930da6ee7e..a19df200c11a 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -162,8 +162,10 @@ export { export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; export { toSimpleTreeSchema } from "./viewSchemaToSimpleSchema.js"; +export { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; export type { TreeChangeEvents } from "./treeChangeEvents.js"; export { getShouldIncrementallySummarizeAllowedTypes, incrementalSummaryHint, } from "./incrementalAllowedTypes.js"; +export { toSerializableCompatibilitySchema } from "./serializableCompatibilitySchema.js"; diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts new file mode 100644 index 000000000000..dad65b6aae1c --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -0,0 +1,176 @@ +import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; +import type { TreeSchema } from "./configuration.js"; +import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; +import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; +import type { + SimpleAllowedTypeAttributes, + SimpleArrayNodeSchema, + SimpleFieldSchema, + SimpleLeafNodeSchema, + SimpleMapNodeSchema, + SimpleNodeSchema, + SimpleObjectFieldSchema, + SimpleObjectNodeSchema, + SimpleRecordNodeSchema, +} from "../simpleSchema.js"; +import { NodeKind } from "../core/index.js"; + +/** + * Converts a view schema to a serializable format for compatibility testing. + * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. + * It is not possible to reconstruct a full view schema from the serialized format. + * @param treeSchema - The tree schema to convert. + * @returns A serializable representation of the view schema. + */ +export function toSerializableCompatibilitySchema(treeSchema: TreeSchema): JsonCompatible { + const simpleSchema = toViewCompatibilityTreeSchema( + treeSchema, + // Copying strips out references to metadata and other non-essential information. + true, + ); + + // Convert types to serializable forms + const serializableDefinitions = new Map(); + + for (const [identifier, schema] of simpleSchema.definitions) { + const serializableDefinition = nodeSchemaToSerializable(schema); + serializableDefinitions.set(identifier, serializableDefinition); + } + + const serializableSchema = { + root: toSerializableField(simpleSchema.root), + definitions: mapToRecord(serializableDefinitions), + } as unknown as JsonCompatible; + + return serializableSchema; +} + +/** + * Converts a node schema to a serializable object. + * @param schema - The node schema to convert. + * @returns A serializable representation of the node schema. + */ +function nodeSchemaToSerializable(schema: SimpleNodeSchema): JsonCompatibleObject { + const kind = schema.kind; + switch (kind) { + case NodeKind.Leaf: + return leafNodeToSerializable(schema); + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return containerNodeToSerializable(schema); + case NodeKind.Object: + return objectNodeToSerializable(schema); + default: { + unreachableCase(kind); + } + } +} + +/** + * Converts a leaf node schema to a serializable object. + * @param schema - The leaf node schema to convert. + * @returns A serializable representation of the leaf node schema. + */ +function leafNodeToSerializable(schema: SimpleLeafNodeSchema): JsonCompatibleObject { + return { + kind: schema.kind, + leafKind: schema.leafKind, + }; +} + +/** + * Converts a container node schema to a serializable object. + * @param schema - The container node schema to convert. + * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different + * container kinds. + */ +function containerNodeToSerializable( + schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, +): JsonCompatibleObject { + return { + kind: schema.kind, + simpleAllowedTypes: simpleAllowedTypesToSerializable(schema.simpleAllowedTypes), + }; +} + +/** + * Converts a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps. + * @param simpleAllowedTypes - The simple allowed types map to convert. + * @returns A serializable representation of the simple allowed types. + */ +function simpleAllowedTypesToSerializable( + simpleAllowedTypes: ReadonlyMap, +): JsonCompatibleObject { + const serializableAllowedTypes: JsonCompatibleObject = {}; + for (const [identifier, attributes] of simpleAllowedTypes) { + serializableAllowedTypes[identifier] = { + ...attributes, + }; + } + return serializableAllowedTypes; +} + +/** + * Converts an object node schema to a serializable object. + * @param schema - The object node schema to convert. + * @returns A serializable representation of the object node schema. + */ +function objectNodeToSerializable(schema: SimpleObjectNodeSchema): JsonCompatibleObject { + const serializableFields: Record = {}; + for (const [fieldKey, fieldSchema] of schema.fields) { + serializableFields[fieldKey] = toSerializableObjectField(fieldSchema); + } + + return { + kind: schema.kind, + fields: serializableFields, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; +} + +/** + * Converts an object field schema to a serializable object. + * @param fieldSchema - The object field schema to convert. + * @returns A serializable representation of the object field schema. + */ +function toSerializableObjectField( + fieldSchema: SimpleObjectFieldSchema, +): JsonCompatibleObject { + const serializableField = toSerializableField(fieldSchema); + serializableField.storedKey = fieldSchema.storedKey; + return serializableField; +} + +/** + * Converts a field schema to a serializable object. + * @param fieldSchema - The field schema to convert. + * @returns A serializable representation of the field schema. + */ +function toSerializableField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { + return { + kind: fieldSchema.kind, + simpleAllowedTypes: simpleAllowedTypesToSerializable(fieldSchema.simpleAllowedTypes), + }; +} + +/** + * Convert a Map to a Record for serialization. + * @remarks This is needed because the JSON serializer does not support Maps. + * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output + * should only be used in snapshots or debugging scenarios. + * @param map - The Map to convert. + * @returns A Record with the contents of the Map. + */ +function mapToRecord(map: ReadonlyMap): Record { + const resultObject: Record = {}; + const sortedKeys = Array.from(map.keys()).sort(); + + for (const key of sortedKeys) { + const value = + map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); + resultObject[`${key}`] = value; + } + + return resultObject; +} diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 15a6e1d3017e..bc49f811fc6b 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -59,7 +59,9 @@ export function toSimpleTreeSchema( nodeSchema instanceof RecordNodeSchema, 0xb60 /* Invalid schema */, ); - const outSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema) : nodeSchema; + const outSchema = copySchemaObjects + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.SimpleSchema) + : nodeSchema; definitions.set(nodeSchema.identifier, outSchema); }, }); @@ -77,64 +79,154 @@ export function toSimpleTreeSchema( }; } +// TODO: Remove? +/** + * Specifies which fields are included when copying a Simple Schema. + */ +export enum SimpleSchemaCopyMode { + // TODO: Rename + SimpleSchema, + ViewCompatibilitySchema, +} + /** * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. * * @remarks Caches the result on the input schema for future calls. */ -function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { +export function copySimpleNodeSchema( + schema: SimpleNodeSchema, + copyMode: SimpleSchemaCopyMode, +): SimpleNodeSchema { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: - return copySimpleLeafSchema(schema); + return copySimpleLeafSchema(schema, copyMode); case NodeKind.Array: case NodeKind.Map: case NodeKind.Record: - return copySimpleSchemaWithAllowedTypes(schema); + return copySimpleSchemaWithAllowedTypes(schema, copyMode); case NodeKind.Object: - return copySimpleObjectSchema(schema); + return copySimpleObjectSchema(schema, copyMode); default: unreachableCase(kind); } } -function copySimpleLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; +function copySimpleLeafSchema( + schema: SimpleLeafNodeSchema, + copyMode: SimpleSchemaCopyMode, +): SimpleLeafNodeSchema { + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + }; + default: + unreachableCase(copyMode); + } } function copySimpleSchemaWithAllowedTypes( schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, + copyMode: SimpleSchemaCopyMode, ): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { - return { - kind: schema.kind, - simpleAllowedTypes: schema.simpleAllowedTypes, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + return { + kind: schema.kind, + simpleAllowedTypes: schema.simpleAllowedTypes, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + return { + kind: schema.kind, + simpleAllowedTypes: schema.simpleAllowedTypes, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + }; + default: + unreachableCase(copyMode); + } } -function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { +function copySimpleObjectSchema( + schema: SimpleObjectNodeSchema, + copyMode: SimpleSchemaCopyMode, +): SimpleObjectNodeSchema { const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - fields.set(propertyKey, { - kind: field.kind, - simpleAllowedTypes: field.simpleAllowedTypes, - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, - storedKey: field.storedKey, - }); + let simpleField: SimpleObjectFieldSchema; + + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + simpleField = { + kind: field.kind, + simpleAllowedTypes: field.simpleAllowedTypes, + metadata: field.metadata, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + }; + break; + + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + simpleField = { + kind: field.kind, + simpleAllowedTypes: field.simpleAllowedTypes, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + storedKey: field.storedKey, + }; + break; + + default: + unreachableCase(copyMode); + } + + fields.set(propertyKey, simpleField); } - return { - kind: NodeKind.Object, - fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; + let simpleObject: SimpleObjectNodeSchema; + + switch (copyMode) { + case SimpleSchemaCopyMode.SimpleSchema: + simpleObject = { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; + break; + + case SimpleSchemaCopyMode.ViewCompatibilitySchema: + simpleObject = { + kind: NodeKind.Object, + fields, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; + break; + + default: + unreachableCase(copyMode); + } + + return simpleObject; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts new file mode 100644 index 000000000000..ce80e6d5368c --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -0,0 +1,65 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { assert } from "@fluidframework/core-utils/internal"; +import type { SimpleNodeSchema, SimpleTreeSchema } from "../simpleSchema.js"; +import type { TreeSchema } from "./configuration.js"; +import { LeafNodeSchema } from "../leafNodeSchema.js"; +import { + ArrayNodeSchema, + MapNodeSchema, + ObjectNodeSchema, + RecordNodeSchema, +} from "../node-kinds/index.js"; +import { copySimpleNodeSchema, SimpleSchemaCopyMode } from "./viewSchemaToSimpleSchema.js"; + +/** + * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. + * + * @param schema - The stored schema to convert. + * @param copySchemaObjects - If true, copies the contents of the schema into new objects. + * @returns The converted SimpleTreeSchema. + * + * @alpha + */ +export function toViewCompatibilityTreeSchema( + schema: TreeSchema, + copySchemaObjects: boolean, +): SimpleTreeSchema { + const definitions = new Map(); + + // Walk the node definitions and convert them one by one. Recurse into fields used in compatibility checks. + for (const nodeSchema of schema.definitions.values()) { + // TODO: Move this assert to a common location so it can be used from both SimpleSchema builders. + // The set of node kinds is extensible, but the typing of SimpleNodeSchema is not, so we need to check that the schema is one of the known kinds. + assert( + nodeSchema instanceof ArrayNodeSchema || + nodeSchema instanceof MapNodeSchema || + nodeSchema instanceof LeafNodeSchema || + nodeSchema instanceof ObjectNodeSchema || + nodeSchema instanceof RecordNodeSchema, + // TODO: New error code. + 0xb60 /* Invalid schema */, + ); + + // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. + const simpleNodeSchema = copySchemaObjects + ? copySimpleNodeSchema(nodeSchema, SimpleSchemaCopyMode.ViewCompatibilitySchema) + : nodeSchema; + definitions.set(nodeSchema.identifier, simpleNodeSchema); + } + + return { + root: copySchemaObjects + ? { + kind: schema.root.kind, + simpleAllowedTypes: schema.root.simpleAllowedTypes, + metadata: schema.root.metadata, + persistedMetadata: schema.root.persistedMetadata, + } + : schema.root, // TODO: Convert the root field + definitions, + }; +} diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 5c23b174daa6..c205803a5944 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -280,3 +280,7 @@ export { nullSchema, } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; +export { + toViewCompatibilityTreeSchema, + toSerializableCompatibilitySchema, +} from "./api/index.js"; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 9d9d41bc30be..aba2805079f8 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -58,6 +58,13 @@ export interface SimpleObjectNodeSchema * especially if/when TreeNodeSchema for objects provide more maps. */ readonly fields: ReadonlyMap; + + /** + * Whether the object node allows unknown optional fields. + * + * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + */ + readonly allowUnknownOptionalFields?: boolean; } /** @@ -163,18 +170,15 @@ export type SimpleNodeSchema = | SimpleRecordNodeSchema; /** - * Information about allowed types. + * Information about allowed types under a field. * * @alpha * @sealed */ export interface SimpleAllowedTypeAttributes { /** - * True if this schema is included as a {@link SchemaStaticsAlpha.staged | staged} schema upgrade, - * allowing the view schema be compatible with stored schema with (post upgrade) or without it (pre-upgrade). - * New documents and schema upgrades will omit any staged schema. - * - * Undefined if derived from a stored schema. + * True if there is an associated schema upgrade that makes this type read-only. + * Undefined if derived from a stored schema, which has no concept of staged allowed type upgrades. */ readonly isStaged: boolean | undefined; } @@ -196,7 +200,7 @@ export interface SimpleFieldSchema { readonly kind: FieldKind; /** - * Information about the allowed types under this field. + * The types allowed under the field. * * @remarks Refers to the types by identifier. * A {@link SimpleTreeSchema} is needed to resolve these identifiers to their schema {@link SimpleTreeSchema.definitions}. diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 0ae5c6503800..4d1e9ca4a07c 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -10,6 +10,9 @@ import { SchemaFactory, SchemaFactoryAlpha, stringSchema, + toSerializableCompatibilitySchema, + toViewCompatibilityTreeSchema, + TreeViewConfigurationAlpha, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -19,6 +22,7 @@ import { import { ValueSchema } from "../../../core/index.js"; // eslint-disable-next-line import/no-internal-modules import { toSimpleTreeSchema } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; +import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -35,364 +39,812 @@ const simpleNumber: SimpleLeafNodeSchema = { }; describe("getSimpleSchema", () => { - it("non-copying", () => { + useSnapshotDirectory("get-simple-schema"); + + describe("non-copying", () => { const Schema = stringSchema; const root = SchemaFactoryAlpha.optional(Schema); - const actual = toSimpleTreeSchema(root, false); - const expected: SimpleTreeSchema = { root, definitions: new Map([[Schema.identifier, Schema]]), }; - assert.deepEqual(actual, expected); - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(root, false); + + assert.deepEqual(actual, expected); + + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toViewCompatibilityTreeSchema(treeView, false); + + assert.deepEqual(actual, expected); + + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); + }); }); - it("Field Schema", () => { + describe("Field Schema", () => { const schemaFactory = new SchemaFactory("test"); const Schema = schemaFactory.optional(schemaFactory.string, { metadata: { description: "An optional string." }, }); - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + metadata: { description: "An optional string." }, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - metadata: { description: "An optional string." }, - simpleAllowedTypes: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - assert.deepEqual(actual, expected); + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + metadata: { description: "An optional string." }, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Field Schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Leaf node", () => { + describe("Leaf node", () => { const Schema = SchemaFactory.string; - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - simpleAllowedTypes: new Map([["com.fluidframework.leaf.string", { isStaged: false }]]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - assert.deepEqual(actual, expected); + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + persistedMetadata: undefined, + }, + definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Leaf node", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Union root", () => { + describe("Union root", () => { const Schema = [SchemaFactory.number, SchemaFactory.string]; - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + definitions: new Map([ + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + definitions: new Map([ + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], ]), - }, - definitions: new Map([ - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Union root", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Array schema", () => { + describe("Array schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.array("array", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.array", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.array", + { + kind: NodeKind.Array, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + metadata: {}, + persistedMetadata: undefined, + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.array", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.array", - { - kind: NodeKind.Array, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - metadata: {}, - persistedMetadata: undefined, - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.array", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.array", + { + kind: NodeKind.Array, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + metadata: {}, + persistedMetadata: undefined, + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Array schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Map schema", () => { + describe("Map schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.map("map", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.map", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.map", - { - kind: NodeKind.Map, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.map", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.map", + { + kind: NodeKind.Map, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.map", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.map", + { + kind: NodeKind.Map, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Map schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Record schema", () => { + describe("Record schema", () => { const schemaFactory = new SchemaFactoryAlpha("test"); class Schema extends schemaFactory.record("record", schemaFactory.string) {} - const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.record", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.record", - { - kind: NodeKind.Record, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.record", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.record", + { + kind: NodeKind.Record, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.record", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.record", + { + kind: NodeKind.Record, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Record schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Object schema", () => { + describe("Object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.optional(schemaFactory.number), bar: schemaFactory.required(schemaFactory.string), }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + [ + "bar", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "bar", + }, + ], + ]), + } satisfies SimpleObjectNodeSchema, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - [ - "bar", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "bar", - }, - ], - ]), - } satisfies SimpleObjectNodeSchema, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + [ + "bar", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "bar", + }, + ], + ]), + } satisfies SimpleObjectNodeSchema, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Object schema including an identifier field", () => { + describe("Object schema including an identifier field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { id: schemaFactory.identifier, }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "id", + { + kind: FieldKind.Identifier, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "id", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "id", - { - kind: FieldKind.Identifier, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "id", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "id", + { + kind: FieldKind.Identifier, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "id", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + const actual = toViewCompatibilityTreeSchema(treeView, true); + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Object schema including an identifier field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Object schema including a union field", () => { + describe("Object schema including a union field", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.object("object", { foo: schemaFactory.required([schemaFactory.number, schemaFactory.string]), }) {} - // Must enable copy so deep equality passes. - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + // Must enable copy so deep equality passes. + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + + // Must enable copy so deep equality passes. + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: false }], + ["com.fluidframework.leaf.string", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.number", simpleNumber], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Object schema including a union field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); - it("Recursive object schema", () => { + describe("Recursive object schema", () => { const schemaFactory = new SchemaFactory("test"); class Schema extends schemaFactory.objectRecursive("recursive-object", { foo: schemaFactory.optionalRecursive([schemaFactory.string, () => Schema]), }) {} - const actual = toSimpleTreeSchema(Schema, true); + it("toSimpleTreeSchema", () => { + const actual = toSimpleTreeSchema(Schema, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.recursive-object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.recursive-object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.recursive-object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.recursive-object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ["test.recursive-object", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); + }); + + it("toViewCompatibilityTreeSchema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([["test.recursive-object", { isStaged: false }]]), + }, + definitions: new Map([ + [ + "test.recursive-object", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: false, + fields: new Map([ + [ + "foo", + { + kind: FieldKind.Optional, + metadata: {}, + persistedMetadata: undefined, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.string", { isStaged: false }], + ["test.recursive-object", { isStaged: false }], + ]), + storedKey: "foo", + }, + ], + ]), + }, + ], + ["com.fluidframework.leaf.string", simpleString], + ]), + }; + + assert.deepEqual(actual, expected); + }); + + it("view compatibility schema - Recursive object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); }); }); diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts new file mode 100644 index 000000000000..2c6f61babe05 --- /dev/null +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -0,0 +1,46 @@ +import { + FieldKind, + SchemaFactoryAlpha, + stringSchema, + toSerializableCompatibilitySchema, + toViewCompatibilityTreeSchema, + TreeViewConfigurationAlpha, + type SimpleTreeSchema, +} from "../../../simple-tree/index.js"; +import { strict as assert } from "node:assert"; +import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; + +describe("getViewCompatibilityTreeSchema", () => { + useSnapshotDirectory("get-view-compatibility-tree-schema"); + + describe("With staged schema upgrades", () => { + const leafSchema = stringSchema; + const schemaFactory = new SchemaFactoryAlpha("test"); + const root = schemaFactory.optional( + // Staged allowed types are read-only for the sake of schema migrations + schemaFactory.types([schemaFactory.staged(leafSchema)]), + ); + + it("Should preserve isReadOnly when converting to SimpleTreeSchema", () => { + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Optional, + simpleAllowedTypes: new Map([[leafSchema.identifier, { isStaged: true }]]), + metadata: {}, + persistedMetadata: undefined, + }, + definitions: new Map([[leafSchema.identifier, leafSchema]]), + }; + + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toViewCompatibilityTreeSchema(treeView, true); + assert.deepEqual(actual.root.simpleAllowedTypes, expected.root.simpleAllowedTypes); + }); + + it("view compatibility schema - simpleAllowedTypes", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = toSerializableCompatibilitySchema(treeView); + takeJsonSnapshot(actual); + }); + }); +}); diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json new file mode 100644 index 000000000000..15f60707e98f --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json @@ -0,0 +1,24 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.array": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.array": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json new file mode 100644 index 000000000000..afe4a3d596d6 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json @@ -0,0 +1,16 @@ +{ + "root": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json new file mode 100644 index 000000000000..dcc197a19793 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json @@ -0,0 +1,16 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json new file mode 100644 index 000000000000..2648d7329fd1 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json @@ -0,0 +1,24 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.map": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.map": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json new file mode 100644 index 000000000000..4e279ae4a008 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json @@ -0,0 +1,38 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.object": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "foo": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.number": { + "isStaged": false + }, + "com.fluidframework.leaf.string": { + "isStaged": false + } + }, + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json new file mode 100644 index 000000000000..9bd8e0f9ca82 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json @@ -0,0 +1,31 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.object": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "id": { + "kind": 2, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + }, + "storedKey": "id" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json new file mode 100644 index 000000000000..f39478b35240 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json @@ -0,0 +1,44 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.object": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.number": { + "isStaged": false + } + }, + "storedKey": "foo" + }, + "bar": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + }, + "storedKey": "bar" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json new file mode 100644 index 000000000000..ece6c152c4b7 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json @@ -0,0 +1,24 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.record": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.record": { + "kind": 4, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json new file mode 100644 index 000000000000..a1eb800e8262 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json @@ -0,0 +1,34 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.recursive-object": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + }, + "test.recursive-object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + }, + "test.recursive-object": { + "isStaged": false + } + }, + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json new file mode 100644 index 000000000000..9c85966024c5 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json @@ -0,0 +1,23 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.number": { + "isStaged": false + }, + "com.fluidframework.leaf.string": { + "isStaged": false + } + } + }, + "definitions": { + "com.fluidframework.leaf.number": { + "kind": 3, + "leafKind": 0 + }, + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json new file mode 100644 index 000000000000..af0a8528b1e6 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json @@ -0,0 +1,16 @@ +{ + "root": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": true + } + } + }, + "definitions": { + "com.fluidframework.leaf.string": { + "kind": 3, + "leafKind": 1 + } + } +} \ No newline at end of file diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 105eaa53de77..21b7b86d65d9 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -1434,6 +1434,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { + readonly allowUnknownOptionalFields?: boolean; readonly fields: ReadonlyMap; } @@ -1701,6 +1702,9 @@ export interface Tagged { // @public export type TelemetryBaseEventPropertyType = string | number | boolean | undefined; +// @alpha +export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; + // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; From 49d33333e385b5132189183e4fec24109b28a0ce Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 30 Oct 2025 17:35:20 -0700 Subject: [PATCH 23/47] Copyright headers. --- .../src/simple-tree/api/serializableCompatibilitySchema.ts | 5 +++++ .../simple-tree/api/getViewCompatibilityTreeSchema.spec.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts index dad65b6aae1c..34ed82d3d7d1 100644 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts @@ -1,3 +1,8 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; import type { TreeSchema } from "./configuration.js"; import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts index fdf887adf980..cb6e1e5c2b09 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -1,3 +1,8 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + import { FieldKind, SchemaFactoryAlpha, From 9878a4b2a05d66ec2f494150d0e06fd9518a5054 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 31 Oct 2025 11:18:42 -0700 Subject: [PATCH 24/47] - Refactored serializer. - Implemented deserializer. --- .../dds/tree/src/simple-tree/api/index.ts | 5 +- .../api/serializableCompatibilitySchema.ts | 181 -------- .../api/viewCompatibilitySchemaSerializer.ts | 387 ++++++++++++++++++ packages/dds/tree/src/simple-tree/index.ts | 3 +- .../simple-tree/api/getSimpleSchema.spec.ts | 49 ++- .../getViewCompatibilityTreeSchema.spec.ts | 13 +- 6 files changed, 442 insertions(+), 196 deletions(-) delete mode 100644 packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts create mode 100644 packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index a19df200c11a..38ca8175a6be 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -168,4 +168,7 @@ export { getShouldIncrementallySummarizeAllowedTypes, incrementalSummaryHint, } from "./incrementalAllowedTypes.js"; -export { toSerializableCompatibilitySchema } from "./serializableCompatibilitySchema.js"; +export { + serializeCompatibilitySchema, + deserializeCompatibilitySchema, +} from "./viewCompatibilitySchemaSerializer.js"; diff --git a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts deleted file mode 100644 index 34ed82d3d7d1..000000000000 --- a/packages/dds/tree/src/simple-tree/api/serializableCompatibilitySchema.ts +++ /dev/null @@ -1,181 +0,0 @@ -/*! - * Copyright (c) Microsoft Corporation and contributors. All rights reserved. - * Licensed under the MIT License. - */ - -import type { JsonCompatible, JsonCompatibleObject } from "../../util/index.js"; -import type { TreeSchema } from "./configuration.js"; -import { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; -import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; -import type { - SimpleAllowedTypeAttributes, - SimpleArrayNodeSchema, - SimpleFieldSchema, - SimpleLeafNodeSchema, - SimpleMapNodeSchema, - SimpleNodeSchema, - SimpleObjectFieldSchema, - SimpleObjectNodeSchema, - SimpleRecordNodeSchema, -} from "../simpleSchema.js"; -import { NodeKind } from "../core/index.js"; - -/** - * Converts a view schema to a serializable format for compatibility testing. - * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. - * It is not possible to reconstruct a full view schema from the serialized format. - * @param treeSchema - The tree schema to convert. - * @returns A serializable representation of the view schema. - */ -export function toSerializableCompatibilitySchema(treeSchema: TreeSchema): JsonCompatible { - const simpleSchema = toViewCompatibilityTreeSchema( - treeSchema, - // Copying strips out references to metadata and other non-essential information. - true, - ); - - // Convert types to serializable forms - const serializableDefinitions = new Map(); - - for (const [identifier, schema] of simpleSchema.definitions) { - const serializableDefinition = nodeSchemaToSerializable(schema); - serializableDefinitions.set(identifier, serializableDefinition); - } - - const serializableSchema = { - root: toSerializableField(simpleSchema.root), - definitions: mapToRecord(serializableDefinitions), - } as unknown as JsonCompatible; - - return serializableSchema; -} - -/** - * Converts a node schema to a serializable object. - * @param schema - The node schema to convert. - * @returns A serializable representation of the node schema. - */ -function nodeSchemaToSerializable(schema: SimpleNodeSchema): JsonCompatibleObject { - const kind = schema.kind; - switch (kind) { - case NodeKind.Leaf: - return leafNodeToSerializable(schema); - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return containerNodeToSerializable(schema); - case NodeKind.Object: - return objectNodeToSerializable(schema); - default: { - unreachableCase(kind); - } - } -} - -/** - * Converts a leaf node schema to a serializable object. - * @param schema - The leaf node schema to convert. - * @returns A serializable representation of the leaf node schema. - */ -function leafNodeToSerializable(schema: SimpleLeafNodeSchema): JsonCompatibleObject { - return { - kind: schema.kind, - leafKind: schema.leafKind, - }; -} - -/** - * Converts a container node schema to a serializable object. - * @param schema - The container node schema to convert. - * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different - * container kinds. - */ -function containerNodeToSerializable( - schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, -): JsonCompatibleObject { - return { - kind: schema.kind, - simpleAllowedTypes: simpleAllowedTypesToSerializable(schema.simpleAllowedTypes), - }; -} - -/** - * Converts a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps. - * @param simpleAllowedTypes - The simple allowed types map to convert. - * @returns A serializable representation of the simple allowed types. - */ -function simpleAllowedTypesToSerializable( - simpleAllowedTypes: ReadonlyMap, -): JsonCompatibleObject { - const serializableAllowedTypes: JsonCompatibleObject = {}; - for (const [identifier, attributes] of simpleAllowedTypes) { - serializableAllowedTypes[identifier] = { - ...attributes, - }; - } - return serializableAllowedTypes; -} - -/** - * Converts an object node schema to a serializable object. - * @param schema - The object node schema to convert. - * @returns A serializable representation of the object node schema. - */ -function objectNodeToSerializable(schema: SimpleObjectNodeSchema): JsonCompatibleObject { - const serializableFields: Record = {}; - for (const [fieldKey, fieldSchema] of schema.fields) { - serializableFields[fieldKey] = toSerializableObjectField(fieldSchema); - } - - return { - kind: schema.kind, - fields: serializableFields, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; -} - -/** - * Converts an object field schema to a serializable object. - * @param fieldSchema - The object field schema to convert. - * @returns A serializable representation of the object field schema. - */ -function toSerializableObjectField( - fieldSchema: SimpleObjectFieldSchema, -): JsonCompatibleObject { - const serializableField = toSerializableField(fieldSchema); - serializableField.storedKey = fieldSchema.storedKey; - return serializableField; -} - -/** - * Converts a field schema to a serializable object. - * @param fieldSchema - The field schema to convert. - * @returns A serializable representation of the field schema. - */ -function toSerializableField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { - return { - kind: fieldSchema.kind, - simpleAllowedTypes: simpleAllowedTypesToSerializable(fieldSchema.simpleAllowedTypes), - }; -} - -/** - * Convert a Map to a Record for serialization. - * @remarks This is needed because the JSON serializer does not support Maps. - * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output - * should only be used in snapshots or debugging scenarios. - * @param map - The Map to convert. - * @returns A Record with the contents of the Map. - */ -function mapToRecord(map: ReadonlyMap): Record { - const resultObject: Record = {}; - const sortedKeys = Array.from(map.keys()).sort(); - - for (const key of sortedKeys) { - const value = - map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); - resultObject[`${key}`] = value; - } - - return resultObject; -} diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts new file mode 100644 index 000000000000..f459ca1a2a25 --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -0,0 +1,387 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { + isJsonObject, + objectToMap, + type JsonCompatible, + type JsonCompatibleObject, +} from "../../util/index.js"; +import { fail, unreachableCase, assert } from "@fluidframework/core-utils/internal"; +import type { + SimpleAllowedTypeAttributes, + SimpleArrayNodeSchema, + SimpleFieldSchema, + SimpleLeafNodeSchema, + SimpleMapNodeSchema, + SimpleNodeSchema, + SimpleObjectFieldSchema, + SimpleObjectNodeSchema, + SimpleRecordNodeSchema, + SimpleTreeSchema, +} from "../simpleSchema.js"; +import { NodeKind } from "../core/index.js"; +import type { FieldKind } from "../fieldSchema.js"; +import type { ValueSchema } from "../../core/index.js"; + +/** + * Converts a view schema to a serializable format for compatibility testing. + * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. + * It is not possible to reconstruct a full view schema from the serialized format. + * @param treeSchema - The tree schema to convert. + * @returns A serializable representation of the view schema. + */ +export function serializeCompatibilitySchema(simpleSchema: SimpleTreeSchema): JsonCompatible { + // Convert types to serializable forms + const serializableDefinitions = new Map(); + + for (const [identifier, schema] of simpleSchema.definitions) { + const serializableDefinition = serializeNodeSchema(schema); + serializableDefinitions.set(identifier, serializableDefinition); + } + + const serializedSchema = { + root: serializeField(simpleSchema.root), + definitions: mapToRecord(serializableDefinitions), + } as unknown as JsonCompatible; + + return serializedSchema; +} + +/** + * Deserializes a JSON-compatible schema into a view schema for compatibility testing. + * @param serializedSchema - The serialized schema to deserialize. + * @returns A deserialized view schema. + */ +export function deserializeCompatibilitySchema( + serializedSchema: JsonCompatible, +): SimpleTreeSchema { + assert(isJsonObject(serializedSchema), "Expected object for serializedSchema"); + const serializedSchemaAsObject = serializedSchema as JsonCompatibleObject; + + assert( + isJsonObject(serializedSchemaAsObject.root ?? fail("Expected root field")), + "Expected object for root field schema", + ); + const serializedRoot = serializedSchemaAsObject.root as JsonCompatibleObject; + + assert( + isJsonObject(serializedSchemaAsObject.definitions ?? fail("Expected definitions field")), + "Expected object for definitions field", + ); + const serializedDefinitions = serializedSchemaAsObject.definitions as JsonCompatibleObject; + + return { + root: deserializeSimpleFieldSchema(serializedRoot), + definitions: new Map( + transformMapValues(objectToMap(serializedDefinitions), (value, key) => { + assert( + isJsonObject(value ?? fail(`Expected node schema for definition ${key}`)), + "Expected object for node schema in definitions", + ); + return deserializeNodeSchema(value as JsonCompatibleObject); + }), + ), + }; +} + +/** + * Converts a node schema to a serializable object. + * @param schema - The node schema to convert. + * @returns A serializable representation of the node schema. + */ +function serializeNodeSchema(schema: SimpleNodeSchema): JsonCompatibleObject { + const kind = schema.kind; + switch (kind) { + case NodeKind.Leaf: + return serializeLeafNode(schema); + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return serializeContainerNode(schema); + case NodeKind.Object: + return serializeObjectNode(schema); + default: { + unreachableCase(kind); + } + } +} + +/** + * Converts a leaf node schema to a serializable object. + * @param schema - The leaf node schema to convert. + * @returns A serializable representation of the leaf node schema. + */ +function serializeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { + return { + kind: schema.kind, + leafKind: schema.leafKind, + }; +} + +/** + * Converts a container node schema to a serializable object. + * @param schema - The container node schema to convert. + * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different + * container kinds. + */ +function serializeContainerNode( + schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, +): JsonCompatibleObject { + return { + kind: schema.kind, + simpleAllowedTypes: serializeSimpleAllowedTypes(schema.simpleAllowedTypes), + }; +} + +/** + * Converts a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps. + * @param simpleAllowedTypes - The simple allowed types map to convert. + * @returns A serializable representation of the simple allowed types. + */ +function serializeSimpleAllowedTypes( + simpleAllowedTypes: ReadonlyMap, +): JsonCompatibleObject { + const serializableAllowedTypes: JsonCompatibleObject = {}; + for (const [identifier, attributes] of simpleAllowedTypes) { + serializableAllowedTypes[identifier] = { + ...attributes, + }; + } + return serializableAllowedTypes; +} + +/** + * Converts an object node schema to a serializable object. + * @param schema - The object node schema to convert. + * @returns A serializable representation of the object node schema. + */ +function serializeObjectNode(schema: SimpleObjectNodeSchema): JsonCompatibleObject { + const serializableFields: Record = {}; + for (const [fieldKey, fieldSchema] of schema.fields) { + serializableFields[fieldKey] = serializeObjectField(fieldSchema); + } + + return { + kind: schema.kind, + fields: serializableFields, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; +} + +/** + * Converts an object field schema to a serializable object. + * @param fieldSchema - The object field schema to convert. + * @returns A serializable representation of the object field schema. + */ +function serializeObjectField(fieldSchema: SimpleObjectFieldSchema): JsonCompatibleObject { + const serializableField = serializeField(fieldSchema); + serializableField.storedKey = fieldSchema.storedKey; + return serializableField; +} + +/** + * Converts a field schema to a serializable object. + * @param fieldSchema - The field schema to convert. + * @returns A serializable representation of the field schema. + */ +function serializeField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { + return { + kind: fieldSchema.kind, + simpleAllowedTypes: serializeSimpleAllowedTypes(fieldSchema.simpleAllowedTypes), + }; +} + +/** + * Deserializes a node schema from a JSON-compatible object. + * @param serializedNodeSchema - The serialized node schema to deserialize. + * @returns The deserialized node schema. + */ +function deserializeNodeSchema( + serializedNodeSchema: JsonCompatibleObject, +): + | SimpleLeafNodeSchema + | SimpleArrayNodeSchema + | SimpleMapNodeSchema + | SimpleRecordNodeSchema + | SimpleObjectNodeSchema { + const kind = serializedNodeSchema.kind as NodeKind; + + switch (kind) { + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return deserializeContainerNode(serializedNodeSchema); + case NodeKind.Leaf: + return deserializeLeafNode(serializedNodeSchema); + case NodeKind.Object: + return deserializeObjectNode(serializedNodeSchema); + default: + unreachableCase(kind); + } +} + +/** + * Deserializes a container node schema (array, map, record) from a JSON-compatible object. + * @param serializedContainerSchema - The serialized schema to deserialize. + * @returns The deserialized container node schema. + */ +function deserializeContainerNode( + serializedContainerSchema: JsonCompatibleObject, +): + | SimpleLeafNodeSchema + | SimpleArrayNodeSchema + | SimpleMapNodeSchema + | SimpleRecordNodeSchema { + return { + kind: NodeKind.Record, + simpleAllowedTypes: deserializeSimpleAllowedTypes( + serializedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, + ), + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Deserializes a leaf node schema from a JSON-compatible object. + * @param serializedLeafSchema - The serialized leaf node schema. + * @returns The deserialized leaf node schema. + */ +function deserializeLeafNode( + serializedLeafSchema: JsonCompatibleObject, +): SimpleLeafNodeSchema { + return { + kind: NodeKind.Leaf, + leafKind: (serializedLeafSchema.leafKind ?? fail("Missing leafKind")) as ValueSchema, + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Deserializes a object node schema from a JSON-compatible object. + * @param serializedObjectSchema - The serialized object node schema. + * @returns The deserialized object node schema. + */ +function deserializeObjectNode( + serializedObjectSchema: JsonCompatibleObject, +): SimpleObjectNodeSchema { + return { + kind: NodeKind.Object, + fields: deserializeObjectFields(serializedObjectSchema.fields ?? fail("Missing fields")), + allowUnknownOptionalFields: (serializedObjectSchema.allowUnknownOptionalFields ?? + fail("Missing allowUnknownOptionalFields")) as boolean, + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Deserializes a map of object fields from a JSON-compatible object. + * @param serializedFields - The serialized fields. + * @returns A map of the deserialized object fields. + */ +function deserializeObjectFields( + serializedFields: JsonCompatible, +): ReadonlyMap { + assert(isJsonObject(serializedFields), "Expected object for serializedFields"); + + const fields = new Map(); + for (const [fieldKey, fieldSchema] of Object.entries( + serializedFields as JsonCompatibleObject, + )) { + fields.set(fieldKey, deserializeObjectField(fieldSchema as JsonCompatibleObject)); + } + return fields; +} + +/** + * Deserializes a {@link SimpleObjectFieldSchema} from a JSON-compatible object. + * @param serializedField - The serialized field schema. + * @returns The deserialized simple object field schema. + */ +function deserializeObjectField( + serializedField: JsonCompatibleObject, +): SimpleObjectFieldSchema { + const baseField = deserializeSimpleFieldSchema(serializedField); + return { + ...baseField, + storedKey: serializedField.storedKey as string, + }; +} + +/** + * Deserializes a {@link SimpleFieldSchema} from a JSON-compatible object. + * @param serializedField - The serialized field schema. + * @returns The deserialized simple field schema. + */ +function deserializeSimpleFieldSchema( + serializedField: JsonCompatibleObject, +): SimpleFieldSchema { + return { + kind: serializedField.kind as FieldKind, + simpleAllowedTypes: deserializeSimpleAllowedTypes( + serializedField.simpleAllowedTypes as JsonCompatibleObject, + ), + metadata: {}, + }; +} + +/** + * Deserializes a simple allowed types map from a JSON-compatible object. + * @param serializedAllowedTypes - The serialized simple allowed types. + * @returns A map of the deserialized simple allowed types. + */ +function deserializeSimpleAllowedTypes( + serializedAllowedTypes: JsonCompatible, +): ReadonlyMap { + assert(isJsonObject(serializedAllowedTypes), "Expected object for simpleAllowedTypes"); + const untypedMap = objectToMap(serializedAllowedTypes as JsonCompatibleObject); + + const simpleAllowedTypes = transformMapValues(untypedMap, (value) => { + return { + isStaged: (value as JsonCompatibleObject).isStaged as boolean | undefined, + } satisfies SimpleAllowedTypeAttributes; + }); + + return simpleAllowedTypes; +} + +/** + * Convert a Map to a Record for serialization. + * @remarks This is needed because the JSON serializer does not support Maps. + * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output + * should only be used in snapshots or debugging scenarios. + * @param map - The Map to convert. + * @returns A Record with the contents of the Map. + */ +function mapToRecord(map: ReadonlyMap): Record { + const resultObject: Record = {}; + const sortedKeys = Array.from(map.keys()).sort(); + + for (const key of sortedKeys) { + const value = + map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); + resultObject[`${key}`] = value; + } + + return resultObject; +} + +/** + * Transform the values of a Map using the provided transform function. + * @param map - The map to transform. + * @param transformValue - A method for transforming values in the map. + * @returns A new map with the transformed values. + */ +function transformMapValues( + map: Map, + transformValue: (value: InputValue, key: Key) => OutputValue, +): Map { + return new Map( + Array.from(map.entries()).map(([key, value]) => [key, transformValue(value, key)]), + ); +} diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index c205803a5944..bbf0e81fd751 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -282,5 +282,6 @@ export { export type { LeafSchema } from "./leafNodeSchema.js"; export { toViewCompatibilityTreeSchema, - toSerializableCompatibilitySchema, + serializeCompatibilitySchema, + deserializeCompatibilitySchema, } from "./api/index.js"; diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 4d1e9ca4a07c..a79a44bd1e83 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -5,12 +5,13 @@ import { strict as assert } from "node:assert"; import { + deserializeCompatibilitySchema, FieldKind, NodeKind, SchemaFactory, SchemaFactoryAlpha, + serializeCompatibilitySchema, stringSchema, - toSerializableCompatibilitySchema, toViewCompatibilityTreeSchema, TreeViewConfigurationAlpha, type SimpleLeafNodeSchema, @@ -115,9 +116,17 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Field Schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Field Schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(treeView)); + assert.deepEqual(actual, treeView); + }); }); describe("Leaf node", () => { @@ -162,7 +171,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Leaf node", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -217,7 +228,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Union root", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -287,7 +300,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Array schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -357,7 +372,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Map schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -427,7 +444,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Record schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -549,7 +568,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -644,7 +665,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Object schema including an identifier field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -746,7 +769,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Object schema including a union field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); @@ -843,7 +868,9 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Recursive object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); }); diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts index cb6e1e5c2b09..143eb3502ec3 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -4,10 +4,11 @@ */ import { + deserializeCompatibilitySchema, FieldKind, SchemaFactoryAlpha, + serializeCompatibilitySchema, stringSchema, - toSerializableCompatibilitySchema, toViewCompatibilityTreeSchema, TreeViewConfigurationAlpha, type SimpleTreeSchema, @@ -44,8 +45,16 @@ describe("getViewCompatibilityTreeSchema", () => { it("view compatibility schema - simpleAllowedTypes", () => { const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toSerializableCompatibilitySchema(treeView); + const actual = serializeCompatibilitySchema( + toViewCompatibilityTreeSchema(treeView, true), + ); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - simpleAllowedTypes", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: root }); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(treeView)); + assert.deepEqual(actual, treeView); + }); }); }); From 264087a3ceff4fac7797566cd375d5e8f7001a5f Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 31 Oct 2025 11:49:17 -0700 Subject: [PATCH 25/47] Fixed a few conversion edge cases. --- .../api/viewCompatibilitySchemaSerializer.ts | 1 + .../viewSchemaToViewCompatibilitySchema.ts | 30 ++++---- .../simple-tree/api/getSimpleSchema.spec.ts | 77 ++++++------------- .../getViewCompatibilityTreeSchema.spec.ts | 11 ++- 4 files changed, 45 insertions(+), 74 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts index f459ca1a2a25..86b91d2cdb66 100644 --- a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -326,6 +326,7 @@ function deserializeSimpleFieldSchema( simpleAllowedTypes: deserializeSimpleAllowedTypes( serializedField.simpleAllowedTypes as JsonCompatibleObject, ), + persistedMetadata: undefined, metadata: {}, }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts index f5918887e588..2e6a1e4a5b5a 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts @@ -18,16 +18,13 @@ import { copySimpleNodeSchema, SimpleSchemaCopyMode } from "./viewSchemaToSimple /** * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. * + * @remarks This method always copies the schema objects to plain JavaScript objects. * @param schema - The stored schema to convert. - * @param copySchemaObjects - If true, copies the contents of the schema into new objects. * @returns The converted SimpleTreeSchema. * * @alpha */ -export function toViewCompatibilityTreeSchema( - schema: TreeSchema, - copySchemaObjects: boolean, -): SimpleTreeSchema { +export function toViewCompatibilityTreeSchema(schema: TreeSchema): SimpleTreeSchema { const definitions = new Map(); // Walk the node definitions and convert them one by one. Recurse into fields used in compatibility checks. @@ -45,21 +42,22 @@ export function toViewCompatibilityTreeSchema( ); // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. - const simpleNodeSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, true, SimpleSchemaCopyMode.ViewCompatibilitySchema) - : nodeSchema; + const simpleNodeSchema = copySimpleNodeSchema( + nodeSchema, + true, + SimpleSchemaCopyMode.ViewCompatibilitySchema, + ); definitions.set(nodeSchema.identifier, simpleNodeSchema); } return { - root: copySchemaObjects - ? { - kind: schema.root.kind, - simpleAllowedTypes: schema.root.simpleAllowedTypes, - metadata: schema.root.metadata, - persistedMetadata: schema.root.persistedMetadata, - } - : schema.root, // TODO: Convert the root field + root: { + kind: schema.root.kind, + simpleAllowedTypes: schema.root.simpleAllowedTypes, + // Don't include metadata or persistedMetadata in view compatibility schema. + metadata: {}, + persistedMetadata: undefined, + }, definitions, }; } diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index a79a44bd1e83..a4bbb0f440ec 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -60,15 +60,7 @@ describe("getSimpleSchema", () => { assert.equal(actual.definitions.get(Schema.identifier), Schema); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toViewCompatibilityTreeSchema(treeView, false); - - assert.deepEqual(actual, expected); - - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); - }); + // There is no non-copying form of {@link toViewCompatibilityTreeSchema}. }); describe("Field Schema", () => { @@ -97,12 +89,12 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { kind: FieldKind.Optional, - metadata: { description: "An optional string." }, + metadata: {}, simpleAllowedTypes: new Map([ ["com.fluidframework.leaf.string", { isStaged: false }], ]), @@ -116,16 +108,15 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Field Schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); it("Roundtrip view compatibility schema serialization - Field Schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(treeView)); - assert.deepEqual(actual, treeView); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); }); }); @@ -152,7 +143,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -171,9 +162,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Leaf node", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -205,7 +194,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -228,9 +217,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Union root", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -270,7 +257,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -300,9 +287,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Array schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -342,7 +327,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -372,9 +357,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Map schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -414,7 +397,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -444,9 +427,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Record schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -513,7 +494,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -568,9 +549,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -659,15 +638,13 @@ describe("getSimpleSchema", () => { ]), }; - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); assert.deepEqual(actual, expected); }); it("view compatibility schema - Object schema including an identifier field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -725,7 +702,7 @@ describe("getSimpleSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); // Must enable copy so deep equality passes. - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -769,9 +746,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Object schema including a union field", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); @@ -825,7 +800,7 @@ describe("getSimpleSchema", () => { it("toViewCompatibilityTreeSchema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); const expected: SimpleTreeSchema = { root: { @@ -868,9 +843,7 @@ describe("getSimpleSchema", () => { it("view compatibility schema - Recursive object schema", () => { const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); }); diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts index 143eb3502ec3..8d6eed468feb 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts @@ -39,22 +39,21 @@ describe("getViewCompatibilityTreeSchema", () => { }; const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toViewCompatibilityTreeSchema(treeView, true); + const actual = toViewCompatibilityTreeSchema(treeView); assert.deepEqual(actual.root.simpleAllowedTypes, expected.root.simpleAllowedTypes); }); it("view compatibility schema - simpleAllowedTypes", () => { const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = serializeCompatibilitySchema( - toViewCompatibilityTreeSchema(treeView, true), - ); + const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); it("Roundtrip view compatibility schema serialization - simpleAllowedTypes", () => { const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(treeView)); - assert.deepEqual(actual, treeView); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); }); }); }); From 41db0f0821bfb5e0b6d27a2eef4cc10a731b8120 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Fri, 31 Oct 2025 13:08:03 -0700 Subject: [PATCH 26/47] Added tests and fixed a bug in the deserializer. --- .../api/viewCompatibilitySchemaSerializer.ts | 8 +-- .../simple-tree/api/getSimpleSchema.spec.ts | 63 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts index 86b91d2cdb66..39bf6b4841b7 100644 --- a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -230,13 +230,9 @@ function deserializeNodeSchema( */ function deserializeContainerNode( serializedContainerSchema: JsonCompatibleObject, -): - | SimpleLeafNodeSchema - | SimpleArrayNodeSchema - | SimpleMapNodeSchema - | SimpleRecordNodeSchema { +): SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema { return { - kind: NodeKind.Record, + kind: serializedContainerSchema.kind as NodeKind.Array | NodeKind.Map | NodeKind.Record, simpleAllowedTypes: deserializeSimpleAllowedTypes( serializedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, ), diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index a4bbb0f440ec..a2f4c8f6dd94 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -165,6 +165,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Leaf node", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Union root", () => { @@ -220,6 +227,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Field Schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Array schema", () => { @@ -290,6 +304,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Array Schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Map schema", () => { @@ -360,6 +381,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Map schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Record schema", () => { @@ -430,6 +458,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Record schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Object schema", () => { @@ -552,6 +587,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Object schema including an identifier field", () => { @@ -647,6 +689,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Object schema including an identifier field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Object schema including a union field", () => { @@ -749,6 +798,13 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Object schema including a union field", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); describe("Recursive object schema", () => { @@ -846,5 +902,12 @@ describe("getSimpleSchema", () => { const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); takeJsonSnapshot(actual); }); + + it("Roundtrip view compatibility schema serialization - Recursive object schema", () => { + const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); + const expected = toViewCompatibilityTreeSchema(treeView); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); }); }); From 795b8631c66cc7d52a64743fee2b2aec0b7397c6 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Mon, 3 Nov 2025 11:59:25 -0800 Subject: [PATCH 27/47] Merged viewSchemaToSimpleSchema and viewSchemaToCompatibilitySchema. Some tests fail. --- packages/dds/tree/src/index.ts | 1 - .../dds/tree/src/shared-tree/sharedTree.ts | 8 +- .../dds/tree/src/simple-tree/api/index.ts | 1 - .../api/viewSchemaToSimpleSchema.ts | 243 ++++----- .../viewSchemaToViewCompatibilitySchema.ts | 63 --- packages/dds/tree/src/simple-tree/index.ts | 1 - .../dds/tree/src/simple-tree/simpleSchema.ts | 4 +- .../src/test/shared-tree/sharedTree.spec.ts | 81 ++- .../simple-tree/api/getSimpleSchema.spec.ts | 462 +++--------------- .../getViewCompatibilityTreeSchema.spec.ts | 59 --- .../api/simpleSchemaToJsonSchema.spec.ts | 4 + 11 files changed, 246 insertions(+), 681 deletions(-) delete mode 100644 packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts delete mode 100644 packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 147b8aaba83e..95adf131fec3 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -342,4 +342,3 @@ export { JsonAsTree } from "./jsonDomainSchema.js"; export { FluidSerializableAsTree } from "./serializableDomainSchema.js"; export { TableSchema, type System_TableSchema } from "./tableSchema.js"; export { asAlpha, asBeta } from "./api.js"; -export { toViewCompatibilityTreeSchema } from "./simple-tree/index.js"; diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index 3e14a3434b9e..cda53ad84b6c 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -1019,7 +1019,13 @@ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeS for (const [storedKey, field] of schema.objectNodeFields) { fields.set(storedKey, { ...exportSimpleFieldSchemaStored(field), storedKey }); } - return { kind: NodeKind.Object, fields, metadata: {}, persistedMetadata: schema.metadata }; + return { + kind: NodeKind.Object, + fields, + allowUnknownOptionalFields: undefined, + metadata: {}, + persistedMetadata: schema.metadata, + }; } if (schema instanceof MapNodeStoredSchema) { assert( diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 38ca8175a6be..8f177419e6e0 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -162,7 +162,6 @@ export { export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; export { toSimpleTreeSchema } from "./viewSchemaToSimpleSchema.js"; -export { toViewCompatibilityTreeSchema } from "./viewSchemaToViewCompatibilitySchema.js"; export type { TreeChangeEvents } from "./treeChangeEvents.js"; export { getShouldIncrementallySummarizeAllowedTypes, diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 3f58dfc3aa3a..71d076084bfd 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -6,7 +6,6 @@ import { assert, unreachableCase } from "@fluidframework/core-utils/internal"; import { normalizeFieldSchema, type ImplicitFieldSchema } from "../fieldSchema.js"; import type { - SimpleAllowedTypeAttributes, SimpleArrayNodeSchema, SimpleFieldSchema, SimpleLeafNodeSchema, @@ -32,7 +31,6 @@ import { LeafNodeSchema } from "../leafNodeSchema.js"; * * @param schema - The schema to convert * @param copySchemaObjects - If true, TreeNodeSchema and FieldSchema are copied into plain JavaScript objects. Either way, custom metadata is referenced and not copied. - * @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. * * @remarks * Given that the Schema types used in {@link ImplicitFieldSchema} already implement the {@link SimpleNodeSchema} interfaces, there are limited use-cases for this function. @@ -47,7 +45,6 @@ import { LeafNodeSchema } from "../leafNodeSchema.js"; export function toSimpleTreeSchema( schema: ImplicitFieldSchema, copySchemaObjects: boolean, - isViewSchema: boolean = true, ): SimpleTreeSchema { const normalizedSchema = normalizeFieldSchema(schema); const definitions = new Map(); @@ -62,9 +59,7 @@ export function toSimpleTreeSchema( nodeSchema instanceof RecordNodeSchema, 0xb60 /* Invalid schema */, ); - const outSchema = copySchemaObjects - ? copySimpleNodeSchema(nodeSchema, isViewSchema, SimpleSchemaCopyMode.SimpleSchema) - : nodeSchema; + const outSchema = copySchemaObjects ? copySimpleNodeSchema(nodeSchema) : nodeSchema; definitions.set(nodeSchema.identifier, outSchema); }, }); @@ -72,10 +67,7 @@ export function toSimpleTreeSchema( return { root: copySchemaObjects ? ({ - simpleAllowedTypes: normalizeSimpleAllowedTypes( - normalizedSchema.simpleAllowedTypes, - isViewSchema, - ), + simpleAllowedTypes: normalizedSchema.simpleAllowedTypes, kind: normalizedSchema.kind, metadata: normalizedSchema.metadata, persistedMetadata: normalizedSchema.persistedMetadata, @@ -85,187 +77,126 @@ export function toSimpleTreeSchema( }; } -// TODO: Remove? -/** - * Specifies which fields are included when copying a Simple Schema. - */ -export enum SimpleSchemaCopyMode { - // TODO: Rename - SimpleSchema, - ViewCompatibilitySchema, -} - -/** - * Normalizes the {@link SimpleAllowedTypeAttributes} by either preserving or omitting view-specific schema properties. - * @param simpleAllowedTypes - The simple allowed types to normalize. - * @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. - * @returns The normalized simple allowed types. - */ -function normalizeSimpleAllowedTypes( - simpleAllowedTypes: ReadonlyMap, - isViewSchema: boolean, -): ReadonlyMap { - if (isViewSchema) { - return simpleAllowedTypes; - } else { - const normalized = new Map(); - for (const [identifier, attributes] of simpleAllowedTypes.entries()) { - normalized.set(identifier, { ...attributes, isStaged: undefined }); - } - return normalized; - } -} - /** * Copies a {@link SimpleNodeSchema} into a new plain JavaScript object. * * @remarks Caches the result on the input schema for future calls. */ -export function copySimpleNodeSchema( - schema: SimpleNodeSchema, - isViewSchema: boolean, - copyMode: SimpleSchemaCopyMode, -): SimpleNodeSchema { +export function copySimpleNodeSchema(schema: SimpleNodeSchema): SimpleNodeSchema { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: - return copySimpleLeafSchema(schema, copyMode); + return copySimpleLeafSchema(schema); case NodeKind.Array: case NodeKind.Map: case NodeKind.Record: - return copySimpleSchemaWithAllowedTypes(schema, isViewSchema, copyMode); + return copySimpleSchemaWithAllowedTypes(schema); case NodeKind.Object: - return copySimpleObjectSchema(schema, isViewSchema, copyMode); + return copySimpleObjectSchema(schema); default: unreachableCase(kind); } } -function copySimpleLeafSchema( - schema: SimpleLeafNodeSchema, - copyMode: SimpleSchemaCopyMode, -): SimpleLeafNodeSchema { - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - return { - kind: NodeKind.Leaf, - leafKind: schema.leafKind, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - }; - default: - unreachableCase(copyMode); - } +function copySimpleLeafSchema(schema: SimpleLeafNodeSchema): SimpleLeafNodeSchema { + return { + kind: NodeKind.Leaf, + leafKind: schema.leafKind, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; } function copySimpleSchemaWithAllowedTypes( schema: SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema, - isViewSchema: boolean, - copyMode: SimpleSchemaCopyMode, ): SimpleMapNodeSchema | SimpleArrayNodeSchema | SimpleRecordNodeSchema { - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - return { - kind: schema.kind, - simpleAllowedTypes: schema.simpleAllowedTypes, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - return { - kind: schema.kind, - simpleAllowedTypes: normalizeSimpleAllowedTypes( - schema.simpleAllowedTypes, - isViewSchema, - ), - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - }; - default: - unreachableCase(copyMode); - } + return { + kind: schema.kind, + simpleAllowedTypes: schema.simpleAllowedTypes, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + }; } -function copySimpleObjectSchema( - schema: SimpleObjectNodeSchema, - isViewSchema: boolean, - copyMode: SimpleSchemaCopyMode, -): SimpleObjectNodeSchema { +function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNodeSchema { const fields: Map = new Map(); for (const [propertyKey, field] of schema.fields) { // field already is a SimpleObjectFieldSchema, but copy the subset of the properties needed by this interface to get a clean simple object. - let simpleField: SimpleObjectFieldSchema; + const simpleField = { + kind: field.kind, + simpleAllowedTypes: field.simpleAllowedTypes, + metadata: {}, + persistedMetadata: field.persistedMetadata, + storedKey: field.storedKey, + }; - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - simpleField = { - kind: field.kind, - simpleAllowedTypes: normalizeSimpleAllowedTypes( - field.simpleAllowedTypes, - isViewSchema, - ), - metadata: field.metadata, - persistedMetadata: field.persistedMetadata, - storedKey: field.storedKey, - }; - break; + fields.set(propertyKey, simpleField); + } + + const simpleObject = { + kind: NodeKind.Object, + fields, + metadata: schema.metadata, + persistedMetadata: schema.persistedMetadata, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + } satisfies SimpleObjectNodeSchema; - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - simpleField = { - kind: field.kind, - simpleAllowedTypes: normalizeSimpleAllowedTypes( - field.simpleAllowedTypes, - isViewSchema, - ), - // Don't include metadata or persistedMetadata in view compatibility schema. + return simpleObject; +} + +/** + * Removes all metadata and persistedMetadata from a SimpleTreeSchema. This is useful for comparing deserialized view schemas with in-memory schemas. + * metadata and persistedMetadata are not relevant for schema compatibility checks and are not serialized by the Simple Schema serializer (TODO: link). + * @param schema - The SimpleTreeSchema to remove metadata from. + * @returns A new SimpleTreeSchema without metadata and persistedMetadata. + */ +export function removeMetadataFromSimpleTreeSchema( + schema: SimpleTreeSchema, +): SimpleTreeSchema { + const definitions = new Map(); + + for (const [identifier, nodeSchema] of schema.definitions.entries()) { + const kind = nodeSchema.kind; + switch (kind) { + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + case NodeKind.Leaf: { + const outputNodeSchema = { ...nodeSchema, metadata: {}, persistedMetadata: undefined }; + definitions.set(identifier, outputNodeSchema); + break; + } + case NodeKind.Object: { + const outputFields = new Map(); + for (const [propertyKey, fieldSchema] of nodeSchema.fields.entries()) { + const outputField: SimpleObjectFieldSchema = { + ...fieldSchema, + metadata: {}, + persistedMetadata: undefined, + }; + outputFields.set(propertyKey, outputField); + } + const outputNodeSchema = { + ...nodeSchema, metadata: {}, persistedMetadata: undefined, - storedKey: field.storedKey, + fields: outputFields, }; + definitions.set(identifier, outputNodeSchema); break; - + } default: - unreachableCase(copyMode); + unreachableCase(kind); } - - fields.set(propertyKey, simpleField); } - let simpleObject: SimpleObjectNodeSchema; - - switch (copyMode) { - case SimpleSchemaCopyMode.SimpleSchema: - simpleObject = { - kind: NodeKind.Object, - fields, - metadata: schema.metadata, - persistedMetadata: schema.persistedMetadata, - }; - break; - - case SimpleSchemaCopyMode.ViewCompatibilitySchema: - simpleObject = { - kind: NodeKind.Object, - fields, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; - break; - - default: - unreachableCase(copyMode); - } - - return simpleObject; + return { + root: { + kind: schema.root.kind, + simpleAllowedTypes: schema.root.simpleAllowedTypes, + metadata: {}, + persistedMetadata: undefined, + }, + definitions, + }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts deleted file mode 100644 index 2e6a1e4a5b5a..000000000000 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToViewCompatibilitySchema.ts +++ /dev/null @@ -1,63 +0,0 @@ -/*! - * Copyright (c) Microsoft Corporation and contributors. All rights reserved. - * Licensed under the MIT License. - */ - -import { assert } from "@fluidframework/core-utils/internal"; -import type { SimpleNodeSchema, SimpleTreeSchema } from "../simpleSchema.js"; -import type { TreeSchema } from "./configuration.js"; -import { LeafNodeSchema } from "../leafNodeSchema.js"; -import { - ArrayNodeSchema, - MapNodeSchema, - ObjectNodeSchema, - RecordNodeSchema, -} from "../node-kinds/index.js"; -import { copySimpleNodeSchema, SimpleSchemaCopyMode } from "./viewSchemaToSimpleSchema.js"; - -/** - * Convert a stored schema to a SimpleSchema and preserve information needed for compatibility testing. - * - * @remarks This method always copies the schema objects to plain JavaScript objects. - * @param schema - The stored schema to convert. - * @returns The converted SimpleTreeSchema. - * - * @alpha - */ -export function toViewCompatibilityTreeSchema(schema: TreeSchema): SimpleTreeSchema { - const definitions = new Map(); - - // Walk the node definitions and convert them one by one. Recurse into fields used in compatibility checks. - for (const nodeSchema of schema.definitions.values()) { - // TODO: Move this assert to a common location so it can be used from both SimpleSchema builders. - // The set of node kinds is extensible, but the typing of SimpleNodeSchema is not, so we need to check that the schema is one of the known kinds. - assert( - nodeSchema instanceof ArrayNodeSchema || - nodeSchema instanceof MapNodeSchema || - nodeSchema instanceof LeafNodeSchema || - nodeSchema instanceof ObjectNodeSchema || - nodeSchema instanceof RecordNodeSchema, - // TODO: New error code. - 0xb60 /* Invalid schema */, - ); - - // Read properties that are needed for compatibility and copy them to a SimpleNodeSchema. - const simpleNodeSchema = copySimpleNodeSchema( - nodeSchema, - true, - SimpleSchemaCopyMode.ViewCompatibilitySchema, - ); - definitions.set(nodeSchema.identifier, simpleNodeSchema); - } - - return { - root: { - kind: schema.root.kind, - simpleAllowedTypes: schema.root.simpleAllowedTypes, - // Don't include metadata or persistedMetadata in view compatibility schema. - metadata: {}, - persistedMetadata: undefined, - }, - definitions, - }; -} diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index bbf0e81fd751..092d63cda3c2 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -281,7 +281,6 @@ export { } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; export { - toViewCompatibilityTreeSchema, serializeCompatibilitySchema, deserializeCompatibilitySchema, } from "./api/index.js"; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 3197f59097ef..31764e4dbb35 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -62,9 +62,9 @@ export interface SimpleObjectNodeSchema /** * Whether the object node allows unknown optional fields. * - * @remarks Used for compatibility checks (see {@link toViewCompatibilityTreeSchema}). Not available in all cases. + * @remarks Only populated for view schemas. Relevant for compatibility checking scenarios. */ - readonly allowUnknownOptionalFields?: boolean; + readonly allowUnknownOptionalFields: boolean | undefined; } /** diff --git a/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts b/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts index 3add685e39d3..8bcc235b689f 100644 --- a/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts +++ b/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts @@ -69,6 +69,11 @@ import { SchemaFactoryAlpha, type ITree, toInitialSchema, + NodeKind, + type SimpleObjectFieldSchema, + type SimpleNodeSchema, + type SimpleTreeSchema, + type SimpleAllowedTypeAttributes, } from "../../simple-tree/index.js"; import { brand } from "../../util/index.js"; import { @@ -117,6 +122,7 @@ import { simpleTreeNodeSlot } from "../../simple-tree/core/treeNodeKernel.js"; import type { TreeSimpleContent } from "../feature-libraries/flex-tree/utils.js"; import { FluidClientVersion } from "../../codec/index.js"; import { asAlpha } from "../../api.js"; +import { unreachableCase } from "@fluidframework/core-utils/internal"; const enableSchemaValidation = true; @@ -2484,6 +2490,71 @@ describe("SharedTree", () => { ); }); + function removeIsStagedFromAllowedTypes( + simpleAllowedTypes: ReadonlyMap, + ): ReadonlyMap { + const output = new Map(); + for (const [typeIdentifier, attributes] of simpleAllowedTypes.entries()) { + const { isStaged, ...rest } = attributes; + output.set(typeIdentifier, { ...rest, isStaged: undefined }); + } + return output; + } + + function removeIsStagedFromSimpleTreeSchema(schema: SimpleTreeSchema): SimpleTreeSchema { + const definitions = new Map(); + + for (const [identifier, nodeSchema] of schema.definitions.entries()) { + const kind = nodeSchema.kind; + switch (kind) { + case NodeKind.Leaf: { + definitions.set(identifier, nodeSchema); + break; + } + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: { + const outputNodeSchema = { + ...nodeSchema, + simpleAllowedTypes: removeIsStagedFromAllowedTypes(nodeSchema.simpleAllowedTypes), + }; + definitions.set(identifier, outputNodeSchema); + break; + } + case NodeKind.Object: { + const outputFields = new Map(); + for (const [propertyKey, fieldSchema] of nodeSchema.fields.entries()) { + const outputField: SimpleObjectFieldSchema = { + ...fieldSchema, + simpleAllowedTypes: removeIsStagedFromAllowedTypes( + fieldSchema.simpleAllowedTypes, + ), + }; + outputFields.set(propertyKey, outputField); + } + const outputNodeSchema = { + ...nodeSchema, + fields: outputFields, + }; + definitions.set(identifier, outputNodeSchema); + break; + } + default: + unreachableCase(kind); + } + } + + return { + root: { + kind: schema.root.kind, + simpleAllowedTypes: removeIsStagedFromAllowedTypes(schema.root.simpleAllowedTypes), + metadata: {}, + persistedMetadata: undefined, + }, + definitions, + }; + } + it("exportVerbose & exportSimpleSchema", () => { const tree = treeTestFactory(); assert.deepEqual(tree.exportVerbose(), undefined); @@ -2499,14 +2570,10 @@ describe("SharedTree", () => { view.initialize(10); assert.deepEqual(tree.exportVerbose(), 10); - assert.deepEqual( - tree.exportSimpleSchema(), - toSimpleTreeSchema( - numberSchema, - true, - false /* Don't process this schema as a view schema (exclude isStaged from simpleAllowedTypes). */, - ), + const schemaWithoutIsStaged = removeIsStagedFromSimpleTreeSchema( + toSimpleTreeSchema(numberSchema, true), ); + assert.deepEqual(tree.exportSimpleSchema(), schemaWithoutIsStaged); }); it("supports multiple shared branches", () => { diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index a2f4c8f6dd94..9e57d171ce5d 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -12,8 +12,6 @@ import { SchemaFactoryAlpha, serializeCompatibilitySchema, stringSchema, - toViewCompatibilityTreeSchema, - TreeViewConfigurationAlpha, type SimpleLeafNodeSchema, type SimpleNodeSchema, type SimpleObjectFieldSchema, @@ -21,8 +19,12 @@ import { type SimpleTreeSchema, } from "../../../simple-tree/index.js"; import { ValueSchema } from "../../../core/index.js"; -// eslint-disable-next-line import/no-internal-modules -import { toSimpleTreeSchema } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; + +import { + removeMetadataFromSimpleTreeSchema, + toSimpleTreeSchema, + // eslint-disable-next-line import/no-internal-modules +} from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; const simpleString: SimpleLeafNodeSchema = { @@ -59,8 +61,6 @@ describe("getSimpleSchema", () => { assert.equal(actual.root, root); assert.equal(actual.definitions.get(Schema.identifier), Schema); }); - - // There is no non-copying form of {@link toViewCompatibilityTreeSchema}. }); describe("Field Schema", () => { @@ -87,34 +87,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - metadata: {}, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Field Schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Field Schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Field Schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Field Schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -141,34 +120,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - persistedMetadata: undefined, - }, - definitions: new Map([["com.fluidframework.leaf.string", simpleString]]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Leaf node", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Leaf node", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Leaf node", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Leaf node", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -199,38 +157,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - definitions: new Map([ - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Union root", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Union root", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Field Schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Field Schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -269,45 +202,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.array", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.array", - { - kind: NodeKind.Array, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - metadata: {}, - persistedMetadata: undefined, - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Array schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Array schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Array Schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Array Schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -346,45 +247,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.map", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.map", - { - kind: NodeKind.Map, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Map schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Map schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Map schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Map schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -423,45 +292,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.record", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.record", - { - kind: NodeKind.Record, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("view compatibility schema - Record schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Record schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Record schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Record schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -477,60 +314,6 @@ describe("getSimpleSchema", () => { it("toSimpleTreeSchema", () => { const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - [ - "bar", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "bar", - }, - ], - ]), - } satisfies SimpleObjectNodeSchema, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); - const expected: SimpleTreeSchema = { root: { kind: FieldKind.Required, @@ -582,15 +365,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("view compatibility schema - Object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Object schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Object schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -605,46 +386,6 @@ describe("getSimpleSchema", () => { it("toSimpleTreeSchema", () => { const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "id", - { - kind: FieldKind.Identifier, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "id", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected: SimpleTreeSchema = { root: { kind: FieldKind.Required, @@ -680,19 +421,16 @@ describe("getSimpleSchema", () => { ]), }; - const actual = toViewCompatibilityTreeSchema(treeView); assert.deepEqual(actual, expected); }); - it("view compatibility schema - Object schema including an identifier field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Object schema including an identifier field", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Object schema including an identifier field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Object schema including an identifier field", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -708,51 +446,6 @@ describe("getSimpleSchema", () => { // Must enable copy so deep equality passes. const actual = toSimpleTreeSchema(Schema, true); - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.object", { isStaged: false }]]), - }, - definitions: new Map([ - [ - "test.object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Required, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.number", { isStaged: false }], - ["com.fluidframework.leaf.string", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.number", simpleNumber], - ["com.fluidframework.leaf.string", simpleString], - ]), - }; - - assert.deepEqual(actual, expected); - }); - - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - - // Must enable copy so deep equality passes. - const actual = toViewCompatibilityTreeSchema(treeView); - const expected: SimpleTreeSchema = { root: { kind: FieldKind.Required, @@ -793,15 +486,13 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("view compatibility schema - Object schema including a union field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - Object schema including a union field", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Object schema including a union field", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - Object schema including a union field", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); @@ -830,6 +521,7 @@ describe("getSimpleSchema", () => { kind: NodeKind.Object, metadata: {}, persistedMetadata: undefined, + allowUnknownOptionalFields: false, fields: new Map([ [ "foo", @@ -854,58 +546,48 @@ describe("getSimpleSchema", () => { assert.deepEqual(actual, expected); }); - it("toViewCompatibilityTreeSchema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = toViewCompatibilityTreeSchema(treeView); + it("serialized - Recursive object schema", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + takeJsonSnapshot(actual); + }); + it("Roundtrip serialization - Recursive object schema", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + assert.deepEqual(actual, expected); + }); + }); + + describe("With staged schema upgrades", () => { + const leafSchema = stringSchema; + const schemaFactory = new SchemaFactoryAlpha("test"); + const root = schemaFactory.optional( + // Staged allowed types are read-only for the sake of schema migrations + schemaFactory.types([schemaFactory.staged(leafSchema)]), + ); + + it("Should preserve isReadOnly when converting to SimpleTreeSchema", () => { const expected: SimpleTreeSchema = { root: { - kind: FieldKind.Required, + kind: FieldKind.Optional, + simpleAllowedTypes: new Map([[leafSchema.identifier, { isStaged: true }]]), metadata: {}, persistedMetadata: undefined, - simpleAllowedTypes: new Map([["test.recursive-object", { isStaged: false }]]), }, - definitions: new Map([ - [ - "test.recursive-object", - { - kind: NodeKind.Object, - metadata: {}, - persistedMetadata: undefined, - allowUnknownOptionalFields: false, - fields: new Map([ - [ - "foo", - { - kind: FieldKind.Optional, - metadata: {}, - persistedMetadata: undefined, - simpleAllowedTypes: new Map([ - ["com.fluidframework.leaf.string", { isStaged: false }], - ["test.recursive-object", { isStaged: false }], - ]), - storedKey: "foo", - }, - ], - ]), - }, - ], - ["com.fluidframework.leaf.string", simpleString], - ]), + definitions: new Map([[leafSchema.identifier, leafSchema]]), }; - assert.deepEqual(actual, expected); + const actual = toSimpleTreeSchema(root, true); + assert.deepEqual(actual.root.simpleAllowedTypes, expected.root.simpleAllowedTypes); }); - it("view compatibility schema - Recursive object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); + it("serialized - simpleAllowedTypes", () => { + const actual = serializeCompatibilitySchema(toSimpleTreeSchema(root, true)); takeJsonSnapshot(actual); }); - it("Roundtrip view compatibility schema serialization - Recursive object schema", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: Schema }); - const expected = toViewCompatibilityTreeSchema(treeView); + it("Roundtrip serialization - simpleAllowedTypes", () => { + const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(root, true)); const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); assert.deepEqual(actual, expected); }); diff --git a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts deleted file mode 100644 index 8d6eed468feb..000000000000 --- a/packages/dds/tree/src/test/simple-tree/api/getViewCompatibilityTreeSchema.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -/*! - * Copyright (c) Microsoft Corporation and contributors. All rights reserved. - * Licensed under the MIT License. - */ - -import { - deserializeCompatibilitySchema, - FieldKind, - SchemaFactoryAlpha, - serializeCompatibilitySchema, - stringSchema, - toViewCompatibilityTreeSchema, - TreeViewConfigurationAlpha, - type SimpleTreeSchema, -} from "../../../simple-tree/index.js"; -import { strict as assert } from "node:assert"; -import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; - -describe("getViewCompatibilityTreeSchema", () => { - useSnapshotDirectory("get-view-compatibility-tree-schema"); - - describe("With staged schema upgrades", () => { - const leafSchema = stringSchema; - const schemaFactory = new SchemaFactoryAlpha("test"); - const root = schemaFactory.optional( - // Staged allowed types are read-only for the sake of schema migrations - schemaFactory.types([schemaFactory.staged(leafSchema)]), - ); - - it("Should preserve isReadOnly when converting to SimpleTreeSchema", () => { - const expected: SimpleTreeSchema = { - root: { - kind: FieldKind.Optional, - simpleAllowedTypes: new Map([[leafSchema.identifier, { isStaged: true }]]), - metadata: {}, - persistedMetadata: undefined, - }, - definitions: new Map([[leafSchema.identifier, leafSchema]]), - }; - - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = toViewCompatibilityTreeSchema(treeView); - assert.deepEqual(actual.root.simpleAllowedTypes, expected.root.simpleAllowedTypes); - }); - - it("view compatibility schema - simpleAllowedTypes", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const actual = serializeCompatibilitySchema(toViewCompatibilityTreeSchema(treeView)); - takeJsonSnapshot(actual); - }); - - it("Roundtrip view compatibility schema serialization - simpleAllowedTypes", () => { - const treeView = new TreeViewConfigurationAlpha({ schema: root }); - const expected = toViewCompatibilityTreeSchema(treeView); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); - assert.deepEqual(actual, expected); - }); - }); -}); diff --git a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts index 6bb4a8ccb03a..f740d7e1f0dc 100644 --- a/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/simpleSchemaToJsonSchema.spec.ts @@ -365,6 +365,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Object, metadata: {}, persistedMetadata: undefined, + allowUnknownOptionalFields: false, fields: new Map([ [ "foo", @@ -510,6 +511,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Object, metadata: {}, persistedMetadata: undefined, + allowUnknownOptionalFields: false, fields: new Map([ [ "id", @@ -570,6 +572,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Object, metadata: {}, persistedMetadata: undefined, + allowUnknownOptionalFields: false, fields: new Map([ [ "foo", @@ -640,6 +643,7 @@ describe("simpleSchemaToJsonSchema", () => { kind: NodeKind.Object, metadata: {}, persistedMetadata: undefined, + allowUnknownOptionalFields: false, fields: new Map([ [ "foo", From f94bfc284c440e82cf738cf2b8540b3597648662 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Mon, 3 Nov 2025 14:23:57 -0800 Subject: [PATCH 28/47] Fixed test "exportVerbose & exportSimpleSchema" --- .../src/test/shared-tree/sharedTree.spec.ts | 99 +++++-------------- 1 file changed, 26 insertions(+), 73 deletions(-) diff --git a/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts b/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts index 8bcc235b689f..aa348996a7c9 100644 --- a/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts +++ b/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts @@ -29,6 +29,7 @@ import { type ChangeFamily, type ChangeFamilyEditor, EmptyKey, + ValueSchema, } from "../../core/index.js"; import { FormatValidatorBasic } from "../../external-utilities/index.js"; import { @@ -70,10 +71,9 @@ import { type ITree, toInitialSchema, NodeKind, - type SimpleObjectFieldSchema, - type SimpleNodeSchema, type SimpleTreeSchema, - type SimpleAllowedTypeAttributes, + FieldKind, + type SimpleLeafNodeSchema, } from "../../simple-tree/index.js"; import { brand } from "../../util/index.js"; import { @@ -122,7 +122,6 @@ import { simpleTreeNodeSlot } from "../../simple-tree/core/treeNodeKernel.js"; import type { TreeSimpleContent } from "../feature-libraries/flex-tree/utils.js"; import { FluidClientVersion } from "../../codec/index.js"; import { asAlpha } from "../../api.js"; -import { unreachableCase } from "@fluidframework/core-utils/internal"; const enableSchemaValidation = true; @@ -2490,71 +2489,6 @@ describe("SharedTree", () => { ); }); - function removeIsStagedFromAllowedTypes( - simpleAllowedTypes: ReadonlyMap, - ): ReadonlyMap { - const output = new Map(); - for (const [typeIdentifier, attributes] of simpleAllowedTypes.entries()) { - const { isStaged, ...rest } = attributes; - output.set(typeIdentifier, { ...rest, isStaged: undefined }); - } - return output; - } - - function removeIsStagedFromSimpleTreeSchema(schema: SimpleTreeSchema): SimpleTreeSchema { - const definitions = new Map(); - - for (const [identifier, nodeSchema] of schema.definitions.entries()) { - const kind = nodeSchema.kind; - switch (kind) { - case NodeKind.Leaf: { - definitions.set(identifier, nodeSchema); - break; - } - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: { - const outputNodeSchema = { - ...nodeSchema, - simpleAllowedTypes: removeIsStagedFromAllowedTypes(nodeSchema.simpleAllowedTypes), - }; - definitions.set(identifier, outputNodeSchema); - break; - } - case NodeKind.Object: { - const outputFields = new Map(); - for (const [propertyKey, fieldSchema] of nodeSchema.fields.entries()) { - const outputField: SimpleObjectFieldSchema = { - ...fieldSchema, - simpleAllowedTypes: removeIsStagedFromAllowedTypes( - fieldSchema.simpleAllowedTypes, - ), - }; - outputFields.set(propertyKey, outputField); - } - const outputNodeSchema = { - ...nodeSchema, - fields: outputFields, - }; - definitions.set(identifier, outputNodeSchema); - break; - } - default: - unreachableCase(kind); - } - } - - return { - root: { - kind: schema.root.kind, - simpleAllowedTypes: removeIsStagedFromAllowedTypes(schema.root.simpleAllowedTypes), - metadata: {}, - persistedMetadata: undefined, - }, - definitions, - }; - } - it("exportVerbose & exportSimpleSchema", () => { const tree = treeTestFactory(); assert.deepEqual(tree.exportVerbose(), undefined); @@ -2570,10 +2504,29 @@ describe("SharedTree", () => { view.initialize(10); assert.deepEqual(tree.exportVerbose(), 10); - const schemaWithoutIsStaged = removeIsStagedFromSimpleTreeSchema( - toSimpleTreeSchema(numberSchema, true), - ); - assert.deepEqual(tree.exportSimpleSchema(), schemaWithoutIsStaged); + + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + simpleAllowedTypes: new Map([ + ["com.fluidframework.leaf.number", { isStaged: undefined }], + ]), + metadata: {}, + persistedMetadata: undefined, + }, + definitions: new Map([ + [ + "com.fluidframework.leaf.number", + { + kind: NodeKind.Leaf, + leafKind: ValueSchema.Number, + metadata: {}, + persistedMetadata: undefined, + } satisfies SimpleLeafNodeSchema, + ], + ]), + }; + assert.deepEqual(tree.exportSimpleSchema(), expected); }); it("supports multiple shared branches", () => { From c77cf503314eecd1ea1d3536404daf6f419af65f Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Mon, 3 Nov 2025 14:58:03 -0800 Subject: [PATCH 29/47] - Fixed a bug in roundtrip serialization test code. - Updated snapshots. - Fixed an issue with allowUnknownSchemaTypes. It was not preserved by generateNode. --- .../src/simple-tree/api/schemaFromSimple.ts | 9 ++- .../api/viewCompatibilitySchemaSerializer.ts | 16 +----- .../api/viewSchemaToSimpleSchema.ts | 15 +++-- .../simple-tree/api/getSimpleSchema.spec.ts | 57 +++++++++++-------- ...ma.json => serialized - Array schema.json} | 0 ...ma.json => serialized - Field Schema.json} | 0 ... node.json => serialized - Leaf node.json} | 0 ...hema.json => serialized - Map schema.json} | 0 ...bject schema including a union field.json} | 0 ...schema including an identifier field.json} | 0 ...a.json => serialized - Object schema.json} | 0 ...a.json => serialized - Record schema.json} | 0 ...serialized - Recursive object schema.json} | 0 ...root.json => serialized - Union root.json} | 0 .../serialized - simpleAllowedTypes.json} | 0 packages/dds/tree/src/util/index.ts | 1 + packages/dds/tree/src/util/utils.ts | 15 +++++ 17 files changed, 67 insertions(+), 46 deletions(-) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Array schema.json => serialized - Array schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Field Schema.json => serialized - Field Schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Leaf node.json => serialized - Leaf node.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Map schema.json => serialized - Map schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Object schema including a union field.json => serialized - Object schema including a union field.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Object schema including an identifier field.json => serialized - Object schema including an identifier field.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Object schema.json => serialized - Object schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Record schema.json => serialized - Record schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Recursive object schema.json => serialized - Recursive object schema.json} (100%) rename packages/dds/tree/src/test/snapshots/get-simple-schema/{view compatibility schema - Union root.json => serialized - Union root.json} (100%) rename packages/dds/tree/src/test/snapshots/{get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json => get-simple-schema/serialized - simpleAllowedTypes.json} (100%) diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts index 0fe717923caa..6980e7cd7d98 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -106,9 +106,12 @@ function generateNode( for (const [key, field] of schema.fields) { fields[key] = generateFieldSchema(field, context, field.storedKey); } - // Here allowUnknownOptionalFields is implicitly defaulting. This is a subjective policy choice: - // users of this code are expected to handle what ever choice this code makes for cases like this. - return factory.objectAlpha(id, fields, { metadata: schema.metadata }); + // Here allowUnknownOptionalFields is implicitly defaulting in the case where the input schema does not explicitly specify the value. + // This is a subjective policy choice: users of this code are expected to handle what ever choice this code makes for cases like this. + return factory.objectAlpha(id, fields, { + metadata: schema.metadata, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields ?? false, + }); } case NodeKind.Array: return factory.arrayAlpha(id, generateAllowedTypes(schema.simpleAllowedTypes, context), { diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts index 39bf6b4841b7..1aea68cc29ea 100644 --- a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -6,6 +6,7 @@ import { isJsonObject, objectToMap, + transformMapValues, type JsonCompatible, type JsonCompatibleObject, } from "../../util/index.js"; @@ -367,18 +368,3 @@ function mapToRecord(map: ReadonlyMap): Record( - map: Map, - transformValue: (value: InputValue, key: Key) => OutputValue, -): Map { - return new Map( - Array.from(map.entries()).map(([key, value]) => [key, transformValue(value, key)]), - ); -} diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 71d076084bfd..f5d5db865541 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -145,12 +145,13 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod } /** - * Removes all metadata and persistedMetadata from a SimpleTreeSchema. This is useful for comparing deserialized view schemas with in-memory schemas. + * Creates a copy of a SimpleTreeSchema without metadata fields. This is useful for comparing deserialized view schemas with in-memory schemas. * metadata and persistedMetadata are not relevant for schema compatibility checks and are not serialized by the Simple Schema serializer (TODO: link). - * @param schema - The SimpleTreeSchema to remove metadata from. - * @returns A new SimpleTreeSchema without metadata and persistedMetadata. + * @param schema - The SimpleTreeSchema to remove fields from. + * @param fieldsToRemove - An object specifying which fields to remove. + * @returns A new SimpleTreeSchema without the specified fields. */ -export function removeMetadataFromSimpleTreeSchema( +export function copySimpleTreeSchemaWithoutMetadata( schema: SimpleTreeSchema, ): SimpleTreeSchema { const definitions = new Map(); @@ -162,7 +163,11 @@ export function removeMetadataFromSimpleTreeSchema( case NodeKind.Map: case NodeKind.Record: case NodeKind.Leaf: { - const outputNodeSchema = { ...nodeSchema, metadata: {}, persistedMetadata: undefined }; + const outputNodeSchema = { + ...nodeSchema, + metadata: {}, + persistedMetadata: undefined, + }; definitions.set(identifier, outputNodeSchema); break; } diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 9e57d171ce5d..b66664a8e214 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -21,7 +21,7 @@ import { import { ValueSchema } from "../../../core/index.js"; import { - removeMetadataFromSimpleTreeSchema, + copySimpleTreeSchemaWithoutMetadata, toSimpleTreeSchema, // eslint-disable-next-line import/no-internal-modules } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; @@ -93,8 +93,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Field Schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -126,8 +127,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Leaf node", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -163,8 +165,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Field Schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -208,8 +211,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Array Schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -253,8 +257,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Map schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -298,8 +303,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Record schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -371,8 +377,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Object schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -430,8 +437,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Object schema including an identifier field", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -492,8 +500,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Object schema including a union field", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -552,8 +561,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - Recursive object schema", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(Schema, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(Schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -587,8 +597,9 @@ describe("getSimpleSchema", () => { }); it("Roundtrip serialization - simpleAllowedTypes", () => { - const expected = removeMetadataFromSimpleTreeSchema(toSimpleTreeSchema(root, true)); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(expected)); + const simpleTree = toSimpleTreeSchema(root, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); assert.deepEqual(actual, expected); }); }); diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Array schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Array schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Array schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Field Schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Field Schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Field Schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Leaf node.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Leaf node.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Leaf node.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Map schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Map schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Map schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including a union field.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including a union field.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including a union field.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including an identifier field.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema including an identifier field.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including an identifier field.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Object schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Record schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Record schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Record schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Recursive object schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Recursive object schema.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Recursive object schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Union root.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/view compatibility schema - Union root.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Union root.json diff --git a/packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - simpleAllowedTypes.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-view-compatibility-tree-schema/view compatibility schema - simpleAllowedTypes.json rename to packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - simpleAllowedTypes.json diff --git a/packages/dds/tree/src/util/index.ts b/packages/dds/tree/src/util/index.ts index 49517f0e13bd..df9d99c92d6e 100644 --- a/packages/dds/tree/src/util/index.ts +++ b/packages/dds/tree/src/util/index.ts @@ -96,6 +96,7 @@ export { copyPropertyIfDefined as copyProperty, getOrAddInMap, iterableHasSome, + transformMapValues, } from "./utils.js"; export { ReferenceCountedBase, type ReferenceCounted } from "./referenceCounting.js"; diff --git a/packages/dds/tree/src/util/utils.ts b/packages/dds/tree/src/util/utils.ts index fa10fb3900ce..63760c1af2b9 100644 --- a/packages/dds/tree/src/util/utils.ts +++ b/packages/dds/tree/src/util/utils.ts @@ -485,6 +485,21 @@ export function transformObjectMap< return output; } +/** + * Transform the values of a Map using the provided transform function. + * @param map - The map to transform. + * @param transformValue - A method for transforming values in the map. + * @returns A new map with the transformed values. + */ +export function transformMapValues( + map: ReadonlyMap, + transformValue: (value: InputValue, key: Key) => OutputValue, +): Map { + return new Map( + Array.from(map.entries()).map(([key, value]) => [key, transformValue(value, key)]), + ); +} + /** * Make an inverted copy of a map. * From ce5701cfb3a3ba5a2c5576c640a0ac4ab33c0dd3 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Mon, 3 Nov 2025 17:26:15 -0800 Subject: [PATCH 30/47] Exposed serialization functions as alpha. --- .../dds/tree/api-report/tree.alpha.api.md | 17 ++++--- packages/dds/tree/api-report/tree.beta.api.md | 4 +- .../tree/api-report/tree.legacy.beta.api.md | 4 +- packages/dds/tree/src/index.ts | 2 + .../dds/tree/src/simple-tree/api/index.ts | 4 +- .../api/viewCompatibilitySchemaSerializer.ts | 12 +++-- packages/dds/tree/src/simple-tree/index.ts | 4 +- .../simple-tree/api/getSimpleSchema.spec.ts | 48 +++++++++---------- 8 files changed, 51 insertions(+), 44 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index c38cdb4a86e4..7917855744b5 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -188,6 +188,9 @@ export function createSimpleTreeIndex { } +// @alpha +export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema; + // @alpha export interface DirtyTreeMap { // (undocumented) @@ -318,7 +321,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -327,7 +330,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; @@ -520,7 +523,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system @@ -982,6 +985,9 @@ export class SchemaUpgrade { // @public @system type ScopedSchemaName = TScope extends undefined ? `${TName}` : `${TScope}.${TName}`; +// @alpha +export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; + // @alpha @input export interface SharedTreeFormatOptions { formatVersion: SharedTreeFormatVersion[keyof SharedTreeFormatVersion]; @@ -1055,7 +1061,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowUnknownOptionalFields?: boolean; + readonly allowUnknownOptionalFields: boolean | undefined; readonly fields: ReadonlyMap; } @@ -1312,9 +1318,6 @@ export namespace TableSchema { }): System_TableSchema.TableSchemaBase; } -// @alpha -export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; - // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index 496725d9c09f..2f941ad97530 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -175,7 +175,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -184,7 +184,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/api-report/tree.legacy.beta.api.md b/packages/dds/tree/api-report/tree.legacy.beta.api.md index a0ff9ab7dfad..e7d4b0351c4c 100644 --- a/packages/dds/tree/api-report/tree.legacy.beta.api.md +++ b/packages/dds/tree/api-report/tree.legacy.beta.api.md @@ -178,7 +178,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -187,7 +187,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 95adf131fec3..9a04b71d7080 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -281,6 +281,8 @@ export { type SchemaFactory_base, type NumberKeys, type SimpleAllowedTypeAttributes, + serializeSimpleSchema, + deserializeSimpleSchema, } from "./simple-tree/index.js"; export { SharedTree, diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 8f177419e6e0..681e148048c4 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -168,6 +168,6 @@ export { incrementalSummaryHint, } from "./incrementalAllowedTypes.js"; export { - serializeCompatibilitySchema, - deserializeCompatibilitySchema, + serializeSimpleSchema, + deserializeSimpleSchema, } from "./viewCompatibilitySchemaSerializer.js"; diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts index 1aea68cc29ea..b89ef70672ae 100644 --- a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -30,11 +30,13 @@ import type { ValueSchema } from "../../core/index.js"; /** * Converts a view schema to a serializable format for compatibility testing. * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. - * It is not possible to reconstruct a full view schema from the serialized format. + * It is not possible to reconstruct a full view schema (including metadata and persistedMetadata) from the serialized format. * @param treeSchema - The tree schema to convert. * @returns A serializable representation of the view schema. + * + * @alpha */ -export function serializeCompatibilitySchema(simpleSchema: SimpleTreeSchema): JsonCompatible { +export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible { // Convert types to serializable forms const serializableDefinitions = new Map(); @@ -55,10 +57,10 @@ export function serializeCompatibilitySchema(simpleSchema: SimpleTreeSchema): Js * Deserializes a JSON-compatible schema into a view schema for compatibility testing. * @param serializedSchema - The serialized schema to deserialize. * @returns A deserialized view schema. + * + * @alpha */ -export function deserializeCompatibilitySchema( - serializedSchema: JsonCompatible, -): SimpleTreeSchema { +export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema { assert(isJsonObject(serializedSchema), "Expected object for serializedSchema"); const serializedSchemaAsObject = serializedSchema as JsonCompatibleObject; diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 092d63cda3c2..3144e75f2b6b 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -281,6 +281,6 @@ export { } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; export { - serializeCompatibilitySchema, - deserializeCompatibilitySchema, + serializeSimpleSchema, + deserializeSimpleSchema, } from "./api/index.js"; diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index b66664a8e214..7e6f3c05436c 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -5,12 +5,12 @@ import { strict as assert } from "node:assert"; import { - deserializeCompatibilitySchema, + deserializeSimpleSchema, FieldKind, NodeKind, SchemaFactory, SchemaFactoryAlpha, - serializeCompatibilitySchema, + serializeSimpleSchema, stringSchema, type SimpleLeafNodeSchema, type SimpleNodeSchema, @@ -88,14 +88,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Field Schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -122,14 +122,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Leaf node", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Leaf node", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -160,14 +160,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Union root", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -206,14 +206,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Array schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Array Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -252,14 +252,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Map schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Map schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -298,14 +298,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Record schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Record schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -372,14 +372,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -432,14 +432,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema including an identifier field", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema including an identifier field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -495,14 +495,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema including a union field", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema including a union field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -556,14 +556,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Recursive object schema", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(Schema, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Recursive object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -592,14 +592,14 @@ describe("getSimpleSchema", () => { }); it("serialized - simpleAllowedTypes", () => { - const actual = serializeCompatibilitySchema(toSimpleTreeSchema(root, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(root, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - simpleAllowedTypes", () => { const simpleTree = toSimpleTreeSchema(root, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeCompatibilitySchema(serializeCompatibilitySchema(simpleTree)); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); From 3bd0e156db20ee0d67b7c3df5e2c7640d3a93961 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 08:52:44 -0800 Subject: [PATCH 31/47] Regenerated API file. --- .../api-report/fluid-framework.alpha.api.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index 21b7b86d65d9..0cb7fcac53ea 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -230,6 +230,9 @@ export function createSimpleTreeIndex { } +// @alpha +export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema; + // @alpha export interface DirtyTreeMap { // (undocumented) @@ -1353,6 +1356,9 @@ export class SchemaUpgrade { // @public @system type ScopedSchemaName = TScope extends undefined ? `${TName}` : `${TScope}.${TName}`; +// @alpha +export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; + // @public @sealed export interface SharedObjectKind extends ErasedType { is(value: IFluidLoadable): value is IFluidLoadable & TSharedObject; @@ -1434,7 +1440,7 @@ export interface SimpleObjectFieldSchema extends SimpleFieldSchema { // @alpha @sealed export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBaseAlpha { - readonly allowUnknownOptionalFields?: boolean; + readonly allowUnknownOptionalFields: boolean | undefined; readonly fields: ReadonlyMap; } @@ -1702,9 +1708,6 @@ export interface Tagged { // @public export type TelemetryBaseEventPropertyType = string | number | boolean | undefined; -// @alpha -export function toViewCompatibilityTreeSchema(schema: TreeSchema, copySchemaObjects: boolean): SimpleTreeSchema; - // @alpha export function trackDirtyNodes(view: TreeViewAlpha, dirty: DirtyTreeMap): () => void; From 7471b77bd86965e7853e80b02ed3adeec7280db9 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 12:10:54 -0800 Subject: [PATCH 32/47] Tests for `allowUnknownOptionalFields`. --- .../simple-tree/api/getSimpleSchema.spec.ts | 53 +++++++++++++++++-- ...rialized - allowUnknownOptionalFields.json | 17 ++++++ 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 7e6f3c05436c..dac31e8d5cf5 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -26,6 +26,7 @@ import { // eslint-disable-next-line import/no-internal-modules } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; +import { HasUnknownOptionalFields } from "../../testTrees.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -571,7 +572,7 @@ describe("getSimpleSchema", () => { describe("With staged schema upgrades", () => { const leafSchema = stringSchema; const schemaFactory = new SchemaFactoryAlpha("test"); - const root = schemaFactory.optional( + const schema = schemaFactory.optional( // Staged allowed types are read-only for the sake of schema migrations schemaFactory.types([schemaFactory.staged(leafSchema)]), ); @@ -587,17 +588,61 @@ describe("getSimpleSchema", () => { definitions: new Map([[leafSchema.identifier, leafSchema]]), }; - const actual = toSimpleTreeSchema(root, true); + const actual = toSimpleTreeSchema(schema, true); assert.deepEqual(actual.root.simpleAllowedTypes, expected.root.simpleAllowedTypes); }); it("serialized - simpleAllowedTypes", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(root, true)); + const actual = serializeSimpleSchema(toSimpleTreeSchema(schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - simpleAllowedTypes", () => { - const simpleTree = toSimpleTreeSchema(root, true); + const simpleTree = toSimpleTreeSchema(schema, true); + const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); + const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + assert.deepEqual(actual, expected); + }); + }); + + describe("With allowUnknownOptionalFields in object schema", () => { + const schema = HasUnknownOptionalFields; + + it("Should preserve allowUnknownOptionalFields when converting to SimpleTreeSchema", () => { + const expected: SimpleTreeSchema = { + root: { + kind: FieldKind.Required, + simpleAllowedTypes: new Map([ + ["test.hasUnknownOptionalFields", { isStaged: false }], + ]), + metadata: {}, + persistedMetadata: undefined, + }, + definitions: new Map([ + [ + "test.hasUnknownOptionalFields", + { + kind: NodeKind.Object, + metadata: {}, + persistedMetadata: undefined, + allowUnknownOptionalFields: true, + fields: new Map([]), + }, + ], + ]), + }; + + const actual = toSimpleTreeSchema(schema, true); + assert.deepEqual(actual, expected); + }); + + it("serialized - allowUnknownOptionalFields", () => { + const actual = serializeSimpleSchema(toSimpleTreeSchema(schema, true)); + takeJsonSnapshot(actual); + }); + + it("Roundtrip serialization - allowUnknownOptionalFields", () => { + const simpleTree = toSimpleTreeSchema(schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json new file mode 100644 index 000000000000..f6d3b9681b13 --- /dev/null +++ b/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json @@ -0,0 +1,17 @@ +{ + "root": { + "kind": 1, + "simpleAllowedTypes": { + "test.hasUnknownOptionalFields": { + "isStaged": false + } + } + }, + "definitions": { + "test.hasUnknownOptionalFields": { + "kind": 2, + "fields": {}, + "allowUnknownOptionalFields": true + } + } +} \ No newline at end of file From dd4b51a71be0b278bd5c4c4bccbdad21fd1e00f9 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 12:29:40 -0800 Subject: [PATCH 33/47] Cleanup and PR feedback. --- .../src/simple-tree/api/viewCompatibilitySchemaSerializer.ts | 1 + .../dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts | 2 +- packages/dds/tree/src/simple-tree/simpleSchema.ts | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts index b89ef70672ae..b1615473500b 100644 --- a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts @@ -150,6 +150,7 @@ function serializeSimpleAllowedTypes( const serializableAllowedTypes: JsonCompatibleObject = {}; for (const [identifier, attributes] of simpleAllowedTypes) { serializableAllowedTypes[identifier] = { + // The type of SimpleAllowedTypeAttributes is not assignable to JsonCompatibleObject, so we splat its fields here. ...attributes, }; } diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index f5d5db865541..60ff61c59591 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -125,7 +125,7 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod const simpleField = { kind: field.kind, simpleAllowedTypes: field.simpleAllowedTypes, - metadata: {}, + metadata: field.metadata, persistedMetadata: field.persistedMetadata, storedKey: field.storedKey, }; diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index 31764e4dbb35..2cc04b6d40a3 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -62,7 +62,9 @@ export interface SimpleObjectNodeSchema /** * Whether the object node allows unknown optional fields. * - * @remarks Only populated for view schemas. Relevant for compatibility checking scenarios. + * See {@link ObjectSchemaOptions.allowUnknownOptionalFields} for the API where this field is set as part of authoring a schema. + * + * @remarks Only populated for view schemas, undefined otherwise. Relevant for compatibility checking scenarios. */ readonly allowUnknownOptionalFields: boolean | undefined; } From 9ab7da16eb8b5e6efab5909f4c8468aba8ba06ce Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 13:59:36 -0800 Subject: [PATCH 34/47] Rename. --- packages/dds/tree/src/simple-tree/api/index.ts | 2 +- ...mpatibilitySchemaSerializer.ts => simpleSchemaSerializer.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/dds/tree/src/simple-tree/api/{viewCompatibilitySchemaSerializer.ts => simpleSchemaSerializer.ts} (100%) diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 681e148048c4..f750fc72f5cf 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -170,4 +170,4 @@ export { export { serializeSimpleSchema, deserializeSimpleSchema, -} from "./viewCompatibilitySchemaSerializer.js"; +} from "./simpleSchemaSerializer.js"; diff --git a/packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts similarity index 100% rename from packages/dds/tree/src/simple-tree/api/viewCompatibilitySchemaSerializer.ts rename to packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts From 86d20d6169b837163e10770e8f0db18306d336d6 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 14:08:40 -0800 Subject: [PATCH 35/47] PR feedback for the serializer/deserializer. --- .../simple-tree/api/simpleSchemaSerializer.ts | 73 +++++++++---------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts index b1615473500b..b35c7355933d 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts @@ -10,7 +10,7 @@ import { type JsonCompatible, type JsonCompatibleObject, } from "../../util/index.js"; -import { fail, unreachableCase, assert } from "@fluidframework/core-utils/internal"; +import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; import type { SimpleAllowedTypeAttributes, SimpleArrayNodeSchema, @@ -26,6 +26,7 @@ import type { import { NodeKind } from "../core/index.js"; import type { FieldKind } from "../fieldSchema.js"; import type { ValueSchema } from "../../core/index.js"; +import { UsageError } from "@fluidframework/telemetry-utils/internal"; /** * Converts a view schema to a serializable format for compatibility testing. @@ -38,16 +39,16 @@ import type { ValueSchema } from "../../core/index.js"; */ export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible { // Convert types to serializable forms - const serializableDefinitions = new Map(); + const serializableDefinitions: Record = {}; for (const [identifier, schema] of simpleSchema.definitions) { const serializableDefinition = serializeNodeSchema(schema); - serializableDefinitions.set(identifier, serializableDefinition); + serializableDefinitions[identifier] = serializableDefinition; } const serializedSchema = { root: serializeField(simpleSchema.root), - definitions: mapToRecord(serializableDefinitions), + definitions: serializableDefinitions, } as unknown as JsonCompatible; return serializedSchema; @@ -57,33 +58,40 @@ export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompa * Deserializes a JSON-compatible schema into a view schema for compatibility testing. * @param serializedSchema - The serialized schema to deserialize. * @returns A deserialized view schema. + * @throws Will throw a usage error if the serialized schema is not in the expected format. * * @alpha */ export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema { - assert(isJsonObject(serializedSchema), "Expected object for serializedSchema"); + if (!isJsonObject(serializedSchema)) { + throw new UsageError("Expected object for serializedSchema"); + } + const serializedSchemaAsObject = serializedSchema as JsonCompatibleObject; - assert( - isJsonObject(serializedSchemaAsObject.root ?? fail("Expected root field")), - "Expected object for root field schema", - ); + if ( + serializedSchemaAsObject.root === undefined || + !isJsonObject(serializedSchemaAsObject.root) + ) { + throw new UsageError("Expected object for root field schema"); + } const serializedRoot = serializedSchemaAsObject.root as JsonCompatibleObject; - assert( - isJsonObject(serializedSchemaAsObject.definitions ?? fail("Expected definitions field")), - "Expected object for definitions field", - ); + if ( + serializedSchemaAsObject.definitions === undefined || + !isJsonObject(serializedSchemaAsObject.definitions) + ) { + throw new UsageError("Expected object for definitions field"); + } const serializedDefinitions = serializedSchemaAsObject.definitions as JsonCompatibleObject; return { root: deserializeSimpleFieldSchema(serializedRoot), definitions: new Map( transformMapValues(objectToMap(serializedDefinitions), (value, key) => { - assert( - isJsonObject(value ?? fail(`Expected node schema for definition ${key}`)), - "Expected object for node schema in definitions", - ); + if (value === undefined || !isJsonObject(value)) { + throw new UsageError(`Expected node schema for definition ${key}`); + } return deserializeNodeSchema(value as JsonCompatibleObject); }), ), @@ -283,11 +291,14 @@ function deserializeObjectNode( * Deserializes a map of object fields from a JSON-compatible object. * @param serializedFields - The serialized fields. * @returns A map of the deserialized object fields. + * @throws Will throw a usage error if the serialized fields are not in the expected format. */ function deserializeObjectFields( serializedFields: JsonCompatible, ): ReadonlyMap { - assert(isJsonObject(serializedFields), "Expected object for serializedFields"); + if (!isJsonObject(serializedFields)) { + throw new UsageError("Expected object for serializedFields"); + } const fields = new Map(); for (const [fieldKey, fieldSchema] of Object.entries( @@ -335,11 +346,14 @@ function deserializeSimpleFieldSchema( * Deserializes a simple allowed types map from a JSON-compatible object. * @param serializedAllowedTypes - The serialized simple allowed types. * @returns A map of the deserialized simple allowed types. + * @throws Will throw a usage error if the serialized allowed types are not in the expected format. */ function deserializeSimpleAllowedTypes( serializedAllowedTypes: JsonCompatible, ): ReadonlyMap { - assert(isJsonObject(serializedAllowedTypes), "Expected object for simpleAllowedTypes"); + if (!isJsonObject(serializedAllowedTypes)) { + throw new UsageError("Expected object for serializedAllowedTypes"); + } const untypedMap = objectToMap(serializedAllowedTypes as JsonCompatibleObject); const simpleAllowedTypes = transformMapValues(untypedMap, (value) => { @@ -350,24 +364,3 @@ function deserializeSimpleAllowedTypes( return simpleAllowedTypes; } - -/** - * Convert a Map to a Record for serialization. - * @remarks This is needed because the JSON serializer does not support Maps. - * It is possible that the keys may not be stringify-able types, so this method is a best-effort implementation and its output - * should only be used in snapshots or debugging scenarios. - * @param map - The Map to convert. - * @returns A Record with the contents of the Map. - */ -function mapToRecord(map: ReadonlyMap): Record { - const resultObject: Record = {}; - const sortedKeys = Array.from(map.keys()).sort(); - - for (const key of sortedKeys) { - const value = - map.get(key) ?? fail("Invalid map: key present in keys() but not found in map."); - resultObject[`${key}`] = value; - } - - return resultObject; -} From cf4beb45e360a95720308a6573e114074c6771c0 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 17:22:11 -0800 Subject: [PATCH 36/47] Moved snapshots. --- .../{ => output}/get-simple-schema/serialized - Array schema.json | 0 .../{ => output}/get-simple-schema/serialized - Field Schema.json | 0 .../{ => output}/get-simple-schema/serialized - Leaf node.json | 0 .../{ => output}/get-simple-schema/serialized - Map schema.json | 0 .../serialized - Object schema including a union field.json | 0 .../serialized - Object schema including an identifier field.json | 0 .../get-simple-schema/serialized - Object schema.json | 0 .../get-simple-schema/serialized - Record schema.json | 0 .../get-simple-schema/serialized - Recursive object schema.json | 0 .../{ => output}/get-simple-schema/serialized - Union root.json | 0 .../serialized - allowUnknownOptionalFields.json | 0 .../get-simple-schema/serialized - simpleAllowedTypes.json | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Array schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Field Schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Leaf node.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Map schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Object schema including a union field.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Object schema including an identifier field.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Object schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Record schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Recursive object schema.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - Union root.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - allowUnknownOptionalFields.json (100%) rename packages/dds/tree/src/test/snapshots/{ => output}/get-simple-schema/serialized - simpleAllowedTypes.json (100%) diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Array schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Array schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Field Schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Field Schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Leaf node.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Leaf node.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Map schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Map schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including a union field.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema including an identifier field.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Object schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Record schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Record schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Recursive object schema.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Union root.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - Union root.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - allowUnknownOptionalFields.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json diff --git a/packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - simpleAllowedTypes.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json similarity index 100% rename from packages/dds/tree/src/test/snapshots/get-simple-schema/serialized - simpleAllowedTypes.json rename to packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json From b65177b9c5d6004f559851f03b3737975ed90a6e Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Tue, 4 Nov 2025 17:52:38 -0800 Subject: [PATCH 37/47] PR feedback. --- .../simple-tree/api/simpleSchemaSerializer.ts | 18 +++++++++++++++--- .../api/viewSchemaToSimpleSchema.ts | 4 +++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts index b35c7355933d..1c0277339e97 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts @@ -133,7 +133,7 @@ function serializeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { } /** - * Converts a container node schema to a serializable object. + * Converts a container node schema (a simple schema that is a Map, Array, or Record) to a serializable object. * @param schema - The container node schema to convert. * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different * container kinds. @@ -248,6 +248,7 @@ function deserializeContainerNode( simpleAllowedTypes: deserializeSimpleAllowedTypes( serializedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, ), + // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, }; @@ -264,6 +265,7 @@ function deserializeLeafNode( return { kind: NodeKind.Leaf, leafKind: (serializedLeafSchema.leafKind ?? fail("Missing leafKind")) as ValueSchema, + // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, }; @@ -273,15 +275,24 @@ function deserializeLeafNode( * Deserializes a object node schema from a JSON-compatible object. * @param serializedObjectSchema - The serialized object node schema. * @returns The deserialized object node schema. + * @throws Will throw a usage error if the serialized object schema is not in the expected format. */ function deserializeObjectNode( serializedObjectSchema: JsonCompatibleObject, ): SimpleObjectNodeSchema { + if (serializedObjectSchema.fields === undefined) { + throw new UsageError("Expected fields for object node schema"); + } + return { kind: NodeKind.Object, fields: deserializeObjectFields(serializedObjectSchema.fields ?? fail("Missing fields")), - allowUnknownOptionalFields: (serializedObjectSchema.allowUnknownOptionalFields ?? - fail("Missing allowUnknownOptionalFields")) as boolean, + // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived + // from a stored schema. + allowUnknownOptionalFields: serializedObjectSchema.allowUnknownOptionalFields as + | boolean + | undefined, + // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, }; @@ -337,6 +348,7 @@ function deserializeSimpleFieldSchema( simpleAllowedTypes: deserializeSimpleAllowedTypes( serializedField.simpleAllowedTypes as JsonCompatibleObject, ), + // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, }; diff --git a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts index 60ff61c59591..1d5e2e774557 100644 --- a/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/viewSchemaToSimpleSchema.ts @@ -146,7 +146,9 @@ function copySimpleObjectSchema(schema: SimpleObjectNodeSchema): SimpleObjectNod /** * Creates a copy of a SimpleTreeSchema without metadata fields. This is useful for comparing deserialized view schemas with in-memory schemas. - * metadata and persistedMetadata are not relevant for schema compatibility checks and are not serialized by the Simple Schema serializer (TODO: link). + * metadata and persistedMetadata are not relevant for schema compatibility checks and are not serialized by the Simple Schema serializer. + * @see {@link simpleSchemaSerializer.ts} for the serialization logic. + * * @param schema - The SimpleTreeSchema to remove fields from. * @param fieldsToRemove - An object specifying which fields to remove. * @returns A new SimpleTreeSchema without the specified fields. From b49d1146db833fd9820db33a16972b89e41c12ef Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 15:43:14 -0800 Subject: [PATCH 38/47] PR feedback. --- packages/common/core-utils/src/index.ts | 1 + packages/common/core-utils/src/map.ts | 21 +++++++++++++++++++ .../dds/tree/api-report/tree.alpha.api.md | 6 +++--- packages/dds/tree/api-report/tree.beta.api.md | 4 ++-- .../tree/api-report/tree.legacy.beta.api.md | 4 ++-- .../simple-tree/api/simpleSchemaSerializer.ts | 7 +++++-- .../dds/tree/src/simple-tree/simpleSchema.ts | 2 +- .../simple-tree/api/getSimpleSchema.spec.ts | 14 ++++++------- packages/dds/tree/src/util/index.ts | 1 - packages/dds/tree/src/util/utils.ts | 15 ------------- 10 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 packages/common/core-utils/src/map.ts diff --git a/packages/common/core-utils/src/index.ts b/packages/common/core-utils/src/index.ts index 4dc3f7527a43..0cc4e184964d 100644 --- a/packages/common/core-utils/src/index.ts +++ b/packages/common/core-utils/src/index.ts @@ -33,3 +33,4 @@ export { PromiseTimer, setLongTimeout, Timer } from "./timer.js"; export { unreachableCase } from "./unreachable.js"; export { isObject, isPromiseLike } from "./typesGuards.js"; export { oob } from "./oob.js"; +export { transformMapValues } from "./map.js"; diff --git a/packages/common/core-utils/src/map.ts b/packages/common/core-utils/src/map.ts new file mode 100644 index 000000000000..88a5412432b2 --- /dev/null +++ b/packages/common/core-utils/src/map.ts @@ -0,0 +1,21 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +/** + * Transform the values of a Map using the provided transform function. + * @param map - The map to transform. + * @param transformValue - A method for transforming values in the map. + * @returns A new map with the transformed values. + * + * @internal + */ +export function transformMapValues( + map: ReadonlyMap, + transformValue: (value: InputValue, key: Key) => OutputValue, +): Map { + return new Map( + [...map.entries()].map(([key, value]) => [key, transformValue(value, key)]), + ); +} diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 5df2473c4824..dfd126e26f00 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -336,7 +336,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -345,7 +345,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; + [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; @@ -538,7 +538,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; + readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index 27b5f74dbd1c..3b4e2fe428c3 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -194,7 +194,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -203,7 +203,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; + [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/api-report/tree.legacy.beta.api.md b/packages/dds/tree/api-report/tree.legacy.beta.api.md index 8121b6b3d3ca..34c341f3d995 100644 --- a/packages/dds/tree/api-report/tree.legacy.beta.api.md +++ b/packages/dds/tree/api-report/tree.legacy.beta.api.md @@ -197,7 +197,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; + readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -206,7 +206,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; + [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts index 1c0277339e97..edb625a7a0c1 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts @@ -6,11 +6,14 @@ import { isJsonObject, objectToMap, - transformMapValues, type JsonCompatible, type JsonCompatibleObject, } from "../../util/index.js"; -import { fail, unreachableCase } from "@fluidframework/core-utils/internal"; +import { + fail, + unreachableCase, + transformMapValues, +} from "@fluidframework/core-utils/internal"; import type { SimpleAllowedTypeAttributes, SimpleArrayNodeSchema, diff --git a/packages/dds/tree/src/simple-tree/simpleSchema.ts b/packages/dds/tree/src/simple-tree/simpleSchema.ts index dc473d0071d8..09f1e0fa5f60 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchema.ts @@ -62,7 +62,7 @@ export interface SimpleObjectNodeSchema /** * Whether the object node allows unknown optional fields. * - * See {@link ObjectSchemaOptions.allowUnknownOptionalFields} for the API where this field is set as part of authoring a schema. + * @see {@link ObjectSchemaOptions.allowUnknownOptionalFields} for the API where this field is set as part of authoring a schema. * * @remarks Only populated for view schemas, undefined otherwise. Relevant for compatibility checking scenarios. */ diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index dac31e8d5cf5..8012fb65a00c 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -23,7 +23,7 @@ import { ValueSchema } from "../../../core/index.js"; import { copySimpleTreeSchemaWithoutMetadata, toSimpleTreeSchema, - // eslint-disable-next-line import/no-internal-modules + // eslint-disable-next-line import-x/no-internal-modules } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; import { HasUnknownOptionalFields } from "../../testTrees.js"; @@ -45,7 +45,7 @@ const simpleNumber: SimpleLeafNodeSchema = { describe("getSimpleSchema", () => { useSnapshotDirectory("get-simple-schema"); - describe("non-copying", () => { + it("non-copying", () => { const Schema = stringSchema; const root = SchemaFactoryAlpha.optional(Schema); @@ -54,14 +54,12 @@ describe("getSimpleSchema", () => { definitions: new Map([[Schema.identifier, Schema]]), }; - it("toSimpleTreeSchema", () => { - const actual = toSimpleTreeSchema(root, false); + const actual = toSimpleTreeSchema(root, false); - assert.deepEqual(actual, expected); + assert.deepEqual(actual, expected); - assert.equal(actual.root, root); - assert.equal(actual.definitions.get(Schema.identifier), Schema); - }); + assert.equal(actual.root, root); + assert.equal(actual.definitions.get(Schema.identifier), Schema); }); describe("Field Schema", () => { diff --git a/packages/dds/tree/src/util/index.ts b/packages/dds/tree/src/util/index.ts index df9d99c92d6e..49517f0e13bd 100644 --- a/packages/dds/tree/src/util/index.ts +++ b/packages/dds/tree/src/util/index.ts @@ -96,7 +96,6 @@ export { copyPropertyIfDefined as copyProperty, getOrAddInMap, iterableHasSome, - transformMapValues, } from "./utils.js"; export { ReferenceCountedBase, type ReferenceCounted } from "./referenceCounting.js"; diff --git a/packages/dds/tree/src/util/utils.ts b/packages/dds/tree/src/util/utils.ts index 63760c1af2b9..fa10fb3900ce 100644 --- a/packages/dds/tree/src/util/utils.ts +++ b/packages/dds/tree/src/util/utils.ts @@ -485,21 +485,6 @@ export function transformObjectMap< return output; } -/** - * Transform the values of a Map using the provided transform function. - * @param map - The map to transform. - * @param transformValue - A method for transforming values in the map. - * @returns A new map with the transformed values. - */ -export function transformMapValues( - map: ReadonlyMap, - transformValue: (value: InputValue, key: Key) => OutputValue, -): Map { - return new Map( - Array.from(map.entries()).map(([key, value]) => [key, transformValue(value, key)]), - ); -} - /** * Make an inverted copy of a map. * From 7478715e22d0108246411d120305efb5d43d0961 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 16:15:48 -0800 Subject: [PATCH 39/47] PR feedback. --- packages/dds/tree/api-report/tree.alpha.api.md | 6 +++--- packages/dds/tree/api-report/tree.beta.api.md | 4 ++-- packages/dds/tree/api-report/tree.legacy.beta.api.md | 4 ++-- packages/dds/tree/src/shared-tree/sharedTree.ts | 2 ++ 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index dfd126e26f00..5df2473c4824 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -336,7 +336,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -345,7 +345,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; @@ -538,7 +538,7 @@ export namespace JsonAsTree { } const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass<"com.fluidframework.json.object", NodeKind.Record, TreeRecordNodeUnsafe, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array]> & WithType<"com.fluidframework.json.object", NodeKind.Record, unknown>, { - readonly [x: string]: string | number | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | JsonObject | Array | null; + readonly [x: string]: string | number | JsonObject | Array | System_Unsafe.InsertableTypedNodeUnsafe, LeafSchema<"boolean", boolean>> | null; }, false, readonly [LeafSchema<"null", null>, LeafSchema<"number", number>, LeafSchema<"string", string>, LeafSchema<"boolean", boolean>, () => typeof JsonObject, () => typeof Array], undefined, unknown>; export type Primitive = TreeNodeFromImplicitAllowedTypes; // @system diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index 3b4e2fe428c3..27b5f74dbd1c 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -194,7 +194,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -203,7 +203,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/api-report/tree.legacy.beta.api.md b/packages/dds/tree/api-report/tree.legacy.beta.api.md index 34c341f3d995..8121b6b3d3ca 100644 --- a/packages/dds/tree/api-report/tree.legacy.beta.api.md +++ b/packages/dds/tree/api-report/tree.legacy.beta.api.md @@ -197,7 +197,7 @@ export namespace FluidSerializableAsTree { export type Data = JsonCompatible; const // @system _APIExtractorWorkaroundObjectBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.object", NodeKind_2.Record, TreeRecordNodeUnsafe_2 typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.object", NodeKind_2.Record, unknown>, { - readonly [x: string]: string | number | IFluidHandle | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null; + readonly [x: string]: string | number | IFluidHandle | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined, unknown>; // @sealed export class FluidSerializableObject extends _APIExtractorWorkaroundObjectBase { @@ -206,7 +206,7 @@ export namespace FluidSerializableAsTree { export type _RecursiveArrayWorkaroundJsonArray = FixRecursiveArraySchema; const // @system _APIExtractorWorkaroundArrayBase: TreeNodeSchemaClass_2<"com.fluidframework.serializable.array", NodeKind_2.Array, System_Unsafe_2.TreeArrayNodeUnsafe typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>]> & WithType_2<"com.fluidframework.serializable.array", NodeKind_2.Array, unknown>, { - [Symbol.iterator](): Iterator | FluidSerializableObject | Array | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | null, any, undefined>; + [Symbol.iterator](): Iterator | System_Unsafe_2.InsertableTypedNodeUnsafe, LeafSchema_2<"boolean", boolean>> | FluidSerializableObject | Array | null, any, undefined>; }, false, readonly [() => typeof FluidSerializableObject, () => typeof Array, LeafSchema_2<"string", string>, LeafSchema_2<"number", number>, LeafSchema_2<"boolean", boolean>, LeafSchema_2<"null", null>, LeafSchema_2<"handle", IFluidHandle>], undefined>; // (undocumented) export type Tree = TreeNodeFromImplicitAllowedTypes; diff --git a/packages/dds/tree/src/shared-tree/sharedTree.ts b/packages/dds/tree/src/shared-tree/sharedTree.ts index fa23d968c1ca..4f4d2975d7a4 100644 --- a/packages/dds/tree/src/shared-tree/sharedTree.ts +++ b/packages/dds/tree/src/shared-tree/sharedTree.ts @@ -935,6 +935,8 @@ function exportSimpleFieldSchemaStored(schema: TreeFieldStoredSchema): SimpleFie * Export a {@link SimpleNodeSchema} from a {@link TreeNodeStoredSchema}. * @privateRemarks * TODO: Persist node metadata once schema FormatV2 is supported. + * Note on SimpleNodeSchema construction: In the persisted format `persistedMetadata` is just called `metadata` whereas the `metadata` + * field on SimpleNodeSchema is not persisted. */ function exportSimpleNodeSchemaStored(schema: TreeNodeStoredSchema): SimpleNodeSchema { const arrayTypes = tryStoredSchemaAsArray(schema); From cfb1a25cae914517e8e88e6927b160a590e55214 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 16:18:24 -0800 Subject: [PATCH 40/47] Updated a comment. --- packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts index edb625a7a0c1..0308a4b6121b 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts @@ -32,7 +32,7 @@ import type { ValueSchema } from "../../core/index.js"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; /** - * Converts a view schema to a serializable format for compatibility testing. + * Converts a simple schema (view or stored) to a serializable format for compatibility testing. * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. * It is not possible to reconstruct a full view schema (including metadata and persistedMetadata) from the serialized format. * @param treeSchema - The tree schema to convert. From b2e21d22590af4d8402826aad87c93e9e23e4079 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 16:27:46 -0800 Subject: [PATCH 41/47] Better doc comments. --- .../dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts index 0308a4b6121b..9dc1dbec112c 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts @@ -32,7 +32,7 @@ import type { ValueSchema } from "../../core/index.js"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; /** - * Converts a simple schema (view or stored) to a serializable format for compatibility testing. + * Converts a simple schema (view or stored) to a serializable format. * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. * It is not possible to reconstruct a full view schema (including metadata and persistedMetadata) from the serialized format. * @param treeSchema - The tree schema to convert. @@ -58,9 +58,9 @@ export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompa } /** - * Deserializes a JSON-compatible schema into a view schema for compatibility testing. + * Deserializes a JSON-compatible schema into a simple schema. * @param serializedSchema - The serialized schema to deserialize. - * @returns A deserialized view schema. + * @returns A deserialized simple schema. * @throws Will throw a usage error if the serialized schema is not in the expected format. * * @alpha From b48bd71283c86bfc316290a845ff8553c9d87e4b Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 16:55:57 -0800 Subject: [PATCH 42/47] Refactored serializer -> codec and updated a few doc comments. --- .../dds/tree/api-report/tree.alpha.api.md | 12 +- packages/dds/tree/src/index.ts | 4 +- .../dds/tree/src/simple-tree/api/index.ts | 6 +- .../src/simple-tree/api/simpleSchemaCodec.ts | 370 +++++++++++++++++ .../simple-tree/api/simpleSchemaSerializer.ts | 381 ------------------ packages/dds/tree/src/simple-tree/index.ts | 4 +- .../simple-tree/api/getSimpleSchema.spec.ts | 52 +-- 7 files changed, 409 insertions(+), 420 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts delete mode 100644 packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 5df2473c4824..2f5412a5caa2 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -198,13 +198,13 @@ export function createSimpleTreeIndex(view: TreeView, indexer: Map, getValue: (nodes: TreeIndexNodes>) => TValue, isKeyValid: (key: TreeIndexKey) => key is TKey, indexableSchema: readonly TSchema[]): SimpleTreeIndex; +// @alpha +export function decodeSimpleSchema(encodedSchema: JsonCompatible): SimpleTreeSchema; + // @public @sealed @system interface DefaultProvider extends ErasedType<"@fluidframework/tree.FieldProvider"> { } -// @alpha -export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema; - // @alpha export interface DirtyTreeMap { // (undocumented) @@ -216,6 +216,9 @@ export interface DirtyTreeMap { // @alpha export type DirtyTreeStatus = "new" | "changed" | "moved"; +// @alpha +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; + // @beta export function enumFromStrings(factory: SchemaFactory, members: Members): ((value: TValue) => TValue extends unknown ? TreeNode & { readonly value: TValue; @@ -1000,9 +1003,6 @@ export class SchemaUpgrade { // @public @system type ScopedSchemaName = TScope extends undefined ? `${TName}` : `${TScope}.${TName}`; -// @alpha -export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; - // @alpha @input export interface SharedTreeFormatOptions { treeEncodeType: TreeCompressionStrategy; diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index ce1441159646..fb204e1fb2c5 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -283,8 +283,8 @@ export { type SchemaFactory_base, type NumberKeys, type SimpleAllowedTypeAttributes, - serializeSimpleSchema, - deserializeSimpleSchema, + encodeSimpleSchema, + decodeSimpleSchema, } from "./simple-tree/index.js"; export { SharedTree, diff --git a/packages/dds/tree/src/simple-tree/api/index.ts b/packages/dds/tree/src/simple-tree/api/index.ts index 053484321929..a7a05f753d82 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -168,6 +168,6 @@ export { incrementalSummaryHint, } from "./incrementalAllowedTypes.js"; export { - serializeSimpleSchema, - deserializeSimpleSchema, -} from "./simpleSchemaSerializer.js"; + encodeSimpleSchema, + decodeSimpleSchema, +} from "./simpleSchemaCodec.js"; diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts new file mode 100644 index 000000000000..e024654bb1c0 --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts @@ -0,0 +1,370 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { + isJsonObject, + objectToMap, + type JsonCompatible, + type JsonCompatibleObject, +} from "../../util/index.js"; +import { + fail, + unreachableCase, + transformMapValues, +} from "@fluidframework/core-utils/internal"; +import type { + SimpleAllowedTypeAttributes, + SimpleArrayNodeSchema, + SimpleFieldSchema, + SimpleLeafNodeSchema, + SimpleMapNodeSchema, + SimpleNodeSchema, + SimpleObjectFieldSchema, + SimpleObjectNodeSchema, + SimpleRecordNodeSchema, + SimpleTreeSchema, +} from "../simpleSchema.js"; +import { NodeKind } from "../core/index.js"; +import type { FieldKind } from "../fieldSchema.js"; +import type { ValueSchema } from "../../core/index.js"; +import { UsageError } from "@fluidframework/telemetry-utils/internal"; + +/** + * Encodes a simple schema (view or stored) into a serializable format. + * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of schemas. + * It is not possible to reconstruct a full schema (including metadata and persistedMetadata) from the encoded format. + * @param treeSchema - The tree schema to convert. + * @returns A serializable representation of the schema. + * + * @alpha + */ +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible { + // Convert types to serializable forms + const encodedDefinitions: Record = {}; + + for (const [identifier, schema] of simpleSchema.definitions) { + const encodedDefinition = encodeNodeSchema(schema); + encodedDefinitions[identifier] = encodedDefinition; + } + + const encodedSchema = { + root: encodeField(simpleSchema.root), + definitions: encodedDefinitions, + } as unknown as JsonCompatible; + + return encodedSchema; +} + +/** + * Decodes a JSON-compatible schema into a simple schema. + * @param encodedSchema - The encoded schema to decode. + * @returns A decoded simple schema. + * @throws Will throw a usage error if the encoded schema is not in the expected format. + * + * @alpha + */ +export function decodeSimpleSchema(encodedSchema: JsonCompatible): SimpleTreeSchema { + if (!isJsonObject(encodedSchema)) { + throw new UsageError("Expected object for encodedSchema"); + } + + const encodedSchemaAsObject = encodedSchema as JsonCompatibleObject; + + if (encodedSchemaAsObject.root === undefined || !isJsonObject(encodedSchemaAsObject.root)) { + throw new UsageError("Expected object for root field schema"); + } + const encodedRoot = encodedSchemaAsObject.root as JsonCompatibleObject; + + if ( + encodedSchemaAsObject.definitions === undefined || + !isJsonObject(encodedSchemaAsObject.definitions) + ) { + throw new UsageError("Expected object for definitions field"); + } + const encodedDefinitions = encodedSchemaAsObject.definitions as JsonCompatibleObject; + + return { + root: decodeSimpleFieldSchema(encodedRoot), + definitions: new Map( + transformMapValues(objectToMap(encodedDefinitions), (value, key) => { + if (value === undefined || !isJsonObject(value)) { + throw new UsageError(`Expected node schema for definition ${key}`); + } + return decodeNodeSchema(value as JsonCompatibleObject); + }), + ), + }; +} + +/** + * Encodes a node schema to a serializable object. + * @param schema - The node schema to convert. + * @returns A serializable representation of the node schema. + */ +function encodeNodeSchema(schema: SimpleNodeSchema): JsonCompatibleObject { + const kind = schema.kind; + switch (kind) { + case NodeKind.Leaf: + return encodeLeafNode(schema); + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return encodeContainerNode(schema); + case NodeKind.Object: + return encodeObjectNode(schema); + default: { + unreachableCase(kind); + } + } +} + +/** + * Encodes a leaf node schema to a serializable object. + * @param schema - The leaf node schema to convert. + * @returns A serializable representation of the leaf node schema. + */ +function encodeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { + return { + kind: schema.kind, + leafKind: schema.leafKind, + }; +} + +/** + * Encodes a container node schema (a simple schema that is a Map, Array, or Record) to a serializable object. + * @param schema - The container node schema to convert. + * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different + * container kinds. + */ +function encodeContainerNode( + schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, +): JsonCompatibleObject { + return { + kind: schema.kind, + simpleAllowedTypes: encodeSimpleAllowedTypes(schema.simpleAllowedTypes), + }; +} + +/** + * Encodes a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps. + * @param simpleAllowedTypes - The simple allowed types map to convert. + * @returns A serializable representation of the simple allowed types. + */ +function encodeSimpleAllowedTypes( + simpleAllowedTypes: ReadonlyMap, +): JsonCompatibleObject { + const serializableAllowedTypes: JsonCompatibleObject = {}; + for (const [identifier, attributes] of simpleAllowedTypes) { + serializableAllowedTypes[identifier] = { + // The type of SimpleAllowedTypeAttributes is not assignable to JsonCompatibleObject, so we splat its fields here. + ...attributes, + }; + } + return serializableAllowedTypes; +} + +/** + * Encodes an object node schema to a serializable object. + * @param schema - The object node schema to convert. + * @returns A serializable representation of the object node schema. + */ +function encodeObjectNode(schema: SimpleObjectNodeSchema): JsonCompatibleObject { + const serializableFields: Record = {}; + for (const [fieldKey, fieldSchema] of schema.fields) { + serializableFields[fieldKey] = encodeObjectField(fieldSchema); + } + + return { + kind: schema.kind, + fields: serializableFields, + allowUnknownOptionalFields: schema.allowUnknownOptionalFields, + }; +} + +/** + * Encodes an object field schema to a serializable object. + * @param fieldSchema - The object field schema to convert. + * @returns A serializable representation of the object field schema. + */ +function encodeObjectField(fieldSchema: SimpleObjectFieldSchema): JsonCompatibleObject { + const serializableField = encodeField(fieldSchema); + serializableField.storedKey = fieldSchema.storedKey; + return serializableField; +} + +/** + * Encodes a field schema to a serializable object. + * @param fieldSchema - The field schema to convert. + * @returns A serializable representation of the field schema. + */ +function encodeField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { + return { + kind: fieldSchema.kind, + simpleAllowedTypes: encodeSimpleAllowedTypes(fieldSchema.simpleAllowedTypes), + }; +} + +/** + * Decodes a node schema from a JSON-compatible object. + * @param encodedNodeSchema - The encoded node schema to decode. + * @returns The decoded node schema. + */ +function decodeNodeSchema( + encodedNodeSchema: JsonCompatibleObject, +): + | SimpleLeafNodeSchema + | SimpleArrayNodeSchema + | SimpleMapNodeSchema + | SimpleRecordNodeSchema + | SimpleObjectNodeSchema { + const kind = encodedNodeSchema.kind as NodeKind; + + switch (kind) { + case NodeKind.Array: + case NodeKind.Map: + case NodeKind.Record: + return decodeContainerNode(encodedNodeSchema); + case NodeKind.Leaf: + return decodeLeafNode(encodedNodeSchema); + case NodeKind.Object: + return decodeObjectNode(encodedNodeSchema); + default: + unreachableCase(kind); + } +} + +/** + * Decodes a container node schema (array, map, record) from a JSON-compatible object. + * @param encodedContainerSchema - The encoded schema to decode. + * @returns The decoded container node schema. + */ +function decodeContainerNode( + encodedContainerSchema: JsonCompatibleObject, +): SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema { + return { + kind: encodedContainerSchema.kind as NodeKind.Array | NodeKind.Map | NodeKind.Record, + simpleAllowedTypes: decodeSimpleAllowedTypes( + encodedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, + ), + // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values. + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Decodes a leaf node schema from a JSON-compatible object. + * @param encodedLeafSchema - The encoded leaf node schema. + * @returns The decoded leaf node schema. + */ +function decodeLeafNode(encodedLeafSchema: JsonCompatibleObject): SimpleLeafNodeSchema { + return { + kind: NodeKind.Leaf, + leafKind: (encodedLeafSchema.leafKind ?? fail("Missing leafKind")) as ValueSchema, + // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values. + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Decodes a object node schema from a JSON-compatible object. + * @param encodedObjectSchema - The encoded object node schema. + * @returns The decoded object node schema. + * @throws Will throw a usage error if the encoded object schema is not in the expected format. + */ +function decodeObjectNode(encodedObjectSchema: JsonCompatibleObject): SimpleObjectNodeSchema { + if (encodedObjectSchema.fields === undefined) { + throw new UsageError("Expected fields for object node schema"); + } + + return { + kind: NodeKind.Object, + fields: decodeObjectFields(encodedObjectSchema.fields ?? fail("Missing fields")), + // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived + // from a stored schema. + allowUnknownOptionalFields: encodedObjectSchema.allowUnknownOptionalFields as + | boolean + | undefined, + // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding. + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Decodes a map of object fields from a JSON-compatible object. + * @param encodedFields - The encoded fields. + * @returns A map of the decoded object fields. + * @throws Will throw a usage error if the encoded fields are not in the expected format. + */ +function decodeObjectFields( + encodedFields: JsonCompatible, +): ReadonlyMap { + if (!isJsonObject(encodedFields)) { + throw new UsageError("Expected object for encodedFields"); + } + + const fields = new Map(); + for (const [fieldKey, fieldSchema] of Object.entries( + encodedFields as JsonCompatibleObject, + )) { + fields.set(fieldKey, decodeObjectField(fieldSchema as JsonCompatibleObject)); + } + return fields; +} + +/** + * Decodes a {@link SimpleObjectFieldSchema} from a JSON-compatible object. + * @param encodedField - The encoded field schema. + * @returns The decoded simple object field schema. + */ +function decodeObjectField(encodedField: JsonCompatibleObject): SimpleObjectFieldSchema { + const baseField = decodeSimpleFieldSchema(encodedField); + return { + ...baseField, + storedKey: encodedField.storedKey as string, + }; +} + +/** + * Decodes a {@link SimpleFieldSchema} from a JSON-compatible object. + * @param encodedField - The encoded field schema. + * @returns The decoded simple field schema. + */ +function decodeSimpleFieldSchema(encodedField: JsonCompatibleObject): SimpleFieldSchema { + return { + kind: encodedField.kind as FieldKind, + simpleAllowedTypes: decodeSimpleAllowedTypes( + encodedField.simpleAllowedTypes as JsonCompatibleObject, + ), + // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding. + persistedMetadata: undefined, + metadata: {}, + }; +} + +/** + * Decodes a simple allowed types map from a JSON-compatible object. + * @param encodedAllowedTypes - The encoded simple allowed types. + * @returns A map of the decoded simple allowed types. + * @throws Will throw a usage error if the encoded allowed types are not in the expected format. + */ +function decodeSimpleAllowedTypes( + encodedAllowedTypes: JsonCompatible, +): ReadonlyMap { + if (!isJsonObject(encodedAllowedTypes)) { + throw new UsageError("Expected object for encodedAllowedTypes"); + } + const untypedMap = objectToMap(encodedAllowedTypes as JsonCompatibleObject); + + const simpleAllowedTypes = transformMapValues(untypedMap, (value) => { + return { + isStaged: (value as JsonCompatibleObject).isStaged as boolean | undefined, + } satisfies SimpleAllowedTypeAttributes; + }); + + return simpleAllowedTypes; +} diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts deleted file mode 100644 index 9dc1dbec112c..000000000000 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaSerializer.ts +++ /dev/null @@ -1,381 +0,0 @@ -/*! - * Copyright (c) Microsoft Corporation and contributors. All rights reserved. - * Licensed under the MIT License. - */ - -import { - isJsonObject, - objectToMap, - type JsonCompatible, - type JsonCompatibleObject, -} from "../../util/index.js"; -import { - fail, - unreachableCase, - transformMapValues, -} from "@fluidframework/core-utils/internal"; -import type { - SimpleAllowedTypeAttributes, - SimpleArrayNodeSchema, - SimpleFieldSchema, - SimpleLeafNodeSchema, - SimpleMapNodeSchema, - SimpleNodeSchema, - SimpleObjectFieldSchema, - SimpleObjectNodeSchema, - SimpleRecordNodeSchema, - SimpleTreeSchema, -} from "../simpleSchema.js"; -import { NodeKind } from "../core/index.js"; -import type { FieldKind } from "../fieldSchema.js"; -import type { ValueSchema } from "../../core/index.js"; -import { UsageError } from "@fluidframework/telemetry-utils/internal"; - -/** - * Converts a simple schema (view or stored) to a serializable format. - * @remarks The JSON-compatible schema returned from this method is only intended for use in snapshots/comparisons of view schemas. - * It is not possible to reconstruct a full view schema (including metadata and persistedMetadata) from the serialized format. - * @param treeSchema - The tree schema to convert. - * @returns A serializable representation of the view schema. - * - * @alpha - */ -export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible { - // Convert types to serializable forms - const serializableDefinitions: Record = {}; - - for (const [identifier, schema] of simpleSchema.definitions) { - const serializableDefinition = serializeNodeSchema(schema); - serializableDefinitions[identifier] = serializableDefinition; - } - - const serializedSchema = { - root: serializeField(simpleSchema.root), - definitions: serializableDefinitions, - } as unknown as JsonCompatible; - - return serializedSchema; -} - -/** - * Deserializes a JSON-compatible schema into a simple schema. - * @param serializedSchema - The serialized schema to deserialize. - * @returns A deserialized simple schema. - * @throws Will throw a usage error if the serialized schema is not in the expected format. - * - * @alpha - */ -export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema { - if (!isJsonObject(serializedSchema)) { - throw new UsageError("Expected object for serializedSchema"); - } - - const serializedSchemaAsObject = serializedSchema as JsonCompatibleObject; - - if ( - serializedSchemaAsObject.root === undefined || - !isJsonObject(serializedSchemaAsObject.root) - ) { - throw new UsageError("Expected object for root field schema"); - } - const serializedRoot = serializedSchemaAsObject.root as JsonCompatibleObject; - - if ( - serializedSchemaAsObject.definitions === undefined || - !isJsonObject(serializedSchemaAsObject.definitions) - ) { - throw new UsageError("Expected object for definitions field"); - } - const serializedDefinitions = serializedSchemaAsObject.definitions as JsonCompatibleObject; - - return { - root: deserializeSimpleFieldSchema(serializedRoot), - definitions: new Map( - transformMapValues(objectToMap(serializedDefinitions), (value, key) => { - if (value === undefined || !isJsonObject(value)) { - throw new UsageError(`Expected node schema for definition ${key}`); - } - return deserializeNodeSchema(value as JsonCompatibleObject); - }), - ), - }; -} - -/** - * Converts a node schema to a serializable object. - * @param schema - The node schema to convert. - * @returns A serializable representation of the node schema. - */ -function serializeNodeSchema(schema: SimpleNodeSchema): JsonCompatibleObject { - const kind = schema.kind; - switch (kind) { - case NodeKind.Leaf: - return serializeLeafNode(schema); - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return serializeContainerNode(schema); - case NodeKind.Object: - return serializeObjectNode(schema); - default: { - unreachableCase(kind); - } - } -} - -/** - * Converts a leaf node schema to a serializable object. - * @param schema - The leaf node schema to convert. - * @returns A serializable representation of the leaf node schema. - */ -function serializeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { - return { - kind: schema.kind, - leafKind: schema.leafKind, - }; -} - -/** - * Converts a container node schema (a simple schema that is a Map, Array, or Record) to a serializable object. - * @param schema - The container node schema to convert. - * @returns A serializable representation of the container node schema. Includes the `kind` for disambiguation between different - * container kinds. - */ -function serializeContainerNode( - schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, -): JsonCompatibleObject { - return { - kind: schema.kind, - simpleAllowedTypes: serializeSimpleAllowedTypes(schema.simpleAllowedTypes), - }; -} - -/** - * Converts a simple allowed types map to a serializable object. Needed because JSON serialization does not support Maps. - * @param simpleAllowedTypes - The simple allowed types map to convert. - * @returns A serializable representation of the simple allowed types. - */ -function serializeSimpleAllowedTypes( - simpleAllowedTypes: ReadonlyMap, -): JsonCompatibleObject { - const serializableAllowedTypes: JsonCompatibleObject = {}; - for (const [identifier, attributes] of simpleAllowedTypes) { - serializableAllowedTypes[identifier] = { - // The type of SimpleAllowedTypeAttributes is not assignable to JsonCompatibleObject, so we splat its fields here. - ...attributes, - }; - } - return serializableAllowedTypes; -} - -/** - * Converts an object node schema to a serializable object. - * @param schema - The object node schema to convert. - * @returns A serializable representation of the object node schema. - */ -function serializeObjectNode(schema: SimpleObjectNodeSchema): JsonCompatibleObject { - const serializableFields: Record = {}; - for (const [fieldKey, fieldSchema] of schema.fields) { - serializableFields[fieldKey] = serializeObjectField(fieldSchema); - } - - return { - kind: schema.kind, - fields: serializableFields, - allowUnknownOptionalFields: schema.allowUnknownOptionalFields, - }; -} - -/** - * Converts an object field schema to a serializable object. - * @param fieldSchema - The object field schema to convert. - * @returns A serializable representation of the object field schema. - */ -function serializeObjectField(fieldSchema: SimpleObjectFieldSchema): JsonCompatibleObject { - const serializableField = serializeField(fieldSchema); - serializableField.storedKey = fieldSchema.storedKey; - return serializableField; -} - -/** - * Converts a field schema to a serializable object. - * @param fieldSchema - The field schema to convert. - * @returns A serializable representation of the field schema. - */ -function serializeField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { - return { - kind: fieldSchema.kind, - simpleAllowedTypes: serializeSimpleAllowedTypes(fieldSchema.simpleAllowedTypes), - }; -} - -/** - * Deserializes a node schema from a JSON-compatible object. - * @param serializedNodeSchema - The serialized node schema to deserialize. - * @returns The deserialized node schema. - */ -function deserializeNodeSchema( - serializedNodeSchema: JsonCompatibleObject, -): - | SimpleLeafNodeSchema - | SimpleArrayNodeSchema - | SimpleMapNodeSchema - | SimpleRecordNodeSchema - | SimpleObjectNodeSchema { - const kind = serializedNodeSchema.kind as NodeKind; - - switch (kind) { - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return deserializeContainerNode(serializedNodeSchema); - case NodeKind.Leaf: - return deserializeLeafNode(serializedNodeSchema); - case NodeKind.Object: - return deserializeObjectNode(serializedNodeSchema); - default: - unreachableCase(kind); - } -} - -/** - * Deserializes a container node schema (array, map, record) from a JSON-compatible object. - * @param serializedContainerSchema - The serialized schema to deserialize. - * @returns The deserialized container node schema. - */ -function deserializeContainerNode( - serializedContainerSchema: JsonCompatibleObject, -): SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema { - return { - kind: serializedContainerSchema.kind as NodeKind.Array | NodeKind.Map | NodeKind.Record, - simpleAllowedTypes: deserializeSimpleAllowedTypes( - serializedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, - ), - // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. - persistedMetadata: undefined, - metadata: {}, - }; -} - -/** - * Deserializes a leaf node schema from a JSON-compatible object. - * @param serializedLeafSchema - The serialized leaf node schema. - * @returns The deserialized leaf node schema. - */ -function deserializeLeafNode( - serializedLeafSchema: JsonCompatibleObject, -): SimpleLeafNodeSchema { - return { - kind: NodeKind.Leaf, - leafKind: (serializedLeafSchema.leafKind ?? fail("Missing leafKind")) as ValueSchema, - // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. - persistedMetadata: undefined, - metadata: {}, - }; -} - -/** - * Deserializes a object node schema from a JSON-compatible object. - * @param serializedObjectSchema - The serialized object node schema. - * @returns The deserialized object node schema. - * @throws Will throw a usage error if the serialized object schema is not in the expected format. - */ -function deserializeObjectNode( - serializedObjectSchema: JsonCompatibleObject, -): SimpleObjectNodeSchema { - if (serializedObjectSchema.fields === undefined) { - throw new UsageError("Expected fields for object node schema"); - } - - return { - kind: NodeKind.Object, - fields: deserializeObjectFields(serializedObjectSchema.fields ?? fail("Missing fields")), - // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived - // from a stored schema. - allowUnknownOptionalFields: serializedObjectSchema.allowUnknownOptionalFields as - | boolean - | undefined, - // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. - persistedMetadata: undefined, - metadata: {}, - }; -} - -/** - * Deserializes a map of object fields from a JSON-compatible object. - * @param serializedFields - The serialized fields. - * @returns A map of the deserialized object fields. - * @throws Will throw a usage error if the serialized fields are not in the expected format. - */ -function deserializeObjectFields( - serializedFields: JsonCompatible, -): ReadonlyMap { - if (!isJsonObject(serializedFields)) { - throw new UsageError("Expected object for serializedFields"); - } - - const fields = new Map(); - for (const [fieldKey, fieldSchema] of Object.entries( - serializedFields as JsonCompatibleObject, - )) { - fields.set(fieldKey, deserializeObjectField(fieldSchema as JsonCompatibleObject)); - } - return fields; -} - -/** - * Deserializes a {@link SimpleObjectFieldSchema} from a JSON-compatible object. - * @param serializedField - The serialized field schema. - * @returns The deserialized simple object field schema. - */ -function deserializeObjectField( - serializedField: JsonCompatibleObject, -): SimpleObjectFieldSchema { - const baseField = deserializeSimpleFieldSchema(serializedField); - return { - ...baseField, - storedKey: serializedField.storedKey as string, - }; -} - -/** - * Deserializes a {@link SimpleFieldSchema} from a JSON-compatible object. - * @param serializedField - The serialized field schema. - * @returns The deserialized simple field schema. - */ -function deserializeSimpleFieldSchema( - serializedField: JsonCompatibleObject, -): SimpleFieldSchema { - return { - kind: serializedField.kind as FieldKind, - simpleAllowedTypes: deserializeSimpleAllowedTypes( - serializedField.simpleAllowedTypes as JsonCompatibleObject, - ), - // We cannot serialize persistedMetadata or metadata, so we explicitly set them to empty values. - persistedMetadata: undefined, - metadata: {}, - }; -} - -/** - * Deserializes a simple allowed types map from a JSON-compatible object. - * @param serializedAllowedTypes - The serialized simple allowed types. - * @returns A map of the deserialized simple allowed types. - * @throws Will throw a usage error if the serialized allowed types are not in the expected format. - */ -function deserializeSimpleAllowedTypes( - serializedAllowedTypes: JsonCompatible, -): ReadonlyMap { - if (!isJsonObject(serializedAllowedTypes)) { - throw new UsageError("Expected object for serializedAllowedTypes"); - } - const untypedMap = objectToMap(serializedAllowedTypes as JsonCompatibleObject); - - const simpleAllowedTypes = transformMapValues(untypedMap, (value) => { - return { - isStaged: (value as JsonCompatibleObject).isStaged as boolean | undefined, - } satisfies SimpleAllowedTypeAttributes; - }); - - return simpleAllowedTypes; -} diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index e16e24f1f5d6..32d99f8f8b14 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -281,6 +281,6 @@ export { } from "./leafNodeSchema.js"; export type { LeafSchema } from "./leafNodeSchema.js"; export { - serializeSimpleSchema, - deserializeSimpleSchema, + encodeSimpleSchema, + decodeSimpleSchema, } from "./api/index.js"; diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 8012fb65a00c..00c514eee90b 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -5,12 +5,12 @@ import { strict as assert } from "node:assert"; import { - deserializeSimpleSchema, + decodeSimpleSchema, FieldKind, NodeKind, SchemaFactory, SchemaFactoryAlpha, - serializeSimpleSchema, + encodeSimpleSchema, stringSchema, type SimpleLeafNodeSchema, type SimpleNodeSchema, @@ -87,14 +87,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Field Schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -121,14 +121,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Leaf node", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Leaf node", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -159,14 +159,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Union root", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -205,14 +205,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Array schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Array Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -251,14 +251,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Map schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Map schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -297,14 +297,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Record schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Record schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -371,14 +371,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -431,14 +431,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema including an identifier field", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema including an identifier field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -494,14 +494,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Object schema including a union field", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Object schema including a union field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -555,14 +555,14 @@ describe("getSimpleSchema", () => { }); it("serialized - Recursive object schema", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(Schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(Schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - Recursive object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -591,14 +591,14 @@ describe("getSimpleSchema", () => { }); it("serialized - simpleAllowedTypes", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - simpleAllowedTypes", () => { const simpleTree = toSimpleTreeSchema(schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); @@ -635,14 +635,14 @@ describe("getSimpleSchema", () => { }); it("serialized - allowUnknownOptionalFields", () => { - const actual = serializeSimpleSchema(toSimpleTreeSchema(schema, true)); + const actual = encodeSimpleSchema(toSimpleTreeSchema(schema, true)); takeJsonSnapshot(actual); }); it("Roundtrip serialization - allowUnknownOptionalFields", () => { const simpleTree = toSimpleTreeSchema(schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = deserializeSimpleSchema(serializeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); assert.deepEqual(actual, expected); }); }); From 6136e7742f5aa8625d8d5326e7b27f474e9e68e4 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Wed, 5 Nov 2025 16:58:22 -0800 Subject: [PATCH 43/47] Removed fail calls that should be exceptions. --- .../dds/tree/src/simple-tree/api/simpleSchemaCodec.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts index e024654bb1c0..d59a6db47153 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts @@ -10,7 +10,6 @@ import { type JsonCompatibleObject, } from "../../util/index.js"; import { - fail, unreachableCase, transformMapValues, } from "@fluidframework/core-utils/internal"; @@ -258,11 +257,16 @@ function decodeContainerNode( * Decodes a leaf node schema from a JSON-compatible object. * @param encodedLeafSchema - The encoded leaf node schema. * @returns The decoded leaf node schema. + * @throws Will throw a usage error if the encoded leaf schema is not in the expected format. */ function decodeLeafNode(encodedLeafSchema: JsonCompatibleObject): SimpleLeafNodeSchema { + if (encodedLeafSchema.leafKind === undefined) { + throw new UsageError("Expected leafKind for leaf node schema"); + } + return { kind: NodeKind.Leaf, - leafKind: (encodedLeafSchema.leafKind ?? fail("Missing leafKind")) as ValueSchema, + leafKind: encodedLeafSchema.leafKind as ValueSchema, // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, @@ -282,7 +286,7 @@ function decodeObjectNode(encodedObjectSchema: JsonCompatibleObject): SimpleObje return { kind: NodeKind.Object, - fields: decodeObjectFields(encodedObjectSchema.fields ?? fail("Missing fields")), + fields: decodeObjectFields(encodedObjectSchema.fields), // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived // from a stored schema. allowUnknownOptionalFields: encodedObjectSchema.allowUnknownOptionalFields as From c0ef68493c7e98a3642522b590245bdb06f6245c Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 6 Nov 2025 13:08:57 -0800 Subject: [PATCH 44/47] Refactored codec to use typebox types for verification. --- .../dds/tree/api-report/tree.alpha.api.md | 4 +- .../src/simple-tree/api/simpleSchemaCodec.ts | 163 ++++++++++-------- .../src/simple-tree/simpleSchemaFormatV1.ts | 122 +++++++++++++ .../simple-tree/api/getSimpleSchema.spec.ts | 29 ++-- .../serialized - Array schema.json | 17 +- .../serialized - Field Schema.json | 7 +- .../serialized - Leaf node.json | 7 +- .../serialized - Map schema.json | 17 +- ...Object schema including a union field.json | 45 +++-- ... schema including an identifier field.json | 35 ++-- .../serialized - Object schema.json | 55 +++--- .../serialized - Record schema.json | 17 +- .../serialized - Recursive object schema.json | 39 +++-- .../serialized - Union root.json | 13 +- ...rialized - allowUnknownOptionalFields.json | 9 +- .../serialized - simpleAllowedTypes.json | 7 +- 16 files changed, 391 insertions(+), 195 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 2f5412a5caa2..28521ea4b042 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -199,7 +199,7 @@ export function createSimpleTreeIndex(view: TreeView, indexer: Map, getValue: (nodes: TreeIndexNodes>) => TValue, isKeyValid: (key: TreeIndexKey) => key is TKey, indexableSchema: readonly TSchema[]): SimpleTreeIndex; // @alpha -export function decodeSimpleSchema(encodedSchema: JsonCompatible): SimpleTreeSchema; +export function decodeSimpleSchema(encodedSchema: JsonCompatibleObject, validator?: FormatValidator): SimpleTreeSchema; // @public @sealed @system interface DefaultProvider extends ErasedType<"@fluidframework/tree.FieldProvider"> { @@ -217,7 +217,7 @@ export interface DirtyTreeMap { export type DirtyTreeStatus = "new" | "changed" | "moved"; // @alpha -export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleObject; // @beta export function enumFromStrings(factory: SchemaFactory, members: Members): ((value: TValue) => TValue extends unknown ? TreeNode & { diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts index d59a6db47153..b65945332372 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts @@ -9,10 +9,7 @@ import { type JsonCompatible, type JsonCompatibleObject, } from "../../util/index.js"; -import { - unreachableCase, - transformMapValues, -} from "@fluidframework/core-utils/internal"; +import { unreachableCase, transformMapValues } from "@fluidframework/core-utils/internal"; import type { SimpleAllowedTypeAttributes, SimpleArrayNodeSchema, @@ -29,6 +26,13 @@ import { NodeKind } from "../core/index.js"; import type { FieldKind } from "../fieldSchema.js"; import type { ValueSchema } from "../../core/index.js"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; +import * as Format from "../simpleSchemaFormatV1.js"; +import { + DiscriminatedUnionDispatcher, + extractJsonValidator, + FormatValidatorNoOp, + type FormatValidator, +} from "../../codec/index.js"; /** * Encodes a simple schema (view or stored) into a serializable format. @@ -39,19 +43,20 @@ import { UsageError } from "@fluidframework/telemetry-utils/internal"; * * @alpha */ -export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible { +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleObject { // Convert types to serializable forms - const encodedDefinitions: Record = {}; + const encodedDefinitions: Format.SimpleSchemaDefinitionsFormat = {}; for (const [identifier, schema] of simpleSchema.definitions) { const encodedDefinition = encodeNodeSchema(schema); encodedDefinitions[identifier] = encodedDefinition; } - const encodedSchema = { + const encodedSchema: Format.SimpleTreeSchemaFormat = { + version: Format.SimpleSchemaFormatVersion.v1, root: encodeField(simpleSchema.root), definitions: encodedDefinitions, - } as unknown as JsonCompatible; + }; return encodedSchema; } @@ -59,39 +64,34 @@ export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatib /** * Decodes a JSON-compatible schema into a simple schema. * @param encodedSchema - The encoded schema to decode. + * @param validator - The format validator to use to validate the encoded schema. * @returns A decoded simple schema. * @throws Will throw a usage error if the encoded schema is not in the expected format. * * @alpha */ -export function decodeSimpleSchema(encodedSchema: JsonCompatible): SimpleTreeSchema { - if (!isJsonObject(encodedSchema)) { - throw new UsageError("Expected object for encodedSchema"); +export function decodeSimpleSchema( + encodedSchema: JsonCompatibleObject, + validator?: FormatValidator, +): SimpleTreeSchema { + const effectiveValidator = validator ?? FormatValidatorNoOp; + const compiledValidator = extractJsonValidator(effectiveValidator).compile( + Format.SimpleTreeSchemaFormat, + ); + if (!compiledValidator.check(encodedSchema)) { + throw new UsageError( + "The provided simple schema is not valid according to the schema format.", + ); } - const encodedSchemaAsObject = encodedSchema as JsonCompatibleObject; - - if (encodedSchemaAsObject.root === undefined || !isJsonObject(encodedSchemaAsObject.root)) { - throw new UsageError("Expected object for root field schema"); - } - const encodedRoot = encodedSchemaAsObject.root as JsonCompatibleObject; - - if ( - encodedSchemaAsObject.definitions === undefined || - !isJsonObject(encodedSchemaAsObject.definitions) - ) { - throw new UsageError("Expected object for definitions field"); - } - const encodedDefinitions = encodedSchemaAsObject.definitions as JsonCompatibleObject; - return { - root: decodeSimpleFieldSchema(encodedRoot), + root: decodeSimpleFieldSchema(encodedSchema.root), definitions: new Map( - transformMapValues(objectToMap(encodedDefinitions), (value, key) => { + transformMapValues(objectToMap(encodedSchema.definitions), (value, key) => { if (value === undefined || !isJsonObject(value)) { throw new UsageError(`Expected node schema for definition ${key}`); } - return decodeNodeSchema(value as JsonCompatibleObject); + return decodeNodeSchema(value); }), ), }; @@ -102,17 +102,19 @@ export function decodeSimpleSchema(encodedSchema: JsonCompatible): SimpleTreeSch * @param schema - The node schema to convert. * @returns A serializable representation of the node schema. */ -function encodeNodeSchema(schema: SimpleNodeSchema): JsonCompatibleObject { +function encodeNodeSchema(schema: SimpleNodeSchema): Format.SimpleNodeSchemaUnionFormat { const kind = schema.kind; switch (kind) { case NodeKind.Leaf: - return encodeLeafNode(schema); + return { leaf: encodeLeafNode(schema) }; case NodeKind.Array: + return { array: encodeContainerNode(schema) }; case NodeKind.Map: + return { map: encodeContainerNode(schema) }; case NodeKind.Record: - return encodeContainerNode(schema); + return { record: encodeContainerNode(schema) }; case NodeKind.Object: - return encodeObjectNode(schema); + return { object: encodeObjectNode(schema) }; default: { unreachableCase(kind); } @@ -124,7 +126,7 @@ function encodeNodeSchema(schema: SimpleNodeSchema): JsonCompatibleObject { * @param schema - The leaf node schema to convert. * @returns A serializable representation of the leaf node schema. */ -function encodeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { +function encodeLeafNode(schema: SimpleLeafNodeSchema): Format.SimpleLeafNodeSchemaFormat { return { kind: schema.kind, leafKind: schema.leafKind, @@ -139,7 +141,10 @@ function encodeLeafNode(schema: SimpleLeafNodeSchema): JsonCompatibleObject { */ function encodeContainerNode( schema: SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema, -): JsonCompatibleObject { +): + | Format.SimpleArrayNodeSchemaFormat + | Format.SimpleMapNodeSchemaFormat + | Format.SimpleRecordNodeSchemaFormat { return { kind: schema.kind, simpleAllowedTypes: encodeSimpleAllowedTypes(schema.simpleAllowedTypes), @@ -153,15 +158,14 @@ function encodeContainerNode( */ function encodeSimpleAllowedTypes( simpleAllowedTypes: ReadonlyMap, -): JsonCompatibleObject { - const serializableAllowedTypes: JsonCompatibleObject = {}; +): Format.SimpleAllowedTypesFormat { + const encodedAllowedTypes: Format.SimpleAllowedTypesFormat = {}; for (const [identifier, attributes] of simpleAllowedTypes) { - serializableAllowedTypes[identifier] = { - // The type of SimpleAllowedTypeAttributes is not assignable to JsonCompatibleObject, so we splat its fields here. - ...attributes, + encodedAllowedTypes[identifier] = { + isStaged: attributes.isStaged, }; } - return serializableAllowedTypes; + return encodedAllowedTypes; } /** @@ -169,15 +173,17 @@ function encodeSimpleAllowedTypes( * @param schema - The object node schema to convert. * @returns A serializable representation of the object node schema. */ -function encodeObjectNode(schema: SimpleObjectNodeSchema): JsonCompatibleObject { - const serializableFields: Record = {}; +function encodeObjectNode( + schema: SimpleObjectNodeSchema, +): Format.SimpleObjectNodeSchemaFormat { + const encodedFields: Format.SimpleObjectFieldSchemasFormat = {}; for (const [fieldKey, fieldSchema] of schema.fields) { - serializableFields[fieldKey] = encodeObjectField(fieldSchema); + encodedFields[fieldKey] = encodeObjectField(fieldSchema); } return { kind: schema.kind, - fields: serializableFields, + fields: encodedFields, allowUnknownOptionalFields: schema.allowUnknownOptionalFields, }; } @@ -187,10 +193,11 @@ function encodeObjectNode(schema: SimpleObjectNodeSchema): JsonCompatibleObject * @param fieldSchema - The object field schema to convert. * @returns A serializable representation of the object field schema. */ -function encodeObjectField(fieldSchema: SimpleObjectFieldSchema): JsonCompatibleObject { - const serializableField = encodeField(fieldSchema); - serializableField.storedKey = fieldSchema.storedKey; - return serializableField; +function encodeObjectField( + fieldSchema: SimpleObjectFieldSchema, +): Format.SimpleObjectFieldSchemaFormat { + const encodedField = encodeField(fieldSchema); + return { ...encodedField, storedKey: fieldSchema.storedKey }; } /** @@ -198,40 +205,43 @@ function encodeObjectField(fieldSchema: SimpleObjectFieldSchema): JsonCompatible * @param fieldSchema - The field schema to convert. * @returns A serializable representation of the field schema. */ -function encodeField(fieldSchema: SimpleFieldSchema): JsonCompatibleObject { +function encodeField(fieldSchema: SimpleFieldSchema): Format.SimpleFieldSchemaFormat { return { kind: fieldSchema.kind, simpleAllowedTypes: encodeSimpleAllowedTypes(fieldSchema.simpleAllowedTypes), }; } +const decodeNodeSchemaDispatcher: DiscriminatedUnionDispatcher< + Format.SimpleNodeSchemaUnionFormat, + [], + | SimpleLeafNodeSchema + | SimpleArrayNodeSchema + | SimpleMapNodeSchema + | SimpleRecordNodeSchema + | SimpleObjectNodeSchema +> = new DiscriminatedUnionDispatcher({ + leaf: decodeLeafNode, + array: decodeContainerNode, + map: decodeContainerNode, + record: decodeContainerNode, + object: decodeObjectNode, +}); + /** * Decodes a node schema from a JSON-compatible object. * @param encodedNodeSchema - The encoded node schema to decode. * @returns The decoded node schema. */ function decodeNodeSchema( - encodedNodeSchema: JsonCompatibleObject, + encodedNodeSchema: Format.SimpleNodeSchemaUnionFormat, ): | SimpleLeafNodeSchema | SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema | SimpleObjectNodeSchema { - const kind = encodedNodeSchema.kind as NodeKind; - - switch (kind) { - case NodeKind.Array: - case NodeKind.Map: - case NodeKind.Record: - return decodeContainerNode(encodedNodeSchema); - case NodeKind.Leaf: - return decodeLeafNode(encodedNodeSchema); - case NodeKind.Object: - return decodeObjectNode(encodedNodeSchema); - default: - unreachableCase(kind); - } + return decodeNodeSchemaDispatcher.dispatch(encodedNodeSchema); } /** @@ -240,13 +250,14 @@ function decodeNodeSchema( * @returns The decoded container node schema. */ function decodeContainerNode( - encodedContainerSchema: JsonCompatibleObject, + encodedContainerSchema: + | Format.SimpleArrayNodeSchemaFormat + | Format.SimpleMapNodeSchemaFormat + | Format.SimpleRecordNodeSchemaFormat, ): SimpleArrayNodeSchema | SimpleMapNodeSchema | SimpleRecordNodeSchema { return { kind: encodedContainerSchema.kind as NodeKind.Array | NodeKind.Map | NodeKind.Record, - simpleAllowedTypes: decodeSimpleAllowedTypes( - encodedContainerSchema.simpleAllowedTypes as JsonCompatibleObject, - ), + simpleAllowedTypes: decodeSimpleAllowedTypes(encodedContainerSchema.simpleAllowedTypes), // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values. persistedMetadata: undefined, metadata: {}, @@ -259,7 +270,9 @@ function decodeContainerNode( * @returns The decoded leaf node schema. * @throws Will throw a usage error if the encoded leaf schema is not in the expected format. */ -function decodeLeafNode(encodedLeafSchema: JsonCompatibleObject): SimpleLeafNodeSchema { +function decodeLeafNode( + encodedLeafSchema: Format.SimpleLeafNodeSchemaFormat, +): SimpleLeafNodeSchema { if (encodedLeafSchema.leafKind === undefined) { throw new UsageError("Expected leafKind for leaf node schema"); } @@ -279,7 +292,9 @@ function decodeLeafNode(encodedLeafSchema: JsonCompatibleObject): SimpleLeafNode * @returns The decoded object node schema. * @throws Will throw a usage error if the encoded object schema is not in the expected format. */ -function decodeObjectNode(encodedObjectSchema: JsonCompatibleObject): SimpleObjectNodeSchema { +function decodeObjectNode( + encodedObjectSchema: Format.SimpleObjectNodeSchemaFormat, +): SimpleObjectNodeSchema { if (encodedObjectSchema.fields === undefined) { throw new UsageError("Expected fields for object node schema"); } @@ -289,9 +304,7 @@ function decodeObjectNode(encodedObjectSchema: JsonCompatibleObject): SimpleObje fields: decodeObjectFields(encodedObjectSchema.fields), // It is possible for allowUnknownOptionalFields to be undefined. This happens when serializing a Simple Schema derived // from a stored schema. - allowUnknownOptionalFields: encodedObjectSchema.allowUnknownOptionalFields as - | boolean - | undefined, + allowUnknownOptionalFields: encodedObjectSchema.allowUnknownOptionalFields, // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding. persistedMetadata: undefined, metadata: {}, @@ -305,7 +318,7 @@ function decodeObjectNode(encodedObjectSchema: JsonCompatibleObject): SimpleObje * @throws Will throw a usage error if the encoded fields are not in the expected format. */ function decodeObjectFields( - encodedFields: JsonCompatible, + encodedFields: Format.SimpleObjectFieldSchemasFormat, ): ReadonlyMap { if (!isJsonObject(encodedFields)) { throw new UsageError("Expected object for encodedFields"); diff --git a/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts new file mode 100644 index 000000000000..49cd5eb8c7de --- /dev/null +++ b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts @@ -0,0 +1,122 @@ +import { Type, type ObjectOptions, type Static } from "@sinclair/typebox"; + +const noAdditionalProps: ObjectOptions = { additionalProperties: false }; + +/** + * The format version for the schema. + */ +export const SimpleSchemaFormatVersion = { + v1: 1, +} as const; + +export const SimpleAllowedTypeAttributesFormat = Type.Object( + { + isStaged: Type.Optional(Type.Boolean()), + }, + noAdditionalProps, +); +export type SimpleAllowedTypeAttributesFormat = Static< + typeof SimpleAllowedTypeAttributesFormat +>; +export const SimpleAllowedTypesFormat = Type.Record( + Type.String(), + SimpleAllowedTypeAttributesFormat, +); +export type SimpleAllowedTypesFormat = Static; + +export const SimpleFieldSchemaFormat = Type.Object( + { + kind: Type.Integer(), + simpleAllowedTypes: SimpleAllowedTypesFormat, + }, + noAdditionalProps, +); +export type SimpleFieldSchemaFormat = Static; + +// TODO: Extend SimpleFieldSchemaFormat +export const SimpleObjectFieldSchemaFormat = Type.Object( + { + kind: Type.Integer(), + simpleAllowedTypes: SimpleAllowedTypesFormat, + storedKey: Type.String(), + }, + noAdditionalProps, +); +export type SimpleObjectFieldSchemaFormat = Static; + +export const SimpleArrayNodeSchemaFormat = Type.Object( + { + kind: Type.Integer(), + simpleAllowedTypes: SimpleAllowedTypesFormat, + }, + noAdditionalProps, +); +export type SimpleArrayNodeSchemaFormat = Static; + +export const SimpleMapNodeSchemaFormat = Type.Object( + { + kind: Type.Integer(), + simpleAllowedTypes: SimpleAllowedTypesFormat, + }, + noAdditionalProps, +); +export type SimpleMapNodeSchemaFormat = Static; + +export const SimpleRecordNodeSchemaFormat = Type.Object( + { + kind: Type.Integer(), + simpleAllowedTypes: SimpleAllowedTypesFormat, + }, + noAdditionalProps, +); +export type SimpleRecordNodeSchemaFormat = Static; + +export const SimpleLeafNodeSchemaFormat = Type.Object( + { + kind: Type.Integer(), + leafKind: Type.Integer(), + }, + noAdditionalProps, +); +export type SimpleLeafNodeSchemaFormat = Static; + +export const SimpleObjectFieldSchemasFormat = Type.Record( + Type.String(), + SimpleObjectFieldSchemaFormat, +); +export type SimpleObjectFieldSchemasFormat = Static; + +export const SimpleObjectNodeSchemaFormat = Type.Object( + { + kind: Type.Integer(), + fields: SimpleObjectFieldSchemasFormat, + allowUnknownOptionalFields: Type.Optional(Type.Boolean()), + }, + noAdditionalProps, +); +export type SimpleObjectNodeSchemaFormat = Static; + +export const SimpleNodeSchemaUnionFormat = Type.Object({ + array: Type.Optional(SimpleArrayNodeSchemaFormat), + map: Type.Optional(SimpleMapNodeSchemaFormat), + record: Type.Optional(SimpleRecordNodeSchemaFormat), + leaf: Type.Optional(SimpleLeafNodeSchemaFormat), + object: Type.Optional(SimpleObjectNodeSchemaFormat), +}); +export type SimpleNodeSchemaUnionFormat = Static; + +export const SimpleSchemaDefinitionsFormat = Type.Record( + Type.String(), + SimpleNodeSchemaUnionFormat, +); +export type SimpleSchemaDefinitionsFormat = Static; + +export const SimpleTreeSchemaFormat = Type.Object( + { + version: Type.Literal(SimpleSchemaFormatVersion.v1), + root: SimpleFieldSchemaFormat, + definitions: SimpleSchemaDefinitionsFormat, + }, + noAdditionalProps, +); +export type SimpleTreeSchemaFormat = Static; diff --git a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts index 00c514eee90b..4eeae9dc0bdc 100644 --- a/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/getSimpleSchema.spec.ts @@ -27,6 +27,8 @@ import { } from "../../../simple-tree/api/viewSchemaToSimpleSchema.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "../../snapshots/index.js"; import { HasUnknownOptionalFields } from "../../testTrees.js"; +import { ajvValidator } from "../../codec/index.js"; +import type { FormatValidator } from "../../../codec/index.js"; const simpleString: SimpleLeafNodeSchema = { leafKind: ValueSchema.String, @@ -42,6 +44,9 @@ const simpleNumber: SimpleLeafNodeSchema = { persistedMetadata: undefined, }; +// The format validator used in these tests +const formatValidator: FormatValidator = ajvValidator; + describe("getSimpleSchema", () => { useSnapshotDirectory("get-simple-schema"); @@ -94,7 +99,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -128,7 +133,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Leaf node", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -166,7 +171,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Field Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -212,7 +217,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Array Schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -258,7 +263,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Map schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -304,7 +309,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Record schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -378,7 +383,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -438,7 +443,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Object schema including an identifier field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -501,7 +506,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Object schema including a union field", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -562,7 +567,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - Recursive object schema", () => { const simpleTree = toSimpleTreeSchema(Schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -598,7 +603,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - simpleAllowedTypes", () => { const simpleTree = toSimpleTreeSchema(schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); @@ -642,7 +647,7 @@ describe("getSimpleSchema", () => { it("Roundtrip serialization - allowUnknownOptionalFields", () => { const simpleTree = toSimpleTreeSchema(schema, true); const expected = copySimpleTreeSchemaWithoutMetadata(simpleTree); - const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree)); + const actual = decodeSimpleSchema(encodeSimpleSchema(simpleTree), formatValidator); assert.deepEqual(actual, expected); }); }); diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json index 15f60707e98f..48b906ed8a48 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Array schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,14 +10,18 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.array": { - "kind": 1, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false + "array": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } } } } diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json index afe4a3d596d6..52f3f57b9d0f 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Field Schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 0, "simpleAllowedTypes": { @@ -9,8 +10,10 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json index dcc197a19793..12ebf0985cc4 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Leaf node.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,8 +10,10 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json index 2648d7329fd1..7fb3b0d28e5e 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Map schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,14 +10,18 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.map": { - "kind": 0, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false + "map": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } } } } diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json index 4e279ae4a008..2cd6d348d65b 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including a union field.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,30 +10,36 @@ }, "definitions": { "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 + "leaf": { + "kind": 3, + "leafKind": 0 + } }, "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.object": { - "kind": 2, - "fields": { - "foo": { - "kind": 1, - "simpleAllowedTypes": { - "com.fluidframework.leaf.number": { - "isStaged": false + "object": { + "kind": 2, + "fields": { + "foo": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.number": { + "isStaged": false + }, + "com.fluidframework.leaf.string": { + "isStaged": false + } }, - "com.fluidframework.leaf.string": { - "isStaged": false - } - }, - "storedKey": "foo" - } - }, - "allowUnknownOptionalFields": false + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json index 9bd8e0f9ca82..0991abe24079 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema including an identifier field.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,23 +10,27 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.object": { - "kind": 2, - "fields": { - "id": { - "kind": 2, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false - } - }, - "storedKey": "id" - } - }, - "allowUnknownOptionalFields": false + "object": { + "kind": 2, + "fields": { + "id": { + "kind": 2, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + }, + "storedKey": "id" + } + }, + "allowUnknownOptionalFields": false + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json index f39478b35240..e122eee7a44b 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Object schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,36 +10,42 @@ }, "definitions": { "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 + "leaf": { + "kind": 3, + "leafKind": 0 + } }, "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.object": { - "kind": 2, - "fields": { - "foo": { - "kind": 0, - "simpleAllowedTypes": { - "com.fluidframework.leaf.number": { - "isStaged": false - } + "object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.number": { + "isStaged": false + } + }, + "storedKey": "foo" }, - "storedKey": "foo" + "bar": { + "kind": 1, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } + }, + "storedKey": "bar" + } }, - "bar": { - "kind": 1, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false - } - }, - "storedKey": "bar" - } - }, - "allowUnknownOptionalFields": false + "allowUnknownOptionalFields": false + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json index ece6c152c4b7..ff2e1a8fb399 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Record schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,14 +10,18 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.record": { - "kind": 4, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false + "record": { + "kind": 4, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + } } } } diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json index a1eb800e8262..58921f908aa7 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Recursive object schema.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,26 +10,30 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } }, "test.recursive-object": { - "kind": 2, - "fields": { - "foo": { - "kind": 0, - "simpleAllowedTypes": { - "com.fluidframework.leaf.string": { - "isStaged": false + "object": { + "kind": 2, + "fields": { + "foo": { + "kind": 0, + "simpleAllowedTypes": { + "com.fluidframework.leaf.string": { + "isStaged": false + }, + "test.recursive-object": { + "isStaged": false + } }, - "test.recursive-object": { - "isStaged": false - } - }, - "storedKey": "foo" - } - }, - "allowUnknownOptionalFields": false + "storedKey": "foo" + } + }, + "allowUnknownOptionalFields": false + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json index 9c85966024c5..51ce59d82047 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - Union root.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -12,12 +13,16 @@ }, "definitions": { "com.fluidframework.leaf.number": { - "kind": 3, - "leafKind": 0 + "leaf": { + "kind": 3, + "leafKind": 0 + } }, "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json index f6d3b9681b13..682268166a04 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - allowUnknownOptionalFields.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 1, "simpleAllowedTypes": { @@ -9,9 +10,11 @@ }, "definitions": { "test.hasUnknownOptionalFields": { - "kind": 2, - "fields": {}, - "allowUnknownOptionalFields": true + "object": { + "kind": 2, + "fields": {}, + "allowUnknownOptionalFields": true + } } } } \ No newline at end of file diff --git a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json index af0a8528b1e6..5a38a8d13bd9 100644 --- a/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json +++ b/packages/dds/tree/src/test/snapshots/output/get-simple-schema/serialized - simpleAllowedTypes.json @@ -1,4 +1,5 @@ { + "version": 1, "root": { "kind": 0, "simpleAllowedTypes": { @@ -9,8 +10,10 @@ }, "definitions": { "com.fluidframework.leaf.string": { - "kind": 3, - "leafKind": 1 + "leaf": { + "kind": 3, + "leafKind": 1 + } } } } \ No newline at end of file From 9b5e6d7e11ce4fc0ea9beea358e89835b56761c9 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 6 Nov 2025 14:06:24 -0800 Subject: [PATCH 45/47] Cleanup. --- .../dds/tree/api-report/tree.alpha.api.md | 4 +- .../src/simple-tree/api/simpleSchemaCodec.ts | 59 +++++-------------- .../src/simple-tree/simpleSchemaFormatV1.ts | 54 ++++++++++++++++- 3 files changed, 71 insertions(+), 46 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 28521ea4b042..c309bc302c0c 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -199,7 +199,7 @@ export function createSimpleTreeIndex(view: TreeView, indexer: Map, getValue: (nodes: TreeIndexNodes>) => TValue, isKeyValid: (key: TreeIndexKey) => key is TKey, indexableSchema: readonly TSchema[]): SimpleTreeIndex; // @alpha -export function decodeSimpleSchema(encodedSchema: JsonCompatibleObject, validator?: FormatValidator): SimpleTreeSchema; +export function decodeSimpleSchema(encodedSchema: JsonCompatibleReadOnly, validator?: FormatValidator): SimpleTreeSchema; // @public @sealed @system interface DefaultProvider extends ErasedType<"@fluidframework/tree.FieldProvider"> { @@ -217,7 +217,7 @@ export interface DirtyTreeMap { export type DirtyTreeStatus = "new" | "changed" | "moved"; // @alpha -export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleObject; +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleReadOnly; // @beta export function enumFromStrings(factory: SchemaFactory, members: Members): ((value: TValue) => TValue extends unknown ? TreeNode & { diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts index b65945332372..dce051d447a2 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchemaCodec.ts @@ -3,12 +3,7 @@ * Licensed under the MIT License. */ -import { - isJsonObject, - objectToMap, - type JsonCompatible, - type JsonCompatibleObject, -} from "../../util/index.js"; +import { objectToMap, type JsonCompatibleReadOnly } from "../../util/index.js"; import { unreachableCase, transformMapValues } from "@fluidframework/core-utils/internal"; import type { SimpleAllowedTypeAttributes, @@ -43,7 +38,7 @@ import { * * @alpha */ -export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleObject { +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleReadOnly { // Convert types to serializable forms const encodedDefinitions: Format.SimpleSchemaDefinitionsFormat = {}; @@ -71,7 +66,7 @@ export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatib * @alpha */ export function decodeSimpleSchema( - encodedSchema: JsonCompatibleObject, + encodedSchema: JsonCompatibleReadOnly, validator?: FormatValidator, ): SimpleTreeSchema { const effectiveValidator = validator ?? FormatValidatorNoOp; @@ -88,9 +83,6 @@ export function decodeSimpleSchema( root: decodeSimpleFieldSchema(encodedSchema.root), definitions: new Map( transformMapValues(objectToMap(encodedSchema.definitions), (value, key) => { - if (value === undefined || !isJsonObject(value)) { - throw new UsageError(`Expected node schema for definition ${key}`); - } return decodeNodeSchema(value); }), ), @@ -268,15 +260,10 @@ function decodeContainerNode( * Decodes a leaf node schema from a JSON-compatible object. * @param encodedLeafSchema - The encoded leaf node schema. * @returns The decoded leaf node schema. - * @throws Will throw a usage error if the encoded leaf schema is not in the expected format. */ function decodeLeafNode( encodedLeafSchema: Format.SimpleLeafNodeSchemaFormat, ): SimpleLeafNodeSchema { - if (encodedLeafSchema.leafKind === undefined) { - throw new UsageError("Expected leafKind for leaf node schema"); - } - return { kind: NodeKind.Leaf, leafKind: encodedLeafSchema.leafKind as ValueSchema, @@ -290,15 +277,10 @@ function decodeLeafNode( * Decodes a object node schema from a JSON-compatible object. * @param encodedObjectSchema - The encoded object node schema. * @returns The decoded object node schema. - * @throws Will throw a usage error if the encoded object schema is not in the expected format. */ function decodeObjectNode( encodedObjectSchema: Format.SimpleObjectNodeSchemaFormat, ): SimpleObjectNodeSchema { - if (encodedObjectSchema.fields === undefined) { - throw new UsageError("Expected fields for object node schema"); - } - return { kind: NodeKind.Object, fields: decodeObjectFields(encodedObjectSchema.fields), @@ -315,20 +297,13 @@ function decodeObjectNode( * Decodes a map of object fields from a JSON-compatible object. * @param encodedFields - The encoded fields. * @returns A map of the decoded object fields. - * @throws Will throw a usage error if the encoded fields are not in the expected format. */ function decodeObjectFields( encodedFields: Format.SimpleObjectFieldSchemasFormat, ): ReadonlyMap { - if (!isJsonObject(encodedFields)) { - throw new UsageError("Expected object for encodedFields"); - } - const fields = new Map(); - for (const [fieldKey, fieldSchema] of Object.entries( - encodedFields as JsonCompatibleObject, - )) { - fields.set(fieldKey, decodeObjectField(fieldSchema as JsonCompatibleObject)); + for (const [fieldKey, fieldSchema] of Object.entries(encodedFields)) { + fields.set(fieldKey, decodeObjectField(fieldSchema)); } return fields; } @@ -338,11 +313,13 @@ function decodeObjectFields( * @param encodedField - The encoded field schema. * @returns The decoded simple object field schema. */ -function decodeObjectField(encodedField: JsonCompatibleObject): SimpleObjectFieldSchema { +function decodeObjectField( + encodedField: Format.SimpleObjectFieldSchemaFormat, +): SimpleObjectFieldSchema { const baseField = decodeSimpleFieldSchema(encodedField); return { ...baseField, - storedKey: encodedField.storedKey as string, + storedKey: encodedField.storedKey, }; } @@ -351,12 +328,12 @@ function decodeObjectField(encodedField: JsonCompatibleObject): SimpleObjectFiel * @param encodedField - The encoded field schema. * @returns The decoded simple field schema. */ -function decodeSimpleFieldSchema(encodedField: JsonCompatibleObject): SimpleFieldSchema { +function decodeSimpleFieldSchema( + encodedField: Format.SimpleFieldSchemaFormat, +): SimpleFieldSchema { return { kind: encodedField.kind as FieldKind, - simpleAllowedTypes: decodeSimpleAllowedTypes( - encodedField.simpleAllowedTypes as JsonCompatibleObject, - ), + simpleAllowedTypes: decodeSimpleAllowedTypes(encodedField.simpleAllowedTypes), // We cannot encode persistedMetadata or metadata, so we explicitly set them to empty values when decoding. persistedMetadata: undefined, metadata: {}, @@ -367,19 +344,15 @@ function decodeSimpleFieldSchema(encodedField: JsonCompatibleObject): SimpleFiel * Decodes a simple allowed types map from a JSON-compatible object. * @param encodedAllowedTypes - The encoded simple allowed types. * @returns A map of the decoded simple allowed types. - * @throws Will throw a usage error if the encoded allowed types are not in the expected format. */ function decodeSimpleAllowedTypes( - encodedAllowedTypes: JsonCompatible, + encodedAllowedTypes: Format.SimpleAllowedTypesFormat, ): ReadonlyMap { - if (!isJsonObject(encodedAllowedTypes)) { - throw new UsageError("Expected object for encodedAllowedTypes"); - } - const untypedMap = objectToMap(encodedAllowedTypes as JsonCompatibleObject); + const untypedMap = objectToMap(encodedAllowedTypes); const simpleAllowedTypes = transformMapValues(untypedMap, (value) => { return { - isStaged: (value as JsonCompatibleObject).isStaged as boolean | undefined, + isStaged: value.isStaged, } satisfies SimpleAllowedTypeAttributes; }); diff --git a/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts index 49cd5eb8c7de..f9635633bd54 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts @@ -9,21 +9,35 @@ export const SimpleSchemaFormatVersion = { v1: 1, } as const; +/** + * The allowed types and their attributes in the simple schema format. + * @see {@link SimpleAllowedTypes}. + */ export const SimpleAllowedTypeAttributesFormat = Type.Object( { isStaged: Type.Optional(Type.Boolean()), }, noAdditionalProps, ); + export type SimpleAllowedTypeAttributesFormat = Static< typeof SimpleAllowedTypeAttributesFormat >; + +/** + * A set of allowed types in the simple schema format. + * The keys are the type identifiers, and the values are their attributes. + */ export const SimpleAllowedTypesFormat = Type.Record( Type.String(), SimpleAllowedTypeAttributesFormat, ); export type SimpleAllowedTypesFormat = Static; +/** + * Persisted format for a field schema in the simple schema format. + * @see {@link SimpleFieldSchema}. + */ export const SimpleFieldSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -33,7 +47,10 @@ export const SimpleFieldSchemaFormat = Type.Object( ); export type SimpleFieldSchemaFormat = Static; -// TODO: Extend SimpleFieldSchemaFormat +/** + * Persisted format for an object field schema in the simple schema format. + * @see {@link SimpleObjectFieldSchema}. + */ export const SimpleObjectFieldSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -44,6 +61,10 @@ export const SimpleObjectFieldSchemaFormat = Type.Object( ); export type SimpleObjectFieldSchemaFormat = Static; +/** + * Persisted format for an array node schema in the simple schema format. + * @see {@link SimpleArrayNodeSchema}. + */ export const SimpleArrayNodeSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -53,6 +74,10 @@ export const SimpleArrayNodeSchemaFormat = Type.Object( ); export type SimpleArrayNodeSchemaFormat = Static; +/** + * Persisted format for a map node schema in the simple schema format. + * @see {@link SimpleMapNodeSchema}. + */ export const SimpleMapNodeSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -62,6 +87,10 @@ export const SimpleMapNodeSchemaFormat = Type.Object( ); export type SimpleMapNodeSchemaFormat = Static; +/** + * Persisted format for a record node schema in the simple schema format. + * @see {@link SimpleRecordNodeSchema}. + */ export const SimpleRecordNodeSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -71,6 +100,10 @@ export const SimpleRecordNodeSchemaFormat = Type.Object( ); export type SimpleRecordNodeSchemaFormat = Static; +/** + * Persisted format for a leaf node schema in the simple schema format. + * @see {@link SimpleLeafNodeSchema}. + */ export const SimpleLeafNodeSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -80,12 +113,19 @@ export const SimpleLeafNodeSchemaFormat = Type.Object( ); export type SimpleLeafNodeSchemaFormat = Static; +/** + * Persisted format for the field schemas of an object node in the simple schema format. + */ export const SimpleObjectFieldSchemasFormat = Type.Record( Type.String(), SimpleObjectFieldSchemaFormat, ); export type SimpleObjectFieldSchemasFormat = Static; +/** + * Persisted format for an object node schema in the simple schema format. + * @see {@link SimpleObjectNodeSchema}. + */ export const SimpleObjectNodeSchemaFormat = Type.Object( { kind: Type.Integer(), @@ -96,6 +136,11 @@ export const SimpleObjectNodeSchemaFormat = Type.Object( ); export type SimpleObjectNodeSchemaFormat = Static; +/** + * Discriminated union of all possible node schemas. + * + * See {@link DiscriminatedUnionDispatcher} for more information on this pattern. + */ export const SimpleNodeSchemaUnionFormat = Type.Object({ array: Type.Optional(SimpleArrayNodeSchemaFormat), map: Type.Optional(SimpleMapNodeSchemaFormat), @@ -105,12 +150,19 @@ export const SimpleNodeSchemaUnionFormat = Type.Object({ }); export type SimpleNodeSchemaUnionFormat = Static; +/** + * Helper type for the schema definitions map in the persisted format. + */ export const SimpleSchemaDefinitionsFormat = Type.Record( Type.String(), SimpleNodeSchemaUnionFormat, ); export type SimpleSchemaDefinitionsFormat = Static; +/** + * Persisted format for the entire tree schema in the simple schema format. + * @see {@link SimpleTreeSchema}. + */ export const SimpleTreeSchemaFormat = Type.Object( { version: Type.Literal(SimpleSchemaFormatVersion.v1), From 32cda8cee71f42441569a1de25f18d93c67adcb9 Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 6 Nov 2025 16:00:18 -0800 Subject: [PATCH 46/47] Copyright header. --- packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts index f9635633bd54..e64418afa5ab 100644 --- a/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts +++ b/packages/dds/tree/src/simple-tree/simpleSchemaFormatV1.ts @@ -1,3 +1,8 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + import { Type, type ObjectOptions, type Static } from "@sinclair/typebox"; const noAdditionalProps: ObjectOptions = { additionalProperties: false }; From 331ab541988dd8cad5bb082558298e331483a91a Mon Sep 17 00:00:00 2001 From: Tommy Brosman Date: Thu, 6 Nov 2025 16:49:19 -0800 Subject: [PATCH 47/47] - Lint - Regenerated API files --- packages/common/core-utils/src/map.ts | 4 +--- .../api-report/fluid-framework.alpha.api.md | 12 ++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/common/core-utils/src/map.ts b/packages/common/core-utils/src/map.ts index 88a5412432b2..67f882c8ed46 100644 --- a/packages/common/core-utils/src/map.ts +++ b/packages/common/core-utils/src/map.ts @@ -15,7 +15,5 @@ export function transformMapValues( map: ReadonlyMap, transformValue: (value: InputValue, key: Key) => OutputValue, ): Map { - return new Map( - [...map.entries()].map(([key, value]) => [key, transformValue(value, key)]), - ); + return new Map([...map.entries()].map(([key, value]) => [key, transformValue(value, key)])); } diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index ba8a985384c3..0ecad0e6fd7b 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -240,13 +240,13 @@ export function createSimpleTreeIndex(view: TreeView, indexer: Map, getValue: (nodes: TreeIndexNodes>) => TValue, isKeyValid: (key: TreeIndexKey) => key is TKey, indexableSchema: readonly TSchema[]): SimpleTreeIndex; +// @alpha +export function decodeSimpleSchema(encodedSchema: JsonCompatibleReadOnly, validator?: FormatValidator): SimpleTreeSchema; + // @public @sealed @system interface DefaultProvider extends ErasedType<"@fluidframework/tree.FieldProvider"> { } -// @alpha -export function deserializeSimpleSchema(serializedSchema: JsonCompatible): SimpleTreeSchema; - // @alpha export interface DirtyTreeMap { // (undocumented) @@ -258,6 +258,9 @@ export interface DirtyTreeMap { // @alpha export type DirtyTreeStatus = "new" | "changed" | "moved"; +// @alpha +export function encodeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatibleReadOnly; + // @beta export function enumFromStrings(factory: SchemaFactory, members: Members): ((value: TValue) => TValue extends unknown ? TreeNode & { readonly value: TValue; @@ -1371,9 +1374,6 @@ export class SchemaUpgrade { // @public @system type ScopedSchemaName = TScope extends undefined ? `${TName}` : `${TScope}.${TName}`; -// @alpha -export function serializeSimpleSchema(simpleSchema: SimpleTreeSchema): JsonCompatible; - // @public @sealed export interface SharedObjectKind extends ErasedType { is(value: IFluidLoadable): value is IFluidLoadable & TSharedObject;