/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { Adapters, FieldKey, TreeNodeStoredSchema, StoredSchemaCollection, TreeFieldStoredSchema, TreeNodeSchemaIdentifier, TreeTypeSet, ValueSchema } from "../../core";
import { MakeNominal, Named, Assume } from "../../util";
import { FieldKinds } from "../default-schema";
import { FieldKind, FullSchemaPolicy } from "../modular-schema";
import { LazyItem } from "./flexList";
/**
 * @alpha
 */
export interface Fields {
    readonly [key: string]: TreeFieldSchema;
}
/**
 * @alpha
 */
export type NormalizeObjectNodeFields<T extends Fields> = {
    readonly [Property in keyof T]: NormalizeField<T[Property]>;
};
/**
 * A placeholder to use in extends constraints when using the real type breaks compilation of some recursive types due to [a design limitation of TypeScript](https://github.com/microsoft/TypeScript/issues/55758).
 *
 * These extends constraints only serve as documentation:
 * to avoid breaking compilation, this type has to not actually enforce anything, and thus is just `unknown`.
 * Therefore the type safety is the responsibility of the user of the API.
 * @alpha
 */
export type Unenforced<_DesiredExtendsConstraint> = unknown;
/**
 * T must extend TreeSchemaSpecification.
 * This can not be enforced using TypeScript since doing so breaks recursive type support.
 * See note on SchemaBuilder.fieldRecursive.
 * @alpha
 */
export declare abstract class TreeNodeSchemaBase<const out Name extends string = string, const out Specification = unknown> implements TreeNodeStoredSchema {
    readonly builder: Named<string>;
    readonly name: TreeNodeSchemaIdentifier<Name>;
    readonly info: Specification;
    protected _typeCheck?: MakeNominal;
    protected constructor(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, info: Specification);
    abstract getFieldSchema(field: FieldKey): TreeFieldSchema;
    abstract readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldSchema>;
    abstract readonly mapFields?: TreeFieldStoredSchema | undefined;
    abstract readonly leafValue?: ValueSchema | undefined;
}
/**
 * @alpha
 */
export declare class MapNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<MapFieldSchema> = MapFieldSchema> extends TreeNodeSchemaBase<Name, Specification> {
    readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldSchema>;
    get mapFields(): MapFieldSchema;
    readonly leafValue: undefined;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends MapFieldSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): MapNodeSchema<Name, Specification>;
    getFieldSchema(field: FieldKey): MapFieldSchema;
}
/**
 * @alpha
 */
export declare class LeafNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<ValueSchema> = ValueSchema> extends TreeNodeSchemaBase<Name, Specification> {
    readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldSchema>;
    readonly mapFields: undefined;
    get leafValue(): ValueSchema;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends ValueSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): LeafNodeSchema<Name, Specification>;
    getFieldSchema(field: FieldKey): TreeFieldSchema;
}
/**
 * @alpha
 */
export declare class ObjectNodeSchema<const out Name extends string = string, const out Specification extends Unenforced<Fields> = Fields> extends TreeNodeSchemaBase<Name, Specification> {
    readonly objectNodeFieldsObject: NormalizeObjectNodeFields<Assume<Specification, Fields>>;
    readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldSchema>;
    readonly mapFields: undefined;
    readonly leafValue: undefined;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends Fields>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): ObjectNodeSchema<Name, Specification>;
    private constructor();
    getFieldSchema(field: FieldKey): TreeFieldSchema;
}
/**
 * @alpha
 * TODO: replace (or subclass) this with more specific types, like "List".
 */
export declare class FieldNodeSchema<Name extends string = string, Specification extends Unenforced<TreeFieldSchema> = TreeFieldSchema> extends TreeNodeSchemaBase<Name, Specification> {
    readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldSchema>;
    readonly mapFields: undefined;
    readonly leafValue: undefined;
    protected _typeCheck2?: MakeNominal;
    static create<const Name extends string, const Specification extends TreeFieldSchema>(builder: Named<string>, name: TreeNodeSchemaIdentifier<Name>, specification: Specification): FieldNodeSchema<Name, Specification>;
    private constructor();
    getFieldSchema(field: FieldKey): TreeFieldSchema;
}
/**
 * @alpha
 * @privateRemarks
 * This could be an exhaustive union, or just the common base type.
 * Using just the base type prevents exhaustive matching, which has both pros and cons.
 *
 * For now this is using just the base type since the union is causing issues with schema aware typing, likely due to it being a union and thus distributing over extends clauses.
 */
export type TreeNodeSchema = TreeNodeSchemaBase;
/**
 * Convert FieldSchemaSpecification | undefined into TreeFieldSchema.
 * @alpha
 */
export type NormalizeField<T extends TreeFieldSchema | undefined> = T extends TreeFieldSchema ? T : TreeFieldSchema<typeof FieldKinds.forbidden, []>;
/**
 * Allow any node (as long as it meets the schema for its own type).
 * @alpha
 */
export declare const Any: "Any";
/**
 * Allow any node (as long as it meets the schema for its own type).
 * @alpha
 */
export type Any = typeof Any;
/**
 * Tree type, but can be wrapped in a function to allow referring to types before they are declared.
 * This makes recursive and co-recursive types possible.
 * @alpha
 */
export type LazyTreeNodeSchema = TreeNodeSchema | (() => TreeNodeSchema);
/**
 * Types for use in fields.
 *
 * "Any" is boxed in an array to allow use as variadic parameter.
 * @alpha
 */
export type AllowedTypes = readonly [Any] | readonly LazyItem<TreeNodeSchema>[];
/**
 * Checks if an {@link AllowedTypes} is {@link (Any:type)}.
 * @alpha
 */
export declare function allowedTypesIsAny(t: AllowedTypes): t is readonly [Any];
/**
 * Subset of TreeFieldSchema thats legal in maps.
 * This requires empty to be a valid value for the map.
 * @alpha
 */
export type MapFieldSchema = TreeFieldSchema<typeof FieldKinds.optional | typeof FieldKinds.sequence>;
/**
 * All policy for a specific field,
 * including functionality that does not have to be kept consistent across versions or deterministic.
 *
 * This can include policy for how to use this schema for "view" purposes, and well as how to expose editing APIs.
 *
 * @remarks
 * `Types` here must extend `AllowedTypes`, but this cannot be enforced with an "extends" clause: see {@link Unenforced} for details.
 *
 * @typeParam TKind - The kind of field.
 * @typeParam TTypes - The types allowed by the field.
 *
 * @sealed
 * @alpha
 */
export declare class TreeFieldSchema<out TKind extends FieldKind = FieldKind, const out TTypes extends Unenforced<AllowedTypes> = AllowedTypes> implements TreeFieldStoredSchema {
    readonly kind: TKind;
    readonly allowedTypes: TTypes;
    /**
     * Schema for a field which must always be empty.
     */
    static readonly empty: TreeFieldSchema<import("../default-schema").Forbidden, readonly []>;
    /**
     * Constructs a TreeFieldSchema.
     * @privateRemarks
     * Alias for the constructor, but with extends clause for the `Types` parameter that {@link TreeFieldSchema} can not have (due to recursive type issues).
     */
    static create<TKind extends FieldKind, const Types extends AllowedTypes>(kind: TKind, allowedTypes: Types): TreeFieldSchema<TKind, Types>;
    /**
     * Constructs a TreeFieldSchema, but missing the extends clause which breaks most recursive types.
     * @remarks
     * `Types` here must extend `AllowedTypes`, but this cannot be enforced with an "extends" clause: see {@link Unenforced} for details.
     * Prefer {@link TreeFieldSchema.create} when possible.
     */
    static createUnsafe<TKind extends FieldKind, const Types extends Unenforced<AllowedTypes>>(kind: TKind, allowedTypes: Types): TreeFieldSchema<TKind, Types>;
    protected _typeCheck?: MakeNominal;
    /**
     * This is computed lazily since types can be recursive, which makes evaluating this have to happen after all the schema are defined.
     */
    private readonly lazyTypes;
    /**
     * @param kind - The {@link https://en.wikipedia.org/wiki/Kind_(type_theory) | kind} of this field.
     * Determine the multiplicity, viewing and editing APIs as well as the merge resolution policy.
     * @param allowedTypes - What types of tree nodes are allowed in this field.
     */
    private constructor();
    /**
     * Types which are allowed in this field (by {@link TreeNodeSchemaIdentifier}), in a format optimized for stored schema.
     * This is the same set of types in {@link TreeFieldSchema.allowedTypes}, just in a different format.
     */
    get types(): TreeTypeSet;
    /**
     * Types which are allowed in this field.
     * This is the same set of types in {@link TreeFieldSchema.allowedTypes}, just as a set with laziness removed.
     * @privateRemarks
     * TODO:
     * 3 ways to access the allowed types are now exposed.
     * Reducing this and/or renaming the more friendly options to take the shorter name (`types`)
     * would be a good idea.
     */
    get allowedTypeSet(): AllowedTypeSet;
    /**
     * If exactly one type of child is allowed in this field, it is provided here.
     * @remarks
     * Some code paths (like unboxing and compressed tree encoding) special case schema with exactly one allowed type.
     * This field allows for simple and optimized handling of this case.
     */
    get monomorphicChildType(): TreeNodeSchema | undefined;
    /**
     * Compare this schema to another.
     *
     * @returns true iff the schema are identical.
     */
    equals(other: TreeFieldSchema): boolean;
}
/**
 * Types for use in fields.
 * This representation is optimized for runtime use in view-schema.
 *
 * @remarks
 * See {@link TreeTypeSet} for a stored-schema compatible version using the {@link TreeNodeSchemaIdentifier}.
 * See {@link AllowedTypes} for a compile time optimized version.
 * @alpha
 */
export type AllowedTypeSet = Any | ReadonlySet<TreeNodeSchema>;
/**
 * Convert {@link AllowedTypes} to {@link TreeTypeSet}.
 * @alpha
 */
export declare function allowedTypesSchemaSet(t: AllowedTypes): AllowedTypeSet;
/**
 * Convert {@link AllowedTypes} to {@link TreeTypeSet}.
 * @alpha
 */
export declare function allowedTypesToTypeSet(t: AllowedTypes): TreeTypeSet;
/**
 * Schema data that can be be used to view a document.
 * Strongly typed over its rootFieldSchema.
 *
 * @remarks
 * The type of the rootFieldSchema is used to implement SchemaAware APIs.
 * Cases that do not require being compile time schema aware can omit the explicit type for it.
 *
 * @alpha
 */
export interface TreeSchema<out T extends TreeFieldSchema = TreeFieldSchema> extends SchemaCollection {
    /**
     * Schema for the root field which contains the whole tree.
     */
    readonly rootFieldSchema: T;
    /**
     * Extra configuration for how this schema is handled at runtime.
     */
    readonly policy: FullSchemaPolicy;
    /**
     * Compatibility information how how to interact with content who's stored schema is not directly compatible with this schema.
     */
    readonly adapters: Adapters;
}
/**
 * Schema data that can be be used to view a document.
 * @alpha
 *
 * @privateRemarks
 * It is convenient that this can be used as a StoredSchemaCollection with no conversion.
 * There there isn't a design requirement for this however, so this extends clause can be removed later if needed.
 */
export interface SchemaCollection extends StoredSchemaCollection {
    /**
     * {@inheritdoc SchemaCollection}
     */
    readonly nodeSchema: ReadonlyMap<TreeNodeSchemaIdentifier, TreeNodeSchema>;
}
/**
 * Checks if a {@link TreeNodeSchema} is a {@link MapNodeSchema}.
 * @alpha
 */
export declare function schemaIsMap(schema: TreeNodeSchema): schema is MapNodeSchema;
/**
 * Checks if a {@link TreeNodeSchema} is a {@link LeafNodeSchema}.
 * @alpha
 */
export declare function schemaIsLeaf(schema: TreeNodeSchema): schema is LeafNodeSchema;
/**
 * Checks if a {@link TreeNodeSchema} is a {@link FieldNodeSchema}.
 * @alpha
 */
export declare function schemaIsFieldNode(schema: TreeNodeSchema): schema is FieldNodeSchema;
/**
 * Checks if a {@link TreeNodeSchema} is a {@link ObjectNodeSchema}.
 * @alpha
 */
export declare function schemaIsObjectNode(schema: TreeNodeSchema): schema is ObjectNodeSchema;
//# sourceMappingURL=typedTreeSchema.d.ts.map