/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { isFlexTreeNode } from "../feature-libraries/index.js";
import { FieldKind, FieldSchema, NodeKind, } from "./schemaTypes.js";
import { SchemaFactory } from "./schemaFactory.js";
export function createFieldSchemaUnsafe(kind, allowedTypes) {
    // At runtime, we still want this to be a FieldSchema instance, but we can't satisfy its extends clause, so just return it as an FieldSchemaUnsafe
    return new FieldSchema(kind, allowedTypes);
}
/**
 * Extends SchemaFactory with utilities for recursive schema.
 * @see {@link ValidateRecursiveSchema}
 * @remarks
 * This is separated from {@link SchemaFactory} as these APIs are more experimental and may be stabilized independently.
 *
 * The non-recursive versions of the schema building methods will run into several issues when used recursively.
 * Consider the following example:
 *
 * ```typescript
 * const Test = sf.array(Test); // Bad
 * ```
 *
 * This has several issues:
 *
 * 1. It is a structurally named schema.
 * Structurally named schema derive their name from the names of their child types, which is not possible when the type is recursive since its name would include itself.
 * Instead a name must be explicitly provided.
 *
 * 2. The schema accesses itself before it's defined.
 * This would be a runtime error if the TypeScript compiler allowed it.
 * This can be fixed by wrapping the type in a function, which also requires explicitly listing the allowed types in an array (`[() => Test]`).
 *
 * 3. TypeScript fails to infer the recursive type and falls back to `any` with the warning or error (depending on the compiler configuration):
 * `'Test' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)`.
 * This issue is what the specialized recursive schema building methods fix.
 * This fix comes at a cost: to make the recursive cases work, the `extends` clauses had to be removed.
 * This means that mistakes declaring recursive schema often don't give compile errors in the schema.
 * Additionally support for implicit construction had to be disabled.
 * This means that new nested {@link Unhydrated} nodes can not be created like `new Test([[]])`.
 * Instead the nested nodes must be created explicitly using the construction like`new Test([new Test([])])`.
 *
 * 4. It is using "POJO" mode since it's not explicitly declaring a new class.
 * This means that if the schema generated d.ts files replace recursive references with `any`, breaking use of recursive schema across compilation boundaries.
 * This is fixed by explicitly creating a class which extends the returned schema.
 *
 * All together, the fixed version looks like:
 * ```typescript
 * class Test extends sf.arrayRecursive("Test", [() => Test]) {} // Good
 * ```
 *
 * Be very careful when declaring recursive schema.
 * Due to the removed extends clauses, subtle mistakes will compile just fine but cause strange errors when the schema is used.
 *
 * For example if the square brackets around the allowed types are forgotten:
 *
 * ```typescript
 * class Test extends sf.arrayRecursive("Test", () => Test) {} // Bad
 * ```
 * This schema will still compile, and some (but not all) usages of it may look like they work correctly while other usages will produce generally unintelligible compile errors.
 * This issue can be partially mitigated using {@link ValidateRecursiveSchema}.
 * @sealed @beta
 */
export class SchemaFactoryRecursive extends SchemaFactory {
    /**
     * {@link SchemaFactory.object} except tweaked to work better for recursive types.
     * @remarks
     * This version of {@link SchemaFactory.object} has fewer type constraints to work around TypeScript limitations, see {@link Unenforced}.
     */
    objectRecursive(name, t) {
        return this.object(name, t);
    }
    /**
     * {@link SchemaFactory.optional} except tweaked to work better for recursive types.
     * @remarks
     * This version of {@link SchemaFactory.optional} has fewer type constraints to work around TypeScript limitations, see {@link Unenforced}.
     */
    optionalRecursive(t) {
        return createFieldSchemaUnsafe(FieldKind.Optional, t);
    }
    /**
     * `SchemaFactory.array` except tweaked to work better for recursive types.
     * @remarks
     * This version of `SchemaFactory.array` has fewer type constraints to work around TypeScript limitations, see {@link Unenforced}.
     *
     * Additionally `ImplicitlyConstructable` is disabled (forcing use of constructor) to avoid
     * `error TS2589: Type instantiation is excessively deep and possibly infinite.`
     * which otherwise gets reported at sometimes incorrect source locations that vary based on incremental builds.
     */
    arrayRecursive(name, allowedTypes) {
        class RecursiveArray extends this.namedArray_internal(name, allowedTypes, true, false) {
            constructor(data) {
                if (isFlexTreeNode(data)) {
                    super(data);
                }
                else {
                    super(data);
                }
            }
        }
        return RecursiveArray;
    }
    /**
     * `SchemaFactory.map` except tweaked to work better for recursive types.
     * @remarks
     * This version of `SchemaFactory.map` uses the same workarounds as {@link SchemaFactoryRecursive.arrayRecursive}
     */
    mapRecursive(name, allowedTypes) {
        class MapSchema extends this.namedMap_internal(name, allowedTypes, true, false) {
            constructor(data) {
                if (isFlexTreeNode(data)) {
                    super(data);
                }
                else {
                    super(new Map(data));
                }
            }
        }
        return MapSchema;
    }
}
//# sourceMappingURL=schemaFactoryRecursive.js.map