diff --git a/packages/dds/tree/src/feature-libraries/flex-tree/context.ts b/packages/dds/tree/src/feature-libraries/flex-tree/context.ts index 943d5ecf6b73..3801e54dae9c 100644 --- a/packages/dds/tree/src/feature-libraries/flex-tree/context.ts +++ b/packages/dds/tree/src/feature-libraries/flex-tree/context.ts @@ -18,7 +18,6 @@ import type { Listenable } from "../../events/index.js"; import { type IDisposable, disposeSymbol } from "../../util/index.js"; import type { FieldGenerator } from "../fieldGenerator.js"; import type { NodeKeyManager } from "../node-key/index.js"; -import type { FlexTreeSchema } from "../typed-schema/index.js"; import type { FlexTreeField } from "./flexTreeTypes.js"; import { type LazyEntity, prepareForEditSymbol } from "./lazyEntity.js"; @@ -29,12 +28,6 @@ import type { ITreeCheckout } from "../../shared-tree/index.js"; * Context for FlexTrees. */ export interface FlexTreeContext { - /** - * Schema used within this context. - * All data must conform to these schema. - */ - readonly flexSchema: FlexTreeSchema; - /** * Schema used within this context. * All data must conform to these schema. @@ -97,7 +90,7 @@ export class Context implements FlexTreeHydratedContext, IDisposable { * @param nodeKeyManager - An object which handles node key generation and conversion */ public constructor( - public readonly flexSchema: FlexTreeSchema, + public readonly schemaPolicy: SchemaPolicy, public readonly checkout: ITreeCheckout, public readonly nodeKeyManager: NodeKeyManager, ) { @@ -114,10 +107,6 @@ export class Context implements FlexTreeHydratedContext, IDisposable { this.checkout.forest.anchors.slots.set(ContextSlot, this); } - public get schemaPolicy(): SchemaPolicy { - return this.flexSchema.policy; - } - public isHydrated(): this is FlexTreeHydratedContext { return true; } @@ -201,7 +190,7 @@ export class Context implements FlexTreeHydratedContext, IDisposable { * This is necessary for supporting using this tree across edits to the forest, and not leaking memory. */ export function getTreeContext( - schema: FlexTreeSchema, + schema: SchemaPolicy, checkout: ITreeCheckout, nodeKeyManager: NodeKeyManager, ): Context { diff --git a/packages/dds/tree/src/shared-tree/checkoutFlexTreeView.ts b/packages/dds/tree/src/shared-tree/checkoutFlexTreeView.ts index 7b4d925766be..afa2e7fe78ae 100644 --- a/packages/dds/tree/src/shared-tree/checkoutFlexTreeView.ts +++ b/packages/dds/tree/src/shared-tree/checkoutFlexTreeView.ts @@ -7,10 +7,10 @@ import { assert } from "@fluidframework/core-utils/internal"; import { type Context, type FlexTreeField, - type FlexTreeSchema, type NodeKeyManager, getTreeContext, type FlexTreeHydratedContext, + type FullSchemaPolicy, } from "../feature-libraries/index.js"; import { tryDisposeTreeNode } from "../simple-tree/index.js"; import { disposeSymbol } from "../util/index.js"; @@ -42,7 +42,7 @@ export class CheckoutFlexTreeView void, ) { diff --git a/packages/dds/tree/src/shared-tree/schematizingTreeView.ts b/packages/dds/tree/src/shared-tree/schematizingTreeView.ts index 679a16aa0902..e499d50dc101 100644 --- a/packages/dds/tree/src/shared-tree/schematizingTreeView.ts +++ b/packages/dds/tree/src/shared-tree/schematizingTreeView.ts @@ -6,7 +6,12 @@ import { assert } from "@fluidframework/core-utils/internal"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; -import { AllowedUpdateType, anchorSlot, Compatibility } from "../core/index.js"; +import { + AllowedUpdateType, + anchorSlot, + Compatibility, + type SchemaPolicy, +} from "../core/index.js"; import { type HasListeners, type IEmitter, @@ -19,7 +24,7 @@ import { defaultSchemaPolicy, ContextSlot, cursorForMapTreeNode, - type FlexTreeSchema, + type FullSchemaPolicy, } from "../feature-libraries/index.js"; import { type FieldSchema, @@ -30,7 +35,6 @@ import { type TreeView, type TreeViewEvents, getTreeNodeForField, - toFlexSchema, setField, normalizeFieldSchema, type InsertableContent, @@ -69,7 +73,7 @@ export class SchematizingSimpleTreeView & IEmitter & HasListeners = createEmitter(); @@ -106,7 +110,7 @@ export class SchematizingSimpleTreeView void, nodeKeyManager: NodeKeyManager, - flexTreeSchema: FlexTreeSchema, + schemaPolicy: FullSchemaPolicy, ): CheckoutFlexTreeView { const slots = checkout.forest.anchors.slots; assert(!slots.has(ContextSlot), 0x8c2 /* Cannot create second view from checkout */); @@ -391,7 +395,7 @@ export function requireSchema( ); } - const view = new CheckoutFlexTreeView(checkout, flexTreeSchema, nodeKeyManager, onDispose); + const view = new CheckoutFlexTreeView(checkout, schemaPolicy, nodeKeyManager, onDispose); assert(slots.has(ContextSlot), 0x90d /* Context should be tracked in slot */); return view; diff --git a/packages/dds/tree/src/simple-tree/api/create.ts b/packages/dds/tree/src/simple-tree/api/create.ts index 4d95e807a34a..79f4fcc3ec41 100644 --- a/packages/dds/tree/src/simple-tree/api/create.ts +++ b/packages/dds/tree/src/simple-tree/api/create.ts @@ -24,12 +24,11 @@ import { cursorForMapTreeNode, defaultSchemaPolicy, FieldKinds, - intoStoredSchema, mapTreeFromCursor, type NodeKeyManager, } from "../../feature-libraries/index.js"; import { isFieldInSchema } from "../../feature-libraries/index.js"; -import { toFlexSchema } from "../toFlexSchema.js"; +import { toStoredSchema } from "../toFlexSchema.js"; import { inSchemaOrThrow, mapTreeFromNodeData, type InsertableContent } from "../toMapTree.js"; import { applySchemaToParserOptions, @@ -80,11 +79,11 @@ export function cursorFromInsertable( ): | ITreeCursorSynchronous | (TSchema extends FieldSchema ? undefined : never) { - const flexSchema = toFlexSchema(schema); + const storedSchema = toStoredSchema(schema); const schemaValidationPolicy: SchemaAndPolicy = { policy: defaultSchemaPolicy, // TODO: optimize: This isn't the most efficient operation since its not cached, and has to convert all the schema. - schema: intoStoredSchema(flexSchema), + schema: storedSchema, }; const mapTree = mapTreeFromNodeData( @@ -95,7 +94,7 @@ export function cursorFromInsertable( ); if (mapTree === undefined) { assert( - flexSchema.rootFieldSchema.kind === FieldKinds.optional, + storedSchema.rootFieldSchema.kind === FieldKinds.optional.identifier, 0xa10 /* missing non-optional field */, ); return undefined as TSchema extends FieldSchema ? undefined : never; diff --git a/packages/dds/tree/src/simple-tree/api/tree.ts b/packages/dds/tree/src/simple-tree/api/tree.ts index 1a6d53edf623..5f637ae64ceb 100644 --- a/packages/dds/tree/src/simple-tree/api/tree.ts +++ b/packages/dds/tree/src/simple-tree/api/tree.ts @@ -16,7 +16,7 @@ import { FieldKind, } from "../schemaTypes.js"; import { NodeKind, type TreeNodeSchema } from "../core/index.js"; -import { toFlexSchema } from "../toFlexSchema.js"; +import { toStoredSchema } from "../toFlexSchema.js"; import { LeafNodeSchema } from "../leafNodeSchema.js"; import { assert } from "@fluidframework/core-utils/internal"; import { isObjectNodeSchema, type ObjectNodeSchema } from "../objectNodeTypes.js"; @@ -242,7 +242,7 @@ export class TreeViewConfiguration( ...options, }; - // TODO: should provide a way to look up schema by name efficiently without converting to flex tree schema and back. - // Maybe cache identifier->schema map on simple tree schema lazily. - const flexSchema = toFlexSchema(schema); + const context = getUnhydratedContext(schema); return { valueConverter: config.valueConverter, @@ -185,9 +183,7 @@ export function applySchemaToParserOptions( : { encode: (type, key: FieldKey): string => { // translate stored key into property key. - const flexNodeSchema = - flexSchema.nodeSchema.get(brand(type)) ?? fail("missing schema"); - const simpleNodeSchema = getSimpleNodeSchema(flexNodeSchema); + const simpleNodeSchema = context.schema.get(brand(type)) ?? fail("missing schema"); if (isObjectNodeSchema(simpleNodeSchema)) { const propertyKey = simpleNodeSchema.storedKeyToPropertyKey.get(key); if (propertyKey !== undefined) { @@ -208,9 +204,7 @@ export function applySchemaToParserOptions( return key; }, parse: (type, inputKey): FieldKey => { - const flexNodeSchema = - flexSchema.nodeSchema.get(brand(type)) ?? fail("missing schema"); - const simpleNodeSchema = getSimpleNodeSchema(flexNodeSchema); + const simpleNodeSchema = context.schema.get(brand(type)) ?? fail("missing schema"); if (isObjectNodeSchema(simpleNodeSchema)) { const info = simpleNodeSchema.flexKeyMap.get(inputKey) ?? fail("missing field info"); diff --git a/packages/dds/tree/src/simple-tree/core/unhydratedFlexTree.ts b/packages/dds/tree/src/simple-tree/core/unhydratedFlexTree.ts index 3919ab6db3ac..74953c63c421 100644 --- a/packages/dds/tree/src/simple-tree/core/unhydratedFlexTree.ts +++ b/packages/dds/tree/src/simple-tree/core/unhydratedFlexTree.ts @@ -35,8 +35,6 @@ import { indexForAt, type FlexTreeHydratedContext, FlexFieldSchema, - type FlexTreeSchema, - intoStoredSchemaCollection, type FlexFieldKind, FieldKinds, type SequenceFieldEditBuilder, @@ -92,7 +90,7 @@ export class UnhydratedFlexTreeNode implements UnhydratedFlexTreeNode { return nodeCache.get(mapTree) ?? new UnhydratedFlexTreeNode(context, mapTree, undefined); } - public get context(): UnhydratedContext { + public get context(): FlexTreeContext { return this.simpleContext.flexContext; } @@ -222,25 +220,17 @@ export class UnhydratedFlexTreeNode implements UnhydratedFlexTreeNode { * @remarks An editor is required to edit the FlexTree. */ export class UnhydratedContext implements FlexTreeContext { - public readonly schema: TreeStoredSchema; - /** * @param flexSchema - Schema to use when working with the tree. */ - public constructor(public readonly flexSchema: FlexTreeSchema) { - this.schema = { - rootFieldSchema: flexSchema.rootFieldSchema.stored, - ...intoStoredSchemaCollection(flexSchema), - }; - } + public constructor( + public readonly schemaPolicy: SchemaPolicy, + public readonly schema: TreeStoredSchema, + ) {} public isHydrated(): this is FlexTreeHydratedContext { return false; } - - public get schemaPolicy(): SchemaPolicy { - return this.flexSchema.policy; - } } // #region Fields @@ -282,7 +272,7 @@ const unparentedLocation: LocationInField = { class UnhydratedFlexTreeField implements FlexTreeField { public [flexTreeMarker] = FlexTreeEntityKind.Field as const; - public get context(): UnhydratedContext { + public get context(): FlexTreeContext { return this.simpleContext.flexContext; } diff --git a/packages/dds/tree/src/simple-tree/createContext.ts b/packages/dds/tree/src/simple-tree/createContext.ts index 64d4b51958ec..f693894465c7 100644 --- a/packages/dds/tree/src/simple-tree/createContext.ts +++ b/packages/dds/tree/src/simple-tree/createContext.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. */ +import { defaultSchemaPolicy } from "../feature-libraries/index.js"; import { getOrCreate } from "../util/index.js"; import { Context, UnhydratedContext } from "./core/index.js"; import { normalizeFieldSchema, type ImplicitFieldSchema } from "./schemaTypes.js"; -import { toFlexSchema } from "./toFlexSchema.js"; +import { toStoredSchema } from "./toFlexSchema.js"; const contextCache: WeakMap = new WeakMap(); @@ -17,7 +18,7 @@ export function getUnhydratedContext(schema: ImplicitFieldSchema): Context { return getOrCreate(contextCache, schema, (s) => { const normalized = normalizeFieldSchema(schema); - const flexContext = new UnhydratedContext(toFlexSchema(normalized)); + const flexContext = new UnhydratedContext(defaultSchemaPolicy, toStoredSchema(schema)); return new Context(normalized.allowedTypeSet, flexContext); }); } diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index 39f443ec2fbf..9ecf738d06e4 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -90,7 +90,6 @@ export { type FieldSchemaMetadata, } from "./schemaTypes.js"; export { getOrCreateInnerNode } from "./proxyBinding.js"; -export { toFlexSchema } from "./toFlexSchema.js"; export type { FieldHasDefaultUnsafe, ObjectFromSchemaRecordUnsafe, diff --git a/packages/dds/tree/src/simple-tree/toFlexSchema.ts b/packages/dds/tree/src/simple-tree/toFlexSchema.ts index 2b523d483091..81ef6bd9343c 100644 --- a/packages/dds/tree/src/simple-tree/toFlexSchema.ts +++ b/packages/dds/tree/src/simple-tree/toFlexSchema.ts @@ -57,7 +57,7 @@ type SchemaMap = Map; * * This also has the side effect of populating the cached view schema on the class-based schema. */ -export function toFlexSchema(root: ImplicitFieldSchema): FlexTreeSchema { +function toFlexSchema(root: ImplicitFieldSchema): FlexTreeSchema { const schemaMap: SchemaMap = new Map(); const field = convertField(schemaMap, root); const nodeSchema = new Map( diff --git a/packages/dds/tree/src/test/feature-libraries/chunked-forest/codec/schemaBasedEncoding.spec.ts b/packages/dds/tree/src/test/feature-libraries/chunked-forest/codec/schemaBasedEncoding.spec.ts index fb29a71e7123..6e4c6a6b7ff1 100644 --- a/packages/dds/tree/src/test/feature-libraries/chunked-forest/codec/schemaBasedEncoding.spec.ts +++ b/packages/dds/tree/src/test/feature-libraries/chunked-forest/codec/schemaBasedEncoding.spec.ts @@ -40,7 +40,6 @@ import { TreeCompressionStrategy, cursorForJsonableTreeField, defaultSchemaPolicy, - intoStoredSchema, } from "../../../../feature-libraries/index.js"; import { type JsonCompatibleReadOnly, brand } from "../../../../util/index.js"; import { ajvValidator } from "../../../codec/index.js"; @@ -336,7 +335,7 @@ describe("schemaBasedEncoding", () => { const idCompressor = createIdCompressor( assertIsSessionId("00000000-0000-4000-b000-000000000000"), ); - const storedSchema = intoStoredSchema(schemaData); + const storedSchema = schemaData; const tree = treeFactory(idCompressor); // Check with checkFieldEncode const cache = buildCache(storedSchema, defaultSchemaPolicy, idCompressor); diff --git a/packages/dds/tree/src/test/feature-libraries/default-schema/schemaChecker.spec.ts b/packages/dds/tree/src/test/feature-libraries/default-schema/schemaChecker.spec.ts index 10e306427941..522efa7ce350 100644 --- a/packages/dds/tree/src/test/feature-libraries/default-schema/schemaChecker.spec.ts +++ b/packages/dds/tree/src/test/feature-libraries/default-schema/schemaChecker.spec.ts @@ -15,8 +15,8 @@ import { } from "../../../feature-libraries/default-schema/schemaChecker.js"; import { cursorForJsonableTreeNode, + defaultSchemaPolicy, FieldKinds, - intoStoredSchema, mapTreeFromCursor, } from "../../../feature-libraries/index.js"; import { @@ -504,11 +504,11 @@ describe("schema validation", () => { const mapTrees = testTree .treeFactory(testIdCompressor) .map((j) => mapTreeFromCursor(cursorForJsonableTreeNode(j))); - const schema = intoStoredSchema(testTree.schemaData); + const schema = testTree.schemaData; assert.equal( isFieldInSchema(mapTrees, schema.rootFieldSchema, { schema, - policy: testTree.schemaData.policy, + policy: defaultSchemaPolicy, }), SchemaValidationErrors.NoError, ); diff --git a/packages/dds/tree/src/test/feature-libraries/flex-tree/lazyField.spec.ts b/packages/dds/tree/src/test/feature-libraries/flex-tree/lazyField.spec.ts index 596f2d88a7ce..4154322969ad 100644 --- a/packages/dds/tree/src/test/feature-libraries/flex-tree/lazyField.spec.ts +++ b/packages/dds/tree/src/test/feature-libraries/flex-tree/lazyField.spec.ts @@ -397,7 +397,7 @@ describe("LazySequence", () => { initialTree: content, }); const context = getTreeContext( - schema, + schema.policy, new MockTreeCheckout(forest, { schema: new TreeStoredSchemaRepository(intoStoredSchema(schema)), }), diff --git a/packages/dds/tree/src/test/feature-libraries/flex-tree/utils.ts b/packages/dds/tree/src/test/feature-libraries/flex-tree/utils.ts index 9a5cc2e98571..10fecf3d044b 100644 --- a/packages/dds/tree/src/test/feature-libraries/flex-tree/utils.ts +++ b/packages/dds/tree/src/test/feature-libraries/flex-tree/utils.ts @@ -16,12 +16,12 @@ import { } from "../../../core/index.js"; // eslint-disable-next-line import/no-internal-modules import { type Context, getTreeContext } from "../../../feature-libraries/flex-tree/context.js"; -import { MockNodeKeyManager } from "../../../feature-libraries/index.js"; +import { defaultSchemaPolicy, MockNodeKeyManager } from "../../../feature-libraries/index.js"; import { MockTreeCheckout, forestWithContent } from "../../utils.js"; import { - toFlexSchema, toStoredSchema, type ImplicitFieldSchema, + type InsertableTreeFieldFromImplicitField, } from "../../../simple-tree/index.js"; export function getReadonlyContext( @@ -29,7 +29,7 @@ export function getReadonlyContext( schema: ImplicitFieldSchema, ): Context { return getTreeContext( - toFlexSchema(schema), + defaultSchemaPolicy, new MockTreeCheckout(forest, { schema: new TreeStoredSchemaRepository(toStoredSchema(schema)), }), @@ -61,6 +61,18 @@ export interface TreeSimpleContent { readonly initialTree: readonly ITreeCursorSynchronous[] | ITreeCursorSynchronous | undefined; } +/** + * Content that can populate a `SharedTree`. + */ +export interface TreeSimpleContentTyped { + readonly schema: T; + /** + * Default tree content to initialize the tree with iff the tree is uninitialized + * (meaning it does not even have any schema set at all). + */ + readonly initialTree: InsertableTreeFieldFromImplicitField; +} + /** * Creates a cursor from the provided `context` and moves it to the provided `anchor`. */ diff --git a/packages/dds/tree/src/test/forestTestSuite.ts b/packages/dds/tree/src/test/forestTestSuite.ts index 4a8b67809651..ff262bee5926 100644 --- a/packages/dds/tree/src/test/forestTestSuite.ts +++ b/packages/dds/tree/src/test/forestTestSuite.ts @@ -32,7 +32,6 @@ import { import { typeboxValidator } from "../external-utilities/index.js"; import { cursorForJsonableTreeNode, - intoStoredSchema, jsonableTreeFromCursor, } from "../feature-libraries/index.js"; import { @@ -56,7 +55,7 @@ import { numberSchema, SchemaFactory, stringSchema, - toFlexSchema, + toStoredSchema, } from "../simple-tree/index.js"; import { jsonSequenceRootSchema } from "./sequenceRootUtils.js"; import { cursorToJsonObject, JsonArray, singleJsonCursor } from "./json/index.js"; @@ -111,9 +110,7 @@ export function testForest(config: ForestTestConfiguration): void { const schemaFactory = new SchemaFactory("forest test suite"); const rootSchema = schemaFactory.optional([JsonArray]); - const schema = new TreeStoredSchemaRepository( - intoStoredSchema(toFlexSchema(rootSchema)), - ); + const schema = new TreeStoredSchemaRepository(toStoredSchema(rootSchema)); const forest = factory(schema); @@ -136,9 +133,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("cursor use", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, [singleJsonCursor([1, 2])], @@ -174,18 +169,14 @@ export function testForest(config: ForestTestConfiguration): void { }); it("isEmpty: rootFieldKey", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); assert(forest.isEmpty); initializeForest(forest, [singleJsonCursor([])], testRevisionTagCodec, testIdCompressor); assert(!forest.isEmpty); }); it("isEmpty: other root", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); assert(forest.isEmpty); const insert: DeltaFieldChanges = { @@ -209,9 +200,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("tryMoveCursorToNode", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, @@ -252,9 +241,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("tryMoveCursorToField", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, @@ -297,9 +284,7 @@ export function testForest(config: ForestTestConfiguration): void { describe("moveCursorToPath", () => { it("moves cursor to specified path.", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, [singleJsonCursor([1, 2])], @@ -320,9 +305,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("getCursorAboveDetachedFields", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, [singleJsonCursor([1, 2])], @@ -341,9 +324,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("anchors creation and use", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, [singleJsonCursor([1, 2])], @@ -411,9 +392,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("using an anchor that went away returns NotFound", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); initializeForest( forest, @@ -446,9 +425,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("can destroy detached fields", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); const content: JsonCompatible[] = [1, 2]; initializeForest( forest, @@ -504,9 +481,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("primitive nodes", () => { - const schema = new TreeStoredSchemaRepository( - intoStoredSchema(toFlexSchema(JsonArray)), - ); + const schema = new TreeStoredSchemaRepository(toStoredSchema(JsonArray)); const forest = factory(schema); const content: JsonCompatible[] = [1, true, "test"]; initializeForest( @@ -529,9 +504,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("multiple fields", () => { - const schema = new TreeStoredSchemaRepository( - intoStoredSchema(toFlexSchema(JsonArray)), - ); + const schema = new TreeStoredSchemaRepository(toStoredSchema(JsonArray)); const forest = factory(schema); initializeForest( forest, @@ -549,9 +522,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("with anchors", () => { - const schema = new TreeStoredSchemaRepository( - intoStoredSchema(toFlexSchema(JsonArray)), - ); + const schema = new TreeStoredSchemaRepository(toStoredSchema(JsonArray)); const forest = factory(schema); initializeForest( forest, @@ -746,9 +717,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("remove", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); const content: JsonCompatible[] = [1, 2]; initializeForest( forest, @@ -773,9 +742,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("a skip", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); const content: JsonCompatible[] = [1, 2]; initializeForest( forest, @@ -805,9 +772,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("insert", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); const content: JsonCompatible[] = [1, 2]; initializeForest( forest, @@ -835,9 +800,7 @@ export function testForest(config: ForestTestConfiguration): void { }); it("move-out under transient node", () => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(JsonArray))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(JsonArray))); const moveId = { minor: 1 }; const moveOut: DeltaMark = { @@ -1118,9 +1081,9 @@ export function testForest(config: ForestTestConfiguration): void { x: schemaFactory.optional(schemaFactory.number), y: schemaFactory.optional(schemaFactory.number), }); - const schema = toFlexSchema(schemaFactory.array(NodeSchema)); + const schema = toStoredSchema(schemaFactory.array(NodeSchema)); - const forest = factory(new TreeStoredSchemaRepository(intoStoredSchema(schema))); + const forest = factory(new TreeStoredSchemaRepository(schema)); initializeForest( forest, [cursorFromInsertable(NodeSchema, { x: 2 })], @@ -1160,9 +1123,7 @@ export function testForest(config: ForestTestConfiguration): void { testGeneralPurposeTreeCursor( "forest cursor", (data): ITreeCursor => { - const forest = factory( - new TreeStoredSchemaRepository(intoStoredSchema(toFlexSchema(testTreeSchema))), - ); + const forest = factory(new TreeStoredSchemaRepository(toStoredSchema(testTreeSchema))); initializeForest( forest, [cursorForJsonableTreeNode(data)], diff --git a/packages/dds/tree/src/test/scalableTestTrees.ts b/packages/dds/tree/src/test/scalableTestTrees.ts index cba84a72922e..1ca0d9b6ae54 100644 --- a/packages/dds/tree/src/test/scalableTestTrees.ts +++ b/packages/dds/tree/src/test/scalableTestTrees.ts @@ -13,7 +13,7 @@ import { rootFieldKey, } from "../core/index.js"; import { FieldKinds, isFlexTreeNode, type FlexTreeNode } from "../feature-libraries/index.js"; -import type { CheckoutFlexTreeView, TreeContent } from "../shared-tree/index.js"; +import type { CheckoutFlexTreeView } from "../shared-tree/index.js"; import { brand } from "../util/index.js"; import { cursorFromInsertable, @@ -23,7 +23,7 @@ import { // eslint-disable-next-line import/no-internal-modules import type { TreeStoredContent } from "../shared-tree/schematizeTree.js"; // eslint-disable-next-line import/no-internal-modules -import { toFlexSchema, toStoredSchema } from "../simple-tree/toFlexSchema.js"; +import { toStoredSchema } from "../simple-tree/toFlexSchema.js"; // eslint-disable-next-line import/no-internal-modules import type { TreeSimpleContent } from "./feature-libraries/flex-tree/utils.js"; @@ -67,14 +67,6 @@ export function makeJsDeepTree(depth: number, leafValue: number): JSDeepTree | n return depth === 0 ? leafValue : { foo: makeJsDeepTree(depth - 1, leafValue) }; } -export function makeDeepContent(depth: number, leafValue: number = 1): TreeContent { - const content = makeDeepContentSimple(depth, leafValue); - return { - ...content, - schema: toFlexSchema(content.schema), - }; -} - export function makeDeepContentSimple( depth: number, leafValue: number = 1, @@ -118,23 +110,6 @@ export function makeWideContentWithEndValueSimple( }; } -/** - * - * @param numberOfNodes - number of nodes of the tree - * @param endLeafValue - the value of the end leaf of the tree. If not provided its index is used. - * @returns a tree with specified number of nodes, with the end leaf node set to the endLeafValue - */ -export function makeWideContentWithEndValue( - numberOfNodes: number, - endLeafValue?: number, -): TreeContent { - const content = makeWideContentWithEndValueSimple(numberOfNodes, endLeafValue); - return { - ...content, - schema: toFlexSchema(content.schema), - }; -} - /** * @param numberOfNodes - number of nodes of the tree * @param endLeafValue - the value of the end leaf of the tree. If not provided its index is used. diff --git a/packages/dds/tree/src/test/shared-tree/schematizingTreeView.spec.ts b/packages/dds/tree/src/test/shared-tree/schematizingTreeView.spec.ts index e7190d574c92..16d4e2c6c894 100644 --- a/packages/dds/tree/src/test/shared-tree/schematizingTreeView.spec.ts +++ b/packages/dds/tree/src/test/shared-tree/schematizingTreeView.spec.ts @@ -7,7 +7,7 @@ import { strict as assert } from "assert"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; -import { intoStoredSchema, MockNodeKeyManager } from "../../feature-libraries/index.js"; +import { MockNodeKeyManager } from "../../feature-libraries/index.js"; import { SchematizingSimpleTreeView, // eslint-disable-next-line import/no-internal-modules @@ -19,7 +19,7 @@ import { type InsertableTreeFieldFromImplicitField, } from "../../simple-tree/index.js"; // eslint-disable-next-line import/no-internal-modules -import { toFlexSchema, toStoredSchema } from "../../simple-tree/toFlexSchema.js"; +import { toStoredSchema } from "../../simple-tree/toFlexSchema.js"; import { checkoutWithContent, createTestUndoRedoStacks, @@ -153,7 +153,7 @@ describe("SchematizingSimpleTreeView", () => { view.events.on("schemaChanged", () => log.push(["schemaChanged", getChangeData(view)])); // Modify schema to invalidate view - checkout.updateSchema(intoStoredSchema(toFlexSchema([schema.number, schema.string]))); + checkout.updateSchema(toStoredSchema([schema.number, schema.string])); assert.deepEqual(log, [ ["schemaChanged", "SchemaCompatibilityStatus canView: false canUpgrade: false"], @@ -169,7 +169,7 @@ describe("SchematizingSimpleTreeView", () => { ); view.breaker.clearError(); // Modify schema to be compatible again - checkout.updateSchema(intoStoredSchema(toFlexSchema([schema.number]))); + checkout.updateSchema(toStoredSchema([schema.number])); assert.equal(view.compatibility.isEquivalent, true); assert.equal(view.compatibility.canUpgrade, true); assert.equal(view.compatibility.canView, true); 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 b0ea4e2b6d19..a10f1d0d3d8c 100644 --- a/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts +++ b/packages/dds/tree/src/test/shared-tree/sharedTree.spec.ts @@ -40,7 +40,6 @@ import { TreeCompressionStrategy, TreeStatus, cursorForJsonableTreeNode, - intoStoredSchema, } from "../../feature-libraries/index.js"; import { ObjectForest, @@ -62,7 +61,7 @@ import type { EditManager } from "../../shared-tree-core/index.js"; import { cursorFromInsertable, SchemaFactory, - toFlexSchema, + toStoredSchema, type TreeFieldFromImplicitField, type TreeView, TreeViewConfiguration, @@ -232,7 +231,7 @@ describe("SharedTree", () => { }, }, ]); - expectSchemaEqual(snapshot.schema, intoStoredSchema(toFlexSchema(StringArray))); + expectSchemaEqual(snapshot.schema, toStoredSchema(StringArray)); } }); @@ -254,10 +253,7 @@ describe("SharedTree", () => { // Ensure that the first tree has the state we expect assert.deepEqual([...view.root], [value]); - expectSchemaEqual( - provider.trees[0].storedSchema, - intoStoredSchema(toFlexSchema(StringArray)), - ); + expectSchemaEqual(provider.trees[0].storedSchema, toStoredSchema(StringArray)); // Ensure that the second tree receives the expected state from the first tree await provider.ensureSynchronized(); validateTreeConsistency(provider.trees[0], provider.trees[1]); @@ -279,7 +275,7 @@ describe("SharedTree", () => { await provider.ensureSynchronized(); const loadingTree = await provider.createTree(); validateTreeContent(loadingTree.checkout, { - schema: toFlexSchema(JsonArray), + schema: JsonArray, initialTree: singleJsonCursor([value]), }); }); @@ -533,7 +529,7 @@ describe("SharedTree", () => { view.root.removeAt(0); await provider.ensureSynchronized(); validateTreeContent(loadingTree.checkout, { - schema: toFlexSchema(StringArray), + schema: StringArray, initialTree: singleJsonCursor(["b", "c"]), }); }); @@ -556,7 +552,7 @@ describe("SharedTree", () => { view.root.removeAt(0); validateTreeContent(summarizingTree.checkout, { - schema: toFlexSchema(StringArray), + schema: StringArray, initialTree: singleJsonCursor(["b", "c"]), }); @@ -570,14 +566,14 @@ describe("SharedTree", () => { revertible.revert(); validateTreeContent(summarizingTree.checkout, { - schema: toFlexSchema(StringArray), + schema: StringArray, initialTree: singleJsonCursor(["a", "b", "c"]), }); await provider.ensureSynchronized(); validateTreeContent(loadingTree.checkout, { - schema: toFlexSchema(StringArray), + schema: StringArray, initialTree: singleJsonCursor(["a", "b", "c"]), }); unsubscribe(); @@ -916,7 +912,7 @@ describe("SharedTree", () => { } = createTestUndoRedoStacks(tree2.checkout.events); const initialState = { - schema: toFlexSchema(StringArray), + schema: StringArray, initialTree: singleJsonCursor(["A", "B", "C", "D"]), }; @@ -1070,7 +1066,7 @@ describe("SharedTree", () => { // Validate insertion validateTreeContent(tree2.checkout, { - schema: toFlexSchema(schema), + schema, initialTree: cursorFromInsertable(schema, [["a"]]), }); @@ -1675,7 +1671,7 @@ describe("SharedTree", () => { await provider.ensureSynchronized(); assert.deepEqual([...view1.root], [value1]); - expectSchemaEqual(tree2.storedSchema, intoStoredSchema(toFlexSchema(StringArray))); + expectSchemaEqual(tree2.storedSchema, toStoredSchema(StringArray)); validateTreeConsistency(tree1, tree2); }); @@ -1720,17 +1716,17 @@ describe("SharedTree", () => { new TreeViewConfiguration({ schema: StringArray, enableSchemaValidation }), ); view.initialize([]); - expectSchemaEqual(tree.storedSchema, intoStoredSchema(toFlexSchema(StringArray))); + expectSchemaEqual(tree.storedSchema, toStoredSchema(StringArray)); tree .viewWith(new TreeViewConfiguration({ schema: JsonArray, enableSchemaValidation })) .upgradeSchema(); - expectSchemaEqual(tree.storedSchema, intoStoredSchema(toFlexSchema(JsonArray))); + expectSchemaEqual(tree.storedSchema, toStoredSchema(JsonArray)); const revertible = undoStack.pop(); revertible?.revert(); - expectSchemaEqual(tree.storedSchema, intoStoredSchema(toFlexSchema(StringArray))); + expectSchemaEqual(tree.storedSchema, toStoredSchema(StringArray)); }); }); @@ -1763,14 +1759,8 @@ describe("SharedTree", () => { await provider.ensureSynchronized(); const otherLoadedTree = provider.trees[1]; - expectSchemaEqual( - tree.contentSnapshot().schema, - intoStoredSchema(toFlexSchema(StringArray)), - ); - expectSchemaEqual( - otherLoadedTree.storedSchema, - intoStoredSchema(toFlexSchema(StringArray)), - ); + expectSchemaEqual(tree.contentSnapshot().schema, toStoredSchema(StringArray)); + expectSchemaEqual(otherLoadedTree.storedSchema, toStoredSchema(StringArray)); }); }); diff --git a/packages/dds/tree/src/test/shared-tree/summary.bench.ts b/packages/dds/tree/src/test/shared-tree/summary.bench.ts index 82a46b4127c6..3435ed790e77 100644 --- a/packages/dds/tree/src/test/shared-tree/summary.bench.ts +++ b/packages/dds/tree/src/test/shared-tree/summary.bench.ts @@ -22,10 +22,17 @@ import { MockStorage, } from "@fluidframework/test-runtime-utils/internal"; -import { AllowedUpdateType } from "../../core/index.js"; -import { SharedTreeFactory, type TreeContent } from "../../shared-tree/index.js"; -import { makeDeepContent, makeWideContentWithEndValue } from "../scalableTestTrees.js"; -import { TestTreeProviderLite, schematizeFlexTree, testIdCompressor } from "../utils.js"; +import { SharedTreeFactory } from "../../shared-tree/index.js"; +import { TestTreeProviderLite, testIdCompressor } from "../utils.js"; +import { TreeViewConfiguration, type ImplicitFieldSchema } from "../../simple-tree/index.js"; +// eslint-disable-next-line import/no-internal-modules +import type { TreeSimpleContentTyped } from "../feature-libraries/flex-tree/utils.js"; +import { + LinkedList, + makeJsDeepTree, + makeJsWideTreeWithEndValue, + WideRoot, +} from "../scalableTestTrees.js"; // TODO: these tests currently only cover tree content. // It might make sense to extend them to cover complex collaboration windows. @@ -74,7 +81,10 @@ describe("Summary benchmarks", () => { type: BenchmarkType.Measurement, title: `a wide tree with ${numberOfNodes} nodes.`, run: async (reporter) => { - const summaryTree = getSummaryTree(makeWideContentWithEndValue(numberOfNodes, 1)); + const summaryTree = getSummaryTree({ + initialTree: makeJsWideTreeWithEndValue(numberOfNodes, 1), + schema: WideRoot, + }); processSummary(summaryTree, reporter, minLength, maxLength); }, }); @@ -85,7 +95,12 @@ describe("Summary benchmarks", () => { type: BenchmarkType.Measurement, title: `a deep tree with ${numberOfNodes} nodes.`, run: async (reporter) => { - const summaryTree = getSummaryTree(makeDeepContent(numberOfNodes)); + const summaryTree = getSummaryTree({ + // Types do not allow implicitly constructing recursive types, so cast is required. + // TODO: Find a better alternative. + initialTree: makeJsDeepTree(numberOfNodes, 1) as LinkedList, + schema: LinkedList, + }); processSummary(summaryTree, reporter, minLength, maxLength); }, }); @@ -93,7 +108,11 @@ describe("Summary benchmarks", () => { }); describe("load speed of", () => { - function runSummaryBenchmark(title: string, content: TreeContent, type: BenchmarkType) { + function runSummaryBenchmark( + title: string, + content: TreeSimpleContentTyped, + type: BenchmarkType, + ) { let summaryTree: ITree; const factory = new SharedTreeFactory(); benchmark({ @@ -125,7 +144,12 @@ describe("Summary benchmarks", () => { ]) { runSummaryBenchmark( `a deep tree with ${nodeCount} nodes}`, - makeDeepContent(nodeCount), + { + // Types do not allow implicitly constructing recursive types, so cast is required. + // TODO: Find a better alternative. + initialTree: makeJsDeepTree(nodeCount, 1) as LinkedList, + schema: LinkedList, + }, type, ); } @@ -136,7 +160,10 @@ describe("Summary benchmarks", () => { ]) { runSummaryBenchmark( `a wide tree with ${nodeCount} nodes}`, - makeWideContentWithEndValue(nodeCount, 1), + { + initialTree: makeJsWideTreeWithEndValue(nodeCount, 1), + schema: WideRoot, + }, type, ); } @@ -147,13 +174,13 @@ describe("Summary benchmarks", () => { * @param content - content to full the tree with * @returns the tree's summary */ -function getSummaryTree(content: TreeContent): ISummaryTree { +function getSummaryTree( + content: TreeSimpleContentTyped, +): ISummaryTree { const provider = new TestTreeProviderLite(); const tree = provider.trees[0]; - schematizeFlexTree(tree, { - ...content, - allowedSchemaModifications: AllowedUpdateType.Initialize, - }); + const view = tree.viewWith(new TreeViewConfiguration({ schema: content.schema })); + view.initialize(content.initialTree); provider.processMessages(); const { summary } = tree.getAttachSummary(true); return summary; diff --git a/packages/dds/tree/src/test/shared-tree/treeCheckout.spec.ts b/packages/dds/tree/src/test/shared-tree/treeCheckout.spec.ts index 4310e6b03597..fe38d27e95b4 100644 --- a/packages/dds/tree/src/test/shared-tree/treeCheckout.spec.ts +++ b/packages/dds/tree/src/test/shared-tree/treeCheckout.spec.ts @@ -20,11 +20,7 @@ import { EmptyKey, type RevertibleFactory, } from "../../core/index.js"; -import { - FieldKinds, - cursorForJsonableTreeField, - intoStoredSchema, -} from "../../feature-libraries/index.js"; +import { FieldKinds, cursorForJsonableTreeField } from "../../feature-libraries/index.js"; import { getBranch, Tree, @@ -52,7 +48,7 @@ import { import { getOrCreateInnerNode } from "../../simple-tree/proxyBinding.js"; // eslint-disable-next-line import/no-internal-modules import { SchematizingSimpleTreeView } from "../../shared-tree/schematizingTreeView.js"; -import { toFlexSchema } from "../../simple-tree/index.js"; +import { toStoredSchema } from "../../simple-tree/index.js"; // eslint-disable-next-line import/no-internal-modules import { stringSchema } from "../../simple-tree/leafNodeSchema.js"; @@ -166,7 +162,7 @@ describe("sharedTreeView", () => { assert.equal(log.length, 0); - checkout.updateSchema(intoStoredSchema(toFlexSchema(mixedSchema))); + checkout.updateSchema(toStoredSchema(mixedSchema)); assert.equal(log.length, 1); @@ -179,7 +175,7 @@ describe("sharedTreeView", () => { assert.equal(log.length, 2); - checkout.updateSchema(intoStoredSchema(toFlexSchema(OptionalString))); + checkout.updateSchema(toStoredSchema(OptionalString)); assert.equal(log.length, 3); unsubscribe(); @@ -196,14 +192,14 @@ describe("sharedTreeView", () => { assert.deepEqual(log, []); - checkout.updateSchema(intoStoredSchema(toFlexSchema(mixedSchema))); + checkout.updateSchema(toStoredSchema(mixedSchema)); checkout.editor .optionalField(rootField) .set( cursorForJsonableTreeField([{ type: brand(stringSchema.identifier), value: "A" }]), true, ); - checkout.updateSchema(intoStoredSchema(toFlexSchema(OptionalString))); + checkout.updateSchema(toStoredSchema(OptionalString)); assert.deepEqual(log, ["not-revertible", "revertible", "not-revertible"]); unsubscribe(); @@ -864,7 +860,7 @@ describe("sharedTreeView", () => { ); view1.initialize(["A", 1, "B", 2]); - const storedSchema1 = intoStoredSchema(toFlexSchema(schema1)); + const storedSchema1 = toStoredSchema(schema1); provider.processMessages(); const checkout1Revertibles = createTestUndoRedoStacks(view1.checkout.events); @@ -894,9 +890,7 @@ describe("sharedTreeView", () => { assert.equal(view2.checkout.getRemovedRoots().length, 2); const sf2 = new SchemaFactory("schema2"); - provider.trees[0].checkout.updateSchema( - intoStoredSchema(toFlexSchema(sf2.array(sf1.number))), - ); + provider.trees[0].checkout.updateSchema(toStoredSchema(sf2.array(sf1.number))); // The undo stack contains the removal of A but not the schema change assert.equal(checkout1Revertibles.undoStack.length, 1); @@ -945,10 +939,7 @@ describe("sharedTreeView", () => { view2.root.removeAt(2); assert(view2 instanceof SchematizingSimpleTreeView); - expectSchemaEqual( - intoStoredSchema(toFlexSchema(newSchema)), - view2.checkout.storedSchema, - ); + expectSchemaEqual(toStoredSchema(newSchema), view2.checkout.storedSchema); assert.deepEqual(view2.root, ["A", "B"]); // Rebase the child branch onto the parent branch. @@ -956,10 +947,7 @@ describe("sharedTreeView", () => { // The schema change and any changes after that should be dropped, // but the changes before the schema change should be preserved - expectSchemaEqual( - intoStoredSchema(toFlexSchema(oldSchema)), - view1.checkout.storedSchema, - ); + expectSchemaEqual(toStoredSchema(oldSchema), view1.checkout.storedSchema); assert.deepEqual(view1.root, ["B", "C"]); }); @@ -1009,14 +997,14 @@ describe("sharedTreeView", () => { view3.upgradeSchema(); view3.root.removeAt(0); - expectSchemaEqual(intoStoredSchema(toFlexSchema(schema2)), view2.checkout.storedSchema); - expectSchemaEqual(intoStoredSchema(toFlexSchema(schema3)), view3.checkout.storedSchema); + expectSchemaEqual(toStoredSchema(schema2), view2.checkout.storedSchema); + expectSchemaEqual(toStoredSchema(schema3), view3.checkout.storedSchema); // Rebase view3 onto view2. (view3.checkout as ITreeCheckoutFork).rebaseOnto(view2.checkout); // All changes on view3 should be dropped but the schema change and edit in view2 should be preserved. - expectSchemaEqual(intoStoredSchema(toFlexSchema(schema2)), view2.checkout.storedSchema); + expectSchemaEqual(toStoredSchema(schema2), view2.checkout.storedSchema); assert.deepEqual(view2.root, ["B"]); }); }); diff --git a/packages/dds/tree/src/test/simple-tree/toFlexSchema.spec.ts b/packages/dds/tree/src/test/simple-tree/toFlexSchema.spec.ts index 5983520eee63..d38d08edabda 100644 --- a/packages/dds/tree/src/test/simple-tree/toFlexSchema.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/toFlexSchema.spec.ts @@ -7,20 +7,20 @@ import { strict as assert } from "node:assert"; import { SchemaFactory } from "../../simple-tree/index.js"; // eslint-disable-next-line import/no-internal-modules -import { toFlexSchema } from "../../simple-tree/toFlexSchema.js"; +import { toStoredSchema } from "../../simple-tree/toFlexSchema.js"; describe("toFlexSchema", () => { it("minimal", () => { const schema = new SchemaFactory("com.example"); class A extends schema.object("A", {}) {} - toFlexSchema(A); + toStoredSchema(A); }); it("name collision", () => { const schema = new SchemaFactory("com.example"); class A extends schema.object("A", {}) {} class B extends schema.object("A", {}) {} - assert.throws(() => toFlexSchema([A, B]), /identifier "com.example.A"/); + assert.throws(() => toStoredSchema([A, B]), /identifier "com.example.A"/); }); it("builtins are the same", () => { const schema = new SchemaFactory("com.example"); diff --git a/packages/dds/tree/src/test/simple-tree/utils.ts b/packages/dds/tree/src/test/simple-tree/utils.ts index 012ccdc30673..da80e97af97f 100644 --- a/packages/dds/tree/src/test/simple-tree/utils.ts +++ b/packages/dds/tree/src/test/simple-tree/utils.ts @@ -8,6 +8,7 @@ import { initializeForest, TreeStoredSchemaRepository } from "../../core/index.j import { buildForest, cursorForMapTreeNode, + defaultSchemaPolicy, getSchemaAndPolicy, MockNodeKeyManager, } from "../../feature-libraries/index.js"; @@ -31,7 +32,7 @@ import { // eslint-disable-next-line import/no-internal-modules } from "../../simple-tree/proxies.js"; // eslint-disable-next-line import/no-internal-modules -import { toFlexSchema, toStoredSchema } from "../../simple-tree/toFlexSchema.js"; +import { toStoredSchema } from "../../simple-tree/toFlexSchema.js"; import { mintRevisionTag, testIdCompressor, testRevisionTagCodec } from "../utils.js"; import type { TreeCheckout } from "../../shared-tree/index.js"; // eslint-disable-next-line import/no-internal-modules @@ -122,7 +123,7 @@ export function hydrate( schema: new TreeStoredSchemaRepository(toStoredSchema(schema)), }); const manager = new MockNodeKeyManager(); - const checkout = new CheckoutFlexTreeView(branch, toFlexSchema(schema), manager); + const checkout = new CheckoutFlexTreeView(branch, defaultSchemaPolicy, manager); const field = checkout.flexTree; branch.forest.anchors.slots.set( SimpleContextSlot, diff --git a/packages/dds/tree/src/test/snapshots/schema.spec.ts b/packages/dds/tree/src/test/snapshots/schema.spec.ts index d8531d68004b..fe8bd6ef2421 100644 --- a/packages/dds/tree/src/test/snapshots/schema.spec.ts +++ b/packages/dds/tree/src/test/snapshots/schema.spec.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { encodeTreeSchema, intoStoredSchema } from "../../feature-libraries/index.js"; +import { encodeTreeSchema } from "../../feature-libraries/index.js"; import { testTrees } from "../testTrees.js"; import { takeJsonSnapshot, useSnapshotDirectory } from "./snapshotTools.js"; @@ -13,7 +13,7 @@ describe("schema snapshots", () => { for (const { name, schemaData } of testTrees) { it(name, () => { - const encoded = encodeTreeSchema(intoStoredSchema(schemaData)); + const encoded = encodeTreeSchema(schemaData); takeJsonSnapshot(encoded); }); } diff --git a/packages/dds/tree/src/test/testTrees.ts b/packages/dds/tree/src/test/testTrees.ts index 03650906584b..4630e5ea63ae 100644 --- a/packages/dds/tree/src/test/testTrees.ts +++ b/packages/dds/tree/src/test/testTrees.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { strict as assert } from "assert"; +import { strict as assert, fail } from "assert"; import { MockHandle } from "@fluidframework/test-runtime-utils/internal"; @@ -11,26 +11,29 @@ import { type ITreeCursorSynchronous, type JsonableTree, Multiplicity, + type TreeStoredSchema, } from "../core/index.js"; import { FieldKinds, + type FlexFieldKind, FlexFieldSchema, FlexObjectNodeSchema, - type FlexTreeSchema, type FullSchemaPolicy, cursorForJsonableTreeNode, defaultSchemaPolicy, + fieldKinds, jsonableTreeFromCursor, } from "../feature-libraries/index.js"; -import type { TreeContent } from "../shared-tree/index.js"; +import type { TreeStoredContent } from "../shared-tree/index.js"; import type { IIdCompressor } from "@fluidframework/id-compressor"; import { cursorFromInsertable, getFlexSchema, + getStoredSchema, numberSchema, SchemaFactory, stringSchema, - toFlexSchema, + toStoredSchema, type ImplicitFieldSchema, type InsertableTreeFieldFromImplicitField, type ValidateRecursiveSchema, @@ -44,7 +47,7 @@ import type { Partial } from "@sinclair/typebox"; interface TestTree { readonly name: string; - readonly schemaData: FlexTreeSchema; + readonly schemaData: TreeStoredSchema; readonly policy: FullSchemaPolicy; readonly treeFactory: (idCompressor?: IIdCompressor) => JsonableTree[]; } @@ -57,12 +60,12 @@ function testSimpleTree( const cursor = cursorFromInsertable(schema, rootNode); return test( name, - toFlexSchema(schema), + toStoredSchema(schema), cursor === undefined ? [] : [jsonableTreeFromCursor(cursor)], ); } -function test(name: string, schemaData: FlexTreeSchema, data: JsonableTree[]): TestTree { +function test(name: string, schemaData: TreeStoredSchema, data: JsonableTree[]): TestTree { return { name, schemaData, @@ -73,9 +76,9 @@ function test(name: string, schemaData: FlexTreeSchema, data: JsonableTree[]): T function cursorsToFieldContent( cursors: readonly ITreeCursorSynchronous[], - schema: FlexFieldSchema, + schema: FlexFieldKind, ): readonly ITreeCursorSynchronous[] | ITreeCursorSynchronous | undefined { - if (schema.kind.multiplicity === Multiplicity.Sequence) { + if (schema.multiplicity === Multiplicity.Sequence) { return cursors; } if (cursors.length === 1) { @@ -85,12 +88,12 @@ function cursorsToFieldContent( return undefined; } -export function treeContentFromTestTree(testData: TestTree): TreeContent { +export function treeContentFromTestTree(testData: TestTree): TreeStoredContent { return { schema: testData.schemaData, initialTree: cursorsToFieldContent( testData.treeFactory().map(cursorForJsonableTreeNode), - testData.schemaData.rootFieldSchema, + fieldKinds.get(testData.schemaData.rootFieldSchema.kind) ?? fail("missing kind"), ), }; } @@ -133,13 +136,11 @@ export class RecursiveType extends factory.objectRecursive("recursiveType", { const library = { nodeSchema: new Map([ - [brand(Minimal.identifier), getFlexSchema(Minimal)], - [allTheFields.name, allTheFields], - [brand(factory.number.identifier), getFlexSchema(factory.number)], + [brand(Minimal.identifier), getStoredSchema(Minimal)], + [allTheFields.name, allTheFields.stored], + [brand(factory.number.identifier), getStoredSchema(factory.number)], ]), - policy: defaultSchemaPolicy, - adapters: {}, -} satisfies Partial; +} satisfies Partial; export const testTrees: readonly TestTree[] = [ testSimpleTree("empty", factory.optional([]), undefined), @@ -150,16 +151,16 @@ export const testTrees: readonly TestTree[] = [ test( "numericSequence", { - ...toFlexSchema(factory.number), + ...toStoredSchema(factory.number), rootFieldSchema: FlexFieldSchema.create(FieldKinds.sequence, [ getFlexSchema(numberSchema), - ]), + ]).stored, }, jsonableTreesFromFieldCursor(fieldJsonCursor([1, 2, 3])), ), { name: "node-with-identifier-field", - schemaData: toFlexSchema(HasIdentifierField), + schemaData: toStoredSchema(HasIdentifierField), treeFactory: (idCompressor?: IIdCompressor) => { assert(idCompressor !== undefined, "idCompressor must be provided"); const id = idCompressor.decompress(idCompressor.generateCompressedId()); @@ -169,7 +170,7 @@ export const testTrees: readonly TestTree[] = [ }, { name: "identifier-field", - schemaData: toFlexSchema(factory.identifier), + schemaData: toStoredSchema(factory.identifier), treeFactory: (idCompressor?: IIdCompressor) => { assert(idCompressor !== undefined, "idCompressor must be provided"); const id = idCompressor.decompress(idCompressor.generateCompressedId()); @@ -187,7 +188,7 @@ export const testTrees: readonly TestTree[] = [ "allTheFields-minimal", { ...library, - rootFieldSchema: FlexFieldSchema.create(FieldKinds.required, [allTheFields]), + rootFieldSchema: FlexFieldSchema.create(FieldKinds.required, [allTheFields]).stored, }, [ { @@ -200,7 +201,7 @@ export const testTrees: readonly TestTree[] = [ "allTheFields-full", { ...library, - rootFieldSchema: FlexFieldSchema.create(FieldKinds.required, [allTheFields]), + rootFieldSchema: FlexFieldSchema.create(FieldKinds.required, [allTheFields]).stored, }, [ { diff --git a/packages/dds/tree/src/test/utils.ts b/packages/dds/tree/src/test/utils.ts index 7542b2db1f53..b1f92b69305d 100644 --- a/packages/dds/tree/src/test/utils.ts +++ b/packages/dds/tree/src/test/utils.ts @@ -91,13 +91,10 @@ import { import type { HasListeners, IEmitter, Listenable } from "../events/index.js"; import { typeboxValidator } from "../external-utilities/index.js"; import { - type FlexFieldSchema, type NodeKeyManager, - ViewSchema, buildForest, cursorForMapTreeNode, defaultSchemaPolicy, - intoStoredSchema, jsonableTreeFromFieldCursor, jsonableTreeFromForest, mapRootChanges, @@ -113,7 +110,6 @@ import { CheckoutFlexTreeView, type ISharedTree, type ITreeCheckout, - type InitializeAndSchematizeConfiguration, SharedTree, type SharedTreeContentSnapshot, SharedTreeFactory, @@ -125,10 +121,9 @@ import { type ITreeCheckoutFork, } from "../shared-tree/index.js"; // eslint-disable-next-line import/no-internal-modules -import { ensureSchema, type TreeStoredContent } from "../shared-tree/schematizeTree.js"; +import type { TreeStoredContent } from "../shared-tree/schematizeTree.js"; import { SchematizingSimpleTreeView, - requireSchema, // eslint-disable-next-line import/no-internal-modules } from "../shared-tree/schematizingTreeView.js"; // eslint-disable-next-line import/no-internal-modules @@ -141,7 +136,6 @@ import { toStoredSchema, type TreeViewEvents, type TreeView, - toFlexSchema, } from "../simple-tree/index.js"; import { type JsonCompatible, @@ -581,13 +575,22 @@ export function validateFuzzTreeConsistency( ); } -function contentToJsonableTree(content: TreeContent): JsonableTree[] { +function contentToJsonableTree( + content: TreeSimpleContent | TreeStoredContent, +): JsonableTree[] { return jsonableTreeFromFieldCursor(normalizeNewFieldContent(content.initialTree)); } -export function validateTreeContent(tree: ITreeCheckout, content: TreeContent): void { +export function validateTreeContent(tree: ITreeCheckout, content: TreeSimpleContent): void { assert.deepEqual(toJsonableTree(tree), contentToJsonableTree(content)); - expectSchemaEqual(tree.storedSchema, intoStoredSchema(content.schema)); + expectSchemaEqual(tree.storedSchema, toStoredSchema(content.schema)); +} +export function validateTreeStoredContent( + tree: ITreeCheckout, + content: TreeStoredContent, +): void { + assert.deepEqual(toJsonableTree(tree), contentToJsonableTree(content)); + expectSchemaEqual(tree.storedSchema, content.schema); } export function expectSchemaEqual( @@ -740,7 +743,7 @@ export function flexTreeViewWithContent( ); return new CheckoutFlexTreeView( view, - toFlexSchema(content.schema), + defaultSchemaPolicy, args?.nodeKeyManager ?? new MockNodeKeyManager(), ); } @@ -1104,34 +1107,6 @@ export function mintRevisionTag(): RevisionTag { export const testRevisionTagCodec = new RevisionTagCodec(testIdCompressor); -/** - * Like {@link ITree.viewWith}, but uses the flex-tree schema system and exposes the tree as a flex-tree. - */ -export function schematizeFlexTree( - tree: SharedTree, - config: InitializeAndSchematizeConfiguration, - onDispose?: () => void, - nodeKeyManager?: NodeKeyManager, -): CheckoutFlexTreeView { - const viewSchema = new ViewSchema(defaultSchemaPolicy, {}, intoStoredSchema(config.schema)); - if ( - !ensureSchema(viewSchema, config.allowedSchemaModifications, tree.checkout, { - initialTree: config.initialTree, - schema: viewSchema.storedSchema, - }) - ) { - assert.fail("Schematize failed"); - } - - return requireSchema( - tree.checkout, - viewSchema, - onDispose ?? (() => {}), - nodeKeyManager ?? new MockNodeKeyManager(), - config.schema, - ); -} - // Session ids used for the created trees' IdCompressors must be deterministic. // TestTreeProviderLite does this by default. // Test trees which manually create their data store runtime must set up their trees'