/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _CustomArrayNodeBase_instances, _CustomArrayNodeBase_mapTreesFromFieldData;
import { Lazy, oob, fail, assert } from "@fluidframework/core-utils/internal";
import { UsageError } from "@fluidframework/telemetry-utils/internal";
import { EmptyKey, ObjectNodeStoredSchema } from "../../../core/index.js";
import { FieldKinds, isTreeValue } from "../../../feature-libraries/index.js";
import { brand, validateIndex, validateIndexRange, } from "../../../util/index.js";
import { CompatibilityLevel, 
// eslint-disable-next-line import-x/no-deprecated
typeNameSymbol, NodeKind, typeSchemaSymbol, getOrCreateNodeFromInnerNode, getSimpleNodeSchemaFromInnerNode, getInnerNode, getKernel, UnhydratedSequenceField, getOrCreateNodeFromInnerUnboxedNode, normalizeAllowedTypes, TreeNodeValid, privateDataSymbol, createTreeNodeSchemaPrivateData, AnnotatedAllowedTypesInternal, } from "../../core/index.js";
import { getTreeNodeSchemaInitializedData, getUnhydratedContext, } from "../../createContext.js";
import { nullSchema } from "../../leafNodeSchema.js";
import { prepareArrayContentForInsertion } from "../../prepareForInsertion.js";
import { unhydratedFlexTreeFromInsertable, unhydratedFlexTreeFromInsertableNode, } from "../../unhydratedFlexTreeFromInsertable.js";
/**
 * A {@link TreeNode} which implements 'readonly T[]' and the array mutation APIs.
 * @public
 */
export const TreeArrayNode = {
    /**
     * Wrap an iterable of items to inserted as consecutive items in a array.
     * @remarks
     * The object returned by this function can be inserted into a {@link (TreeArrayNode:interface)}.
     * Its contents will be inserted consecutively in the corresponding location in the array.
     * @example
     * ```ts
     * array.insertAtEnd(TreeArrayNode.spread(iterable))
     * ```
     */
    spread: (content) => create(content),
};
/**
 * Package internal construction API.
 * Use {@link (TreeArrayNode:variable).spread} to create an instance of this type instead.
 */
let create;
/**
 * Used to insert iterable content into a {@link (TreeArrayNode:interface)}.
 * Use {@link (TreeArrayNode:variable).spread} to create an instance of this type.
 * @sealed @public
 */
export class IterableTreeArrayContent {
    constructor(content) {
        this.content = content;
    }
    /**
     * Iterates over content for nodes to insert.
     */
    [Symbol.iterator]() {
        return this.content[Symbol.iterator]();
    }
}
(() => {
    create = (content) => new IterableTreeArrayContent(content);
})();
/**
 * Given a array node proxy, returns its underlying LazySequence field.
 */
function getSequenceField(arrayNode) {
    return getInnerNode(arrayNode).getBoxed(EmptyKey);
}
// For compatibility, we are initially implement 'readonly T[]' by applying the Array.prototype methods
// to the array node proxy.  Over time, we should replace these with efficient implementations on LazySequence
// to avoid re-entering the proxy as these methods access 'length' and the indexed properties.
//
// For brevity, the current implementation dynamically builds a property descriptor map from a list of
// Array functions we want to re-expose via the proxy.
const arrayPrototypeKeys = [
    "concat",
    "entries",
    "every",
    "filter",
    "find",
    "findIndex",
    "flat",
    "flatMap",
    "forEach",
    "includes",
    "indexOf",
    "join",
    "keys",
    "lastIndexOf",
    "map",
    "reduce",
    "reduceRight",
    "slice",
    "some",
    "toLocaleString",
    "toString",
    // "copyWithin",
    // "fill",
    // "length",
    // "pop",
    // "push",
    // "reverse",
    // "shift",
    // "sort",
    // "splice",
    // "unshift",
];
/**
 * {@link TreeNodeValid}, but modified to add members from Array.prototype named in {@link arrayPrototypeKeys}.
 * @privateRemarks
 * Since a lot of scratch types and values are involved with creating this,
 * it's generating using an immediately invoked function expression (IIFE).
 * This is a common JavaScript pattern for cases like this to avoid cluttering the scope.
 */
const TreeNodeWithArrayFeatures = (() => {
    /**
     * {@link TreeNodeValid}, but modified to add members from Array.prototype named in {@link arrayPrototypeKeys}.
     */
    class TreeNodeWithArrayFeaturesUntyped extends TreeNodeValid {
    }
    // Modify TreeNodeWithArrayFeaturesUntyped to add the members from Array.prototype
    for (const key of arrayPrototypeKeys) {
        Object.defineProperty(TreeNodeWithArrayFeaturesUntyped.prototype, key, {
            value: Array.prototype[key],
        });
    }
    return TreeNodeWithArrayFeaturesUntyped;
})();
/* eslint-enable @typescript-eslint/explicit-member-accessibility, @typescript-eslint/no-explicit-any */
/**
 * Attempts to coerce the given property key to an integer index property.
 * @param key - The property key to coerce.
 * @param exclusiveMax - This restricts the range in which the resulting index is allowed to be.
 * The coerced index of `key` must be less than `exclusiveMax` or else this function will return `undefined`.
 * This is useful for reading an array within the bounds of its length, e.g. `asIndex(key, array.length)`.
 */
export function asIndex(key, exclusiveMax) {
    if (typeof key !== "string") {
        return undefined;
    }
    // TODO: It may be worth a '0' <= ch <= '9' check before calling 'Number' to quickly
    // reject 'length' as an index, or even parsing integers ourselves.
    const asNumber = Number(key);
    if (!Number.isInteger(asNumber)) {
        return undefined;
    }
    // Check that the original string is the same after converting to a number and back again.
    // This prevents keys like "5.0", "0x5", " 5" from coercing to 5, and keys like " " or "" from coercing to 0.
    const asString = String(asNumber);
    if (asString !== key) {
        return undefined;
    }
    // TODO: See 'matrix/range.ts' for fast integer coercing + range check.
    return 0 <= asNumber && asNumber < exclusiveMax ? asNumber : undefined;
}
/**
 * Create a proxy which implements the {@link TreeArrayNode} API.
 * @param allowAdditionalProperties - If true, setting of unexpected properties will be forwarded to the target object.
 * Otherwise setting of unexpected properties will error.
 * @param proxyTarget - Target object of the proxy. Must provide an own `length` value property
 * (which is not used but must exist for getOwnPropertyDescriptor invariants) and the array functionality from {@link arrayNodePrototype}.
 * Controls the prototype exposed by the produced proxy.
 * @param dispatchTarget - provides the functionally of the node, implementing all fields.
 */
function createArrayNodeProxy(allowAdditionalProperties, proxyTarget, dispatchTarget) {
    // To satisfy 'deepEquals' level scrutiny, the target of the proxy must be an array literal in order
    // to pass 'Object.getPrototypeOf'.  It also satisfies 'Array.isArray' and 'Object.prototype.toString'
    // requirements without use of Array[Symbol.species], which is potentially on a path to deprecation.
    const proxy = new Proxy(proxyTarget, {
        get: (target, key, receiver) => {
            const field = getSequenceField(receiver);
            const maybeIndex = asIndex(key, field.length);
            if (maybeIndex === undefined) {
                if (key === "length") {
                    return field.length;
                }
                // In NodeJS 22, assert.strict.deepEqual started special casing well known constructors like Array.
                // That made this necessary, ensuring that in POJO mode, TreeArrayNode are still deepEqual to arrays.
                if (key === "constructor") {
                    return proxyTarget.constructor;
                }
                // Pass the proxy as the receiver here, so that any methods on
                // the prototype receive `proxy` as `this`.
                return Reflect.get(dispatchTarget, key, receiver);
            }
            const maybeContent = field.at(maybeIndex);
            return maybeContent === undefined
                ? undefined
                : getOrCreateNodeFromInnerUnboxedNode(maybeContent);
        },
        set: (target, key, newValue, receiver) => {
            if (key === "length") {
                // To allow "length" to look like "length" on an array, getOwnPropertyDescriptor has to report it as a writable value.
                // This means the proxy target must provide a length value, but since it can't use getters and setters, it can't be correct.
                // Therefor length has to be handled in this proxy.
                // Since it's not actually mutable, return false so setting it will produce a type error.
                return false;
            }
            // 'Symbol.isConcatSpreadable' may be set on an Array instance to modify the behavior of
            // the concat method.  We allow this property to be added to the dispatch object.
            if (key === Symbol.isConcatSpreadable) {
                return Reflect.set(dispatchTarget, key, newValue, receiver);
            }
            // Array nodes treat all non-negative integer indexes as array access.
            // Using Infinity here (rather than length) ensures that indexing past the end doesn't create additional session local properties.
            const maybeIndex = asIndex(key, Number.POSITIVE_INFINITY);
            if (maybeIndex !== undefined) {
                // For MVP, we otherwise disallow setting properties (mutation is only available via the array node mutation APIs).
                // To ensure a clear and actionable error experience, we will throw explicitly here, rather than just returning false.
                throw new UsageError("Cannot set indexed properties on array nodes. Use array node mutation APIs to alter the array.");
            }
            return allowAdditionalProperties ? Reflect.set(target, key, newValue, receiver) : false;
        },
        has: (target, key) => {
            const field = getSequenceField(proxy);
            const maybeIndex = asIndex(key, field.length);
            return maybeIndex !== undefined || Reflect.has(dispatchTarget, key);
        },
        ownKeys: (target) => {
            const field = getSequenceField(proxy);
            // TODO: Would a lazy iterator to produce the indexes work / be more efficient?
            // TODO: Need to surface 'Symbol.isConcatSpreadable' as an own key.
            const keys = Array.from({ length: field.length }, (_, index) => `${index}`);
            if (allowAdditionalProperties) {
                keys.push(...Reflect.ownKeys(target));
            }
            else {
                keys.push("length");
            }
            return keys;
        },
        getOwnPropertyDescriptor: (target, key) => {
            const field = getSequenceField(proxy);
            const maybeIndex = asIndex(key, field.length);
            if (maybeIndex !== undefined) {
                const val = field.at(maybeIndex);
                // To satisfy 'deepEquals' level scrutiny, the property descriptor for indexed properties must
                // be a simple value property (as opposed to using getter) and declared writable/enumerable/configurable.
                return {
                    value: val === undefined ? undefined : getOrCreateNodeFromInnerUnboxedNode(val),
                    writable: true, // For MVP, setting indexed properties is reported as allowed here (for deep equals compatibility noted above), but not actually supported.
                    enumerable: true,
                    configurable: true,
                };
            }
            else if (key === "length") {
                // To satisfy 'deepEquals' level scrutiny, the property descriptor for 'length' must be a simple
                // value property (as opposed to using getter) and be declared writable / non-configurable.
                return {
                    value: field.length,
                    writable: true,
                    enumerable: false,
                    configurable: false,
                };
            }
            return Reflect.getOwnPropertyDescriptor(dispatchTarget, key);
        },
        defineProperty(target, key, attributes) {
            const maybeIndex = asIndex(key, Number.POSITIVE_INFINITY);
            if (maybeIndex !== undefined) {
                throw new UsageError("Shadowing of array indices is not permitted.");
            }
            return Reflect.defineProperty(dispatchTarget, key, attributes);
        },
    });
    return proxy;
}
class CustomArrayNodeBase extends TreeNodeWithArrayFeatures {
    constructor(input) {
        super(input ?? []);
        _CustomArrayNodeBase_instances.add(this);
    }
    toJSON() {
        // This override causes the class instance to `JSON.stringify` as `[a, b]` rather than `{0: a, 1: b}`.
        return [...this];
    }
    // Instances of this class are used as the dispatch object for the proxy,
    // and thus its set of keys is used to implement `has` (for the `in` operator) for the non-numeric cases.
    // Therefore it must include `length`,
    // even though this "length" is never invoked (due to being shadowed by the proxy provided own property).
    get length() {
        return fail(0xadb /* Proxy should intercept length */);
    }
    [(_CustomArrayNodeBase_instances = new WeakSet(), _CustomArrayNodeBase_mapTreesFromFieldData = function _CustomArrayNodeBase_mapTreesFromFieldData(value) {
        const sequenceField = getSequenceField(this);
        const content = value;
        const contentArray = content.flatMap((c) => c instanceof IterableTreeArrayContent ? [...c] : [c]);
        const kernel = getKernel(this);
        const flexContext = kernel.getInnerNode().context;
        assert(flexContext === kernel.context.flexContext, 0xc14 /* Expected flexContext to match */);
        const innerSchema = kernel.context.flexContext.schema.nodeSchema.get(brand(kernel.schema.identifier));
        assert(innerSchema instanceof ObjectNodeStoredSchema, 0xc15 /* Expected ObjectNodeStoredSchema */);
        const fieldSchema = innerSchema.getFieldSchema(EmptyKey);
        const mapTrees = prepareArrayContentForInsertion(contentArray, this.childSchema, sequenceField.context, fieldSchema.types);
        return mapTrees;
    }, Symbol.iterator)]() {
        return this.values();
    }
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    get [Symbol.unscopables]() {
        // This might not be the exact right set of values, but it only matters for `with` clauses which are deprecated and are banned in strict mode, so it shouldn't matter much.
        // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with for details.
        return Array.prototype[Symbol.unscopables];
    }
    at(index) {
        const field = getSequenceField(this);
        const val = field.boxedAt(index);
        if (val === undefined) {
            return val;
        }
        return getOrCreateNodeFromInnerNode(val);
    }
    insertAt(index, ...value) {
        const field = getSequenceField(this);
        validateIndex(index, field, "TreeArrayNode.insertAt", true);
        const content = __classPrivateFieldGet(this, _CustomArrayNodeBase_instances, "m", _CustomArrayNodeBase_mapTreesFromFieldData).call(this, value);
        field.editor.insert(index, content);
    }
    insertAtStart(...value) {
        this.insertAt(0, ...value);
    }
    insertAtEnd(...value) {
        this.insertAt(this.length, ...value);
    }
    push(...value) {
        this.insertAt(this.length, ...value);
    }
    removeAt(index) {
        const field = getSequenceField(this);
        validateIndex(index, field, "TreeArrayNode.removeAt");
        field.editor.remove(index, 1);
    }
    removeRange(start, end) {
        const field = getSequenceField(this);
        const { length, editor } = field;
        const removeStart = start ?? 0;
        validateIndex(removeStart, field, "TreeArrayNode.removeRange", true);
        const removeEnd = Math.min(length, end ?? length);
        validateIndexRange(removeStart, removeEnd, field, "TreeArrayNode.removeRange");
        editor.remove(removeStart, removeEnd - removeStart);
    }
    splice(start, deleteCount, ...items) {
        const length = this.length;
        const actualStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
        const actualDeleteCount = deleteCount === undefined
            ? length - actualStart
            : Math.min(Math.max(deleteCount, 0), length - actualStart);
        validateIndexRange(actualStart, actualStart + actualDeleteCount, getSequenceField(this), "TreeArrayNode.splice");
        const removed = [];
        for (let index = actualStart; index < actualStart + actualDeleteCount; index++) {
            removed.push(this.at(index) ?? oob());
        }
        const innerNode = getInnerNode(this);
        const transaction = innerNode.isHydrated()
            ? innerNode.context.checkout.transaction
            : undefined;
        transaction?.start();
        this.removeRange(actualStart, actualStart + actualDeleteCount);
        if (items.length > 0) {
            this.insertAt(actualStart, ...items);
        }
        transaction?.commit();
        return removed;
    }
    moveToStart(sourceIndex, source) {
        const sourceArray = source ?? this;
        const sourceField = getSequenceField(sourceArray);
        validateIndex(sourceIndex, sourceField, "TreeArrayNode.moveToStart");
        this.moveRangeToIndex(0, sourceIndex, sourceIndex + 1, source);
    }
    moveToEnd(sourceIndex, source) {
        const sourceArray = source ?? this;
        const sourceField = getSequenceField(sourceArray);
        validateIndex(sourceIndex, sourceField, "TreeArrayNode.moveToEnd");
        this.moveRangeToIndex(this.length, sourceIndex, sourceIndex + 1, source);
    }
    moveToIndex(destinationGap, sourceIndex, source) {
        const sourceArray = source ?? this;
        const sourceField = getSequenceField(sourceArray);
        const destinationField = getSequenceField(this);
        validateIndex(destinationGap, destinationField, "TreeArrayNode.moveToIndex", true);
        validateIndex(sourceIndex, sourceField, "TreeArrayNode.moveToIndex");
        this.moveRangeToIndex(destinationGap, sourceIndex, sourceIndex + 1, source);
    }
    moveRangeToStart(sourceStart, sourceEnd, source) {
        validateIndexRange(sourceStart, sourceEnd, source ?? getSequenceField(this), "TreeArrayNode.moveRangeToStart");
        this.moveRangeToIndex(0, sourceStart, sourceEnd, source);
    }
    moveRangeToEnd(sourceStart, sourceEnd, source) {
        validateIndexRange(sourceStart, sourceEnd, source ?? getSequenceField(this), "TreeArrayNode.moveRangeToEnd");
        this.moveRangeToIndex(this.length, sourceStart, sourceEnd, source);
    }
    moveRangeToIndex(destinationGap, sourceStart, sourceEnd, source) {
        const destinationField = getSequenceField(this);
        const destinationSchema = this.allowedTypes;
        const kernel = getKernel(this);
        const destinationStored = (kernel.context.flexContext.schema.nodeSchema.get(brand(kernel.schema.identifier)) ??
            fail(0xc16 /* missing schema for array node */)).getFieldSchema(EmptyKey).types;
        const sourceField = source === undefined ? destinationField : getSequenceField(source);
        validateIndex(destinationGap, destinationField, "TreeArrayNode.moveRangeToIndex", true);
        validateIndexRange(sourceStart, sourceEnd, source ?? destinationField, "TreeArrayNode.moveRangeToIndex");
        // TODO: determine support for move across different sequence types
        if (sourceField !== destinationField) {
            for (let i = sourceStart; i < sourceEnd; i++) {
                const sourceNode = sourceField.boxedAt(i) ?? oob();
                const sourceSchema = getSimpleNodeSchemaFromInnerNode(sourceNode);
                if (!destinationSchema.has(sourceSchema)) {
                    throw new UsageError(`Type ${sourceNode.type} in source sequence is not allowed in destination.`);
                }
                if (!destinationStored.has(sourceNode.type)) {
                    // TODO: better and centralized messages for missing staged schema updates.
                    throw new UsageError(`Type ${sourceNode.type} in source sequence is not allowed in destination's stored schema: this would likely require upgrading the document to permit a staged schema.`);
                }
            }
        }
        const movedCount = sourceEnd - sourceStart;
        if (destinationField.context.isHydrated()) {
            if (!sourceField.context.isHydrated()) {
                throw new UsageError("Cannot move elements from an unhydrated array to a hydrated array.");
            }
            if (sourceField.context !== destinationField.context) {
                throw new UsageError("Cannot move elements between two different TreeViews.");
            }
            destinationField.context.checkout.editor.move(sourceField.getFieldPath(), sourceStart, movedCount, destinationField.getFieldPath(), destinationGap);
        }
        else {
            if (!(sourceField instanceof UnhydratedSequenceField)) {
                throw new UsageError("Cannot move elements from a hydrated array to an unhydrated array.");
            }
            if (sourceField.context.isHydrated()) {
                throw new UsageError("Cannot move elements from an unhydrated array to a hydrated array.");
            }
            assert(destinationField instanceof UnhydratedSequenceField, 0xcd5 /* destinationField should be unhydrated since we're in the else branch of isHydrated() check */);
            // Use native move which handles the operation atomically for within-field moves
            // to ensure only a single event is emitted per affected field.
            destinationField.editor.move(sourceStart, movedCount, destinationGap, sourceField === destinationField ? undefined : sourceField);
        }
    }
    values() {
        return this.generateValues(getKernel(this).generationNumber);
    }
    *generateValues(initialLastUpdatedStamp) {
        const kernel = getKernel(this);
        if (initialLastUpdatedStamp !== kernel.generationNumber) {
            throw new UsageError(`Concurrent editing and iteration is not allowed.`);
        }
        for (let i = 0; i < this.length; i++) {
            yield this.at(i) ?? fail(0xadc /* Index is out of bounds */);
            if (initialLastUpdatedStamp !== kernel.generationNumber) {
                throw new UsageError(`Concurrent editing and iteration is not allowed.`);
            }
        }
    }
}
CustomArrayNodeBase.kind = NodeKind.Array;
/**
 * Define a {@link TreeNodeSchema} for a {@link (TreeArrayNode:interface)}.
 *
 * @param name - Unique identifier for this schema including the factory's scope.
 * @param persistedMetadata - Optional persisted metadata for the object node schema.
 */
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function arraySchema(identifier, info, implicitlyConstructable, customizable, nodeOptions) {
    const persistedMetadata = nodeOptions?.persistedMetadata;
    const normalizedTypes = normalizeAllowedTypes(info);
    const lazyAllowedTypesIdentifiers = new Lazy(() => new Set(normalizedTypes.evaluate().map((type) => type.identifier)));
    const lazySimpleAllowedTypes = new Lazy(() => {
        return AnnotatedAllowedTypesInternal.evaluateSimpleAllowedTypes(normalizedTypes);
    });
    let privateData;
    // This class returns a proxy from its constructor to handle numeric indexing.
    // Alternatively it could extend a normal class which gets tons of numeric properties added.
    class Schema extends CustomArrayNodeBase {
        static prepareInstance(instance, flexNode) {
            const proxyTarget = customizable ? instance : [];
            if (customizable) {
                // Since proxy reports this as a "non-configurable" property, it must exist on the underlying object used as the proxy target, not as an inherited property.
                // This should not get used as the proxy should intercept all use.
                Object.defineProperty(instance, "length", {
                    value: Number.NaN,
                    writable: true,
                    enumerable: false,
                    configurable: false,
                });
            }
            return createArrayNodeProxy(customizable, proxyTarget, instance);
        }
        static buildRawNode(instance, input) {
            return unhydratedFlexTreeFromInsertable(input, this);
        }
        static get allowedTypesIdentifiers() {
            return lazyAllowedTypesIdentifiers.value;
        }
        static get simpleAllowedTypes() {
            return lazySimpleAllowedTypes.value;
        }
        static oneTimeSetup() {
            // First run, do extra validation.
            // TODO: provide a way for TreeConfiguration to trigger this same validation to ensure it gets run early.
            // Scan for shadowing inherited members which won't work, but stop scan early to allow shadowing built in (which seems to work ok).
            {
                let prototype = this.prototype;
                // There isn't a clear cleaner way to author this loop.
                while (prototype !== Schema.prototype) {
                    // Search prototype keys and check for positive integers. Throw if any are found.
                    // Shadowing of index properties on array nodes is not supported.
                    for (const key of Object.getOwnPropertyNames(prototype)) {
                        const maybeIndex = asIndex(key, Number.POSITIVE_INFINITY);
                        if (maybeIndex !== undefined) {
                            throw new UsageError(`Schema ${identifier} defines an inherited index property "${key.toString()}" which shadows a possible array index. Shadowing of array indices is not permitted.`);
                        }
                    }
                    // Since this stops at the array node base schema, it should never see a null prototype, so this case is safe.
                    // Additionally, if the prototype chain is ever messed up such that the array base schema is not in it,
                    // the null that would show up here does at least ensure this code throws instead of hanging.
                    prototype = Reflect.getPrototypeOf(prototype);
                }
            }
            const schema = this;
            return getTreeNodeSchemaInitializedData(this, {
                shallowCompatibilityTest: (data) => shallowCompatibilityTest(data, schema),
                toFlexContent: (data, allowedTypes) => arrayToFlexContent(data, schema),
            });
        }
        static get childTypes() {
            return normalizedTypes.evaluateSet();
        }
        // eslint-disable-next-line import-x/no-deprecated
        get [typeNameSymbol]() {
            return identifier;
        }
        get [typeSchemaSymbol]() {
            return Schema.constructorCached?.constructor;
        }
        get childSchema() {
            return info;
        }
        get allowedTypes() {
            return normalizedTypes.evaluateSet();
        }
        static get [privateDataSymbol]() {
            return (privateData ??= createTreeNodeSchemaPrivateData(this, [normalizedTypes]));
        }
    }
    Schema.constructorCached = undefined;
    Schema.identifier = identifier;
    Schema.info = info;
    Schema.implicitlyConstructable = implicitlyConstructable;
    Schema.metadata = nodeOptions.metadata ?? {};
    Schema.persistedMetadata = persistedMetadata;
    const output = Schema;
    return output;
}
/**
 * Transforms data for a child of an array.
 * @param child - The tree data to be transformed.
 * @param allowedTypes - The set of types allowed by the parent context. Used to validate the input tree.
 */
function arrayChildToFlexTree(child, allowedTypes) {
    // We do not support undefined sequence entries.
    // If we encounter an undefined entry, use null instead if supported by the schema, otherwise throw.
    let childWithFallback = child;
    if (child === undefined) {
        if (allowedTypes.has(nullSchema)) {
            childWithFallback = null;
        }
        else {
            throw new TypeError(`Received unsupported array entry value: ${child}.`);
        }
    }
    return unhydratedFlexTreeFromInsertableNode(childWithFallback, allowedTypes);
}
/**
 * {@link TreeNodeSchemaInitializedData.toFlexContent} for Array nodes.
 *
 * @param data - The tree data to be transformed. Must be an iterable.
 * @param schema - The schema to comply with.
 */
function arrayToFlexContent(data, schema) {
    if (!(typeof data === "object" && data !== null && Symbol.iterator in data)) {
        // eslint-disable-next-line @typescript-eslint/no-base-to-string
        throw new UsageError(`Input data is incompatible with Array schema: ${data}`);
    }
    const allowedChildTypes = normalizeAllowedTypes(schema.info);
    const mappedData = Array.from(data, (child) => arrayChildToFlexTree(child, allowedChildTypes.evaluateSet()));
    const context = getUnhydratedContext(schema).flexContext;
    // Array nodes have a single `EmptyKey` field:
    const fieldsEntries = mappedData.length === 0
        ? []
        : [
            [
                EmptyKey,
                new UnhydratedSequenceField(context, FieldKinds.sequence.identifier, EmptyKey, mappedData),
            ],
        ];
    return [
        {
            type: brand(schema.identifier),
        },
        new Map(fieldsEntries),
    ];
}
/**
 * {@link TreeNodeSchemaInitializedData.shallowCompatibilityTest} for Array nodes.
 */
function shallowCompatibilityTest(data, schema) {
    if (isTreeValue(data)) {
        return CompatibilityLevel.None;
    }
    if (data instanceof Map) {
        // Maps are iterable, so type checking does allow constructing an ArrayNode from a map if the ArrayNode's type is an array that includes the key and value types of the map.
        return CompatibilityLevel.Low;
    }
    if (Symbol.iterator in data) {
        return CompatibilityLevel.Normal;
    }
    return CompatibilityLevel.None;
}
/**
 * Create an {@link ArrayPlaceAnchor} tracking an insertion point in the array which is currently at the provided index.
 *
 * @param node - The array node to anchor into.
 * @param currentIndex - The current index of the place to track.
 * @remarks
 * This anchor will track the logical position in the array across changes.
 * As long as the subsection of the array surrounding the anchor point is not edited,
 * this anchor will move with them, keeping its relative position to those children fixed.
 * How exactly it behaves when the adjacent portion of the array is modified is subject to change,
 * but this will always report a valid index to insert content at (which can be the index after the last item in the array).
 *
 * This is intended to track a location that might be used for an insertion point (for example in a text editor): future changes to its details should
 * make it behave better for such uses.
 *
 * The current implementation is known to behave particularly poorly if the child which was at the original anchor point's index is removed
 * (jumps to the end of the array): this behavior is subject to change.
 * @privateRemarks
 * When stabilized, this should probably become a method on {@link (TreeArrayNode:interface)}.
 * Future versions of this should use rebaser / changeset logic to do a better job of tracking a location across removals or reinsertion.
 * How this would work, especially for unhydrated nodes is not yet clear.
 * @alpha
 */
export function createArrayInsertionAnchor(node, currentIndex) {
    const field = getInnerNode(node).getBoxed(EmptyKey);
    const child = field.boxedAt(currentIndex);
    return {
        get index() {
            if (child === undefined) {
                return field.length;
            }
            if (child.parentField.parent !== field) {
                return field.length;
            }
            return child.parentField.index;
        },
    };
}
//# sourceMappingURL=arrayNode.js.map