"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.integerIndexable = exports.IntegerIndexable = void 0;
const core_utils_1 = require("@fluidframework/core-utils");
/**
 * Class which can be indexed with integers from 0 to length -1 like arrays.
 * Unlike with arrays, this class asserts for out of bounds indexing on both read and write.
 *
 * @privateRemarks
 * This class works by returning a proxy from its constructor which
 * causes the derived class to get the proxy as its "this" object.
 */
class IntegerIndexable {
    constructor(length) {
        this.length = length;
        return integerIndexable(this, this);
    }
}
exports.IntegerIndexable = IntegerIndexable;
function integerIndexable(outerTarget, provider) {
    function isValidIndex(index) {
        return index >= 0 && index < provider.length;
    }
    function keyToNumber(key) {
        if (typeof key !== "string") {
            return undefined;
        }
        const index = Number.parseInt(key, 10);
        return Number.isNaN(index) ? undefined : index;
    }
    const proxyHandler = {
        get: (target, key, receiver) => {
            const index = keyToNumber(key);
            if (index !== undefined) {
                (0, core_utils_1.assert)(isValidIndex(index), "invalid index");
                return provider.read(index);
            }
            // Pass the proxy as the receiver here, so that any methods on the prototype receive `proxy` as `this`.
            return Reflect.get(target, key, receiver);
        },
        getOwnPropertyDescriptor: (target, key) => {
            const index = keyToNumber(key);
            if (index !== undefined) {
                if (isValidIndex(index)) {
                    const descriptor = {
                        get() {
                            return provider.read(index);
                        },
                        set(value) {
                            provider.write(index, value);
                        },
                        writable: true,
                        enumerable: true,
                        configurable: true, // Must be 'configurable' if property is absent from proxy target.
                    };
                    return descriptor;
                }
                else {
                    return undefined;
                }
            }
            return Reflect.getOwnPropertyDescriptor(target, key);
        },
        has: (target, key) => {
            if (typeof key === "string") {
                const numericKey = Number.parseInt(key, 10);
                if (!Number.isNaN(numericKey)) {
                    return isValidIndex(numericKey);
                }
            }
            return Reflect.has(target, key);
        },
        set: (target, key, newValue) => {
            const index = keyToNumber(key);
            if (index !== undefined) {
                (0, core_utils_1.assert)(isValidIndex(index), "invalid index");
                return provider.write(index, newValue);
            }
            return Reflect.set(target, key, newValue);
        },
        ownKeys: (target) => {
            const keys = Reflect.ownKeys(target);
            for (let index = 0; index < provider.length; index++) {
                keys.push(index.toString());
            }
            return keys;
        },
    };
    const proxy = new Proxy(outerTarget, proxyHandler);
    return proxy;
}
exports.integerIndexable = integerIndexable;
//# sourceMappingURL=integerIndexable.js.map