请评价一下以下JS代码，并且告诉我有什么可以优化的地方，如果能提供这些优化实现更好。谢谢。


---

const Operation = {
    Remove: 'remove',
    Replace: 'replace',
    Add: 'add',
};

// Don't use `Symbol()` just for 3rd party access the draft
const PROXY_DRAFT = Symbol.for('__MUTATIVE_PROXY_DRAFT__');
const RAW_RETURN_SYMBOL = Symbol('__MUTATIVE_RAW_RETURN_SYMBOL__');
const iteratorSymbol = Symbol.iterator;
const dataTypes = {
    mutable: 'mutable',
    immutable: 'immutable',
};

const internal = {};

function latest(proxyDraft) {
    var _a;
    return (_a = proxyDraft.copy) !== null && _a !== void 0 ? _a : proxyDraft.original;
}
/**
 * Check if the value is a draft
 */
function isDraft(target) {
    return !!getProxyDraft(target);
}
function getProxyDraft(value) {
    if (typeof value !== 'object')
        return null;
    return value === null || value === void 0 ? void 0 : value[PROXY_DRAFT];
}
function getValue(value) {
    var _a;
    const proxyDraft = getProxyDraft(value);
    return proxyDraft ? (_a = proxyDraft.copy) !== null && _a !== void 0 ? _a : proxyDraft.original : value;
}
/**
 * Check if a value is draftable
 */
function isDraftable(value, options) {
    if (!value || typeof value !== 'object')
        return false;
    let markResult;
    return (Object.getPrototypeOf(value) === Object.prototype ||
        Array.isArray(value) ||
        value instanceof Map ||
        value instanceof Set ||
        (!!(options === null || options === void 0 ? void 0 : options.mark) &&
            ((markResult = options.mark(value, dataTypes)) === dataTypes.immutable ||
                typeof markResult === 'function')));
}
function getPath(target, path = []) {
    if (Object.hasOwnProperty.call(target, 'key')) {
        // check if the parent is a draft and the original value is not equal to the current value
        const proxyDraft = getProxyDraft(get(target.parent.copy, target.key));
        if (proxyDraft !== null && (proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.original) !== target.original) {
            return null;
        }
        path.push(target.parent.type === 3 /* DraftType.Set */
            ? Array.from(target.parent.setMap.keys()).indexOf(target.key)
            : target.key);
    }
    if (target.parent) {
        return getPath(target.parent, path);
    }
    return path.reverse();
}
function getType(target) {
    if (Array.isArray(target))
        return 1 /* DraftType.Array */;
    if (target instanceof Map)
        return 2 /* DraftType.Map */;
    if (target instanceof Set)
        return 3 /* DraftType.Set */;
    return 0 /* DraftType.Object */;
}
function get(target, key) {
    return getType(target) === 2 /* DraftType.Map */ ? target.get(key) : target[key];
}
function set(target, key, value) {
    const type = getType(target);
    if (type === 2 /* DraftType.Map */) {
        target.set(key, value);
    }
    else {
        target[key] = value;
    }
}
function peek(target, key) {
    const state = getProxyDraft(target);
    const source = state ? latest(state) : target;
    return source[key];
}
function isEqual(x, y) {
    if (x === y) {
        return x !== 0 || 1 / x === 1 / y;
    }
    else {
        return x !== x && y !== y;
    }
}
function revokeProxy(proxyDraft) {
    if (!proxyDraft)
        return;
    while (proxyDraft.finalities.revoke.length > 0) {
        const revoke = proxyDraft.finalities.revoke.pop();
        revoke();
    }
}
// handle JSON Pointer path with spec https://www.rfc-editor.org/rfc/rfc6901
function escapePath(path, pathAsArray) {
    return pathAsArray
        ? path
        : ['']
            .concat(path)
            .map((_item) => {
            const item = `${_item}`;
            if (item.indexOf('/') === -1 && item.indexOf('~') === -1)
                return item;
            return item.replace(/~/g, '~0').replace(/\//g, '~1');
        })
            .join('/');
}
function unescapePath(path) {
    if (Array.isArray(path))
        return path;
    return path
        .split('/')
        .map((_item) => _item.replace(/~1/g, '/').replace(/~0/g, '~'))
        .slice(1);
}

function strictCopy(target) {
    const copy = Object.create(Object.getPrototypeOf(target));
    Reflect.ownKeys(target).forEach((key) => {
        let desc = Reflect.getOwnPropertyDescriptor(target, key);
        if (desc.enumerable && desc.configurable && desc.writable) {
            copy[key] = target[key];
            return;
        }
        // for freeze
        if (!desc.writable) {
            desc.writable = true;
            desc.configurable = true;
        }
        if (desc.get || desc.set)
            desc = {
                configurable: true,
                writable: true,
                enumerable: desc.enumerable,
                value: target[key],
            };
        Reflect.defineProperty(copy, key, desc);
    });
    return copy;
}
const propIsEnum = Object.prototype.propertyIsEnumerable;
function shallowCopy(original, options) {
    let markResult;
    if (Array.isArray(original)) {
        return Array.prototype.concat.call(original);
    }
    else if (original instanceof Set) {
        return new Set(original.values());
    }
    else if (original instanceof Map) {
        return new Map(original);
    }
    else if ((options === null || options === void 0 ? void 0 : options.mark) &&
        ((markResult = options.mark(original, dataTypes)),
            markResult !== undefined) &&
        markResult !== dataTypes.mutable) {
        if (markResult === dataTypes.immutable) {
            return strictCopy(original);
        }
        else if (typeof markResult === 'function') {
            if ((options.enablePatches || options.enableAutoFreeze)) {
                throw new Error(`You can't use mark and patches or auto freeze together.`);
            }
            return markResult();
        }
        throw new Error(`Unsupported mark result: ${markResult}`);
    }
    else if (typeof original === 'object' &&
        Object.getPrototypeOf(original) === Object.prototype) {
        // For best performance with shallow copies,
        // don't use `Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));` by default.
        const copy = {};
        Object.keys(original).forEach((key) => {
            copy[key] = original[key];
        });
        Object.getOwnPropertySymbols(original).forEach((key) => {
            if (propIsEnum.call(original, key)) {
                copy[key] = original[key];
            }
        });
        return copy;
    }
    else {
        throw new Error(`Please check mark() to ensure that it is a stable marker draftable function.`);
    }
}
function ensureShallowCopy(target) {
    if (target.copy)
        return;
    target.copy = shallowCopy(target.original, target.options);
}
function deepClone(target) {
    if (!isDraftable(target))
        return getValue(target);
    if (Array.isArray(target))
        return target.map(deepClone);
    if (target instanceof Map)
        return new Map(Array.from(target.entries()).map(([k, v]) => [k, deepClone(v)]));
    if (target instanceof Set)
        return new Set(Array.from(target).map(deepClone));
    const copy = Object.create(Object.getPrototypeOf(target));
    for (const key in target)
        copy[key] = deepClone(target[key]);
    return copy;
}
function cloneIfNeeded(target) {
    return isDraft(target) ? deepClone(target) : target;
}

function markChanged(proxyDraft) {
    var _a;
    proxyDraft.assignedMap = (_a = proxyDraft.assignedMap) !== null && _a !== void 0 ? _a : new Map();
    if (!proxyDraft.operated) {
        proxyDraft.operated = true;
        if (proxyDraft.parent) {
            markChanged(proxyDraft.parent);
        }
    }
}

function throwFrozenError() {
    throw new Error('Cannot modify frozen object');
}
function deepFreeze(target, subKey, updatedValues, stack, keys) {
    {
        updatedValues = updatedValues !== null && updatedValues !== void 0 ? updatedValues : new WeakMap();
        stack = stack !== null && stack !== void 0 ? stack : [];
        keys = keys !== null && keys !== void 0 ? keys : [];
        const value = updatedValues.has(target)
            ? updatedValues.get(target)
            : target;
        if (stack.length > 0) {
            const index = stack.indexOf(value);
            if (value && typeof value === 'object' && index !== -1) {
                if (stack[0] === value) {
                    throw new Error(`Forbids circular reference`);
                }
                throw new Error(`Forbids circular reference: ~/${keys
                    .slice(0, index)
                    .map((key, index) => {
                    if (typeof key === 'symbol')
                        return `[${key.toString()}]`;
                    const parent = stack[index];
                    if (typeof key === 'object' &&
                        (parent instanceof Map || parent instanceof Set))
                        return Array.from(parent.keys()).indexOf(key);
                    return key;
                })
                    .join('/')}`);
            }
            stack.push(value);
            keys.push(subKey);
        }
        else {
            stack.push(value);
        }
    }
    if (Object.isFrozen(target) || isDraft(target)) {
        {
            stack.pop();
            keys.pop();
        }
        return;
    }
    const type = getType(target);
    switch (type) {
        case 2 /* DraftType.Map */:
            for (const [key, value] of target) {
                deepFreeze(key, key, updatedValues, stack, keys);
                deepFreeze(value, key, updatedValues, stack, keys);
            }
            target.set = target.clear = target.delete = throwFrozenError;
            break;
        case 3 /* DraftType.Set */:
            for (const value of target) {
                deepFreeze(value, value, updatedValues, stack, keys);
            }
            target.add = target.clear = target.delete = throwFrozenError;
            break;
        case 1 /* DraftType.Array */:
            Object.freeze(target);
            let index = 0;
            for (const value of target) {
                deepFreeze(value, index, updatedValues, stack, keys);
                index += 1;
            }
            break;
        default:
            Object.freeze(target);
            // ignore non-enumerable or symbol properties
            Object.keys(target).forEach((name) => {
                const value = target[name];
                deepFreeze(value, name, updatedValues, stack, keys);
            });
    }
    {
        stack.pop();
        keys.pop();
    }
}

function has(target, key) {
    return target instanceof Map
        ? target.has(key)
        : Object.prototype.hasOwnProperty.call(target, key);
}
function getDescriptor(target, key) {
    if (key in target) {
        let prototype = Reflect.getPrototypeOf(target);
        while (prototype) {
            const descriptor = Reflect.getOwnPropertyDescriptor(prototype, key);
            if (descriptor)
                return descriptor;
            prototype = Reflect.getPrototypeOf(prototype);
        }
    }
    return;
}

function forEach(target, iter) {
    const type = getType(target);
    if (type === 0 /* DraftType.Object */) {
        Reflect.ownKeys(target).forEach((key) => {
            iter(key, target[key], target);
        });
    }
    else if (type === 1 /* DraftType.Array */) {
        let index = 0;
        for (const entry of target) {
            iter(index, entry, target);
            index += 1;
        }
    }
    else {
        target.forEach((entry, index) => iter(index, entry, target));
    }
}

function handleValue(target, handledSet, options) {
    if (isDraft(target) ||
        !isDraftable(target, options) ||
        handledSet.has(target) ||
        Object.isFrozen(target))
        return;
    const isSet = target instanceof Set;
    const setMap = isSet ? new Map() : undefined;
    handledSet.add(target);
    forEach(target, (key, value) => {
        var _a;
        if (isDraft(value)) {
            const proxyDraft = getProxyDraft(value);
            ensureShallowCopy(proxyDraft);
            // A draft where a child node has been changed, or assigned a value
            const updatedValue = ((_a = proxyDraft.assignedMap) === null || _a === void 0 ? void 0 : _a.size) || proxyDraft.operated
                ? proxyDraft.copy
                : proxyDraft.original;
            // final update value
            set(isSet ? setMap : target, key, updatedValue);
        }
        else {
            handleValue(value, handledSet, options);
        }
    });
    if (setMap) {
        const set = target;
        const values = Array.from(set);
        set.clear();
        values.forEach((value) => {
            set.add(setMap.has(value) ? setMap.get(value) : value);
        });
    }
}
function finalizeAssigned(proxyDraft, key) {
    // handle the draftable assigned values， and the value is not a draft
    const copy = proxyDraft.type === 3 /* DraftType.Set */ ? proxyDraft.setMap : proxyDraft.copy;
    if (proxyDraft.finalities.revoke.length > 1 &&
        proxyDraft.assignedMap.get(key) &&
        copy) {
        handleValue(get(copy, key), proxyDraft.finalities.handledSet, proxyDraft.options);
    }
}
function finalizeSetValue(target) {
    if (target.type === 3 /* DraftType.Set */ && target.copy) {
        target.copy.clear();
        target.setMap.forEach((value) => {
            target.copy.add(getValue(value));
        });
    }
}
function finalizePatches(target, generatePatches, patches, inversePatches) {
    const shouldFinalize = target.operated &&
        target.assignedMap &&
        target.assignedMap.size > 0 &&
        !target.finalized;
    if (shouldFinalize) {
        if (patches && inversePatches) {
            const basePath = getPath(target);
            if (basePath) {
                generatePatches(target, basePath, patches, inversePatches);
            }
        }
        target.finalized = true;
    }
}
function markFinalization(target, key, value, generatePatches) {
    const proxyDraft = getProxyDraft(value);
    if (proxyDraft) {
        // !case: assign the draft value
        if (!proxyDraft.callbacks) {
            proxyDraft.callbacks = [];
        }
        proxyDraft.callbacks.push((patches, inversePatches) => {
            var _a;
            const copy = target.type === 3 /* DraftType.Set */ ? target.setMap : target.copy;
            if (isEqual(get(copy, key), value)) {
                let updatedValue = proxyDraft.original;
                if (proxyDraft.copy) {
                    updatedValue = proxyDraft.copy;
                }
                finalizeSetValue(target);
                finalizePatches(target, generatePatches, patches, inversePatches);
                if (target.options.enableAutoFreeze) {
                    target.options.updatedValues =
                        (_a = target.options.updatedValues) !== null && _a !== void 0 ? _a : new WeakMap();
                    target.options.updatedValues.set(updatedValue, proxyDraft.original);
                }
                // final update value
                set(copy, key, updatedValue);
            }
        });
        if (target.options.enableAutoFreeze) {
            // !case: assign the draft value in cross draft tree
            if (proxyDraft.finalities !== target.finalities) {
                target.options.enableAutoFreeze = false;
            }
        }
    }
    if (isDraftable(value, target.options)) {
        // !case: assign the non-draft value
        target.finalities.draft.push(() => {
            const copy = target.type === 3 /* DraftType.Set */ ? target.setMap : target.copy;
            if (isEqual(get(copy, key), value)) {
                finalizeAssigned(target, key);
            }
        });
    }
}

function generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray) {
    let { original, assignedMap, options } = proxyState;
    let copy = proxyState.copy;
    if (copy.length < original.length) {
        [original, copy] = [copy, original];
        [patches, inversePatches] = [inversePatches, patches];
    }
    for (let index = 0; index < original.length; index += 1) {
        if (assignedMap.get(index.toString()) && copy[index] !== original[index]) {
            const _path = basePath.concat([index]);
            const path = escapePath(_path, pathAsArray);
            patches.push({
                op: Operation.Replace,
                path,
                // If it is a draft, it needs to be deep cloned, and it may also be non-draft.
                value: cloneIfNeeded(copy[index]),
            });
            inversePatches.push({
                op: Operation.Replace,
                path,
                // If it is a draft, it needs to be deep cloned, and it may also be non-draft.
                value: cloneIfNeeded(original[index]),
            });
        }
    }
    for (let index = original.length; index < copy.length; index += 1) {
        const _path = basePath.concat([index]);
        const path = escapePath(_path, pathAsArray);
        patches.push({
            op: Operation.Add,
            path,
            // If it is a draft, it needs to be deep cloned, and it may also be non-draft.
            value: cloneIfNeeded(copy[index]),
        });
    }
    if (original.length < copy.length) {
        // https://www.rfc-editor.org/rfc/rfc6902#appendix-A.4
        // For performance, here we only generate an operation that replaces the length of the array,
        // which is inconsistent with JSON Patch specification
        const { arrayLengthAssignment = true } = options.enablePatches;
        if (arrayLengthAssignment) {
            const _path = basePath.concat(['length']);
            const path = escapePath(_path, pathAsArray);
            inversePatches.push({
                op: Operation.Replace,
                path,
                value: original.length,
            });
        }
        else {
            for (let index = copy.length; original.length < index; index -= 1) {
                const _path = basePath.concat([index - 1]);
                const path = escapePath(_path, pathAsArray);
                inversePatches.push({
                    op: Operation.Remove,
                    path,
                });
            }
        }
    }
}
function generatePatchesFromAssigned({ original, copy, assignedMap }, basePath, patches, inversePatches, pathAsArray) {
    assignedMap.forEach((assignedValue, key) => {
        const originalValue = get(original, key);
        const value = cloneIfNeeded(get(copy, key));
        const op = !assignedValue
            ? Operation.Remove
            : has(original, key)
                ? Operation.Replace
                : Operation.Add;
        if (isEqual(originalValue, value) && op === Operation.Replace)
            return;
        const _path = basePath.concat(key);
        const path = escapePath(_path, pathAsArray);
        patches.push(op === Operation.Remove ? { op, path } : { op, path, value });
        inversePatches.push(op === Operation.Add
            ? { op: Operation.Remove, path }
            : op === Operation.Remove
                ? { op: Operation.Add, path, value: originalValue }
                : { op: Operation.Replace, path, value: originalValue });
    });
}
function generateSetPatches({ original, copy }, basePath, patches, inversePatches, pathAsArray) {
    let index = 0;
    original.forEach((value) => {
        if (!copy.has(value)) {
            const _path = basePath.concat([index]);
            const path = escapePath(_path, pathAsArray);
            patches.push({
                op: Operation.Remove,
                path,
                value,
            });
            inversePatches.unshift({
                op: Operation.Add,
                path,
                value,
            });
        }
        index += 1;
    });
    index = 0;
    copy.forEach((value) => {
        if (!original.has(value)) {
            const _path = basePath.concat([index]);
            const path = escapePath(_path, pathAsArray);
            patches.push({
                op: Operation.Add,
                path,
                value,
            });
            inversePatches.unshift({
                op: Operation.Remove,
                path,
                value,
            });
        }
        index += 1;
    });
}
function generatePatches(proxyState, basePath, patches, inversePatches) {
    const { pathAsArray = true } = proxyState.options.enablePatches;
    switch (proxyState.type) {
        case 0 /* DraftType.Object */:
        case 2 /* DraftType.Map */:
            return generatePatchesFromAssigned(proxyState, basePath, patches, inversePatches, pathAsArray);
        case 1 /* DraftType.Array */:
            return generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray);
        case 3 /* DraftType.Set */:
            return generateSetPatches(proxyState, basePath, patches, inversePatches, pathAsArray);
    }
}

let readable = false;
const checkReadable = (value, options, ignoreCheckDraftable = false) => {
    if (typeof value === 'object' &&
        value !== null &&
        (!isDraftable(value, options) || ignoreCheckDraftable) &&
        !readable) {
        throw new Error(`Strict mode: Mutable data cannot be accessed directly, please use 'unsafe(callback)' wrap.`);
    }
};
/**
 * `unsafe(callback)` to access mutable data directly in strict mode.
 *
 * ## Example
 *
 * ```ts
 * import { create, unsafe } from '../index';
 *
 * class Foobar {
 *   bar = 1;
 * }
 *
 * const baseState = { foobar: new Foobar() };
 * const state = create(
 *   baseState,
 *   (draft) => {
 *    unsafe(() => {
 *      draft.foobar.bar = 2;
 *    });
 *   },
 *   {
 *     strict: true,
 *   }
 * );
 *
 * expect(state).toBe(baseState);
 * expect(state.foobar).toBe(baseState.foobar);
 * expect(state.foobar.bar).toBe(2);
 * ```
 */
function unsafe(callback) {
    readable = true;
    let result;
    try {
        result = callback();
    }
    finally {
        readable = false;
    }
    return result;
}

const mapHandler = {
    get size() {
        const current = latest(getProxyDraft(this));
        return current.size;
    },
    has(key) {
        return latest(getProxyDraft(this)).has(key);
    },
    set(key, value) {
        const target = getProxyDraft(this);
        const source = latest(target);
        if (!source.has(key) || !isEqual(source.get(key), value)) {
            ensureShallowCopy(target);
            markChanged(target);
            target.assignedMap.set(key, true);
            target.copy.set(key, value);
            markFinalization(target, key, value, generatePatches);
        }
        return this;
    },
    delete(key) {
        if (!this.has(key)) {
            return false;
        }
        const target = getProxyDraft(this);
        ensureShallowCopy(target);
        markChanged(target);
        if (target.original.has(key)) {
            target.assignedMap.set(key, false);
        }
        else {
            target.assignedMap.delete(key);
        }
        target.copy.delete(key);
        return true;
    },
    clear() {
        const target = getProxyDraft(this);
        if (!this.size)
            return;
        ensureShallowCopy(target);
        markChanged(target);
        target.assignedMap = new Map();
        for (const [key] of target.original) {
            target.assignedMap.set(key, false);
        }
        target.copy.clear();
    },
    forEach(callback, thisArg) {
        const target = getProxyDraft(this);
        latest(target).forEach((_value, _key) => {
            callback.call(thisArg, this.get(_key), _key, this);
        });
    },
    get(key) {
        var _a, _b;
        const target = getProxyDraft(this);
        const value = latest(target).get(key);
        const mutable = ((_b = (_a = target.options).mark) === null || _b === void 0 ? void 0 : _b.call(_a, value, dataTypes)) === dataTypes.mutable;
        if (target.options.strict) {
            checkReadable(value, target.options, mutable);
        }
        if (mutable) {
            return value;
        }
        if (target.finalized || !isDraftable(value, target.options)) {
            return value;
        }
        // drafted or reassigned
        if (value !== target.original.get(key)) {
            return value;
        }
        const draft = internal.createDraft({
            original: value,
            parentDraft: target,
            key,
            finalities: target.finalities,
            options: target.options,
        });
        ensureShallowCopy(target);
        target.copy.set(key, draft);
        return draft;
    },
    keys() {
        return latest(getProxyDraft(this)).keys();
    },
    values() {
        const iterator = this.keys();
        return {
            [iteratorSymbol]: () => this.values(),
            next: () => {
                const result = iterator.next();
                if (result.done)
                    return result;
                const value = this.get(result.value);
                return {
                    done: false,
                    value,
                };
            },
        };
    },
    entries() {
        const iterator = this.keys();
        return {
            [iteratorSymbol]: () => this.entries(),
            next: () => {
                const result = iterator.next();
                if (result.done)
                    return result;
                const value = this.get(result.value);
                return {
                    done: false,
                    value: [result.value, value],
                };
            },
        };
    },
    [iteratorSymbol]() {
        return this.entries();
    },
};
const mapHandlerKeys = Reflect.ownKeys(mapHandler);

const getNextIterator = (target, iterator, { isValuesIterator }) => () => {
    var _a, _b;
    const result = iterator.next();
    if (result.done)
        return result;
    const key = result.value;
    let value = target.setMap.get(key);
    const currentDraft = getProxyDraft(value);
    const mutable = ((_b = (_a = target.options).mark) === null || _b === void 0 ? void 0 : _b.call(_a, value, dataTypes)) === dataTypes.mutable;
    if (target.options.strict) {
        checkReadable(key, target.options, mutable);
    }
    if (!mutable &&
        !currentDraft &&
        isDraftable(key, target.options) &&
        !target.finalized &&
        target.original.has(key)) {
        // draft a draftable original set item
        const proxy = internal.createDraft({
            original: key,
            parentDraft: target,
            key,
            finalities: target.finalities,
            options: target.options,
        });
        target.setMap.set(key, proxy);
        value = proxy;
    }
    else if (currentDraft) {
        // drafted
        value = currentDraft.proxy;
    }
    return {
        done: false,
        value: isValuesIterator ? value : [value, value],
    };
};
const setHandler = {
    get size() {
        const target = getProxyDraft(this);
        return target.setMap.size;
    },
    has(value) {
        const target = getProxyDraft(this);
        // reassigned or non-draftable values
        if (target.setMap.has(value))
            return true;
        ensureShallowCopy(target);
        const valueProxyDraft = getProxyDraft(value);
        // drafted
        if (valueProxyDraft && target.setMap.has(valueProxyDraft.original))
            return true;
        return false;
    },
    add(value) {
        const target = getProxyDraft(this);
        if (!this.has(value)) {
            ensureShallowCopy(target);
            markChanged(target);
            target.assignedMap.set(value, true);
            target.setMap.set(value, value);
            markFinalization(target, value, value, generatePatches);
        }
        return this;
    },
    delete(value) {
        if (!this.has(value)) {
            return false;
        }
        const target = getProxyDraft(this);
        ensureShallowCopy(target);
        markChanged(target);
        const valueProxyDraft = getProxyDraft(value);
        if (valueProxyDraft && target.setMap.has(valueProxyDraft.original)) {
            // delete drafted
            target.assignedMap.set(valueProxyDraft.original, false);
            return target.setMap.delete(valueProxyDraft.original);
        }
        if (!valueProxyDraft && target.setMap.has(value)) {
            // non-draftable values
            target.assignedMap.set(value, false);
        }
        else {
            // reassigned
            target.assignedMap.delete(value);
        }
        // delete reassigned or non-draftable values
        return target.setMap.delete(value);
    },
    clear() {
        if (!this.size)
            return;
        const target = getProxyDraft(this);
        ensureShallowCopy(target);
        markChanged(target);
        for (const value of target.original) {
            target.assignedMap.set(value, false);
        }
        target.setMap.clear();
    },
    values() {
        const target = getProxyDraft(this);
        ensureShallowCopy(target);
        const iterator = target.setMap.keys();
        return {
            [Symbol.iterator]: () => this.values(),
            next: getNextIterator(target, iterator, { isValuesIterator: true }),
        };
    },
    entries() {
        const target = getProxyDraft(this);
        ensureShallowCopy(target);
        const iterator = target.setMap.keys();
        return {
            [Symbol.iterator]: () => this.entries(),
            next: getNextIterator(target, iterator, {
                isValuesIterator: false,
            }),
        };
    },
    keys() {
        return this.values();
    },
    [iteratorSymbol]() {
        return this.values();
    },
    forEach(callback, thisArg) {
        const iterator = this.values();
        let result = iterator.next();
        while (!result.done) {
            callback.call(thisArg, result.value, result.value, this);
            result = iterator.next();
        }
    },
};
const setHandlerKeys = Reflect.ownKeys(setHandler);

const draftsCache = new WeakSet();
const proxyHandler = {
    get(target, key, receiver) {
        var _a, _b;
        const copy = (_a = target.copy) === null || _a === void 0 ? void 0 : _a[key];
        // Improve draft reading performance by caching the draft copy.
        if (copy && draftsCache.has(copy)) {
            return copy;
        }
        if (key === PROXY_DRAFT)
            return target;
        let markResult;
        if (target.options.mark) {
            // handle `Uncaught TypeError: Method get Map.prototype.size called on incompatible receiver #<Map>`
            // or `Uncaught TypeError: Method get Set.prototype.size called on incompatible receiver #<Set>`
            const value = key === 'size' &&
                (target.original instanceof Map || target.original instanceof Set)
                ? Reflect.get(target.original, key)
                : Reflect.get(target.original, key, receiver);
            markResult = target.options.mark(value, dataTypes);
            if (markResult === dataTypes.mutable) {
                if (target.options.strict) {
                    checkReadable(value, target.options, true);
                }
                return value;
            }
        }
        const source = latest(target);
        if (source instanceof Map && mapHandlerKeys.includes(key)) {
            if (key === 'size') {
                return Object.getOwnPropertyDescriptor(mapHandler, 'size').get.call(target.proxy);
            }
            const handle = mapHandler[key];
            if (handle) {
                return handle.bind(target.proxy);
            }
        }
        if (source instanceof Set && setHandlerKeys.includes(key)) {
            if (key === 'size') {
                return Object.getOwnPropertyDescriptor(setHandler, 'size').get.call(target.proxy);
            }
            const handle = setHandler[key];
            if (handle) {
                return handle.bind(target.proxy);
            }
        }
        if (!has(source, key)) {
            const desc = getDescriptor(source, key);
            return desc
                ? `value` in desc
                    ? desc.value
                    : // !case: support for getter
                        (_b = desc.get) === null || _b === void 0 ? void 0 : _b.call(target.proxy)
                : undefined;
        }
        const value = source[key];
        if (target.options.strict) {
            checkReadable(value, target.options);
        }
        if (target.finalized || !isDraftable(value, target.options)) {
            return value;
        }
        // Ensure that the assigned values are not drafted
        if (value === peek(target.original, key)) {
            ensureShallowCopy(target);
            target.copy[key] = createDraft({
                original: target.original[key],
                parentDraft: target,
                key: target.type === 1 /* DraftType.Array */ ? Number(key) : key,
                finalities: target.finalities,
                options: target.options,
            });
            // !case: support for custom shallow copy function
            if (typeof markResult === 'function') {
                const subProxyDraft = getProxyDraft(target.copy[key]);
                ensureShallowCopy(subProxyDraft);
                // Trigger a custom shallow copy to update to a new copy
                markChanged(subProxyDraft);
                return subProxyDraft.copy;
            }
            return target.copy[key];
        }
        return value;
    },
    set(target, key, value) {
        var _a;
        if (target.type === 3 /* DraftType.Set */ || target.type === 2 /* DraftType.Map */) {
            throw new Error(`Map/Set draft does not support any property assignment.`);
        }
        let _key;
        if (target.type === 1 /* DraftType.Array */ &&
            key !== 'length' &&
            !(Number.isInteger((_key = Number(key))) &&
                _key >= 0 &&
                (key === 0 || _key === 0 || String(_key) === String(key)))) {
            throw new Error(`Only supports setting array indices and the 'length' property.`);
        }
        const desc = getDescriptor(latest(target), key);
        if (desc === null || desc === void 0 ? void 0 : desc.set) {
            // !case: cover the case of setter
            desc.set.call(target.proxy, value);
            return true;
        }
        const current = peek(latest(target), key);
        const currentProxyDraft = getProxyDraft(current);
        if (currentProxyDraft && isEqual(currentProxyDraft.original, value)) {
            // !case: ignore the case of assigning the original draftable value to a draft
            target.copy[key] = value;
            target.assignedMap = (_a = target.assignedMap) !== null && _a !== void 0 ? _a : new Map();
            target.assignedMap.set(key, false);
            return true;
        }
        // !case: handle new props with value 'undefined'
        if (isEqual(value, current) &&
            (value !== undefined || has(target.original, key)))
            return true;
        ensureShallowCopy(target);
        markChanged(target);
        if (has(target.original, key) && isEqual(value, target.original[key])) {
            // !case: handle the case of assigning the original non-draftable value to a draft
            target.assignedMap.delete(key);
        }
        else {
            target.assignedMap.set(key, true);
        }
        target.copy[key] = value;
        markFinalization(target, key, value, generatePatches);
        return true;
    },
    has(target, key) {
        return key in latest(target);
    },
    ownKeys(target) {
        return Reflect.ownKeys(latest(target));
    },
    getOwnPropertyDescriptor(target, key) {
        const source = latest(target);
        const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
        if (!descriptor)
            return descriptor;
        return {
            writable: true,
            configurable: target.type !== 1 /* DraftType.Array */ || key !== 'length',
            enumerable: descriptor.enumerable,
            value: source[key],
        };
    },
    getPrototypeOf(target) {
        return Reflect.getPrototypeOf(target.original);
    },
    setPrototypeOf() {
        throw new Error(`Cannot call 'setPrototypeOf()' on drafts`);
    },
    defineProperty() {
        throw new Error(`Cannot call 'defineProperty()' on drafts`);
    },
    deleteProperty(target, key) {
        var _a;
        if (target.type === 1 /* DraftType.Array */) {
            return proxyHandler.set.call(this, target, key, undefined, target.proxy);
        }
        if (peek(target.original, key) !== undefined || key in target.original) {
            // !case: delete an existing key
            ensureShallowCopy(target);
            markChanged(target);
            target.assignedMap.set(key, false);
        }
        else {
            target.assignedMap = (_a = target.assignedMap) !== null && _a !== void 0 ? _a : new Map();
            // The original non-existent key has been deleted
            target.assignedMap.delete(key);
        }
        if (target.copy)
            delete target.copy[key];
        return true;
    },
};
function createDraft(createDraftOptions) {
    const { original, parentDraft, key, finalities, options } = createDraftOptions;
    const type = getType(original);
    const proxyDraft = {
        type,
        finalized: false,
        parent: parentDraft,
        original,
        copy: null,
        proxy: null,
        finalities,
        options,
        // Mapping of draft Set items to their corresponding draft values.
        setMap: type === 3 /* DraftType.Set */
            ? new Map(original.entries())
            : undefined,
    };
    // !case: undefined as a draft map key
    if (key || 'key' in createDraftOptions) {
        proxyDraft.key = key;
    }
    const { proxy, revoke } = Proxy.revocable(type === 1 /* DraftType.Array */ ? Object.assign([], proxyDraft) : proxyDraft, proxyHandler);
    finalities.revoke.push(revoke);
    draftsCache.add(proxy);
    proxyDraft.proxy = proxy;
    if (parentDraft) {
        const target = parentDraft;
        target.finalities.draft.push((patches, inversePatches) => {
            var _a, _b;
            const oldProxyDraft = getProxyDraft(proxy);
            // if target is a Set draft, `setMap` is the real Set copies proxy mapping.
            let copy = target.type === 3 /* DraftType.Set */ ? target.setMap : target.copy;
            const draft = get(copy, key);
            const proxyDraft = getProxyDraft(draft);
            if (proxyDraft) {
                // assign the updated value to the copy object
                let updatedValue = proxyDraft.original;
                if (proxyDraft.operated) {
                    updatedValue = getValue(draft);
                }
                finalizeSetValue(proxyDraft);
                finalizePatches(proxyDraft, generatePatches, patches, inversePatches);
                if (target.options.enableAutoFreeze) {
                    target.options.updatedValues =
                        (_a = target.options.updatedValues) !== null && _a !== void 0 ? _a : new WeakMap();
                    target.options.updatedValues.set(updatedValue, proxyDraft.original);
                }
                // final update value
                set(copy, key, updatedValue);
            }
            // !case: handle the deleted key
            (_b = oldProxyDraft.callbacks) === null || _b === void 0 ? void 0 : _b.forEach((callback) => {
                callback(patches, inversePatches);
            });
        });
    }
    else {
        // !case: handle the root draft
        const target = getProxyDraft(proxy);
        target.finalities.draft.push((patches, inversePatches) => {
            finalizeSetValue(target);
            finalizePatches(target, generatePatches, patches, inversePatches);
        });
    }
    return proxy;
}
internal.createDraft = createDraft;
function finalizeDraft(result, returnedValue, patches, inversePatches, enableAutoFreeze) {
    var _a;
    const proxyDraft = getProxyDraft(result);
    const original = (_a = proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.original) !== null && _a !== void 0 ? _a : result;
    const hasReturnedValue = !!returnedValue.length;
    if (proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.operated) {
        while (proxyDraft.finalities.draft.length > 0) {
            const finalize = proxyDraft.finalities.draft.pop();
            finalize(patches, inversePatches);
        }
    }
    const state = hasReturnedValue
        ? returnedValue[0]
        : proxyDraft
            ? proxyDraft.operated
                ? proxyDraft.copy
                : proxyDraft.original
            : result;
    if (proxyDraft)
        revokeProxy(proxyDraft);
    if (enableAutoFreeze) {
        deepFreeze(state, state, proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.options.updatedValues);
    }
    return [
        state,
        patches && hasReturnedValue
            ? [{ op: Operation.Replace, path: [], value: returnedValue[0] }]
            : patches,
        inversePatches && hasReturnedValue
            ? [{ op: Operation.Replace, path: [], value: original }]
            : inversePatches,
    ];
}

function draftify(baseState, options) {
    var _a;
    const finalities = {
        draft: [],
        revoke: [],
        handledSet: new WeakSet(),
    };
    let patches;
    let inversePatches;
    if (options.enablePatches) {
        patches = [];
        inversePatches = [];
    }
    const isMutable = ((_a = options.mark) === null || _a === void 0 ? void 0 : _a.call(options, baseState, dataTypes)) === dataTypes.mutable ||
        !isDraftable(baseState, options);
    const draft = isMutable
        ? baseState
        : createDraft({
            original: baseState,
            parentDraft: null,
            finalities,
            options,
        });
    return [
        draft,
        (returnedValue = []) => {
            const [finalizedState, finalizedPatches, finalizedInversePatches] = finalizeDraft(draft, returnedValue, patches, inversePatches, options.enableAutoFreeze);
            return (options.enablePatches
                ? [finalizedState, finalizedPatches, finalizedInversePatches]
                : finalizedState);
        },
    ];
}

function handleReturnValue(options) {
    const { rootDraft, value, useRawReturn = false, isRoot = true } = options;
    forEach(value, (key, item, source) => {
        const proxyDraft = getProxyDraft(item);
        // just handle the draft which is created by the same rootDraft
        if (proxyDraft &&
            rootDraft &&
            proxyDraft.finalities === rootDraft.finalities) {
            options.isContainDraft = true;
            const currentValue = proxyDraft.original;
            // final update value, but just handle return value
            if (source instanceof Set) {
                const arr = Array.from(source);
                source.clear();
                arr.forEach((_item) => source.add(key === _item ? currentValue : _item));
            }
            else {
                set(source, key, currentValue);
            }
        }
        else if (typeof item === 'object' && item !== null) {
            options.value = item;
            options.isRoot = false;
            handleReturnValue(options);
        }
    });
    if (isRoot) {
        if (!options.isContainDraft)
            console.warn(`The return value does not contain any draft, please use 'rawReturn()' to wrap the return value to improve performance.`);
        if (useRawReturn) {
            console.warn(`The return value contains drafts, please don't use 'rawReturn()' to wrap the return value.`);
        }
    }
}
function getCurrent(target) {
    const proxyDraft = getProxyDraft(target);
    if (!isDraftable(target, proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.options))
        return target;
    const type = getType(target);
    if (proxyDraft && !proxyDraft.operated)
        return proxyDraft.original;
    let currentValue;
    function ensureShallowCopy() {
        currentValue =
            type === 2 /* DraftType.Map */
                ? new Map(target)
                : type === 3 /* DraftType.Set */
                    ? Array.from(proxyDraft.setMap.values())
                    : shallowCopy(target, proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.options);
    }
    if (proxyDraft) {
        // It's a proxy draft, let's create a shallow copy eagerly
        proxyDraft.finalized = true;
        try {
            ensureShallowCopy();
        }
        finally {
            proxyDraft.finalized = false;
        }
    }
    else {
        // It's not a proxy draft, let's use the target directly and let's see
        // lazily if we need to create a shallow copy
        currentValue = target;
    }
    forEach(currentValue, (key, value) => {
        if (proxyDraft && isEqual(get(proxyDraft.original, key), value))
            return;
        const newValue = getCurrent(value);
        if (newValue !== value) {
            if (currentValue === target)
                ensureShallowCopy();
            set(currentValue, key, newValue);
        }
    });
    return type === 3 /* DraftType.Set */ ? new Set(currentValue) : currentValue;
}
/**
 * `current(draft)` to get current state in the draft mutation function.
 *
 * ## Example
 *
 * ```ts
 * import { create, current } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const state = create(
 *   baseState,
 *   (draft) => {
 *     draft.foo.bar = 'str2';
 *     expect(current(draft.foo)).toEqual({ bar: 'str2' });
 *   },
 * );
 * ```
 */
function current(target) {
    if (!isDraft(target)) {
        throw new Error(`current() is only used for Draft, parameter: ${target}`);
    }
    return getCurrent(target);
}

/**
 * `makeCreator(options)` to make a creator function.
 *
 * ## Example
 *
 * ```ts
 * import { makeCreator } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const create = makeCreator({ enableAutoFreeze: true });
 * const state = create(
 *   baseState,
 *   (draft) => {
 *     draft.foo.bar = 'str2';
 *   },
 * );
 *
 * expect(state).toEqual({ foo: { bar: 'str2' }, arr: [] });
 * expect(state).not.toBe(baseState);
 * expect(state.foo).not.toBe(baseState.foo);
 * expect(state.arr).toBe(baseState.arr);
 * expect(Object.isFrozen(state)).toBeTruthy();
 * ```
 */
const makeCreator = (arg) => {
    if (arg !== undefined &&
        Object.prototype.toString.call(arg) !== '[object Object]') {
        throw new Error(`Invalid options: ${String(arg)}, 'options' should be an object.`);
    }
    return function create(arg0, arg1, arg2) {
        var _a, _b, _c;
        if (typeof arg0 === 'function' && typeof arg1 !== 'function') {
            return function (base, ...args) {
                return create(base, (draft) => arg0.call(this, draft, ...args), arg1);
            };
        }
        const base = arg0;
        const mutate = arg1;
        let options = arg2;
        if (typeof arg1 !== 'function') {
            options = arg1;
        }
        if (options !== undefined &&
            Object.prototype.toString.call(options) !== '[object Object]') {
            throw new Error(`Invalid options: ${options}, 'options' should be an object.`);
        }
        options = Object.assign(Object.assign({}, arg), options);
        const state = isDraft(base) ? current(base) : base;
        const mark = Array.isArray(options.mark)
            ? ((value, types) => {
                for (const mark of options.mark) {
                    if (typeof mark !== 'function') {
                        throw new Error(`Invalid mark: ${mark}, 'mark' should be a function.`);
                    }
                    const result = mark(value, types);
                    if (result) {
                        return result;
                    }
                }
                return;
            })
            : options.mark;
        const enablePatches = (_a = options.enablePatches) !== null && _a !== void 0 ? _a : false;
        const strict = (_b = options.strict) !== null && _b !== void 0 ? _b : false;
        const enableAutoFreeze = (_c = options.enableAutoFreeze) !== null && _c !== void 0 ? _c : false;
        const _options = {
            enableAutoFreeze,
            mark,
            strict,
            enablePatches,
        };
        if (!isDraftable(state, _options) &&
            typeof state === 'object' &&
            state !== null) {
            throw new Error(`Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable.`);
        }
        const [draft, finalize] = draftify(state, _options);
        if (typeof arg1 !== 'function') {
            if (!isDraftable(state, _options)) {
                throw new Error(`Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable.`);
            }
            return [draft, finalize];
        }
        let result;
        try {
            result = mutate(draft);
        }
        catch (error) {
            revokeProxy(getProxyDraft(draft));
            throw error;
        }
        const returnValue = (value) => {
            const proxyDraft = getProxyDraft(draft);
            if (!isDraft(value)) {
                if (value !== undefined &&
                    !isEqual(value, draft) &&
                    (proxyDraft === null || proxyDraft === void 0 ? void 0 : proxyDraft.operated)) {
                    throw new Error(`Either the value is returned as a new non-draft value, or only the draft is modified without returning any value.`);
                }
                const rawReturnValue = value === null || value === void 0 ? void 0 : value[RAW_RETURN_SYMBOL];
                if (rawReturnValue) {
                    const _value = rawReturnValue[0];
                    if (_options.strict && typeof value === 'object' && value !== null) {
                        handleReturnValue({
                            rootDraft: proxyDraft,
                            value,
                            useRawReturn: true,
                        });
                    }
                    return finalize([_value]);
                }
                if (value !== undefined) {
                    if (typeof value === 'object' && value !== null) {
                        handleReturnValue({ rootDraft: proxyDraft, value });
                    }
                    return finalize([value]);
                }
            }
            if (value === draft || value === undefined) {
                return finalize([]);
            }
            const returnedProxyDraft = getProxyDraft(value);
            if (_options === returnedProxyDraft.options) {
                if (returnedProxyDraft.operated) {
                    throw new Error(`Cannot return a modified child draft.`);
                }
                return finalize([current(value)]);
            }
            return finalize([value]);
        };
        if (result instanceof Promise) {
            return result.then(returnValue, (error) => {
                revokeProxy(getProxyDraft(draft));
                throw error;
            });
        }
        return returnValue(result);
    };
};

/**
 * `create(baseState, callback, options)` to create the next state
 *
 * ## Example
 *
 * ```ts
 * import { create } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const state = create(
 *   baseState,
 *   (draft) => {
 *     draft.foo.bar = 'str2';
 *   },
 * );
 *
 * expect(state).toEqual({ foo: { bar: 'str2' }, arr: [] });
 * expect(state).not.toBe(baseState);
 * expect(state.foo).not.toBe(baseState.foo);
 * expect(state.arr).toBe(baseState.arr);
 * ```
 */
const create = makeCreator();

/**
 * `apply(state, patches)` to apply patches to state
 *
 * ## Example
 *
 * ```ts
 * import { create, apply } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const [state, patches] = create(
 *   baseState,
 *   (draft) => {
 *     draft.foo.bar = 'str2';
 *   },
 *   { enablePatches: true }
 * );
 * expect(state).toEqual({ foo: { bar: 'str2' }, arr: [] });
 * expect(patches).toEqual([{ op: 'replace', path: ['foo', 'bar'], value: 'str2' }]);
 * expect(state).toEqual(apply(baseState, patches));
 * ```
 */
function apply(state, patches, applyOptions) {
    let i;
    for (i = patches.length - 1; i >= 0; i -= 1) {
        const { value, op, path } = patches[i];
        if ((!path.length && op === Operation.Replace) ||
            (path === '' && op === Operation.Add)) {
            state = value;
            break;
        }
    }
    if (i > -1) {
        patches = patches.slice(i + 1);
    }
    const mutate = (draft) => {
        patches.forEach((patch) => {
            const { path: _path, op } = patch;
            const path = unescapePath(_path);
            let base = draft;
            for (let index = 0; index < path.length - 1; index += 1) {
                const parentType = getType(base);
                let key = path[index];
                if (typeof key !== 'string' && typeof key !== 'number') {
                    key = String(key);
                }
                if (((parentType === 0 /* DraftType.Object */ ||
                    parentType === 1 /* DraftType.Array */) &&
                    (key === '__proto__' || key === 'constructor')) ||
                    (typeof base === 'function' && key === 'prototype')) {
                    throw new Error(`Patching reserved attributes like __proto__ and constructor is not allowed.`);
                }
                // use `index` in Set draft
                base = get(getType(base) === 3 /* DraftType.Set */ ? Array.from(base) : base, key);
                if (typeof base !== 'object') {
                    throw new Error(`Cannot apply patch at '${path.join('/')}'.`);
                }
            }
            const type = getType(base);
            // ensure the original patch is not modified.
            const value = deepClone(patch.value);
            const key = path[path.length - 1];
            switch (op) {
                case Operation.Replace:
                    switch (type) {
                        case 2 /* DraftType.Map */:
                            return base.set(key, value);
                        case 3 /* DraftType.Set */:
                            throw new Error(`Cannot apply replace patch to set.`);
                        default:
                            return (base[key] = value);
                    }
                case Operation.Add:
                    switch (type) {
                        case 1 /* DraftType.Array */:
                            // If the "-" character is used to
                            // index the end of the array (see [RFC6901](https://datatracker.ietf.org/doc/html/rfc6902)),
                            // this has the effect of appending the value to the array.
                            return key === '-'
                                ? base.push(value)
                                : base.splice(key, 0, value);
                        case 2 /* DraftType.Map */:
                            return base.set(key, value);
                        case 3 /* DraftType.Set */:
                            return base.add(value);
                        default:
                            return (base[key] = value);
                    }
                case Operation.Remove:
                    switch (type) {
                        case 1 /* DraftType.Array */:
                            return base.splice(key, 1);
                        case 2 /* DraftType.Map */:
                            return base.delete(key);
                        case 3 /* DraftType.Set */:
                            return base.delete(patch.value);
                        default:
                            return delete base[key];
                    }
                default:
                    throw new Error(`Unsupported patch operation: ${op}.`);
            }
        });
    };
    if (applyOptions === null || applyOptions === void 0 ? void 0 : applyOptions.mutable) {
        mutate(state);
        return undefined;
    }
    if (isDraft(state)) {
        if (applyOptions !== undefined) {
            throw new Error(`Cannot apply patches with options to a draft.`);
        }
        mutate(state);
        return state;
    }
    return create(state, mutate, Object.assign(Object.assign({}, applyOptions), { enablePatches: false }));
}

/**
 * `original(draft)` to get original state in the draft mutation function.
 *
 * ## Example
 *
 * ```ts
 * import { create, original } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const state = create(
 *   baseState,
 *   (draft) => {
 *     draft.foo.bar = 'str2';
 *     expect(original(draft.foo)).toEqual({ bar: 'str' });
 *   }
 * );
 * ```
 */
function original(target) {
    const proxyDraft = getProxyDraft(target);
    if (!proxyDraft) {
        throw new Error(`original() is only used for a draft, parameter: ${target}`);
    }
    return proxyDraft.original;
}

/**
 * Use rawReturn() to wrap the return value to skip the draft check and thus improve performance.
 *
 * ## Example
 *
 * ```ts
 * import { create, rawReturn } from '../index';
 *
 * const baseState = { foo: { bar: 'str' }, arr: [] };
 * const state = create(
 *   baseState,
 *   (draft) => {
 *     return rawReturn(baseState);
 *   },
 * );
 * expect(state).toBe(baseState);
 * ```
 */
function rawReturn(value) {
    if (arguments.length === 0) {
        throw new Error('rawReturn() must be called with a value.');
    }
    if (arguments.length > 1) {
        throw new Error('rawReturn() must be called with one argument.');
    }
    if (value !== undefined &&
        (typeof value !== 'object' || value === null)) {
        console.warn('rawReturn() must be called with an object(including plain object, arrays, Set, Map, etc.) or `undefined`, other types do not need to be returned via rawReturn().');
    }
    return {
        [RAW_RETURN_SYMBOL]: [value],
    };
}

const constructorString = Object.prototype.constructor.toString();
/**
 * Check if the value is a simple object(No prototype chain object or iframe same-origin object),
 * support case: https://github.com/unadlib/mutative/issues/17
 */
const isSimpleObject = (value) => {
    if (!value || typeof value !== 'object')
        return false;
    const prototype = Object.getPrototypeOf(value);
    if (prototype === null) {
        return true;
    }
    const constructor = Object.hasOwnProperty.call(prototype, 'constructor') &&
        prototype.constructor;
    if (constructor === Object)
        return true;
    return (typeof constructor === 'function' &&
        Function.toString.call(constructor) === constructorString);
};
const markSimpleObject = (value) => {
    if (isSimpleObject(value)) {
        return dataTypes.immutable;
    }
    return;
};

/**
 * Cast a value to an Draft type value.
 */
function castDraft(value) {
    return value;
}
/**
 * Cast a value to an Immutable type value.
 */
function castImmutable(value) {
    return value;
}

export { apply, castDraft, castImmutable, create, current, isDraft, isDraftable, makeCreator, markSimpleObject, original, rawReturn, unsafe };
//# sourceMappingURL=mutative.esm.js.map