/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { DiscriminatedUnionDispatcher } from "../../codec/index.js";
import { Brand, Erased, MakeNominal } from "../../util/index.js";
import { FieldKey, FieldKindIdentifier, FieldSchemaFormat, TreeNodeSchemaDataFormat, TreeNodeSchemaIdentifier } from "./format.js";
/**
 * Schema for what {@link TreeValue} is allowed on a Leaf node.
 * @internal
 */
export declare enum ValueSchema {
    Number = 0,
    String = 1,
    Boolean = 2,
    FluidHandle = 3,
    Null = 4
}
/**
 * Set of allowed tree types.
 * Providing multiple values here allows polymorphism, tagged union style.
 *
 * If not specified, types are unconstrained
 * (equivalent to the set containing every TreeNodeSchemaIdentifier defined in the document).
 *
 * Note that even when unconstrained, children must still be in-schema for their own type.
 *
 * In the future, this could be extended to allow inlining a TreeNodeStoredSchema here
 * (or some similar structural schema system).
 * For structural types which could go here, there are a few interesting options:
 *
 * - Allow replacing the whole set with a structural type for terminal / non-tree data,
 * and use this as a replacement for values on the tree nodes.
 *
 * - Allow expression structural constraints for child trees, for example requiring specific traits
 * (ex: via TreeNodeStoredSchema), instead of by type.
 *
 * There are two ways this could work:
 *
 * - Constrain the child nodes based on their shape:
 * this makes schema safe editing difficult because nodes would incur extra editing constraints to prevent them
 * from going out of schema based on their location in such a field.
 *
 * - Constrain the types allowed based on which types guarantee their data will always meet the constraints.
 *
 * Care would need to be taken to make sure this is sound for the schema updating mechanisms.
 * @internal
 */
export type TreeTypeSet = ReadonlySet<TreeNodeSchemaIdentifier> | undefined;
/**
 * Specifies which field kind to use.
 *
 * @remarks
 * This is used instead of just the FieldKindIdentifier so that it can be subtyped into a more expressive type with additional information.
 *
 * @internal
 */
export interface FieldKindSpecifier<T = FieldKindIdentifier> {
    identifier: T;
}
/**
 * Schema for a field.
 * Object implementing this interface should never be modified.
 * @internal
 */
export interface TreeFieldStoredSchema {
    readonly kind: FieldKindSpecifier;
    /**
     * The set of allowed child types.
     * If not specified, types are unconstrained.
     */
    readonly types?: TreeTypeSet;
}
/**
 * Identifier used for the FieldKind for fields which must be empty.
 *
 * @remarks
 * This mainly show up in:
 * 1. The root default field for documents.
 * 2. The schema used for out of schema fields (which thus must be empty/not exist) on object and leaf nodes.
 *
 * @internal
 */
export declare const forbiddenFieldKindIdentifier = "Forbidden";
/**
 * A schema for empty fields (fields which must always be empty).
 * There are multiple ways this could be encoded, but this is the most explicit.
 */
export declare const storedEmptyFieldSchema: TreeFieldStoredSchema;
/**
 * Opaque type erased handle to the encoded representation of the contents of a stored schema.
 * @internal
 */
export interface ErasedTreeNodeSchemaDataFormat extends Erased<"TreeNodeSchemaDataFormat"> {
}
export interface BrandedTreeNodeSchemaDataFormat extends Brand<TreeNodeSchemaDataFormat, ErasedTreeNodeSchemaDataFormat> {
}
/**
 * @internal
 */
export declare abstract class TreeNodeStoredSchema {
    protected _typeCheck: MakeNominal;
    /**
     * @privateRemarks
     * Returns TreeNodeSchemaDataFormat.
     * This is uses an opaque type to avoid leaking these types out of the package,
     * and is runtime validated by the codec.
     */
    abstract encode(): ErasedTreeNodeSchemaDataFormat;
}
/**
 * @internal
 */
export declare class ObjectNodeStoredSchema extends TreeNodeStoredSchema {
    readonly objectNodeFields: ReadonlyMap<FieldKey, TreeFieldStoredSchema>;
    /**
     * @param objectNodeFields -
     * Schema for fields with keys scoped to this TreeNodeStoredSchema.
     * This refers to the TreeFieldStoredSchema directly
     * (as opposed to just supporting FieldSchemaIdentifier and having a central FieldKey -\> TreeFieldStoredSchema map).
     * This allows os short friendly field keys which can ergonomically used as field names in code.
     * It also interoperates well with mapFields being used as a map with arbitrary data as keys.
     */
    constructor(objectNodeFields: ReadonlyMap<FieldKey, TreeFieldStoredSchema>);
    encode(): ErasedTreeNodeSchemaDataFormat;
}
/**
 * @internal
 */
export declare class MapNodeStoredSchema extends TreeNodeStoredSchema {
    readonly mapFields: TreeFieldStoredSchema;
    /**
     * @param mapFields -
     * Allows using using the fields as a map, with the keys being
     * FieldKeys and the values being constrained by this TreeFieldStoredSchema.
     * Usually `FieldKind.Value` should NOT be used here
     * since no nodes can ever be in schema are in schema if you use `FieldKind.Value` here
     * (that would require infinite children).
     */
    constructor(mapFields: TreeFieldStoredSchema);
    encode(): ErasedTreeNodeSchemaDataFormat;
}
/**
 * @internal
 */
export declare class LeafNodeStoredSchema extends TreeNodeStoredSchema {
    readonly leafValue: ValueSchema;
    /**
     * @param leafValue -
     * There are several approaches for how to store actual data in the tree
     * (special node types, special field contents, data on nodes etc.)
     * as well as several options about how the data should be modeled at this level
     * (byte sequence? javascript type? json?),
     * as well as options for how much of this would be exposed in the schema language
     * (ex: would all nodes with values be special built-ins, or could any schema add them?)
     * A simple easy to do in javascript approach is taken here:
     * this is not intended to be a suggestion of what approach to take, or what to expose in the schema language.
     * This is simply one approach that can work for modeling them in the internal schema representation.
     */
    constructor(leafValue: ValueSchema);
    encode(): ErasedTreeNodeSchemaDataFormat;
}
export declare const storedSchemaDecodeDispatcher: DiscriminatedUnionDispatcher<TreeNodeSchemaDataFormat, [
], TreeNodeStoredSchema>;
export declare function encodeFieldSchema(schema: TreeFieldStoredSchema): FieldSchemaFormat;
export declare function decodeFieldSchema(schema: FieldSchemaFormat): TreeFieldStoredSchema;
/**
 * Document schema data that can be stored in a document.
 *
 * @remarks
 * Note: the owner of this may modify it over time:
 * thus if needing to hand onto a specific version, make a copy.
 * @internal
 */
export interface TreeStoredSchema extends StoredSchemaCollection {
    /**
     * Schema for the root field which contains the whole tree.
     */
    readonly rootFieldSchema: TreeFieldStoredSchema;
}
/**
 * Collection of TreeNodeSchema data that can be stored in a document.
 *
 * @remarks
 * Note: the owner of this may modify it over time:
 * thus if needing to hang onto a specific version, make a copy.
 * @internal
 */
export interface StoredSchemaCollection {
    /**
     * {@inheritdoc StoredSchemaCollection}
     */
    readonly nodeSchema: ReadonlyMap<TreeNodeSchemaIdentifier, TreeNodeStoredSchema>;
}
//# sourceMappingURL=schema.d.ts.map