diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index ba59f9482a5f..043ef51c6501 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -216,6 +216,7 @@ export { type TransactionResultSuccess, type TransactionResultFailed, rollback, + generateSchemaFromSimpleSchema, } 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 e531037fa106..d8030f834919 100644 --- a/packages/dds/tree/src/simple-tree/api/index.ts +++ b/packages/dds/tree/src/simple-tree/api/index.ts @@ -133,6 +133,8 @@ export { rollback, } from "./transactionTypes.js"; +export { generateSchemaFromSimpleSchema } from "./schemaFromSimple.js"; + // Exporting the schema (RecursiveObject) to test that recursive types are working correctly. // These are `@internal` so they can't be included in the `InternalClassTreeTypes` due to https://github.com/microsoft/rushstack/issues/3639 export { diff --git a/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts new file mode 100644 index 000000000000..1513da33ccc0 --- /dev/null +++ b/packages/dds/tree/src/simple-tree/api/schemaFromSimple.ts @@ -0,0 +1,70 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { unreachableCase } from "@fluidframework/core-utils/internal"; +import { fail } from "../../util/index.js"; +import { NodeKind, type TreeNodeSchema } from "../core/index.js"; +import { createFieldSchema, type FieldSchema, type AllowedTypes } from "../schemaTypes.js"; +import { SchemaFactory } from "./schemaFactory.js"; +import type { SimpleFieldSchema, SimpleNodeSchema, SimpleTreeSchema } from "./simpleSchema.js"; + +/* + * TODO: Tests for this file + */ + +const factory = new SchemaFactory(undefined); + +/** + * Create {@link FieldSchema} from a SimpleTreeSchema. + * @remarks + * Only use this API if a hand written (produced using {@link SchemaFactory} cannot be provided. + * + * Using this resulting schema with schema aware APIs (designed to work with strongly typed schema) like {@link TreeViewConfiguration} + * will produce a poor TypeScript typing experience which is subject to change. + * + * Editing through a view produced using this schema can easily violate invariants other users of the document might expect and must be done with great care. + * @internal + */ +export function generateSchemaFromSimpleSchema(simple: SimpleTreeSchema): FieldSchema { + const context: Context = new Map( + [...simple.definitions].map(([id, schema]): [string, () => TreeNodeSchema] => [ + id, + () => generateNode(id, schema, context), + ]), + ); + return generateFieldSchema(simple, context); +} + +type Context = ReadonlyMap TreeNodeSchema>; + +function generateFieldSchema(simple: SimpleFieldSchema, context: Context): FieldSchema { + return createFieldSchema(simple.kind, generateAllowedTypes(simple.allowedTypes, context)); +} + +function generateAllowedTypes(allowed: ReadonlySet, context: Context): AllowedTypes { + return [...allowed].map((id) => context.get(id) ?? fail(`Missing schema`)); +} + +function generateNode(id: string, schema: SimpleNodeSchema, context: Context): TreeNodeSchema { + switch (schema.kind) { + case NodeKind.Object: { + const fields: Record = {}; + for (const [key, field] of Object.entries(schema.fields)) { + fields[key] = generateFieldSchema(field, context); + } + return factory.object(id, fields); + } + case NodeKind.Array: + return factory.array(id, generateAllowedTypes(schema.allowedTypes, context)); + case NodeKind.Map: + return factory.map(id, generateAllowedTypes(schema.allowedTypes, context)); + case NodeKind.Leaf: + return ( + SchemaFactory.leaves.find((leaf) => leaf.identifier === id) ?? fail(`Missing schema`) + ); + default: + return unreachableCase(schema); + } +} diff --git a/packages/dds/tree/src/simple-tree/api/simpleSchema.ts b/packages/dds/tree/src/simple-tree/api/simpleSchema.ts index 225a035e6abf..3bc24baee1c6 100644 --- a/packages/dds/tree/src/simple-tree/api/simpleSchema.ts +++ b/packages/dds/tree/src/simple-tree/api/simpleSchema.ts @@ -7,6 +7,13 @@ import type { ValueSchema } from "../../core/index.js"; import type { NodeKind } from "../core/index.js"; import type { FieldKind, FieldSchemaMetadata, NodeSchemaMetadata } from "../schemaTypes.js"; +/* + * TODO: + * - Make TreeNodeSchema implement these interfaces directly. + * - Customize their JSON serialization to use these formats or provide some other serialization scheme. + * - Promote these to alpha + */ + /** * Base interface for all {@link SimpleNodeSchema} implementations. * @@ -38,6 +45,9 @@ export interface SimpleObjectNodeSchema extends SimpleNodeSchemaBase; } diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 4e8376c1c050..1eb3577825d3 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -136,6 +136,7 @@ export { type TransactionResultSuccess, type TransactionResultFailed, rollback, + generateSchemaFromSimpleSchema, } from "./api/index.js"; export { type NodeFromSchema,