/* eslint-disable */
'use strict';
class FiggyPudding {
    constructor(specs, opts, providers) {
        this.__specs = specs || {};
        Object.keys(this.__specs).forEach(alias => {
            if (typeof this.__specs[alias] === 'string') {
                const key = this.__specs[alias];
                const realSpec = this.__specs[key];
                if (realSpec) {
                    const aliasArr = realSpec.aliases || [];
                    aliasArr.push(alias, key);
                    realSpec.aliases = [...(new Set(aliasArr))];
                    this.__specs[alias] = realSpec;
                }
                else {
                    throw new Error(`Alias refers to invalid key: ${key} -> ${alias}`);
                }
            }
        });
        this.__opts = opts || {};
        this.__providers = reverse((providers).filter(x => x != null && typeof x === 'object'));
        this.__isFiggyPudding = true;
    }
    get(key) {
        return pudGet(this, key, true);
    }
    get [Symbol.toStringTag]() { return 'FiggyPudding'; }
    forEach(fn, thisArg = this) {
        for (let [key, value] of this.entries()) {
            fn.call(thisArg, value, key, this);
        }
    }
    toJSON() {
        const obj = {};
        this.forEach((val, key) => {
            obj[key] = val;
        });
        return obj;
    }
    *entries(_matcher) {
        for (let key of Object.keys(this.__specs)) {
            yield [key, this.get(key)];
        }
        const matcher = _matcher || this.__opts.other;
        if (matcher) {
            const seen = new Set();
            for (let p of this.__providers) {
                const iter = p.entries ? p.entries(matcher) : entries(p);
                for (let [key, val] of iter) {
                    if (matcher(key) && !seen.has(key)) {
                        seen.add(key);
                        yield [key, val];
                    }
                }
            }
        }
    }
    *[Symbol.iterator]() {
        for (let [key, value] of this.entries()) {
            yield [key, value];
        }
    }
    *keys() {
        for (let [key] of this.entries()) {
            yield key;
        }
    }
    *values() {
        for (let [, value] of this.entries()) {
            yield value;
        }
    }
    concat(...moreConfig) {
        return new Proxy(new FiggyPudding(this.__specs, this.__opts, reverse(this.__providers).concat(moreConfig)), proxyHandler);
    }
}
try {
    const util = require('util');
    FiggyPudding.prototype[util.inspect.custom] = function (depth, opts) {
        return (this[Symbol.toStringTag] + ' ') + util.inspect(this.toJSON(), opts);
    };
}
catch (e) { }
function BadKeyError(key) {
    throw Object.assign(new Error(`invalid config key requested: ${key}`), { code: 'EBADKEY' });
}
function pudGet(pud, key, validate) {
    let spec = pud.__specs[key];
    if (validate && !spec && (!pud.__opts.other || !pud.__opts.other(key))) {
        BadKeyError(key);
    }
    else {
        if (!spec) {
            spec = {};
        }
        let ret;
        for (let p of pud.__providers) {
            ret = tryGet(key, p);
            if (ret === undefined && spec.aliases && spec.aliases.length) {
                for (let alias of spec.aliases) {
                    if (alias === key) {
                        continue;
                    }
                    ret = tryGet(alias, p);
                    if (ret !== undefined) {
                        break;
                    }
                }
            }
            if (ret !== undefined) {
                break;
            }
        }
        if (ret === undefined && spec.default !== undefined) {
            if (typeof spec.default === 'function') {
                return spec.default(pud);
            }
            else {
                return spec.default;
            }
        }
        else {
            return ret;
        }
    }
}
function tryGet(key, p) {
    let ret;
    if (p.__isFiggyPudding) {
        ret = pudGet(p, key, false);
    }
    else if (typeof p.get === 'function') {
        ret = p.get(key);
    }
    else {
        ret = p[key];
    }
    return ret;
}
const proxyHandler = {
    has(obj, prop) {
        return prop in obj.__specs && pudGet(obj, prop, false) !== undefined;
    },
    ownKeys(obj) {
        return Object.keys(obj.__specs);
    },
    get(obj, prop) {
        if (typeof prop === 'symbol' ||
            prop.slice(0, 2) === '__' ||
            prop in FiggyPudding.prototype) {
            return obj[prop];
        }
        return obj.get(prop);
    },
    set(obj, prop, value) {
        if (typeof prop === 'symbol' ||
            prop.slice(0, 2) === '__') {
            obj[prop] = value;
            return true;
        }
        else {
            throw new Error('figgyPudding options cannot be modified. Use .concat() instead.');
        }
    },
    deleteProperty() {
        throw new Error('figgyPudding options cannot be deleted. Use .concat() and shadow them instead.');
    }
};
module.exports = figgyPudding;
function figgyPudding(specs, opts) {
    function factory(...providers) {
        return new Proxy(new FiggyPudding(specs, opts, providers), proxyHandler);
    }
    return factory;
}
function reverse(arr) {
    const ret = [];
    arr.forEach(x => ret.unshift(x));
    return ret;
}
function entries(obj) {
    return Object.keys(obj).map(k => [k, obj[k]]);
}
//# sourceMappingURL=index.js.map