Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/all-glasses-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"fluid-framework": minor
"@fluidframework/tree": minor
"__section": tree
---
All non-structurally named beta schema factory APIs now support node schema metadata

The "options" parameter which allows providing metadata for `TreeNodeSchema` is now available consistently on `SchemaFactoryBeta`,
not just `SchemaFactoryAlpha` and a subset of `SchemaFactoryBeta`.
10 changes: 5 additions & 5 deletions packages/dds/tree/api-report/tree.alpha.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -760,14 +760,14 @@ TSchema
] extends [ImplicitFieldSchema] ? TSchema : ImplicitFieldSchema;

// @alpha @sealed @system
export interface RecordNodeCustomizableSchema<out TName extends string = string, in out T extends ImplicitAllowedTypes = ImplicitAllowedTypes, out ImplicitlyConstructable extends boolean = true, out TCustomMetadata = unknown> extends TreeNodeSchemaClass<TName, NodeKind.Record, TreeRecordNode<T> & WithType<TName, NodeKind.Record, T>, RecordNodeInsertableData<T>, ImplicitlyConstructable, T, never, TCustomMetadata>, SimpleRecordNodeSchema<TCustomMetadata> {
export interface RecordNodeCustomizableSchema<out TName extends string = string, in out T extends ImplicitAllowedTypes = ImplicitAllowedTypes, out ImplicitlyConstructable extends boolean = true, out TCustomMetadata = unknown> extends TreeNodeSchemaClass<TName, NodeKind.Record, TreeRecordNode<T> & WithType<TName, NodeKind.Record, T>, RecordNodeInsertableData<T>, ImplicitlyConstructable, T, undefined, TCustomMetadata>, SimpleRecordNodeSchema<TCustomMetadata> {
}

// @beta @system
export type RecordNodeInsertableData<T extends ImplicitAllowedTypes> = RestrictiveStringRecord<InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @alpha @sealed @system
export interface RecordNodePojoEmulationSchema<out TName extends string = string, in out T extends ImplicitAllowedTypes = ImplicitAllowedTypes, out ImplicitlyConstructable extends boolean = true, out TCustomMetadata = unknown> extends TreeNodeSchemaNonClass<TName, NodeKind.Record, TreeRecordNode<T> & WithType<TName, NodeKind.Record, T>, RecordNodeInsertableData<T>, ImplicitlyConstructable, T, never, TCustomMetadata>, SimpleRecordNodeSchema<TCustomMetadata> {
export interface RecordNodePojoEmulationSchema<out TName extends string = string, in out T extends ImplicitAllowedTypes = ImplicitAllowedTypes, out ImplicitlyConstructable extends boolean = true, out TCustomMetadata = unknown> extends TreeNodeSchemaNonClass<TName, NodeKind.Record, TreeRecordNode<T> & WithType<TName, NodeKind.Record, T>, RecordNodeInsertableData<T>, ImplicitlyConstructable, T, undefined, TCustomMetadata>, SimpleRecordNodeSchema<TCustomMetadata> {
}

// @alpha
Expand Down Expand Up @@ -928,7 +928,7 @@ export class SchemaFactoryBeta<out TScope extends string | undefined = string |
// (undocumented)
objectRecursive<const Name extends TName, const T extends RestrictiveStringRecord<System_Unsafe.ImplicitFieldSchemaUnsafe>, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe<T, ScopedSchemaName<TScope, Name>>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe<T>, false, T>;
record<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchemaNonClass<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined, TCustomMetadata>;
recordRecursive<Name extends TName, const T extends System_Unsafe.ImplicitAllowedTypesUnsafe, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record, unknown>, {
readonly [x: string]: System_Unsafe.InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
}, false, T, undefined, TCustomMetadata>;
Expand Down Expand Up @@ -1084,15 +1084,15 @@ export namespace System_TableSchema {
// @sealed
export function createRowSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TPropsSchema extends ImplicitFieldSchema>(inputSchemaFactory: SchemaFactoryAlpha<TInputScope>, cellSchema: TCellSchema, propsSchema: TPropsSchema): TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row">, NodeKind.Object, TreeNode & TableSchema.Row<TCellSchema, TPropsSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row">, NodeKind, unknown>, object & {
readonly id?: string | undefined;
readonly cells: (InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined>> | undefined) & InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined>>;
readonly cells: (InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>> | undefined) & InsertableTypedNode_2<TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>>;
} & (FieldHasDefault<TPropsSchema> extends true ? {
props?: InsertableTreeFieldFromImplicitField<TPropsSchema> | undefined;
} : {
props: InsertableTreeFieldFromImplicitField<TPropsSchema>;
}), true, {
readonly props: TPropsSchema;
readonly id: FieldSchema_2<FieldKind_2.Identifier, LeafSchema_2<"string", string>, unknown>;
readonly cells: FieldSchemaAlpha_2<FieldKind_2.Required, TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined>, unknown>;
readonly cells: FieldSchemaAlpha_2<FieldKind_2.Required, TreeNodeSchemaClass<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, TreeRecordNode_2<TCellSchema> & WithType<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Row.cells">, NodeKind.Record, unknown>, RecordNodeInsertableData_2<TCellSchema>, true, TCellSchema, undefined, unknown>, unknown>;
}>;
// @system
export function createTableSchema<const TInputScope extends string | undefined, const TCellSchema extends ImplicitAllowedTypes, const TColumnSchema extends ColumnSchemaBase<TInputScope, TCellSchema>, const TRowSchema extends RowSchemaBase<TInputScope, TCellSchema>>(inputSchemaFactory: SchemaFactoryAlpha<TInputScope>, _cellSchema: TCellSchema, columnSchema: TColumnSchema, rowSchema: TRowSchema): TreeNodeSchemaCore_2<ScopedSchemaName<ScopedSchemaName<TInputScope, "table">, "Table">, NodeKind.Object, true, {
Expand Down
2 changes: 1 addition & 1 deletion packages/dds/tree/api-report/tree.beta.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ export class SchemaFactoryBeta<out TScope extends string | undefined = string |
// (undocumented)
objectRecursive<const Name extends TName, const T extends RestrictiveStringRecord<System_Unsafe.ImplicitFieldSchemaUnsafe>, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe<T, ScopedSchemaName<TScope, Name>>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe<T>, false, T>;
record<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchemaNonClass<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined, TCustomMetadata>;
recordRecursive<Name extends TName, const T extends System_Unsafe.ImplicitAllowedTypesUnsafe, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record, unknown>, {
readonly [x: string]: System_Unsafe.InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
}, false, T, undefined, TCustomMetadata>;
Expand Down
2 changes: 1 addition & 1 deletion packages/dds/tree/api-report/tree.legacy.beta.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export class SchemaFactoryBeta<out TScope extends string | undefined = string |
// (undocumented)
objectRecursive<const Name extends TName, const T extends RestrictiveStringRecord<System_Unsafe.ImplicitFieldSchemaUnsafe>, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe<T, ScopedSchemaName<TScope, Name>>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe<T>, false, T>;
record<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchemaNonClass<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, `Record<${string}>`>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined>;
record<const Name extends TName, const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record>, RecordNodeInsertableData<T>, true, T, undefined, TCustomMetadata>;
recordRecursive<Name extends TName, const T extends System_Unsafe.ImplicitAllowedTypesUnsafe, const TCustomMetadata = unknown>(name: Name, allowedTypes: T, options?: NodeSchemaOptions<TCustomMetadata>): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Record, TreeRecordNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Record, unknown>, {
readonly [x: string]: System_Unsafe.InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>;
}, false, T, undefined, TCustomMetadata>;
Expand Down
60 changes: 38 additions & 22 deletions packages/dds/tree/src/simple-tree/api/schemaFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License.
*/

import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
import { assert, debugAssert, unreachableCase } from "@fluidframework/core-utils/internal";
import { isFluidHandle } from "@fluidframework/runtime-utils/internal";
import { UsageError } from "@fluidframework/telemetry-utils/internal";

Expand Down Expand Up @@ -164,7 +164,7 @@ export interface ObjectSchemaOptionsAlpha<TCustomMetadata = unknown>
* @remarks Omits parameters that are not relevant for common use cases.
*/
export const defaultSchemaFactoryObjectOptions: Required<
Omit<ObjectSchemaOptionsAlpha, "metadata" | "persistedMetadata">
Pick<ObjectSchemaOptions, "allowUnknownOptionalFields">
> = {
allowUnknownOptionalFields: false,
};
Expand Down Expand Up @@ -397,12 +397,9 @@ export class SchemaFactory<
true,
T
> {
return objectSchema(
scoped(this, name),
fields,
true,
defaultSchemaFactoryObjectOptions.allowUnknownOptionalFields,
);
return objectSchema(scoped(this, name), fields, true, {
...defaultSchemaFactoryObjectOptions,
});
}

/**
Expand Down Expand Up @@ -474,23 +471,26 @@ export class SchemaFactory<
* This seems like a TypeScript bug getting variance backwards for overload return types since it's erroring when the relation between the overload
* and the implementation is type safe, and forcing an unsafe typing instead.
*/
public map<const T extends ImplicitAllowedTypes>(
public map<const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(
nameOrAllowedTypes: TName | ((T & TreeNodeSchema) | readonly TreeNodeSchema[]),
allowedTypes?: T,
options?: NodeSchemaOptions<TCustomMetadata>,
): TreeNodeSchema<string, NodeKind.Map, TreeMapNode<T>, MapNodeInsertableData<T>, true, T> {
if (allowedTypes === undefined) {
const types = nameOrAllowedTypes as (T & TreeNodeSchema) | readonly TreeNodeSchema[];
const fullName = structuralName("Map", types);
debugAssert(() => options === undefined || "No options for structural types");
return this.getStructuralType(fullName, types, () =>
this.namedMap(fullName, nameOrAllowedTypes as T, false, true),
this.namedMap(fullName, nameOrAllowedTypes as T, false, true, {}),
) as TreeNodeSchemaBoth<
string,
NodeKind.Map,
TreeMapNode<T>,
MapNodeInsertableData<T>,
true,
T,
undefined
undefined,
TCustomMetadata
>;
}
// To actually have type safety, assign to the type this method should return before implicitly up-casting when returning.
Expand All @@ -501,8 +501,9 @@ export class SchemaFactory<
MapNodeInsertableData<T>,
true,
T,
undefined
> = this.namedMap(nameOrAllowedTypes as TName, allowedTypes, true, true);
undefined,
TCustomMetadata
> = this.namedMap(nameOrAllowedTypes as TName, allowedTypes, true, true, options ?? {});
return out;
}

Expand All @@ -515,27 +516,30 @@ export class SchemaFactory<
Name extends TName | string,
const T extends ImplicitAllowedTypes,
const ImplicitlyConstructable extends boolean,
const TCustomMetadata = unknown,
>(
name: Name,
allowedTypes: T,
customizable: boolean,
implicitlyConstructable: ImplicitlyConstructable,
options: NodeSchemaOptionsAlpha<TCustomMetadata>,
): TreeNodeSchemaBoth<
ScopedSchemaName<TScope, Name>,
NodeKind.Map,
TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>, NodeKind.Map>,
MapNodeInsertableData<T>,
ImplicitlyConstructable,
T,
undefined
undefined,
TCustomMetadata
> {
return mapSchema(
scoped(this, name),
allowedTypes,
implicitlyConstructable,
// The current policy is customizable nodes don't get fake prototypes.
!customizable,
undefined,
options ?? {},
);
}

Expand Down Expand Up @@ -618,41 +622,47 @@ export class SchemaFactory<
* @privateRemarks
* This should return TreeNodeSchemaBoth: see note on "map" implementation for details.
*/
public array<const T extends ImplicitAllowedTypes>(
public array<const T extends ImplicitAllowedTypes, const TCustomMetadata = unknown>(
nameOrAllowedTypes: TName | ((T & TreeNodeSchema) | readonly TreeNodeSchema[]),
allowedTypes?: T,
options?: NodeSchemaOptions<TCustomMetadata>,
): TreeNodeSchema<
ScopedSchemaName<TScope, string>,
NodeKind.Array,
TreeArrayNode<T>,
Iterable<InsertableTreeNodeFromImplicitAllowedTypes<T>>,
true,
T
T,
TCustomMetadata
> {
if (allowedTypes === undefined) {
const types = nameOrAllowedTypes as (T & TreeNodeSchema) | readonly TreeNodeSchema[];
const fullName = structuralName("Array", types);
debugAssert(() => options === undefined || "No options for structural types");
return this.getStructuralType(fullName, types, () =>
this.namedArray(fullName, nameOrAllowedTypes as T, false, true),
this.namedArray(fullName, nameOrAllowedTypes as T, false, true, {}),
) as TreeNodeSchemaClass<
ScopedSchemaName<TScope, string>,
NodeKind.Array,
TreeArrayNode<T>,
Iterable<InsertableTreeNodeFromImplicitAllowedTypes<T>>,
true,
T,
undefined
undefined,
TCustomMetadata
>;
}

const out: TreeNodeSchemaBoth<
ScopedSchemaName<TScope, string>,
NodeKind.Array,
TreeArrayNode<T>,
Iterable<InsertableTreeNodeFromImplicitAllowedTypes<T>>,
true,
T,
undefined
> = this.namedArray(nameOrAllowedTypes as TName, allowedTypes, true, true);
undefined,
TCustomMetadata
> = this.namedArray(nameOrAllowedTypes as TName, allowedTypes, true, true, options ?? {});
return out;
}

Expand Down Expand Up @@ -698,25 +708,29 @@ export class SchemaFactory<
Name extends TName | string,
const T extends ImplicitAllowedTypes,
const ImplicitlyConstructable extends boolean,
const TCustomMetadata = unknown,
>(
name: Name,
allowedTypes: T,
customizable: boolean,
implicitlyConstructable: ImplicitlyConstructable,
options: NodeSchemaOptionsAlpha<TCustomMetadata>,
): TreeNodeSchemaBoth<
ScopedSchemaName<TScope, Name>,
NodeKind.Array,
TreeArrayNode<T> & WithType<ScopedSchemaName<TScope, string>, NodeKind.Array>,
Iterable<InsertableTreeNodeFromImplicitAllowedTypes<T>>,
ImplicitlyConstructable,
T,
undefined
undefined,
TCustomMetadata
> {
return arraySchema(
scoped(this, name),
allowedTypes,
implicitlyConstructable,
customizable,
options,
);
}

Expand Down Expand Up @@ -822,6 +836,7 @@ export class SchemaFactory<
allowedTypes as T & ImplicitAllowedTypes,
true,
false,
{},
);

return RecursiveArray as TreeNodeSchemaClass<
Expand Down Expand Up @@ -870,6 +885,7 @@ export class SchemaFactory<
// Setting this (implicitlyConstructable) to true seems to work ok currently, but not for other node kinds.
// Supporting this could be fragile and might break other future changes, so it's being kept as false for now.
false,
{},
);

return MapSchema as TreeNodeSchemaClass<
Expand Down
Loading
Loading