Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: SchemaFactoryBeta<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: FieldSchema_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: FieldSchema_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: SchemaFactoryBeta<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