Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tree: Allow MapNodes from Records #22042

Merged
merged 6 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
24 changes: 24 additions & 0 deletions .changeset/green-spies-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
"fluid-framework": minor
"@fluidframework/tree": minor
---

Allow `Record` typed object to be used to construct MapNodes.

It is now allowed to construct MapNodes from `Record` typed objects, similar to how maps are expressed in JSON.

Before this change, an `Iterable<string, Child>` was required, but now an object like `{key1: Child1, key2: Child2}` is allowed.

Full example using this new API:
```typescript
class Schema extends schemaFactory.map("ExampleMap", schemaFactory.number) {}
const fromRecord = new Schema({ x: 5 });
```

This new feature makes it possible for schema,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewording suggestion. I think I got the meaning right.

Suggested change
This new feature makes it possible for schema,
This new feature makes it possible for schemas to construct a tree entirely from JSON compatible objects using their constructors, as long as they do not require unhydrated nodes to differentiate ambiguous unions.

Copy link
Contributor Author

@CraigMacomber CraigMacomber Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to suggest changing multiple lines, you should select multiple lines when making the comment. Applying that suggestion would just replace line 18 with that, which I'm pretty sure is not what you intended. You can also use the preview feature to ensure the diff is replacing the right things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied slightly reworked version of change separate from this thread. Good rephrasing!

which do not require unhydrated nodes to differentiate ambiguous unions,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detection of which schema have this ambiguity issue, and some related details and API which build on the Json support added in this PR are included in #22022 as drafts.

to construct trees entirely from JSON compatible objects using their constructors.
CraigMacomber marked this conversation as resolved.
Show resolved Hide resolved

Due to limitations of TypeScript and recursive types,
recursive maps do not advertise support for this feature in their typing,
but it works at runtime.
7 changes: 5 additions & 2 deletions packages/dds/tree/api-report/tree.alpha.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ export type Listeners<T extends object> = {
export interface MakeNominal {
}

// @public
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> = Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]> | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @public
type NodeBuilderData<T extends TreeNodeSchema> = T extends TreeNodeSchema<string, NodeKind, unknown, infer TBuild> ? TBuild : never;

Expand Down Expand Up @@ -314,8 +317,8 @@ export class SchemaFactory<out TScope extends string | undefined = string | unde
readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>;
readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle<unknown>, IFluidHandle<unknown>>;
get identifier(): FieldSchema<FieldKind.Identifier, typeof SchemaFactory.string>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, MapNodeInsertableData<T>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, MapNodeInsertableData<T>, true, T>;
mapRecursive<Name extends TName, const T extends Unenforced<ImplicitAllowedTypes>>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>>, {
[Symbol.iterator](): Iterator<[
string,
Expand Down
7 changes: 5 additions & 2 deletions packages/dds/tree/api-report/tree.beta.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ export type Listeners<T extends object> = {
export interface MakeNominal {
}

// @public
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> = Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]> | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @public
type NodeBuilderData<T extends TreeNodeSchema> = T extends TreeNodeSchema<string, NodeKind, unknown, infer TBuild> ? TBuild : never;

Expand Down Expand Up @@ -314,8 +317,8 @@ export class SchemaFactory<out TScope extends string | undefined = string | unde
readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>;
readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle<unknown>, IFluidHandle<unknown>>;
get identifier(): FieldSchema<FieldKind.Identifier, typeof SchemaFactory.string>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, MapNodeInsertableData<T>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, MapNodeInsertableData<T>, true, T>;
mapRecursive<Name extends TName, const T extends Unenforced<ImplicitAllowedTypes>>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>>, {
[Symbol.iterator](): Iterator<[
string,
Expand Down
7 changes: 5 additions & 2 deletions packages/dds/tree/api-report/tree.public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ export type Listeners<T extends object> = {
export interface MakeNominal {
}

// @public
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> = Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]> | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @public
type NodeBuilderData<T extends TreeNodeSchema> = T extends TreeNodeSchema<string, NodeKind, unknown, infer TBuild> ? TBuild : never;

Expand Down Expand Up @@ -314,8 +317,8 @@ export class SchemaFactory<out TScope extends string | undefined = string | unde
readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>;
readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle<unknown>, IFluidHandle<unknown>>;
get identifier(): FieldSchema<FieldKind.Identifier, typeof SchemaFactory.string>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, MapNodeInsertableData<T>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, MapNodeInsertableData<T>, true, T>;
mapRecursive<Name extends TName, const T extends Unenforced<ImplicitAllowedTypes>>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>>, {
[Symbol.iterator](): Iterator<[
string,
Expand Down
5 changes: 5 additions & 0 deletions packages/dds/tree/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,8 @@ export {
*/
InternalTypes,
};

// Internal/System types:
// These would be put in `internalTypes` except doing so tents to cause errors like:
// The inferred type of 'NodeMap' cannot be named without a reference to '../../node_modules/@fluidframework/tree/lib/internalTypes.js'. This is likely not portable. A type annotation is necessary.
export type { MapNodeInsertableData } from "./simple-tree/index.js";
2 changes: 1 addition & 1 deletion packages/dds/tree/src/simple-tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,5 @@ export {
type TreeObjectNode,
setField,
} from "./objectNode.js";
export type { TreeMapNode } from "./mapNode.js";
export type { TreeMapNode, MapNodeInsertableData } from "./mapNode.js";
export { mapTreeFromNodeData } from "./toMapTree.js";
19 changes: 13 additions & 6 deletions packages/dds/tree/src/simple-tree/mapNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isMapTreeNode,
} from "../feature-libraries/index.js";
import {
type FactoryContent,
type InsertableContent,
getProxyForField,
prepareContentForHydration,
Expand All @@ -33,6 +34,7 @@ import { mapTreeFromNodeData } from "./toMapTree.js";
import { type MostDerivedData, type TreeNode, TreeNodeValid } from "./types.js";
import { getFlexSchema } from "./toFlexSchema.js";
import { UsageError } from "@fluidframework/telemetry-utils/internal";
import type { RestrictiveReadonlyRecord } from "../util/index.js";

/**
* A map of string keys to tree objects.
Expand Down Expand Up @@ -125,7 +127,7 @@ const handler: ProxyHandler<TreeMapNode> = {
};

abstract class CustomMapNodeBase<const T extends ImplicitAllowedTypes> extends TreeNodeValid<
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>
MapNodeInsertableData<T>
> {
public static readonly kind = NodeKind.Map;

Expand Down Expand Up @@ -237,10 +239,7 @@ export function mapSchema<
): MapTreeNode {
return getOrCreateMapTreeNode(
flexSchema,
mapTreeFromNodeData(
input as Iterable<readonly [string, InsertableContent]>,
this as unknown as ImplicitAllowedTypes,
),
mapTreeFromNodeData(input as FactoryContent, this as unknown as ImplicitAllowedTypes),
);
}

Expand All @@ -263,9 +262,17 @@ export function mapSchema<
TName,
NodeKind.Map,
TreeMapNode<T> & WithType<TName>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
MapNodeInsertableData<T>,
ImplicitlyConstructable,
T
> = schema;
return schemaErased;
}

/**
* Content which can be used to construct a Map node, explicitly or implicitly.
* @system @public
*/
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> =
| Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>
| RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;
21 changes: 8 additions & 13 deletions packages/dds/tree/src/simple-tree/schemaFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import {
type TreeObjectNode,
objectSchema,
} from "./objectNode.js";
import { type TreeMapNode, mapSchema } from "./mapNode.js";
import { type MapNodeInsertableData, type TreeMapNode, mapSchema } from "./mapNode.js";
import type {
FieldSchemaUnsafe,
// Adding these unused imports makes the generated d.ts file produced by TypeScript stop breaking API-Extractor's rollup generation.
Expand Down Expand Up @@ -297,7 +297,7 @@ export class SchemaFactory<
ScopedSchemaName<TScope, `Map<${string}>`>,
NodeKind.Map,
TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
MapNodeInsertableData<T>,
true,
T
>;
Expand All @@ -319,22 +319,15 @@ export class SchemaFactory<
ScopedSchemaName<TScope, Name>,
NodeKind.Map,
TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
MapNodeInsertableData<T>,
true,
T
>;

public map<const T extends ImplicitAllowedTypes>(
nameOrAllowedTypes: TName | ((T & TreeNodeSchema) | readonly TreeNodeSchema[]),
allowedTypes?: T,
): TreeNodeSchema<
string,
NodeKind.Map,
TreeMapNode<T>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
true,
T
> {
): 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);
Expand All @@ -352,7 +345,7 @@ export class SchemaFactory<
string,
NodeKind.Map,
TreeMapNode<T>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
MapNodeInsertableData<T>,
true,
T
>;
Expand All @@ -378,7 +371,7 @@ export class SchemaFactory<
ScopedSchemaName<TScope, Name>,
NodeKind.Map,
TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>,
Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>,
MapNodeInsertableData<T>,
ImplicitlyConstructable,
T
> {
Expand Down Expand Up @@ -722,6 +715,8 @@ export class SchemaFactory<
[string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>]
>;
},
// Ideally this would be included, but doing so breaks recursive types.
// | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe<T>>,
false,
T
>;
Expand Down
22 changes: 18 additions & 4 deletions packages/dds/tree/src/simple-tree/toMapTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,14 +375,22 @@ function arrayToMapTree(data: InsertableContent, schema: TreeNodeSchema): Exclus
*/
function mapToMapTree(data: InsertableContent, schema: TreeNodeSchema): ExclusiveMapTree {
assert(schema.kind === NodeKind.Map, 0x923 /* Expected a Map schema. */);
if (!(typeof data === "object" && data !== null && Symbol.iterator in data)) {
if (!(typeof data === "object" && data !== null)) {
throw new UsageError(`Input data is incompatible with Map schema: ${data}`);
}

const allowedChildTypes = normalizeAllowedTypes(schema.info as ImplicitAllowedTypes);

const fieldsIterator = (
Symbol.iterator in data
? // Support iterables of key value pairs (including Map objects)
data
: // Support record objects for JSON style Map data
Object.entries(data)
) as Iterable<readonly [string, InsertableContent]>;

const transformedFields = new Map<FieldKey, ExclusiveMapTree[]>();
for (const item of data as Iterable<readonly [string, InsertableContent]>) {
for (const item of fieldsIterator) {
if (!isReadonlyArray(item) || item.length !== 2 || typeof item[0] !== "string") {
throw new UsageError(`Input data is incompatible with map entry: ${item}`);
}
Expand Down Expand Up @@ -595,11 +603,17 @@ function shallowCompatibilityTest(
return mapOrArray ? CompatibilityLevel.Normal : CompatibilityLevel.None;
}

if (mapOrArray) {
// At this point, it is assumed data is a record-like object since all the other cases have been eliminated.

if (schema.kind === NodeKind.Array) {
CraigMacomber marked this conversation as resolved.
Show resolved Hide resolved
return CompatibilityLevel.None;
}

// Assume record-like object
if (schema.kind === NodeKind.Map) {
// When not unioned with an ObjectNode, allow objects to be used to create maps.
return CompatibilityLevel.Low;
}

assert(isObjectNodeSchema(schema), "unexpected schema kind");

// TODO: Improve type inference by making this logic more thorough. Handle at least:
Expand Down
6 changes: 6 additions & 0 deletions packages/dds/tree/src/test/simple-tree/mapNode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ describe("MapNode", () => {
assert.deepEqual([...fromMap], data);
const fromIterable = new Schema(new Map(data).entries());
assert.deepEqual([...fromIterable], data);
const fromRecord = new Schema({ x: 5 });
assert.deepEqual([...fromRecord], data);
});

describe("implicit construction", () => {
Expand All @@ -91,5 +93,9 @@ describe("MapNode", () => {
const fromIterable = new Root({ data: new Map(data).entries() });
assert.deepEqual([...fromIterable.data], data);
});
it("fromRecord", () => {
const fromRecord = new Root({ data: { x: 5 } });
assert.deepEqual([...fromRecord.data], data);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,9 @@ export type Listeners<T extends object> = {
export interface MakeNominal {
}

// @public
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> = Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]> | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @public
export type MemberChangedListener<M extends IMember> = (clientId: string, member: M) => void;

Expand Down Expand Up @@ -660,8 +663,8 @@ export class SchemaFactory<out TScope extends string | undefined = string | unde
readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>;
readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle<unknown>, IFluidHandle<unknown>>;
get identifier(): FieldSchema<FieldKind.Identifier, typeof SchemaFactory.string>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, MapNodeInsertableData<T>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, MapNodeInsertableData<T>, true, T>;
mapRecursive<Name extends TName, const T extends Unenforced<ImplicitAllowedTypes>>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>>, {
[Symbol.iterator](): Iterator<[
string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,9 @@ export type Listeners<T extends object> = {
export interface MakeNominal {
}

// @public
export type MapNodeInsertableData<T extends ImplicitAllowedTypes> = Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]> | RestrictiveReadonlyRecord<string, InsertableTreeNodeFromImplicitAllowedTypes<T>>;

// @public
export type MemberChangedListener<M extends IMember> = (clientId: string, member: M) => void;

Expand Down Expand Up @@ -966,8 +969,8 @@ export class SchemaFactory<out TScope extends string | undefined = string | unde
readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>;
readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle<unknown>, IFluidHandle<unknown>>;
get identifier(): FieldSchema<FieldKind.Identifier, typeof SchemaFactory.string>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, Iterable<readonly [string, InsertableTreeNodeFromImplicitAllowedTypes<T>]>, true, T>;
map<const T extends TreeNodeSchema | readonly TreeNodeSchema[]>(allowedTypes: T): TreeNodeSchema<ScopedSchemaName<TScope, `Map<${string}>`>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, `Map<${string}>`>>, MapNodeInsertableData<T>, true, T>;
map<Name extends TName, const T extends ImplicitAllowedTypes>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNode<T> & WithType<ScopedSchemaName<TScope, Name>>, MapNodeInsertableData<T>, true, T>;
mapRecursive<Name extends TName, const T extends Unenforced<ImplicitAllowedTypes>>(name: Name, allowedTypes: T): TreeNodeSchemaClass<ScopedSchemaName<TScope, Name>, NodeKind.Map, TreeMapNodeUnsafe<T> & WithType<ScopedSchemaName<TScope, Name>>, {
[Symbol.iterator](): Iterator<[
string,
Expand Down
Loading
Loading