Skip to content

Commit

Permalink
tree: Make SchematizingSimpleTreeView implement TreeViewAlpha (#22953)
Browse files Browse the repository at this point in the history
## Description

Make SchematizingSimpleTreeView implement TreeViewAlpha, and add some
helper types.
  • Loading branch information
CraigMacomber authored Oct 31, 2024
1 parent 62ae4c5 commit 512e53f
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 47 deletions.
14 changes: 11 additions & 3 deletions packages/dds/tree/api-report/tree.alpha.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export enum ForestType {
export function getBranch(tree: ITree): TreeBranch;

// @alpha
export function getBranch<T extends ImplicitFieldSchema>(view: TreeView<T>): TreeBranch;
export function getBranch<T extends ImplicitFieldSchema | UnsafeUnknownSchema>(view: TreeViewAlpha<T>): TreeBranch;

// @alpha
export function getJsonSchema(schema: ImplicitFieldSchema): JsonTreeSchema;
Expand Down Expand Up @@ -447,6 +447,9 @@ export type Off = () => void;
// @alpha
export type PopUnion<Union, AsOverloadedFunction = UnionToIntersection<Union extends unknown ? (f: Union) => void : never>> = AsOverloadedFunction extends (a: infer First) => void ? First : never;

// @alpha
export type ReadableField<TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> = TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeLeafValue | TreeNode;

// @public @sealed
export interface ReadonlyArrayNode<out T = TreeNode | TreeLeafValue> extends ReadonlyArray<T>, Awaited<TreeNode & WithType<string, NodeKind.Array>> {
}
Expand All @@ -467,6 +470,11 @@ interface ReadonlyMapInlined<K, T extends Unenforced<ImplicitAllowedTypes>> {
values(): IterableIterator<TreeNodeFromImplicitAllowedTypesUnsafe<T>>;
}

// @alpha
export type ReadSchema<TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> = [
TSchema
] extends [ImplicitFieldSchema] ? TSchema : ImplicitFieldSchema;

// @public @deprecated
export type RestrictiveReadonlyRecord<K extends symbol | string, T> = {
readonly [P in symbol | string]: P extends K ? T : never;
Expand Down Expand Up @@ -793,11 +801,11 @@ export interface TreeView<in out TSchema extends ImplicitFieldSchema> extends ID
}

// @alpha
export interface TreeViewAlpha<in out TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> extends Omit<TreeView<TSchema extends ImplicitFieldSchema ? TSchema : ImplicitFieldSchema>, "root" | "initialize"> {
export interface TreeViewAlpha<in out TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> extends Omit<TreeView<ReadSchema<TSchema>>, "root" | "initialize"> {
// (undocumented)
initialize(content: InsertableField<TSchema>): void;
// (undocumented)
get root(): TSchema extends ImplicitFieldSchema ? TreeFieldFromImplicitField<TSchema> : TreeLeafValue | TreeNode;
get root(): ReadableField<TSchema>;
set root(newRoot: InsertableField<TSchema>);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/dds/tree/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ export {
type InsertableContent,
type FactoryContent,
type FactoryContentObject,
type ReadableField,
type ReadSchema,
// test recursive schema for checking that d.ts files handles schema correctly
test_RecursiveObject,
test_RecursiveObject_base,
Expand Down
24 changes: 14 additions & 10 deletions packages/dds/tree/src/shared-tree/schematizingTreeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import {
type FieldSchema,
type ImplicitFieldSchema,
type SchemaCompatibilityStatus,
type InsertableTreeFieldFromImplicitField,
type TreeFieldFromImplicitField,
type TreeView,
type TreeViewEvents,
getTreeNodeForField,
Expand All @@ -43,6 +41,11 @@ import {
prepareContentForHydration,
comparePersistedSchemaInternal,
toStoredSchema,
type TreeViewAlpha,
type InsertableField,
type ReadableField,
type ReadSchema,
type UnsafeUnknownSchema,
} from "../simple-tree/index.js";
import { Breakable, breakingClass, disposeSymbol, type WithBreakable } from "../util/index.js";

Expand All @@ -60,8 +63,9 @@ export const ViewSlot = anchorSlot<TreeView<ImplicitFieldSchema>>();
* Implementation of TreeView wrapping a FlexTreeView.
*/
@breakingClass
export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitFieldSchema>
implements TreeView<TRootSchema>, WithBreakable
export class SchematizingSimpleTreeView<
in out TRootSchema extends ImplicitFieldSchema | UnsafeUnknownSchema,
> implements TreeViewAlpha<TRootSchema>, WithBreakable
{
/**
* The view is set to undefined when this object is disposed or the view schema does not support viewing the document's stored schema.
Expand Down Expand Up @@ -96,7 +100,7 @@ export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitField

public constructor(
public readonly checkout: TreeCheckout,
public readonly config: TreeViewConfiguration<TRootSchema>,
public readonly config: TreeViewConfiguration<ReadSchema<TRootSchema>>,
public readonly nodeKeyManager: NodeKeyManager,
public readonly breaker: Breakable = new Breakable("SchematizingSimpleTreeView"),
private readonly onDispose?: () => void,
Expand Down Expand Up @@ -130,11 +134,11 @@ export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitField
);
}

public get schema(): TRootSchema {
public get schema(): ReadSchema<TRootSchema> {
return this.config.schema;
}

public initialize(content: InsertableTreeFieldFromImplicitField<TRootSchema>): void {
public initialize(content: InsertableField<TRootSchema>): void {
this.ensureUndisposed();

const compatibility = this.compatibility;
Expand Down Expand Up @@ -340,18 +344,18 @@ export class SchematizingSimpleTreeView<in out TRootSchema extends ImplicitField
this.onDispose?.();
}

public get root(): TreeFieldFromImplicitField<TRootSchema> {
public get root(): ReadableField<TRootSchema> {
this.breaker.use();
if (!this.compatibility.canView) {
throw new UsageError(
"Document is out of schema. Check TreeView.compatibility before accessing TreeView.root.",
);
}
const view = this.getView();
return getTreeNodeForField(view.flexTree) as TreeFieldFromImplicitField<TRootSchema>;
return getTreeNodeForField(view.flexTree) as ReadableField<TRootSchema>;
}

public set root(newRoot: InsertableTreeFieldFromImplicitField<TRootSchema>) {
public set root(newRoot: InsertableField<TRootSchema>) {
this.breaker.use();
if (!this.compatibility.canView) {
throw new UsageError(
Expand Down
26 changes: 21 additions & 5 deletions packages/dds/tree/src/shared-tree/sharedTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ import {
import type {
ITree,
ImplicitFieldSchema,
ReadSchema,
TreeView,
TreeViewAlpha,
TreeViewConfiguration,
UnsafeUnknownSchema,
} from "../simple-tree/index.js";

import { SchematizingSimpleTreeView } from "./schematizingTreeView.js";
Expand Down Expand Up @@ -324,10 +327,21 @@ export class SharedTree
}
}

// For the new TreeViewAlpha API
public viewWith<TRoot extends ImplicitFieldSchema | UnsafeUnknownSchema>(
config: TreeViewConfiguration<ReadSchema<TRoot>>,
): SchematizingSimpleTreeView<TRoot> & TreeView<ReadSchema<TRoot>>;

// For the old TreeView API
public viewWith<TRoot extends ImplicitFieldSchema>(
config: TreeViewConfiguration<TRoot>,
): SchematizingSimpleTreeView<TRoot> {
return this.checkout.viewWith(config);
): SchematizingSimpleTreeView<TRoot> & TreeView<TRoot>;

public viewWith<TRoot extends ImplicitFieldSchema | UnsafeUnknownSchema>(
config: TreeViewConfiguration<ReadSchema<TRoot>>,
): SchematizingSimpleTreeView<TRoot> & TreeView<ReadSchema<TRoot>> {
return this.checkout.viewWith(config) as SchematizingSimpleTreeView<TRoot> &
TreeView<ReadSchema<TRoot>>;
}

protected override async loadCore(services: IChannelStorageService): Promise<void> {
Expand All @@ -353,9 +367,11 @@ export function getBranch(tree: ITree): TreeBranch;
* but it (or something like it) is necessary in the meantime to prevent the alpha types from being exposed as public.
* @alpha
*/
export function getBranch<T extends ImplicitFieldSchema>(view: TreeView<T>): TreeBranch;
export function getBranch<T extends ImplicitFieldSchema>(
treeOrView: ITree | TreeView<T>,
export function getBranch<T extends ImplicitFieldSchema | UnsafeUnknownSchema>(
view: TreeViewAlpha<T>,
): TreeBranch;
export function getBranch<T extends ImplicitFieldSchema | UnsafeUnknownSchema>(
treeOrView: ITree | TreeViewAlpha<T>,
): TreeBranch {
assert(
treeOrView instanceof SharedTree || treeOrView instanceof SchematizingSimpleTreeView,
Expand Down
12 changes: 12 additions & 0 deletions packages/dds/tree/src/shared-tree/treeCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ import type { ISharedTreeEditor, SharedTreeEditBuilder } from "./sharedTreeEditB
import type { IDisposable } from "@fluidframework/core-interfaces";
import type {
ImplicitFieldSchema,
ReadSchema,
TreeView,
TreeViewConfiguration,
UnsafeUnknownSchema,
ViewableTree,
} from "../simple-tree/index.js";
import { SchematizingSimpleTreeView } from "./schematizingTreeView.js";
Expand Down Expand Up @@ -622,8 +624,18 @@ export class TreeCheckout implements ITreeCheckoutFork {
}
}

// For the new TreeViewAlpha API
public viewWith<TRoot extends ImplicitFieldSchema | UnsafeUnknownSchema>(
config: TreeViewConfiguration<ReadSchema<TRoot>>,
): SchematizingSimpleTreeView<TRoot>;

// For the old TreeView API
public viewWith<TRoot extends ImplicitFieldSchema>(
config: TreeViewConfiguration<TRoot>,
): TreeView<TRoot>;

public viewWith<TRoot extends ImplicitFieldSchema | UnsafeUnknownSchema>(
config: TreeViewConfiguration<ReadSchema<TRoot>>,
): SchematizingSimpleTreeView<TRoot> {
const view = new SchematizingSimpleTreeView(
this,
Expand Down
14 changes: 5 additions & 9 deletions packages/dds/tree/src/simple-tree/api/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import {
type ImplicitFieldSchema,
type InsertableField,
type InsertableTreeFieldFromImplicitField,
type ReadableField,
type ReadSchema,
type TreeFieldFromImplicitField,
type TreeLeafValue,
type UnsafeUnknownSchema,
FieldKind,
} from "../schemaTypes.js";
import { NodeKind, type TreeNode, type TreeNodeSchema } from "../core/index.js";
import { NodeKind, type TreeNodeSchema } from "../core/index.js";
import { toStoredSchema } from "../toStoredSchema.js";
import { LeafNodeSchema } from "../leafNodeSchema.js";
import { assert } from "@fluidframework/core-utils/internal";
Expand Down Expand Up @@ -434,13 +435,8 @@ export interface TreeView<in out TSchema extends ImplicitFieldSchema> extends ID
*/
export interface TreeViewAlpha<
in out TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema,
> extends Omit<
TreeView<TSchema extends ImplicitFieldSchema ? TSchema : ImplicitFieldSchema>,
"root" | "initialize"
> {
get root(): TSchema extends ImplicitFieldSchema
? TreeFieldFromImplicitField<TSchema>
: TreeLeafValue | TreeNode;
> extends Omit<TreeView<ReadSchema<TSchema>>, "root" | "initialize"> {
get root(): ReadableField<TSchema>;

set root(newRoot: InsertableField<TSchema>);

Expand Down
2 changes: 2 additions & 0 deletions packages/dds/tree/src/simple-tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export {
type ApplyKindInput,
type InsertableTreeNodeFromAllowedTypes,
type Input,
type ReadableField,
type ReadSchema,
} from "./schemaTypes.js";
export {
getTreeNodeForField,
Expand Down
27 changes: 27 additions & 0 deletions packages/dds/tree/src/simple-tree/schemaTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,33 @@ export type InsertableField<TSchema extends ImplicitFieldSchema | UnsafeUnknownS
? InsertableContent | undefined
: never;

/**
* Content which could be read from a field within a tree.
*
* @remarks
* Extended version of {@link TreeFieldFromImplicitField} that also allows {@link (UnsafeUnknownSchema:type)}.
* Since reading from fields with non-exact schema is still safe, this is only useful (compared to {@link TreeFieldFromImplicitField}) when the schema is also used as input and thus allows {@link (UnsafeUnknownSchema:type)}
* for use
* @system @alpha
*/
export type ReadableField<TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> =
TSchema extends ImplicitFieldSchema
? TreeFieldFromImplicitField<TSchema>
: TreeLeafValue | TreeNode;

/**
* Adapter to remove {@link (UnsafeUnknownSchema:type)} from a schema type so it can be used with types for generating APIs for reading data.
*
* @remarks
* Since reading with non-exact schema is still safe, this is mainly useful when the schema is also used as input and thus allows {@link (UnsafeUnknownSchema:type)}.
* @system @alpha
*/
export type ReadSchema<TSchema extends ImplicitFieldSchema | UnsafeUnknownSchema> = [
TSchema,
] extends [ImplicitFieldSchema]
? TSchema
: ImplicitFieldSchema;

/**
* Suitable for output.
* For input must error on side of excluding undefined instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { MockTreeCheckout, forestWithContent } from "../../utils.js";
import {
toStoredSchema,
type ImplicitFieldSchema,
type InsertableTreeFieldFromImplicitField,
type InsertableField,
} from "../../../simple-tree/index.js";

export function getReadonlyContext(
Expand Down Expand Up @@ -70,7 +70,7 @@ export interface TreeSimpleContentTyped<T extends ImplicitFieldSchema> {
* 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<T>;
readonly initialTree: InsertableField<T>;
}

/**
Expand Down
8 changes: 6 additions & 2 deletions packages/dds/tree/src/test/shared-tree/treeCheckout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ import {
} from "../../index.js";
// eslint-disable-next-line import/no-internal-modules
import { SchematizingSimpleTreeView } from "../../shared-tree/schematizingTreeView.js";
import { getOrCreateInnerNode, toStoredSchema } from "../../simple-tree/index.js";
import {
getOrCreateInnerNode,
toStoredSchema,
type InsertableField,
} from "../../simple-tree/index.js";
// eslint-disable-next-line import/no-internal-modules
import { stringSchema } from "../../simple-tree/leafNodeSchema.js";

Expand Down Expand Up @@ -1274,7 +1278,7 @@ function itView(
},
): void;
function itView<
T extends InsertableTreeFieldFromImplicitField<TRootSchema>,
T extends InsertableField<TRootSchema>,
TRootSchema extends ImplicitFieldSchema = typeof rootArray,
>(
title: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,17 @@ export class SharedTreeBranchManager {
nodeIdAttributeName?: string;
});
applyDiff(diff: Difference, objectToUpdate: Record<string, unknown> | TreeArrayNode): boolean;
checkoutNewMergedBranch<T extends ImplicitFieldSchema>(treeView: TreeView<T>, treeViewConfiguration: TreeViewConfiguration<T>, absolutePathToObjectNode: ObjectPath, llmResponse: Record<string, unknown> | unknown[]): {
checkoutNewMergedBranch<T extends ImplicitFieldSchema>(treeView: TreeViewAlpha<T>, treeViewConfiguration: TreeViewConfiguration<T>, absolutePathToObjectNode: ObjectPath, llmResponse: Record<string, unknown> | unknown[]): {
differences: Difference[];
originalBranch: TreeBranch;
forkBranch: TreeBranchFork;
forkView: TreeView<T>;
forkView: TreeViewAlpha<T>;
newBranchTargetNode: Record<string, unknown> | TreeArrayNode;
};
checkoutNewMergedBranchV2<T extends ImplicitFieldSchema>(treeView: TreeView<T>, treeViewConfiguration: TreeViewConfiguration<T>, absolutePathToObjectNode: ObjectPath): {
checkoutNewMergedBranchV2<T extends ImplicitFieldSchema>(treeView: TreeViewAlpha<T>, treeViewConfiguration: TreeViewConfiguration<T>, absolutePathToObjectNode: ObjectPath): {
originalBranch: TreeBranch;
forkBranch: TreeBranchFork;
forkView: TreeView<T>;
forkView: TreeViewAlpha<T>;
newBranchTargetNode: Record<string, unknown> | TreeArrayNode;
};
compare(obj: Record<string, unknown> | TreeArrayNode, newObj: Record<string, unknown> | unknown[]): Difference[];
Expand Down
Loading

0 comments on commit 512e53f

Please sign in to comment.