/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
var _TreeNode_brand;
import { assert } from "@fluidframework/core-utils/internal";
import { UsageError } from "@fluidframework/telemetry-utils/internal";
import { NodeKind } from "./treeNodeSchema.js";
// eslint-disable-next-line import/no-deprecated
import { typeNameSymbol } from "./withType.js";
import { tryGetTreeNodeSchema } from "./treeNodeKernel.js";
import { isFlexTreeNode } from "../../feature-libraries/index.js";
/**
 * A non-{@link NodeKind.Leaf|leaf} SharedTree node. Includes objects, arrays, and maps.
 *
 * @remarks
 * Base type which all nodes implement.
 *
 * This can be used as a type to indicate/document values which should be tree nodes.
 * Runtime use of this class object (for example when used with `instanceof` or extending it), is not currently supported.
 *
 * Instances of tree nodes must be created by opening an existing document, inserting values into the document,
 * or by using the constructors and create functions of {@link TreeNodeSchema} produced by {@link SchemaFactory}.
 * @privateRemarks
 * This is a class not an interface to enable stricter type checking (see {@link TreeNode.#brand})
 * and some runtime enforcement of schema class policy (see the the validation in the constructor).
 * This class is however only `type` exported not value exported, preventing the class object from being used,
 * similar to how interfaces work.
 *
 * Not all node implementations include this in their prototype chain (some hide it with a proxy),
 * and thus cause the default/built in `instanceof` to return false despite our type checking and all other APIs treating them as TreeNodes.
 * This class provides a custom `Symbol.hasInstance` to fix `instanceof` for this class and all classes extending it.
 * For now the type-only export prevents use of `instanceof` on this class (but allows it in subclasses like schema classes).
 * @sealed @public
 */
export class TreeNode {
    static [(_TreeNode_brand = new WeakMap(), Symbol.hasInstance)](value) {
        const schema = tryGetTreeNodeSchema(value);
        if (schema === undefined || schema.kind === NodeKind.Leaf) {
            return false;
        }
        assert("prototype" in schema, 0x98a /* expected class based schema */);
        return inPrototypeChain(schema.prototype, this.prototype);
    }
    /**
     * TreeNodes must extend schema classes created by SchemaFactory, and therefore this constructor should not be invoked directly by code outside this package.
     * @privateRemarks
     * `token` must be the {@link privateToken} value, which is not package exported.
     * This is used to detect invalid subclasses.
     *
     * All valid subclass should use {@link TreeNodeValid}, but this code doesn't directly reference it to avoid cyclic dependencies.
     */
    constructor(token) {
        /**
         * This is added to prevent TypeScript from implicitly allowing non-TreeNode types to be used as TreeNodes.
         * @remarks
         * This field forces TypeScript to use nominal instead of structural typing,
         * preventing compiler error messages and tools like "add missing properties"
         * from adding the [type] field as a solution when using a non-TreeNode object where a TreeNode is required.
         * Instead TreeNodes must be created through the appropriate APIs, see the documentation on {@link TreeNode} for details.
         *
         * @privateRemarks
         * This is a JavaScript private field, so is not accessible from outside this class.
         * This prevents it from having name collisions with object fields.
         * Since this is private, the type of this field is stripped in the d.ts file.
         * To get matching type checking within and from outside the package, the least informative type (`unknown`) is used.
         * To avoid this having any runtime impact, the field is uninitialized.
         *
         * Making this field optional results in different type checking within this project than outside of it, since the d.ts file drops the optional aspect of the field.
         * This is extra confusing since since the tests get in-project typing for intellisense and separate project checking at build time.
         * To avoid all this mess, this field is required, not optional.
         *
         * Another option would be to use a symbol (possibly as a private field).
         * That approach ran into some strange difficulties causing SchemaFactory to fail to compile, and was not investigated further.
         *
         * The [type] symbol provides a lot of the value this private brand does, but is not all of it:
         * someone could manually (or via Intellisense auto-implement completion, or in response to a type error)
         * make an object literal with the [type] field and pass it off as a node: this private brand prevents that.
         */
        _TreeNode_brand.set(this, void 0);
        if (token !== privateToken) {
            throw new UsageError("TreeNodes must extend schema classes created by SchemaFactory");
        }
    }
}
/**
 * `token` to pass to {@link TreeNode}'s constructor used to detect invalid subclasses.
 */
export const privateToken = {};
/**
 * Check if the prototype derived's prototype chain contains `base`.
 * @param derived - prototype to check
 * @param base - prototype to search for
 * @returns true iff `base` is in the prototype chain starting at `derived`.
 */
// eslint-disable-next-line @rushstack/no-new-null
export function inPrototypeChain(derived, base) {
    let checking = derived;
    while (checking !== null) {
        if (base === checking) {
            return true;
        }
        checking = Reflect.getPrototypeOf(checking);
    }
    return false;
}
export function toFlexTreeNode(node) {
    assert(isFlexTreeNode(node), 0x963 /* Invalid InternalTreeNode */);
    return node;
}
//# sourceMappingURL=types.js.map