Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

immer

Package Overview
Dependencies
Maintainers
2
Versions
173
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

immer - npm Package Compare versions

Comparing version 4.0.0-beta.0 to 4.0.0

291

dist/immer.d.ts
type Tail<T extends any[]> = ((...t: T) => any) extends ((
_: any,
...tail: infer TT
_: any,
...tail: infer TT
) => any)
? TT
: []
? TT
: []
/** Object types that should never be mapped */
type AtomicObject =
| Function
| Map<any, any>
| WeakMap<any, any>
| Set<any>
| WeakSet<any>
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String
| Function
| Map<any, any>
| WeakMap<any, any>
| Set<any>
| WeakSet<any>
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String
export type Draft<T> = T extends AtomicObject
? T
: T extends object
? {-readonly [K in keyof T]: Draft<T[K]>}
: T
? T
: T extends object
? {-readonly [K in keyof T]: Draft<T[K]>}
: T
/** Convert a mutable type into a readonly type */
export type Immutable<T> = T extends AtomicObject
? T
: T extends object
? {readonly [K in keyof T]: Immutable<T[K]>}
: T
? T
: T extends object
? {readonly [K in keyof T]: Immutable<T[K]>}
: T
export interface Patch {
op: "replace" | "remove" | "add"
path: (string | number)[]
value?: any
op: "replace" | "remove" | "add"
path: (string | number)[]
value?: any
}

@@ -48,6 +48,6 @@

export type Produced<Base, Return> = Return extends void
? Base
: Return extends Promise<infer Result>
? Promise<Result extends void ? Base : FromNothing<Result>>
: FromNothing<Return>
? Base
: Return extends Promise<infer Result>
? Promise<Result extends void ? Base : FromNothing<Result>>
: FromNothing<Return>

@@ -74,36 +74,36 @@ /**

export interface IProduce {
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return,
listener?: PatchListener
): Produced<Base, Return>
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return,
listener?: PatchListener
): Produced<Base, Return>
}

@@ -114,6 +114,49 @@

/**
* Like `produce`, but instead of just returning the new state,
* a tuple is returned with [nextState, patches, inversePatches]
*
* Like produce, this function supports currying
*/
export interface IProduceWithPatches {
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return
): [Produced<Base, Return>, Patch[], Patch[]]
}
export const produceWithPatches: IProduceWithPatches
/** Use a class type for `nothing` so its type is unique */
declare class Nothing {
// This lets us do `Exclude<T, Nothing>`
private _: any
// This lets us do `Exclude<T, Nothing>`
private _: any
}

@@ -184,61 +227,61 @@

export class Immer {
constructor(config: {
useProxies?: boolean
autoFreeze?: boolean
onAssign?: (
state: ImmerState,
prop: string | number,
value: unknown
) => void
onDelete?: (state: ImmerState, prop: string | number) => void
onCopy?: (state: ImmerState) => void
})
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
produce: IProduce
/**
* When true, `produce` will freeze the copies it creates.
*/
readonly autoFreeze: boolean
/**
* When true, drafts are ES2015 proxies.
*/
readonly useProxies: boolean
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is disabled in production.
*/
setAutoFreeze(autoFreeze: boolean): void
/**
* Pass true to use the ES2015 `Proxy` class when creating drafts, which is
* always faster than using ES5 proxies.
*
* By default, feature detection is used, so calling this is rarely necessary.
*/
setUseProxies(useProxies: boolean): void
constructor(config: {
useProxies?: boolean
autoFreeze?: boolean
onAssign?: (
state: ImmerState,
prop: string | number,
value: unknown
) => void
onDelete?: (state: ImmerState, prop: string | number) => void
onCopy?: (state: ImmerState) => void
})
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
produce: IProduce
/**
* When true, `produce` will freeze the copies it creates.
*/
readonly autoFreeze: boolean
/**
* When true, drafts are ES2015 proxies.
*/
readonly useProxies: boolean
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is disabled in production.
*/
setAutoFreeze(autoFreeze: boolean): void
/**
* Pass true to use the ES2015 `Proxy` class when creating drafts, which is
* always faster than using ES5 proxies.
*
* By default, feature detection is used, so calling this is rarely necessary.
*/
setUseProxies(useProxies: boolean): void
}
export interface ImmerState<T = any> {
parent?: ImmerState
base: T
copy: T
assigned: {[prop: string]: boolean; [index: number]: boolean}
parent?: ImmerState
base: T
copy: T
assigned: {[prop: string]: boolean; [index: number]: boolean}
}

@@ -248,6 +291,6 @@

declare global {
interface Set<T> {}
interface Map<K, V> {}
interface WeakSet<T> {}
interface WeakMap<K extends object, V> {}
interface Set<T> {}
interface Map<K, V> {}
interface WeakSet<T> {}
interface WeakMap<K extends object, V> {}
}

@@ -7,4 +7,4 @@ 'use strict';

var NOTHING = typeof Symbol !== "undefined" ? Symbol("immer-nothing") : ( obj = {}, obj["immer-nothing"] = true, obj );
var DRAFTABLE = typeof Symbol !== "undefined" ? Symbol.for("immer-draftable") : "__$immer_draftable";
var DRAFT_STATE = typeof Symbol !== "undefined" ? Symbol.for("immer-state") : "__$immer_state";
var DRAFTABLE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-draftable") : "__$immer_draftable";
var DRAFT_STATE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-state") : "__$immer_state";
function isDraft(value) {

@@ -14,8 +14,10 @@ return !!value && !!value[DRAFT_STATE];

function isDraftable(value) {
if (!value) { return false; }
return isPlainObject(value) || !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE];
}
function isPlainObject(value) {
if (!value || typeof value !== "object") { return false; }
if (Array.isArray(value)) { return true; }
var proto = Object.getPrototypeOf(value);
if (!proto || proto === Object.prototype) { return true; }
if (isMap(value) || isSet(value)) { return true; }
return !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE];
return !proto || proto === Object.prototype;
}

@@ -27,27 +29,12 @@ function original(value) {

} // We use Maps as `drafts` for Sets, not Objects
// See proxy.js
function assignSet(target, override) {
override.forEach(function (value) {
// When we add new drafts we have to remove their originals if present
var prev = original(value);
if (prev) { target.delete(prev); }
target.add(value);
});
return target;
} // We use Maps as `drafts` for Maps, not Objects
// See proxy.js
function assignMap(target, override) {
override.forEach(function (value, key) { return target.set(key, value); });
return target;
}
var assign = Object.assign || (function (target) {
var overrides = [], len = arguments.length - 1;
while ( len-- > 0 ) overrides[ len ] = arguments[ len + 1 ];
var assign = Object.assign || function assign(target, value) {
for (var key in value) {
if (has(value, key)) {
target[key] = value[key];
}
}
overrides.forEach(function (override) { return Object.keys(override).forEach(function (key) { return target[key] = override[key]; }); });
return target;
});
};
var ownKeys = typeof Reflect !== "undefined" && Reflect.ownKeys ? Reflect.ownKeys : typeof Object.getOwnPropertySymbols !== "undefined" ? function (obj) { return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); } : Object.getOwnPropertyNames;

@@ -58,4 +45,2 @@ function shallowCopy(base, invokeGetters) {

if (Array.isArray(base)) { return base.slice(); }
if (isMap(base)) { return new Map(base); }
if (isSet(base)) { return new Set(base); }
var clone = Object.create(Object.getPrototypeOf(base));

@@ -90,7 +75,7 @@ ownKeys(base).forEach(function (key) {

}
function each(obj, iter) {
if (Array.isArray(obj) || isMap(obj) || isSet(obj)) {
obj.forEach(function (entry, index) { return iter(index, entry, obj); });
function each(value, cb) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) { cb(i, value[i], value); }
} else {
ownKeys(obj).forEach(function (key) { return iter(key, obj[key], obj); });
ownKeys(value).forEach(function (key) { return cb(key, value[key], value); });
}

@@ -103,7 +88,4 @@ }

function has(thing, prop) {
return isMap(thing) ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop);
return Object.prototype.hasOwnProperty.call(thing, prop);
}
function get(thing, prop) {
return isMap(thing) ? thing.get(prop) : thing[prop];
}
function is(x, y) {

@@ -117,7 +99,15 @@ // From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js

}
function isMap(target) {
return target instanceof Map;
function clone(obj) {
if (!isDraftable(obj)) { return obj; }
if (Array.isArray(obj)) { return obj.map(clone); }
var cloned = Object.create(Object.getPrototypeOf(obj));
for (var key in obj) { cloned[key] = clone(obj[key]); }
return cloned;
}
function isSet(target) {
return target instanceof Set;
function deepFreeze(obj) {
if (!isDraftable(obj) || isDraft(obj) || Object.isFrozen(obj)) { return; }
Object.freeze(obj);
if (Array.isArray(obj)) { obj.forEach(deepFreeze); }else { for (var key in obj) { deepFreeze(obj[key]); } }
}

@@ -218,3 +208,3 @@

function latest(state) {
function source(state) {
return state.copy || state.base;

@@ -237,5 +227,5 @@ } // Access a property without creating an Immer draft.

function get$1(state, prop) {
function get(state, prop) {
assertUnrevoked(state);
var value = peek(latest(state), prop);
var value = peek(source(state), prop);
if (state.finalizing) { return value; } // Create a draft if the value is unmodified.

@@ -256,3 +246,3 @@

if (!state.modified) {
if (is(value, peek(latest(state), prop))) { return; }
if (is(value, peek(source(state), prop))) { return; }
markChanged(state);

@@ -299,4 +289,4 @@ prepareCopy(state);

get: function get$1$1() {
return get$1(this[DRAFT_STATE], prop);
get: function get$1() {
return get(this[DRAFT_STATE], prop);
},

@@ -315,3 +305,3 @@

function assertUnrevoked(state) {
if (state.revoked === true) { throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(latest(state))); }
if (state.revoked === true) { throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(source(state))); }
} // This looks expensive, but only proxies are visited, and only objects without known changes are scanned.

@@ -437,15 +427,7 @@

var legacyProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize,
createProxy: createProxy
willFinalize: willFinalize,
createProxy: createProxy
});
var obj$1, obj$1$1;
function willFinalize$1() {}
/**
* Returns a new draft of the `base` object.
*
* The second argument is the parent draft-state (used internally).
*/
function createProxy$1(base, parent) {

@@ -475,21 +457,5 @@ var scope = parent ? parent.scope : ImmerScope.current;

};
var target = state;
var traps = objectTraps;
if (Array.isArray(base)) {
target = [state];
traps = arrayTraps;
} // Map drafts must support object keys, so we use Map objects to track changes.
else if (isMap(base)) {
traps = mapTraps;
state.drafts = new Map();
state.assigned = new Map();
} // Set drafts use a Map object to track which of its values are drafted.
// And we don't need the "assigned" property, because Set objects have no keys.
else if (isSet(base)) {
traps = setTraps;
state.drafts = new Map();
}
var ref = Proxy.revocable(target, traps);
var ref = Array.isArray(base) ? // [state] is used for arrays, to make sure the proxy is array-ish and not violate invariants,
// although state itself is an object
Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps);
var revoke = ref.revoke;

@@ -502,81 +468,17 @@ var proxy = ref.proxy;

}
/**
* Object drafts
*/
var objectTraps = {
get: function get(state, prop) {
if (prop === DRAFT_STATE) { return state; }
var drafts = state.drafts; // Check for existing draft in unmodified state.
get: get$1,
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
var value = latest$1(state)[prop];
if (state.finalized || !isDraftable(value)) {
return value;
} // Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek$1(state.base, prop)) { return value; } // Store drafts on the copy (when one exists).
drafts = state.copy;
}
return drafts[prop] = createProxy$1(value, state);
has: function has(target, prop) {
return prop in source$1(target);
},
has: function has(state, prop) {
return prop in latest$1(state);
ownKeys: function ownKeys(target) {
return Reflect.ownKeys(source$1(target));
},
ownKeys: function ownKeys(state) {
return Reflect.ownKeys(latest$1(state));
},
set: set$1,
deleteProperty: deleteProperty,
getOwnPropertyDescriptor: getOwnPropertyDescriptor,
set: function set(state, prop, value) {
if (!state.modified) {
var baseValue = peek$1(state.base, prop); // Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var isUnchanged = value ? is(baseValue, value) || value === state.drafts[prop] : is(baseValue, value) && prop in state.base;
if (isUnchanged) { return true; }
markChanged$1(state);
}
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
},
deleteProperty: function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek$1(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
}
if (state.copy) { delete state.copy[prop]; }
return true;
},
// Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
getOwnPropertyDescriptor: function getOwnPropertyDescriptor(state, prop) {
var owner = latest$1(state);
var desc = Reflect.getOwnPropertyDescriptor(owner, prop);
if (desc) {
desc.writable = true;
desc.configurable = !Array.isArray(owner) || prop !== "length";
}
return desc;
},
defineProperty: function defineProperty() {

@@ -586,4 +488,4 @@ throw new Error("Object.defineProperty() cannot be used on an Immer draft"); // prettier-ignore

getPrototypeOf: function getPrototypeOf(state) {
return Object.getPrototypeOf(state.base);
getPrototypeOf: function getPrototypeOf(target) {
return Object.getPrototypeOf(target.base);
},

@@ -596,6 +498,2 @@

};
/**
* Array drafts
*/
var arrayTraps = {};

@@ -623,158 +521,83 @@ each(objectTraps, function (key, fn) {

return objectTraps.set.call(this, state[0], prop, value);
}; // Used by Map and Set drafts
}; // returns the object we should be reading the current value from, which is base, until some change has been made
var reflectTraps = makeReflectTraps(["ownKeys", "has", "set", "deleteProperty", "defineProperty", "getOwnPropertyDescriptor", "preventExtensions", "isExtensible", "getPrototypeOf"]);
/**
* Map drafts
*/
function source$1(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
var mapTraps = makeTrapsForGetters(( obj$1 = {}, obj$1[DRAFT_STATE] = function (state) { return state; }, obj$1.size = function (state) { return latest$1(state).size; }, obj$1.has = function (state) { return function (key) { return latest$1(state).has(key); }; }, obj$1.set = function (state) { return function (key, value) {
var values = latest$1(state);
if (!values.has(key) || values.get(key) !== value) {
markChanged$1(state);
state.assigned.set(key, true);
state.copy.set(key, value);
}
function peek$1(draft, prop) {
var state = draft[DRAFT_STATE];
var desc = Reflect.getOwnPropertyDescriptor(state ? source$1(state) : draft, prop);
return desc && desc.value;
}
return state.draft;
}; }, obj$1.delete = function (state) { return function (key) {
if (latest$1(state).has(key)) {
markChanged$1(state);
state.assigned.set(key, false);
return state.copy.delete(key);
}
function get$1(state, prop) {
if (prop === DRAFT_STATE) { return state; }
var drafts = state.drafts; // Check for existing draft in unmodified state.
return false;
}; }, obj$1.clear = function (state) { return function () {
markChanged$1(state);
state.assigned = new Map();
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
for (var i = 0, list = latest$1(state).keys(); i < list.length; i += 1) {
var key = list[i];
var value = source$1(state)[prop];
state.assigned.set(key, false);
}
if (state.finalized || !isDraftable(value)) {
return value;
} // Check for existing draft in modified state.
return state.copy.clear();
}; }, obj$1.forEach = function (state, _, receiver) { return function (cb, thisArg) { return latest$1(state).forEach(function (_, key, map) {
var value = receiver.get(key);
cb.call(thisArg, value, key, map);
}); }; }, obj$1.get = function (state) { return function (key) {
var drafts = state[state.modified ? "copy" : "drafts"];
if (drafts.has(key)) {
return drafts.get(key);
}
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek$1(state.base, prop)) { return value; } // Store drafts on the copy (when one exists).
var value = latest$1(state).get(key);
drafts = state.copy;
}
if (state.finalized || !isDraftable(value)) {
return value;
}
var draft = createProxy$1(value, state);
drafts.set(key, draft);
return draft;
}; }, obj$1.keys = function (state) { return function () { return latest$1(state).keys(); }; }, obj$1.values = iterateMapValues, obj$1.entries = iterateMapValues, obj$1[Symbol.iterator] = iterateMapValues, obj$1 ));
/** Map.prototype.values _-or-_ Map.prototype.entries */
function iterateMapValues(state, prop, receiver) {
var isEntries = prop !== "values";
return function () {
var iterator = latest$1(state)[Symbol.iterator]();
return makeIterable(function () {
var result = iterator.next();
if (!result.done) {
var ref = result.value;
var key = ref[0];
var value = receiver.get(key);
result.value = isEntries ? [key, value] : value;
}
return result;
});
};
return drafts[prop] = createProxy$1(value, state);
}
/**
* Set drafts
*/
function set$1(state, prop, value) {
if (!state.modified) {
var baseValue = peek$1(state.base, prop); // Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var setTraps = makeTrapsForGetters(( obj$1$1 = {}, obj$1$1[DRAFT_STATE] = function (state) { return state; }, obj$1$1.size = function (state) { return latest$1(state).size; }, obj$1$1.has = function (state) { return function (key) { return latest$1(state).has(key); }; }, obj$1$1.add = function (state) { return function (value) {
if (!latest$1(state).has(value)) {
markChanged$1(state);
state.copy.add(value);
}
return state.draft;
}; }, obj$1$1.delete = function (state) { return function (value) {
var isUnchanged = value ? is(baseValue, value) || value === state.drafts[prop] : is(baseValue, value) && prop in state.base;
if (isUnchanged) { return true; }
markChanged$1(state);
return state.copy.delete(value);
}; }, obj$1$1.clear = function (state) { return function () {
markChanged$1(state);
return state.copy.clear();
}; }, obj$1$1.forEach = function (state) { return function (cb, thisArg) {
var iterator = iterateSetValues(state)();
var result = iterator.next();
}
while (!result.done) {
cb.call(thisArg, result.value, result.value, state.draft);
result = iterator.next();
}
}; }, obj$1$1.keys = iterateSetValues, obj$1$1.values = iterateSetValues, obj$1$1.entries = iterateSetValues, obj$1$1[Symbol.iterator] = iterateSetValues, obj$1$1 ));
function iterateSetValues(state, prop) {
var isEntries = prop === "entries";
return function () {
var iterator = latest$1(state)[Symbol.iterator]();
return makeIterable(function () {
var result = iterator.next();
if (!result.done) {
var value = wrapSetValue(state, result.value);
result.value = isEntries ? [value, value] : value;
}
return result;
});
};
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
}
function wrapSetValue(state, value) {
var key = original(value) || value;
var draft = state.drafts.get(key);
if (!draft) {
if (state.finalized || !isDraftable(value)) {
return value;
}
draft = createProxy$1(value, state);
state.drafts.set(key, draft);
if (state.modified) {
state.copy.add(draft);
}
function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek$1(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
} else if (state.assigned[prop]) {
// if an originally not assigned property was deleted
delete state.assigned[prop];
}
return draft;
}
/**
* Helpers
*/
// Retrieve the latest values of the draft.
if (state.copy) { delete state.copy[prop]; }
return true;
} // Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
function latest$1(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
function getOwnPropertyDescriptor(state, prop) {
var owner = source$1(state);
var desc = Reflect.getOwnPropertyDescriptor(owner, prop);
if (desc) {
desc.writable = true;
desc.configurable = !Array.isArray(owner) || prop !== "length";
}
function peek$1(draft, prop) {
var state = draft[DRAFT_STATE];
var desc = Reflect.getOwnPropertyDescriptor(state ? latest$1(state) : draft, prop);
return desc && desc.value;
return desc;
}

@@ -785,68 +608,15 @@

state.modified = true;
var base = state.base;
var drafts = state.drafts;
var parent = state.parent;
var copy = shallowCopy(base);
if (isSet(base)) {
// Note: The `drafts` property is preserved for Set objects, since
// we need to keep track of which values are drafted.
assignSet(copy, drafts);
} else {
// Merge nested drafts into the copy.
if (isMap(base)) { assignMap(copy, drafts); }else { assign(copy, drafts); }
state.drafts = null;
}
state.copy = copy;
if (parent) {
markChanged$1(parent);
}
state.copy = assign(shallowCopy(state.base), state.drafts);
state.drafts = null;
if (state.parent) { markChanged$1(state.parent); }
}
}
function makeIterable(next) {
var obj;
var self;
return self = ( obj = {}, obj[Symbol.iterator] = function () { return self; }, obj.next = next, obj );
}
/** Create traps that all use the `Reflect` API on the `latest(state)` */
function makeReflectTraps(names) {
return names.reduce(function (traps, name) {
traps[name] = function (state) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return Reflect[name].apply(Reflect, [ latest$1(state) ].concat( args ));
};
return traps;
}, {});
}
function makeTrapsForGetters(getters) {
return Object.assign({}, reflectTraps, {
get: function get(state, prop, receiver) {
return getters.hasOwnProperty(prop) ? getters[prop](state, prop, receiver) : Reflect.get(state, prop, receiver);
},
setPrototypeOf: function setPrototypeOf(state) {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft"); // prettier-ignore
}
});
}
var modernProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize$1,
createProxy: createProxy$1
willFinalize: willFinalize$1,
createProxy: createProxy$1
});
function generatePatches(state, basePath, patches, inversePatches) {
var generatePatchesFn = Array.isArray(state.base) ? generateArrayPatches : isSet(state.base) ? generateSetPatches : generatePatchesFromAssigned;
generatePatchesFn(state, basePath, patches, inversePatches);
Array.isArray(state.base) ? generateArrayPatches(state, basePath, patches, inversePatches) : generateObjectPatches(state, basePath, patches, inversePatches);
}

@@ -898,3 +668,2 @@

var useRemove = end != base.length;
var replaceCount = patches.length; // Process added indices.

@@ -909,29 +678,16 @@

};
if (useRemove) {
inversePatches.push({
op: "remove",
path: path$1
});
}
} // One "replace" patch reverses all non-splicing "add" patches.
if (!useRemove) {
inversePatches.push({
op: "replace",
path: basePath.concat(["length"]),
value: base.length
op: "remove",
path: path$1
});
}
} // This is used for both Map objects and normal objects.
}
function generatePatchesFromAssigned(state, basePath, patches, inversePatches) {
function generateObjectPatches(state, basePath, patches, inversePatches) {
var base = state.base;
var copy = state.copy;
each(state.assigned, function (key, assignedValue) {
var origValue = get(base, key);
var value = get(copy, key);
var op = !assignedValue ? "remove" : has(base, key) ? "replace" : "add";
var origValue = base[key];
var value = copy[key];
var op = !assignedValue ? "remove" : key in base ? "replace" : "add";
if (origValue === value && op === "replace") { return; }

@@ -962,100 +718,49 @@ var path = basePath.concat(key);

function generateSetPatches(state, basePath, patches, inversePatches) {
var base = state.base;
var copy = state.copy;
var i = 0;
var applyPatches = function (draft, patches) {
for (var i$1 = 0, list = patches; i$1 < list.length; i$1 += 1) {
var patch = list[i$1];
for (var i$1 = 0, list = base; i$1 < list.length; i$1 += 1) {
var value = list[i$1];
var path = patch.path;
var op = patch.op;
var value = clone(patch.value); // used to clone patch to ensure original patch is not modified, see #411
if (!copy.has(value)) {
var path = basePath.concat([i]);
patches.push({
op: "remove",
path: path,
value: value
});
inversePatches.unshift({
op: "add",
path: path,
value: value
});
}
if (!path.length) { throw new Error("Illegal state"); }
var base = draft;
i++;
}
i = 0;
for (var i$2 = 0, list$1 = copy; i$2 < list$1.length; i$2 += 1) {
var value$1 = list$1[i$2];
if (!base.has(value$1)) {
var path$1 = basePath.concat([i]);
patches.push({
op: "add",
path: path$1,
value: value$1
});
inversePatches.unshift({
op: "remove",
path: path$1,
value: value$1
});
for (var i = 0; i < path.length - 1; i++) {
base = base[path[i]];
if (!base || typeof base !== "object") { throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); } // prettier-ignore
}
i++;
}
}
var key = path[path.length - 1];
function applyPatches(draft, patches) {
for (var i = 0; i < patches.length; i++) {
var patch = patches[i];
var path = patch.path;
switch (op) {
case "replace":
// if value is an object, then it's assigned by reference
// in the following add or remove ops, the value field inside the patch will also be modifyed
// so we use value from the cloned patch
base[key] = value;
break;
if (path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var base = draft;
case "add":
if (Array.isArray(base)) {
// TODO: support "foo/-" paths for appending to an array
base.splice(key, 0, value);
} else {
base[key] = value;
}
for (var i$1 = 0; i$1 < path.length - 1; i$1++) {
base = get(base, path[i$1]);
if (!base || typeof base !== "object") { throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); } // prettier-ignore
}
break;
var key = path[path.length - 1];
var replace = function (key, value) {
if (isMap(base)) {
base.set(key, value);
return;
case "remove":
if (Array.isArray(base)) {
base.splice(key, 1);
} else {
delete base[key];
}
if (isSet(base)) {
throw new Error('Sets cannot have "replace" patches.');
}
break;
base[key] = value;
};
var add = function (key, value) { return Array.isArray(base) ? base.splice(key, 0, value) : isMap(base) ? base.set(key, value) : isSet(base) ? base.add(value) : base[key] = value; };
var remove = function (key, value) { return Array.isArray(base) ? base.splice(key, 1) : isMap(base) ? base.delete(key) : isSet(base) ? base.delete(value) : delete base[key]; };
switch (patch.op) {
case "replace":
replace(key, patch.value);
break;
case "add":
add(key, patch.value);
break;
case "remove":
remove(key, patch.value);
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
default:
throw new Error("Unsupported patch operation: " + op);
}

@@ -1065,3 +770,3 @@ }

return draft;
}
};

@@ -1140,7 +845,31 @@ function verifyMinified() {}

result = recipe(base);
if (result === undefined) { return base; }
return result !== NOTHING ? result : undefined;
if (result === NOTHING) { return undefined; }
if (result === undefined) { result = base; }
this.maybeFreeze(result, true);
return result;
}
};
Immer.prototype.produceWithPatches = function produceWithPatches (arg1, arg2, arg3) {
var this$1 = this;
if (typeof arg1 === "function") {
return function (state) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return this$1.produceWithPatches(state, function (draft) { return arg1.apply(void 0, [ draft ].concat( args )); });
};
} // non-curried form
if (arg3) { throw new Error("A patch listener cannot be passed to produceWithPatches"); }
var patches, inversePatches;
var nextState = this.produce(arg1, arg2, function (p, ip) {
patches = p;
inversePatches = ip;
});
return [nextState, patches, inversePatches];
};
Immer.prototype.createDraft = function createDraft (base) {

@@ -1184,4 +913,17 @@ if (!isDraftable(base)) {

Immer.prototype.applyPatches = function applyPatches$1 (base, patches) {
// Mutate the base state when a draft is passed.
// If a patch replaces the entire state, take that replacement as base
// before applying patches
var i;
for (i = patches.length - 1; i >= 0; i--) {
var patch = patches[i];
if (patch.path.length === 0 && patch.op === "replace") {
base = patch.value;
break;
}
}
if (isDraft(base)) {
// N.B: never hits if some patch a replacement, patches are never drafts
return applyPatches(base, patches);

@@ -1191,3 +933,3 @@ } // Otherwise, produce a copy of the base state.

return this.produce(base, function (draft) { return applyPatches(draft, patches); });
return this.produce(base, function (draft) { return applyPatches(draft, patches.slice(i + 1)); });
};

@@ -1211,2 +953,3 @@ /** @internal */

result = this.finalize(result, null, scope);
this.maybeFreeze(result);
}

@@ -1262,2 +1005,3 @@

if (!state.modified) {
this.maybeFreeze(state.base, true);
return state.base;

@@ -1268,13 +1012,13 @@ }

state.finalized = true;
this.finalizeTree(state.draft, path, scope); // We cannot really delete anything inside of a Set. We can only replace the whole Set.
this.finalizeTree(state.draft, path, scope);
if (this.onDelete && !isSet(state.base)) {
if (this.onDelete) {
// The `assigned` object is unreliable with ES5 drafts.
if (this.useProxies) {
var assigned = state.assigned;
each(assigned, function (prop, exists) {
if (!exists) { this$1.onDelete(state, prop); }
});
for (var prop in assigned) {
if (!assigned[prop]) { this.onDelete(state, prop); }
}
} else {
// TODO: Figure it out for Maps and Sets if we need to support ES5
var base = state.base;

@@ -1334,20 +1078,25 @@ var copy = state.copy;

var isDraftProp = !!state && parent === root;
var isSetMember = isSet(parent);
if (isDraft(value)) {
var path = isDraftProp && needPatches && !isSetMember && // Set objects are atomic since they have no keys.
!has(state.assigned, prop) // Skip deep patches for assigned keys.
? rootPath.concat(prop) : null; // Drafts owned by `scope` are finalized here.
var path = isDraftProp && needPatches && !state.assigned[prop] ? rootPath.concat(prop) : null; // Drafts owned by `scope` are finalized here.
value = this$1.finalize(value, path, scope);
replace(parent, prop, value); // Drafts from another scope must prevent auto-freezing.
value = this$1.finalize(value, path, scope); // Drafts from another scope must prevent auto-freezing.
if (isDraft(value)) {
scope.canAutoFreeze = false;
} // Preserve non-enumerable properties.
if (Array.isArray(parent) || isEnumerable(parent, prop)) {
parent[prop] = value;
} else {
Object.defineProperty(parent, prop, {
value: value
});
} // Unchanged drafts are never passed to the `onAssign` hook.
if (isDraftProp && value === get(state.base, prop)) { return; }
if (isDraftProp && value === state.base[prop]) { return; }
} // Unchanged draft properties are ignored.
else if (isDraftProp && is(value, get(state.base, prop))) {
else if (isDraftProp && is(value, state.base[prop])) {
return;

@@ -1357,6 +1106,6 @@ } // Search new objects for unfinalized drafts. Frozen objects should never contain drafts.

each(value, finalizeProperty);
} // We cannot really assign anything inside of a Set. We can only replace the whole Set.
this$1.maybeFreeze(value);
}
if (isDraftProp && this$1.onAssign && !isSetMember) {
if (isDraftProp && this$1.onAssign) {
this$1.onAssign(state, prop, value);

@@ -1370,20 +1119,9 @@ }

function replace(parent, prop, value) {
if (isMap(parent)) {
parent.set(prop, value);
} else if (isSet(parent)) {
// In this case, the `prop` is actually a draft.
parent.delete(prop);
parent.add(value);
} else if (Array.isArray(parent) || isEnumerable(parent, prop)) {
// Preserve non-enumerable properties.
parent[prop] = value;
} else {
Object.defineProperty(parent, prop, {
value: value,
writable: true,
configurable: true
});
Immer.prototype.maybeFreeze = function maybeFreeze (value, deep) {
if ( deep === void 0 ) deep = false;
if (this.autoFreeze && !isDraft(value)) {
if (deep) { deepFreeze(value); }else { Object.freeze(value); }
}
}
};

@@ -1413,2 +1151,8 @@ var immer = new Immer();

/**
* Like `produce`, but `produceWithPatches` always returns a tuple
* [nextState, patches, inversePatches] (instead of just the next state)
*/
var produceWithPatches = immer.produceWithPatches.bind(immer);
/**
* Pass true to automatically freeze all copies created by Immer.

@@ -1463,4 +1207,5 @@ *

exports.produce = produce;
exports.produceWithPatches = produceWithPatches;
exports.setAutoFreeze = setAutoFreeze;
exports.setUseProxies = setUseProxies;
//# sourceMappingURL=immer.js.map
var obj;
var NOTHING = typeof Symbol !== "undefined" ? Symbol("immer-nothing") : ( obj = {}, obj["immer-nothing"] = true, obj );
var DRAFTABLE = typeof Symbol !== "undefined" ? Symbol.for("immer-draftable") : "__$immer_draftable";
var DRAFT_STATE = typeof Symbol !== "undefined" ? Symbol.for("immer-state") : "__$immer_state";
var DRAFTABLE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-draftable") : "__$immer_draftable";
var DRAFT_STATE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-state") : "__$immer_state";
function isDraft(value) {

@@ -9,8 +9,10 @@ return !!value && !!value[DRAFT_STATE];

function isDraftable(value) {
if (!value) { return false; }
return isPlainObject(value) || !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE];
}
function isPlainObject(value) {
if (!value || typeof value !== "object") { return false; }
if (Array.isArray(value)) { return true; }
var proto = Object.getPrototypeOf(value);
if (!proto || proto === Object.prototype) { return true; }
if (isMap(value) || isSet(value)) { return true; }
return !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE];
return !proto || proto === Object.prototype;
}

@@ -22,27 +24,12 @@ function original(value) {

} // We use Maps as `drafts` for Sets, not Objects
// See proxy.js
function assignSet(target, override) {
override.forEach(function (value) {
// When we add new drafts we have to remove their originals if present
var prev = original(value);
if (prev) { target.delete(prev); }
target.add(value);
});
return target;
} // We use Maps as `drafts` for Maps, not Objects
// See proxy.js
function assignMap(target, override) {
override.forEach(function (value, key) { return target.set(key, value); });
return target;
}
var assign = Object.assign || (function (target) {
var overrides = [], len = arguments.length - 1;
while ( len-- > 0 ) overrides[ len ] = arguments[ len + 1 ];
var assign = Object.assign || function assign(target, value) {
for (var key in value) {
if (has(value, key)) {
target[key] = value[key];
}
}
overrides.forEach(function (override) { return Object.keys(override).forEach(function (key) { return target[key] = override[key]; }); });
return target;
});
};
var ownKeys = typeof Reflect !== "undefined" && Reflect.ownKeys ? Reflect.ownKeys : typeof Object.getOwnPropertySymbols !== "undefined" ? function (obj) { return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); } : Object.getOwnPropertyNames;

@@ -53,4 +40,2 @@ function shallowCopy(base, invokeGetters) {

if (Array.isArray(base)) { return base.slice(); }
if (isMap(base)) { return new Map(base); }
if (isSet(base)) { return new Set(base); }
var clone = Object.create(Object.getPrototypeOf(base));

@@ -85,7 +70,7 @@ ownKeys(base).forEach(function (key) {

}
function each(obj, iter) {
if (Array.isArray(obj) || isMap(obj) || isSet(obj)) {
obj.forEach(function (entry, index) { return iter(index, entry, obj); });
function each(value, cb) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) { cb(i, value[i], value); }
} else {
ownKeys(obj).forEach(function (key) { return iter(key, obj[key], obj); });
ownKeys(value).forEach(function (key) { return cb(key, value[key], value); });
}

@@ -98,7 +83,4 @@ }

function has(thing, prop) {
return isMap(thing) ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop);
return Object.prototype.hasOwnProperty.call(thing, prop);
}
function get(thing, prop) {
return isMap(thing) ? thing.get(prop) : thing[prop];
}
function is(x, y) {

@@ -112,7 +94,15 @@ // From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js

}
function isMap(target) {
return target instanceof Map;
function clone(obj) {
if (!isDraftable(obj)) { return obj; }
if (Array.isArray(obj)) { return obj.map(clone); }
var cloned = Object.create(Object.getPrototypeOf(obj));
for (var key in obj) { cloned[key] = clone(obj[key]); }
return cloned;
}
function isSet(target) {
return target instanceof Set;
function deepFreeze(obj) {
if (!isDraftable(obj) || isDraft(obj) || Object.isFrozen(obj)) { return; }
Object.freeze(obj);
if (Array.isArray(obj)) { obj.forEach(deepFreeze); }else { for (var key in obj) { deepFreeze(obj[key]); } }
}

@@ -213,3 +203,3 @@

function latest(state) {
function source(state) {
return state.copy || state.base;

@@ -232,5 +222,5 @@ } // Access a property without creating an Immer draft.

function get$1(state, prop) {
function get(state, prop) {
assertUnrevoked(state);
var value = peek(latest(state), prop);
var value = peek(source(state), prop);
if (state.finalizing) { return value; } // Create a draft if the value is unmodified.

@@ -251,3 +241,3 @@

if (!state.modified) {
if (is(value, peek(latest(state), prop))) { return; }
if (is(value, peek(source(state), prop))) { return; }
markChanged(state);

@@ -294,4 +284,4 @@ prepareCopy(state);

get: function get$1$1() {
return get$1(this[DRAFT_STATE], prop);
get: function get$1() {
return get(this[DRAFT_STATE], prop);
},

@@ -310,3 +300,3 @@

function assertUnrevoked(state) {
if (state.revoked === true) { throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(latest(state))); }
if (state.revoked === true) { throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(source(state))); }
} // This looks expensive, but only proxies are visited, and only objects without known changes are scanned.

@@ -432,15 +422,7 @@

var legacyProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize,
createProxy: createProxy
willFinalize: willFinalize,
createProxy: createProxy
});
var obj$1, obj$1$1;
function willFinalize$1() {}
/**
* Returns a new draft of the `base` object.
*
* The second argument is the parent draft-state (used internally).
*/
function createProxy$1(base, parent) {

@@ -470,21 +452,5 @@ var scope = parent ? parent.scope : ImmerScope.current;

};
var target = state;
var traps = objectTraps;
if (Array.isArray(base)) {
target = [state];
traps = arrayTraps;
} // Map drafts must support object keys, so we use Map objects to track changes.
else if (isMap(base)) {
traps = mapTraps;
state.drafts = new Map();
state.assigned = new Map();
} // Set drafts use a Map object to track which of its values are drafted.
// And we don't need the "assigned" property, because Set objects have no keys.
else if (isSet(base)) {
traps = setTraps;
state.drafts = new Map();
}
var ref = Proxy.revocable(target, traps);
var ref = Array.isArray(base) ? // [state] is used for arrays, to make sure the proxy is array-ish and not violate invariants,
// although state itself is an object
Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps);
var revoke = ref.revoke;

@@ -497,81 +463,17 @@ var proxy = ref.proxy;

}
/**
* Object drafts
*/
var objectTraps = {
get: function get(state, prop) {
if (prop === DRAFT_STATE) { return state; }
var drafts = state.drafts; // Check for existing draft in unmodified state.
get: get$1,
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
var value = latest$1(state)[prop];
if (state.finalized || !isDraftable(value)) {
return value;
} // Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek$1(state.base, prop)) { return value; } // Store drafts on the copy (when one exists).
drafts = state.copy;
}
return drafts[prop] = createProxy$1(value, state);
has: function has(target, prop) {
return prop in source$1(target);
},
has: function has(state, prop) {
return prop in latest$1(state);
ownKeys: function ownKeys(target) {
return Reflect.ownKeys(source$1(target));
},
ownKeys: function ownKeys(state) {
return Reflect.ownKeys(latest$1(state));
},
set: set$1,
deleteProperty: deleteProperty,
getOwnPropertyDescriptor: getOwnPropertyDescriptor,
set: function set(state, prop, value) {
if (!state.modified) {
var baseValue = peek$1(state.base, prop); // Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var isUnchanged = value ? is(baseValue, value) || value === state.drafts[prop] : is(baseValue, value) && prop in state.base;
if (isUnchanged) { return true; }
markChanged$1(state);
}
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
},
deleteProperty: function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek$1(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
}
if (state.copy) { delete state.copy[prop]; }
return true;
},
// Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
getOwnPropertyDescriptor: function getOwnPropertyDescriptor(state, prop) {
var owner = latest$1(state);
var desc = Reflect.getOwnPropertyDescriptor(owner, prop);
if (desc) {
desc.writable = true;
desc.configurable = !Array.isArray(owner) || prop !== "length";
}
return desc;
},
defineProperty: function defineProperty() {

@@ -581,4 +483,4 @@ throw new Error("Object.defineProperty() cannot be used on an Immer draft"); // prettier-ignore

getPrototypeOf: function getPrototypeOf(state) {
return Object.getPrototypeOf(state.base);
getPrototypeOf: function getPrototypeOf(target) {
return Object.getPrototypeOf(target.base);
},

@@ -591,6 +493,2 @@

};
/**
* Array drafts
*/
var arrayTraps = {};

@@ -618,158 +516,83 @@ each(objectTraps, function (key, fn) {

return objectTraps.set.call(this, state[0], prop, value);
}; // Used by Map and Set drafts
}; // returns the object we should be reading the current value from, which is base, until some change has been made
var reflectTraps = makeReflectTraps(["ownKeys", "has", "set", "deleteProperty", "defineProperty", "getOwnPropertyDescriptor", "preventExtensions", "isExtensible", "getPrototypeOf"]);
/**
* Map drafts
*/
function source$1(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
var mapTraps = makeTrapsForGetters(( obj$1 = {}, obj$1[DRAFT_STATE] = function (state) { return state; }, obj$1.size = function (state) { return latest$1(state).size; }, obj$1.has = function (state) { return function (key) { return latest$1(state).has(key); }; }, obj$1.set = function (state) { return function (key, value) {
var values = latest$1(state);
if (!values.has(key) || values.get(key) !== value) {
markChanged$1(state);
state.assigned.set(key, true);
state.copy.set(key, value);
}
function peek$1(draft, prop) {
var state = draft[DRAFT_STATE];
var desc = Reflect.getOwnPropertyDescriptor(state ? source$1(state) : draft, prop);
return desc && desc.value;
}
return state.draft;
}; }, obj$1.delete = function (state) { return function (key) {
if (latest$1(state).has(key)) {
markChanged$1(state);
state.assigned.set(key, false);
return state.copy.delete(key);
}
function get$1(state, prop) {
if (prop === DRAFT_STATE) { return state; }
var drafts = state.drafts; // Check for existing draft in unmodified state.
return false;
}; }, obj$1.clear = function (state) { return function () {
markChanged$1(state);
state.assigned = new Map();
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
for (var i = 0, list = latest$1(state).keys(); i < list.length; i += 1) {
var key = list[i];
var value = source$1(state)[prop];
state.assigned.set(key, false);
}
if (state.finalized || !isDraftable(value)) {
return value;
} // Check for existing draft in modified state.
return state.copy.clear();
}; }, obj$1.forEach = function (state, _, receiver) { return function (cb, thisArg) { return latest$1(state).forEach(function (_, key, map) {
var value = receiver.get(key);
cb.call(thisArg, value, key, map);
}); }; }, obj$1.get = function (state) { return function (key) {
var drafts = state[state.modified ? "copy" : "drafts"];
if (drafts.has(key)) {
return drafts.get(key);
}
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek$1(state.base, prop)) { return value; } // Store drafts on the copy (when one exists).
var value = latest$1(state).get(key);
drafts = state.copy;
}
if (state.finalized || !isDraftable(value)) {
return value;
}
var draft = createProxy$1(value, state);
drafts.set(key, draft);
return draft;
}; }, obj$1.keys = function (state) { return function () { return latest$1(state).keys(); }; }, obj$1.values = iterateMapValues, obj$1.entries = iterateMapValues, obj$1[Symbol.iterator] = iterateMapValues, obj$1 ));
/** Map.prototype.values _-or-_ Map.prototype.entries */
function iterateMapValues(state, prop, receiver) {
var isEntries = prop !== "values";
return function () {
var iterator = latest$1(state)[Symbol.iterator]();
return makeIterable(function () {
var result = iterator.next();
if (!result.done) {
var ref = result.value;
var key = ref[0];
var value = receiver.get(key);
result.value = isEntries ? [key, value] : value;
}
return result;
});
};
return drafts[prop] = createProxy$1(value, state);
}
/**
* Set drafts
*/
function set$1(state, prop, value) {
if (!state.modified) {
var baseValue = peek$1(state.base, prop); // Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var setTraps = makeTrapsForGetters(( obj$1$1 = {}, obj$1$1[DRAFT_STATE] = function (state) { return state; }, obj$1$1.size = function (state) { return latest$1(state).size; }, obj$1$1.has = function (state) { return function (key) { return latest$1(state).has(key); }; }, obj$1$1.add = function (state) { return function (value) {
if (!latest$1(state).has(value)) {
markChanged$1(state);
state.copy.add(value);
}
return state.draft;
}; }, obj$1$1.delete = function (state) { return function (value) {
var isUnchanged = value ? is(baseValue, value) || value === state.drafts[prop] : is(baseValue, value) && prop in state.base;
if (isUnchanged) { return true; }
markChanged$1(state);
return state.copy.delete(value);
}; }, obj$1$1.clear = function (state) { return function () {
markChanged$1(state);
return state.copy.clear();
}; }, obj$1$1.forEach = function (state) { return function (cb, thisArg) {
var iterator = iterateSetValues(state)();
var result = iterator.next();
}
while (!result.done) {
cb.call(thisArg, result.value, result.value, state.draft);
result = iterator.next();
}
}; }, obj$1$1.keys = iterateSetValues, obj$1$1.values = iterateSetValues, obj$1$1.entries = iterateSetValues, obj$1$1[Symbol.iterator] = iterateSetValues, obj$1$1 ));
function iterateSetValues(state, prop) {
var isEntries = prop === "entries";
return function () {
var iterator = latest$1(state)[Symbol.iterator]();
return makeIterable(function () {
var result = iterator.next();
if (!result.done) {
var value = wrapSetValue(state, result.value);
result.value = isEntries ? [value, value] : value;
}
return result;
});
};
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
}
function wrapSetValue(state, value) {
var key = original(value) || value;
var draft = state.drafts.get(key);
if (!draft) {
if (state.finalized || !isDraftable(value)) {
return value;
}
draft = createProxy$1(value, state);
state.drafts.set(key, draft);
if (state.modified) {
state.copy.add(draft);
}
function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek$1(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
} else if (state.assigned[prop]) {
// if an originally not assigned property was deleted
delete state.assigned[prop];
}
return draft;
}
/**
* Helpers
*/
// Retrieve the latest values of the draft.
if (state.copy) { delete state.copy[prop]; }
return true;
} // Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
function latest$1(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
function getOwnPropertyDescriptor(state, prop) {
var owner = source$1(state);
var desc = Reflect.getOwnPropertyDescriptor(owner, prop);
if (desc) {
desc.writable = true;
desc.configurable = !Array.isArray(owner) || prop !== "length";
}
function peek$1(draft, prop) {
var state = draft[DRAFT_STATE];
var desc = Reflect.getOwnPropertyDescriptor(state ? latest$1(state) : draft, prop);
return desc && desc.value;
return desc;
}

@@ -780,68 +603,15 @@

state.modified = true;
var base = state.base;
var drafts = state.drafts;
var parent = state.parent;
var copy = shallowCopy(base);
if (isSet(base)) {
// Note: The `drafts` property is preserved for Set objects, since
// we need to keep track of which values are drafted.
assignSet(copy, drafts);
} else {
// Merge nested drafts into the copy.
if (isMap(base)) { assignMap(copy, drafts); }else { assign(copy, drafts); }
state.drafts = null;
}
state.copy = copy;
if (parent) {
markChanged$1(parent);
}
state.copy = assign(shallowCopy(state.base), state.drafts);
state.drafts = null;
if (state.parent) { markChanged$1(state.parent); }
}
}
function makeIterable(next) {
var obj;
var self;
return self = ( obj = {}, obj[Symbol.iterator] = function () { return self; }, obj.next = next, obj );
}
/** Create traps that all use the `Reflect` API on the `latest(state)` */
function makeReflectTraps(names) {
return names.reduce(function (traps, name) {
traps[name] = function (state) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return Reflect[name].apply(Reflect, [ latest$1(state) ].concat( args ));
};
return traps;
}, {});
}
function makeTrapsForGetters(getters) {
return Object.assign({}, reflectTraps, {
get: function get(state, prop, receiver) {
return getters.hasOwnProperty(prop) ? getters[prop](state, prop, receiver) : Reflect.get(state, prop, receiver);
},
setPrototypeOf: function setPrototypeOf(state) {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft"); // prettier-ignore
}
});
}
var modernProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize$1,
createProxy: createProxy$1
willFinalize: willFinalize$1,
createProxy: createProxy$1
});
function generatePatches(state, basePath, patches, inversePatches) {
var generatePatchesFn = Array.isArray(state.base) ? generateArrayPatches : isSet(state.base) ? generateSetPatches : generatePatchesFromAssigned;
generatePatchesFn(state, basePath, patches, inversePatches);
Array.isArray(state.base) ? generateArrayPatches(state, basePath, patches, inversePatches) : generateObjectPatches(state, basePath, patches, inversePatches);
}

@@ -893,3 +663,2 @@

var useRemove = end != base.length;
var replaceCount = patches.length; // Process added indices.

@@ -904,29 +673,16 @@

};
if (useRemove) {
inversePatches.push({
op: "remove",
path: path$1
});
}
} // One "replace" patch reverses all non-splicing "add" patches.
if (!useRemove) {
inversePatches.push({
op: "replace",
path: basePath.concat(["length"]),
value: base.length
op: "remove",
path: path$1
});
}
} // This is used for both Map objects and normal objects.
}
function generatePatchesFromAssigned(state, basePath, patches, inversePatches) {
function generateObjectPatches(state, basePath, patches, inversePatches) {
var base = state.base;
var copy = state.copy;
each(state.assigned, function (key, assignedValue) {
var origValue = get(base, key);
var value = get(copy, key);
var op = !assignedValue ? "remove" : has(base, key) ? "replace" : "add";
var origValue = base[key];
var value = copy[key];
var op = !assignedValue ? "remove" : key in base ? "replace" : "add";
if (origValue === value && op === "replace") { return; }

@@ -957,100 +713,49 @@ var path = basePath.concat(key);

function generateSetPatches(state, basePath, patches, inversePatches) {
var base = state.base;
var copy = state.copy;
var i = 0;
var applyPatches = function (draft, patches) {
for (var i$1 = 0, list = patches; i$1 < list.length; i$1 += 1) {
var patch = list[i$1];
for (var i$1 = 0, list = base; i$1 < list.length; i$1 += 1) {
var value = list[i$1];
var path = patch.path;
var op = patch.op;
var value = clone(patch.value); // used to clone patch to ensure original patch is not modified, see #411
if (!copy.has(value)) {
var path = basePath.concat([i]);
patches.push({
op: "remove",
path: path,
value: value
});
inversePatches.unshift({
op: "add",
path: path,
value: value
});
}
if (!path.length) { throw new Error("Illegal state"); }
var base = draft;
i++;
}
i = 0;
for (var i$2 = 0, list$1 = copy; i$2 < list$1.length; i$2 += 1) {
var value$1 = list$1[i$2];
if (!base.has(value$1)) {
var path$1 = basePath.concat([i]);
patches.push({
op: "add",
path: path$1,
value: value$1
});
inversePatches.unshift({
op: "remove",
path: path$1,
value: value$1
});
for (var i = 0; i < path.length - 1; i++) {
base = base[path[i]];
if (!base || typeof base !== "object") { throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); } // prettier-ignore
}
i++;
}
}
var key = path[path.length - 1];
function applyPatches(draft, patches) {
for (var i = 0; i < patches.length; i++) {
var patch = patches[i];
var path = patch.path;
switch (op) {
case "replace":
// if value is an object, then it's assigned by reference
// in the following add or remove ops, the value field inside the patch will also be modifyed
// so we use value from the cloned patch
base[key] = value;
break;
if (path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var base = draft;
case "add":
if (Array.isArray(base)) {
// TODO: support "foo/-" paths for appending to an array
base.splice(key, 0, value);
} else {
base[key] = value;
}
for (var i$1 = 0; i$1 < path.length - 1; i$1++) {
base = get(base, path[i$1]);
if (!base || typeof base !== "object") { throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); } // prettier-ignore
}
break;
var key = path[path.length - 1];
var replace = function (key, value) {
if (isMap(base)) {
base.set(key, value);
return;
case "remove":
if (Array.isArray(base)) {
base.splice(key, 1);
} else {
delete base[key];
}
if (isSet(base)) {
throw new Error('Sets cannot have "replace" patches.');
}
break;
base[key] = value;
};
var add = function (key, value) { return Array.isArray(base) ? base.splice(key, 0, value) : isMap(base) ? base.set(key, value) : isSet(base) ? base.add(value) : base[key] = value; };
var remove = function (key, value) { return Array.isArray(base) ? base.splice(key, 1) : isMap(base) ? base.delete(key) : isSet(base) ? base.delete(value) : delete base[key]; };
switch (patch.op) {
case "replace":
replace(key, patch.value);
break;
case "add":
add(key, patch.value);
break;
case "remove":
remove(key, patch.value);
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
default:
throw new Error("Unsupported patch operation: " + op);
}

@@ -1060,3 +765,3 @@ }

return draft;
}
};

@@ -1135,7 +840,31 @@ function verifyMinified() {}

result = recipe(base);
if (result === undefined) { return base; }
return result !== NOTHING ? result : undefined;
if (result === NOTHING) { return undefined; }
if (result === undefined) { result = base; }
this.maybeFreeze(result, true);
return result;
}
};
Immer.prototype.produceWithPatches = function produceWithPatches (arg1, arg2, arg3) {
var this$1 = this;
if (typeof arg1 === "function") {
return function (state) {
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return this$1.produceWithPatches(state, function (draft) { return arg1.apply(void 0, [ draft ].concat( args )); });
};
} // non-curried form
if (arg3) { throw new Error("A patch listener cannot be passed to produceWithPatches"); }
var patches, inversePatches;
var nextState = this.produce(arg1, arg2, function (p, ip) {
patches = p;
inversePatches = ip;
});
return [nextState, patches, inversePatches];
};
Immer.prototype.createDraft = function createDraft (base) {

@@ -1179,4 +908,17 @@ if (!isDraftable(base)) {

Immer.prototype.applyPatches = function applyPatches$1 (base, patches) {
// Mutate the base state when a draft is passed.
// If a patch replaces the entire state, take that replacement as base
// before applying patches
var i;
for (i = patches.length - 1; i >= 0; i--) {
var patch = patches[i];
if (patch.path.length === 0 && patch.op === "replace") {
base = patch.value;
break;
}
}
if (isDraft(base)) {
// N.B: never hits if some patch a replacement, patches are never drafts
return applyPatches(base, patches);

@@ -1186,3 +928,3 @@ } // Otherwise, produce a copy of the base state.

return this.produce(base, function (draft) { return applyPatches(draft, patches); });
return this.produce(base, function (draft) { return applyPatches(draft, patches.slice(i + 1)); });
};

@@ -1206,2 +948,3 @@ /** @internal */

result = this.finalize(result, null, scope);
this.maybeFreeze(result);
}

@@ -1257,2 +1000,3 @@

if (!state.modified) {
this.maybeFreeze(state.base, true);
return state.base;

@@ -1263,13 +1007,13 @@ }

state.finalized = true;
this.finalizeTree(state.draft, path, scope); // We cannot really delete anything inside of a Set. We can only replace the whole Set.
this.finalizeTree(state.draft, path, scope);
if (this.onDelete && !isSet(state.base)) {
if (this.onDelete) {
// The `assigned` object is unreliable with ES5 drafts.
if (this.useProxies) {
var assigned = state.assigned;
each(assigned, function (prop, exists) {
if (!exists) { this$1.onDelete(state, prop); }
});
for (var prop in assigned) {
if (!assigned[prop]) { this.onDelete(state, prop); }
}
} else {
// TODO: Figure it out for Maps and Sets if we need to support ES5
var base = state.base;

@@ -1329,20 +1073,25 @@ var copy = state.copy;

var isDraftProp = !!state && parent === root;
var isSetMember = isSet(parent);
if (isDraft(value)) {
var path = isDraftProp && needPatches && !isSetMember && // Set objects are atomic since they have no keys.
!has(state.assigned, prop) // Skip deep patches for assigned keys.
? rootPath.concat(prop) : null; // Drafts owned by `scope` are finalized here.
var path = isDraftProp && needPatches && !state.assigned[prop] ? rootPath.concat(prop) : null; // Drafts owned by `scope` are finalized here.
value = this$1.finalize(value, path, scope);
replace(parent, prop, value); // Drafts from another scope must prevent auto-freezing.
value = this$1.finalize(value, path, scope); // Drafts from another scope must prevent auto-freezing.
if (isDraft(value)) {
scope.canAutoFreeze = false;
} // Preserve non-enumerable properties.
if (Array.isArray(parent) || isEnumerable(parent, prop)) {
parent[prop] = value;
} else {
Object.defineProperty(parent, prop, {
value: value
});
} // Unchanged drafts are never passed to the `onAssign` hook.
if (isDraftProp && value === get(state.base, prop)) { return; }
if (isDraftProp && value === state.base[prop]) { return; }
} // Unchanged draft properties are ignored.
else if (isDraftProp && is(value, get(state.base, prop))) {
else if (isDraftProp && is(value, state.base[prop])) {
return;

@@ -1352,6 +1101,6 @@ } // Search new objects for unfinalized drafts. Frozen objects should never contain drafts.

each(value, finalizeProperty);
} // We cannot really assign anything inside of a Set. We can only replace the whole Set.
this$1.maybeFreeze(value);
}
if (isDraftProp && this$1.onAssign && !isSetMember) {
if (isDraftProp && this$1.onAssign) {
this$1.onAssign(state, prop, value);

@@ -1365,20 +1114,9 @@ }

function replace(parent, prop, value) {
if (isMap(parent)) {
parent.set(prop, value);
} else if (isSet(parent)) {
// In this case, the `prop` is actually a draft.
parent.delete(prop);
parent.add(value);
} else if (Array.isArray(parent) || isEnumerable(parent, prop)) {
// Preserve non-enumerable properties.
parent[prop] = value;
} else {
Object.defineProperty(parent, prop, {
value: value,
writable: true,
configurable: true
});
Immer.prototype.maybeFreeze = function maybeFreeze (value, deep) {
if ( deep === void 0 ) deep = false;
if (this.autoFreeze && !isDraft(value)) {
if (deep) { deepFreeze(value); }else { Object.freeze(value); }
}
}
};

@@ -1408,2 +1146,8 @@ var immer = new Immer();

/**
* Like `produce`, but `produceWithPatches` always returns a tuple
* [nextState, patches, inversePatches] (instead of just the next state)
*/
var produceWithPatches = immer.produceWithPatches.bind(immer);
/**
* Pass true to automatically freeze all copies created by Immer.

@@ -1448,3 +1192,3 @@ *

export default produce;
export { Immer, applyPatches$1 as applyPatches, createDraft, finishDraft, DRAFTABLE as immerable, isDraft, isDraftable, NOTHING as nothing, original, produce, setAutoFreeze, setUseProxies };
export { Immer, applyPatches$1 as applyPatches, createDraft, finishDraft, DRAFTABLE as immerable, isDraft, isDraftable, NOTHING as nothing, original, produce, produceWithPatches, setAutoFreeze, setUseProxies };
//# sourceMappingURL=immer.module.js.map

@@ -1,2 +0,2 @@

!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).immer={})}(this,function(e){"use strict";var r,t="undefined"!=typeof Symbol?Symbol("immer-nothing"):((r={})["immer-nothing"]=!0,r),n="undefined"!=typeof Symbol?Symbol.for("immer-draftable"):"__$immer_draftable",o="undefined"!=typeof Symbol?Symbol.for("immer-state"):"__$immer_state";function i(e){return!!e&&!!e[o]}function a(e){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e))return!0;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype||(!(!y(e)&&!b(e))||(!!e[n]||!!e.constructor[n]))}function f(e){if(e&&e[o])return e[o].base}var u=Object.assign||function(e){for(var r=[],t=arguments.length-1;t-- >0;)r[t]=arguments[t+1];return r.forEach(function(r){return Object.keys(r).forEach(function(t){return e[t]=r[t]})}),e},s="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames;function c(e,r){if(void 0===r&&(r=!1),Array.isArray(e))return e.slice();if(y(e))return new Map(e);if(b(e))return new Set(e);var t=Object.create(Object.getPrototypeOf(e));return s(e).forEach(function(n){if(n!==o){var i=Object.getOwnPropertyDescriptor(e,n),a=i.value;if(i.get){if(!r)throw new Error("Immer drafts cannot have computed properties");a=i.get.call(e)}i.enumerable?t[n]=a:Object.defineProperty(t,n,{value:a,writable:!0,configurable:!0})}}),t}function p(e,r){Array.isArray(e)||y(e)||b(e)?e.forEach(function(t,n){return r(n,t,e)}):s(e).forEach(function(t){return r(t,e[t],e)})}function l(e,r){var t=Object.getOwnPropertyDescriptor(e,r);return!!t&&t.enumerable}function d(e,r){return y(e)?e.has(r):Object.prototype.hasOwnProperty.call(e,r)}function h(e,r){return y(e)?e.get(r):e[r]}function v(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}function y(e){return e instanceof Map}function b(e){return e instanceof Set}var g=function(e){this.drafts=[],this.parent=e,this.canAutoFreeze=!0,this.patches=null};function m(e){e[o].revoke()}g.prototype.usePatches=function(e){e&&(this.patches=[],this.inversePatches=[],this.patchListener=e)},g.prototype.revoke=function(){this.leave(),this.drafts.forEach(m),this.drafts=null},g.prototype.leave=function(){this===g.current&&(g.current=this.parent)},g.current=null,g.enter=function(){return this.current=new g(this.current)};var w={};function P(e,r){var t=Array.isArray(e),n=x(e);p(n,function(r){!function(e,r,t){var n=w[r];n?n.enumerable=t:w[r]=n={configurable:!0,enumerable:t,get:function(){return function(e,r){k(e);var t=j(z(e),r);if(e.finalizing)return t;if(t===j(e.base,r)&&a(t))return A(e),e.copy[r]=P(t,e);return t}(this[o],r)},set:function(e){!function(e,r,t){if(k(e),e.assigned[r]=!0,!e.modified){if(v(t,j(z(e),r)))return;E(e),A(e)}e.copy[r]=t}(this[o],r,e)}};Object.defineProperty(e,r,n)}(n,r,t||l(e,r))});var i,f,u,s=r?r.scope:g.current;return i=n,f=o,u={scope:s,modified:!1,finalizing:!1,finalized:!1,assigned:{},parent:r,base:e,draft:n,copy:null,revoke:O,revoked:!1},Object.defineProperty(i,f,{value:u,enumerable:!1,writable:!0}),s.drafts.push(n),n}function O(){this.revoked=!0}function z(e){return e.copy||e.base}function j(e,r){var t=e[o];if(t&&!t.finalizing){t.finalizing=!0;var n=e[r];return t.finalizing=!1,n}return e[r]}function E(e){e.modified||(e.modified=!0,e.parent&&E(e.parent))}function A(e){e.copy||(e.copy=x(e.base))}function x(e){var r=e&&e[o];if(r){r.finalizing=!0;var t=c(r.draft,!0);return r.finalizing=!1,t}return c(e)}function k(e){if(!0===e.revoked)throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+JSON.stringify(z(e)))}function D(e){for(var r=e.length-1;r>=0;r--){var t=e[r][o];t.modified||(Array.isArray(t.base)?F(t)&&E(t):S(t)&&E(t))}}function S(e){for(var r=e.base,t=e.draft,n=Object.keys(t),i=n.length-1;i>=0;i--){var a=n[i],f=r[a];if(void 0===f&&!d(r,a))return!0;var u=t[a],s=u&&u[o];if(s?s.base!==f:!v(u,f))return!0}return n.length!==Object.keys(r).length}function F(e){var r=e.draft;if(r.length!==e.base.length)return!0;var t=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!t||t.get)}var R,I,M=Object.freeze({willFinalize:function(e,r,t){e.drafts.forEach(function(e){e[o].finalizing=!0}),t?i(r)&&r[o].scope===e&&D(e.drafts):(e.patches&&function e(r){if(r&&"object"==typeof r){var t=r[o];if(t){var n=t.base,i=t.draft,a=t.assigned;if(Array.isArray(r)){if(F(t)){if(E(t),a.length=!0,i.length<n.length)for(var f=i.length;f<n.length;f++)a[f]=!1;else for(var u=n.length;u<i.length;u++)a[u]=!0;for(var s=0;s<i.length;s++)void 0===a[s]&&e(i[s])}}else Object.keys(i).forEach(function(r){void 0!==n[r]||d(n,r)?a[r]||e(i[r]):(a[r]=!0,E(t))}),Object.keys(n).forEach(function(e){void 0!==i[e]||d(i,e)||(a[e]=!1,E(t))})}}}(e.drafts[0]),D(e.drafts))},createProxy:P});function N(e,r){var t=r?r.scope:g.current,n={scope:t,modified:!1,finalized:!1,assigned:{},parent:r,base:e,draft:null,drafts:{},copy:null,revoke:null},o=n,i=_;Array.isArray(e)?(o=[n],i=T):y(e)?(i=K,n.drafts=new Map,n.assigned=new Map):b(e)&&(i=L,n.drafts=new Map);var a=Proxy.revocable(o,i),f=a.revoke,u=a.proxy;return n.draft=u,n.revoke=f,t.drafts.push(u),u}var _={get:function(e,r){if(r===o)return e;var t=e.drafts;if(!e.modified&&d(t,r))return t[r];var n=J(e)[r];if(e.finalized||!a(n))return n;if(e.modified){if(n!==V(e.base,r))return n;t=e.copy}return t[r]=N(n,e)},has:function(e,r){return r in J(e)},ownKeys:function(e){return Reflect.ownKeys(J(e))},set:function(e,r,t){if(!e.modified){var n=V(e.base,r);if(t?v(n,t)||t===e.drafts[r]:v(n,t)&&r in e.base)return!0;q(e)}return e.assigned[r]=!0,e.copy[r]=t,!0},deleteProperty:function(e,r){return(void 0!==V(e.base,r)||r in e.base)&&(e.assigned[r]=!1,q(e)),e.copy&&delete e.copy[r],!0},getOwnPropertyDescriptor:function(e,r){var t=J(e),n=Reflect.getOwnPropertyDescriptor(t,r);return n&&(n.writable=!0,n.configurable=!Array.isArray(t)||"length"!==r),n},defineProperty:function(){throw new Error("Object.defineProperty() cannot be used on an Immer draft")},getPrototypeOf:function(e){return Object.getPrototypeOf(e.base)},setPrototypeOf:function(){throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft")}},T={};p(_,function(e,r){T[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}}),T.deleteProperty=function(e,r){if(isNaN(parseInt(r)))throw new Error("Immer only supports deleting array indices");return _.deleteProperty.call(this,e[0],r)},T.set=function(e,r,t){if("length"!==r&&isNaN(parseInt(r)))throw new Error("Immer only supports setting array indices and the 'length' property");return _.set.call(this,e[0],r,t)};var C=["ownKeys","has","set","deleteProperty","defineProperty","getOwnPropertyDescriptor","preventExtensions","isExtensible","getPrototypeOf"].reduce(function(e,r){return e[r]=function(e){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];return Reflect[r].apply(Reflect,[J(e)].concat(t))},e},{}),K=G(((R={})[o]=function(e){return e},R.size=function(e){return J(e).size},R.has=function(e){return function(r){return J(e).has(r)}},R.set=function(e){return function(r,t){var n=J(e);return n.has(r)&&n.get(r)===t||(q(e),e.assigned.set(r,!0),e.copy.set(r,t)),e.draft}},R.delete=function(e){return function(r){return!!J(e).has(r)&&(q(e),e.assigned.set(r,!1),e.copy.delete(r))}},R.clear=function(e){return function(){q(e),e.assigned=new Map;for(var r=0,t=J(e).keys();r<t.length;r+=1){var n=t[r];e.assigned.set(n,!1)}return e.copy.clear()}},R.forEach=function(e,r,t){return function(r,n){return J(e).forEach(function(e,o,i){var a=t.get(o);r.call(n,a,o,i)})}},R.get=function(e){return function(r){var t=e[e.modified?"copy":"drafts"];if(t.has(r))return t.get(r);var n=J(e).get(r);if(e.finalized||!a(n))return n;var o=N(n,e);return t.set(r,o),o}},R.keys=function(e){return function(){return J(e).keys()}},R.values=U,R.entries=U,R[Symbol.iterator]=U,R));function U(e,r,t){var n="values"!==r;return function(){var r=J(e)[Symbol.iterator]();return B(function(){var e=r.next();if(!e.done){var o=e.value[0],i=t.get(o);e.value=n?[o,i]:i}return e})}}var L=G(((I={})[o]=function(e){return e},I.size=function(e){return J(e).size},I.has=function(e){return function(r){return J(e).has(r)}},I.add=function(e){return function(r){return J(e).has(r)||(q(e),e.copy.add(r)),e.draft}},I.delete=function(e){return function(r){return q(e),e.copy.delete(r)}},I.clear=function(e){return function(){return q(e),e.copy.clear()}},I.forEach=function(e){return function(r,t){for(var n=$(e)(),o=n.next();!o.done;)r.call(t,o.value,o.value,e.draft),o=n.next()}},I.keys=$,I.values=$,I.entries=$,I[Symbol.iterator]=$,I));function $(e,r){var t="entries"===r;return function(){var r=J(e)[Symbol.iterator]();return B(function(){var n=r.next();if(!n.done){var o=function(e,r){var t=f(r)||r,n=e.drafts.get(t);if(!n){if(e.finalized||!a(r))return r;n=N(r,e),e.drafts.set(t,n),e.modified&&e.copy.add(n)}return n}(e,n.value);n.value=t?[o,o]:o}return n})}}function J(e){return e.copy||e.base}function V(e,r){var t=e[o],n=Reflect.getOwnPropertyDescriptor(t?J(t):e,r);return n&&n.value}function q(e){if(!e.modified){e.modified=!0;var r=e.base,t=e.drafts,n=e.parent,o=c(r);b(r)?(i=o,t.forEach(function(e){var r=f(e);r&&i.delete(r),i.add(e)})):(y(r)?function(e,r){r.forEach(function(r,t){return e.set(t,r)})}(o,t):u(o,t),e.drafts=null),e.copy=o,n&&q(n)}var i}function B(e){var r,t;return(r={})[Symbol.iterator]=function(){return t},r.next=e,t=r}function G(e){return Object.assign({},C,{get:function(r,t,n){return e.hasOwnProperty(t)?e[t](r,t,n):Reflect.get(r,t,n)},setPrototypeOf:function(e){throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft")}})}var H=Object.freeze({willFinalize:function(){},createProxy:N});function Q(e,r,t,n){var o,i,a=e.base,f=e.copy,u=e.assigned;f.length<a.length&&(a=(o=[f,a])[0],f=o[1],t=(i=[n,t])[0],n=i[1]);for(var s=f.length-a.length,c=0;a[c]===f[c]&&c<a.length;)++c;for(var p=a.length;p>c&&a[p-1]===f[p+s-1];)--p;for(var l=c;l<p;++l)if(u[l]&&f[l]!==a[l]){var d=r.concat([l]);t.push({op:"replace",path:d,value:f[l]}),n.push({op:"replace",path:d,value:a[l]})}for(var h=p!=a.length,v=t.length,y=p+s-1;y>=p;--y){var b=r.concat([y]);t[v+y-p]={op:"add",path:b,value:f[y]},h&&n.push({op:"remove",path:b})}h||n.push({op:"replace",path:r.concat(["length"]),value:a.length})}function W(e,r,t,n){var o=e.base,i=e.copy;p(e.assigned,function(e,a){var f=h(o,e),u=h(i,e),s=a?d(o,e)?"replace":"add":"remove";if(f!==u||"replace"!==s){var c=r.concat(e);t.push("remove"===s?{op:s,path:c}:{op:s,path:c,value:u}),n.push("add"===s?{op:"remove",path:c}:"remove"===s?{op:"add",path:c,value:f}:{op:"replace",path:c,value:f})}})}function X(e,r,t,n){for(var o=e.base,i=e.copy,a=0,f=0,u=o;f<u.length;f+=1){var s=u[f];if(!i.has(s)){var c=r.concat([a]);t.push({op:"remove",path:c,value:s}),n.unshift({op:"add",path:c,value:s})}a++}a=0;for(var p=0,l=i;p<l.length;p+=1){var d=l[p];if(!o.has(d)){var h=r.concat([a]);t.push({op:"add",path:h,value:d}),n.unshift({op:"remove",path:h,value:d})}a++}}function Y(e,r){for(var t=0;t<r.length;t++){var n=r[t],o=n.path;if(0===o.length&&"replace"===n.op)e=n.value;else{for(var i=e,a=0;a<o.length-1;a++)if(!(i=h(i,o[a]))||"object"!=typeof i)throw new Error("Cannot apply patch, path doesn't resolve: "+o.join("/"));var f=o[o.length-1];switch(n.op){case"replace":!function(e,r){if(y(i))i.set(e,r);else{if(b(i))throw new Error('Sets cannot have "replace" patches.');i[e]=r}}(f,n.value);break;case"add":!function(e,r){Array.isArray(i)?i.splice(e,0,r):y(i)?i.set(e,r):b(i)?i.add(r):i[e]=r}(f,n.value);break;case"remove":!function(e,r){Array.isArray(i)?i.splice(e,1):y(i)?i.delete(e):b(i)?i.delete(r):delete i[e]}(f,n.value);break;default:throw new Error("Unsupported patch operation: "+n.op)}}}return e}var Z={useProxies:"undefined"!=typeof Proxy&&"undefined"!=typeof Reflect,autoFreeze:"undefined"!=typeof process?"production"!==process.env.NODE_ENV:"verifyMinified"===function(){}.name,onAssign:null,onDelete:null,onCopy:null},ee=function(e){u(this,Z,e),this.setUseProxies(this.useProxies),this.produce=this.produce.bind(this)};ee.prototype.produce=function(e,r,n){var o,i=this;if("function"==typeof e&&"function"!=typeof r){var f=r;r=e;var u=this;return function(e){var t=this;void 0===e&&(e=f);for(var n=[],o=arguments.length-1;o-- >0;)n[o]=arguments[o+1];return u.produce(e,function(e){return r.call.apply(r,[t,e].concat(n))})}}if("function"!=typeof r)throw new Error("The first or second argument to `produce` must be a function");if(void 0!==n&&"function"!=typeof n)throw new Error("The third argument to `produce` must be a function or undefined");if(a(e)){var s=g.enter(),c=this.createProxy(e),p=!0;try{o=r(c),p=!1}finally{p?s.revoke():s.leave()}return o instanceof Promise?o.then(function(e){return s.usePatches(n),i.processResult(e,s)},function(e){throw s.revoke(),e}):(s.usePatches(n),this.processResult(o,s))}return void 0===(o=r(e))?e:o!==t?o:void 0},ee.prototype.createDraft=function(e){if(!a(e))throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object");var r=g.enter(),t=this.createProxy(e);return t[o].isManual=!0,r.leave(),t},ee.prototype.finishDraft=function(e,r){var t=e&&e[o];if(!t||!t.isManual)throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`");if(t.finalized)throw new Error("The given draft is already finalized");var n=t.scope;return n.usePatches(r),this.processResult(void 0,n)},ee.prototype.setAutoFreeze=function(e){this.autoFreeze=e},ee.prototype.setUseProxies=function(e){this.useProxies=e,u(this,e?H:M)},ee.prototype.applyPatches=function(e,r){return i(e)?Y(e,r):this.produce(e,function(e){return Y(e,r)})},ee.prototype.processResult=function(e,r){var n=r.drafts[0],i=void 0!==e&&e!==n;if(this.willFinalize(r,e,i),i){if(n[o].modified)throw r.revoke(),new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.");a(e)&&(e=this.finalize(e,null,r)),r.patches&&(r.patches.push({op:"replace",path:[],value:e}),r.inversePatches.push({op:"replace",path:[],value:n[o].base}))}else e=this.finalize(n,[],r);return r.revoke(),r.patches&&r.patchListener(r.patches,r.inversePatches),e!==t?e:void 0},ee.prototype.finalize=function(e,r,t){var n=this,i=e[o];if(!i)return Object.isFrozen(e)?e:this.finalizeTree(e,null,t);if(i.scope!==t)return e;if(!i.modified)return i.base;if(!i.finalized){if(i.finalized=!0,this.finalizeTree(i.draft,r,t),this.onDelete&&!b(i.base))if(this.useProxies){p(i.assigned,function(e,r){r||n.onDelete(i,e)})}else{var a=i.base,f=i.copy;p(a,function(e){d(f,e)||n.onDelete(i,e)})}this.onCopy&&this.onCopy(i),this.autoFreeze&&t.canAutoFreeze&&Object.freeze(i.copy),r&&t.patches&&function(e,r,t,n){(Array.isArray(e.base)?Q:b(e.base)?X:W)(e,r,t,n)}(i,r,t.patches,t.inversePatches)}return i.copy},ee.prototype.finalizeTree=function(e,r,t){var n=this,f=e[o];f&&(this.useProxies||(f.copy=c(f.draft,!0)),e=f.copy);var u=!!r&&!!t.patches,s=function(o,c,g){if(c===g)throw Error("Immer forbids circular references");var m=!!f&&g===e,w=b(g);if(i(c)){var P=m&&u&&!w&&!d(f.assigned,o)?r.concat(o):null;if(function(e,r,t){y(e)?e.set(r,t):b(e)?(e.delete(r),e.add(t)):Array.isArray(e)||l(e,r)?e[r]=t:Object.defineProperty(e,r,{value:t,writable:!0,configurable:!0})}(g,o,c=n.finalize(c,P,t)),i(c)&&(t.canAutoFreeze=!1),m&&c===h(f.base,o))return}else{if(m&&v(c,h(f.base,o)))return;a(c)&&!Object.isFrozen(c)&&p(c,s)}m&&n.onAssign&&!w&&n.onAssign(f,o,c)};return p(e,s),e};var re=new ee,te=re.produce,ne=re.setAutoFreeze.bind(re),oe=re.setUseProxies.bind(re),ie=re.applyPatches.bind(re),ae=re.createDraft.bind(re),fe=re.finishDraft.bind(re);e.Immer=ee,e.applyPatches=ie,e.createDraft=ae,e.default=te,e.finishDraft=fe,e.immerable=n,e.isDraft=i,e.isDraftable=a,e.nothing=t,e.original=f,e.produce=te,e.setAutoFreeze=ne,e.setUseProxies=oe,Object.defineProperty(e,"__esModule",{value:!0})});
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).immer={})}(this,function(e){"use strict";var r,t="undefined"!=typeof Symbol?Symbol("immer-nothing"):((r={})["immer-nothing"]=!0,r),n="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-draftable"):"__$immer_draftable",o="undefined"!=typeof Symbol&&Symbol.for?Symbol.for("immer-state"):"__$immer_state";function i(e){return!!e&&!!e[o]}function a(e){return!!e&&(function(e){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e))return!0;var r=Object.getPrototypeOf(e);return!r||r===Object.prototype}(e)||!!e[n]||!!e.constructor[n])}var f=Object.assign||function(e,r){for(var t in r)l(r,t)&&(e[t]=r[t]);return e},s="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:Object.getOwnPropertyNames;function c(e,r){if(void 0===r&&(r=!1),Array.isArray(e))return e.slice();var t=Object.create(Object.getPrototypeOf(e));return s(e).forEach(function(n){if(n!==o){var i=Object.getOwnPropertyDescriptor(e,n),a=i.value;if(i.get){if(!r)throw new Error("Immer drafts cannot have computed properties");a=i.get.call(e)}i.enumerable?t[n]=a:Object.defineProperty(t,n,{value:a,writable:!0,configurable:!0})}}),t}function u(e,r){if(Array.isArray(e))for(var t=0;t<e.length;t++)r(t,e[t],e);else s(e).forEach(function(t){return r(t,e[t],e)})}function p(e,r){var t=Object.getOwnPropertyDescriptor(e,r);return!!t&&t.enumerable}function l(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function h(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}function d(e){if(!a(e))return e;if(Array.isArray(e))return e.map(d);var r=Object.create(Object.getPrototypeOf(e));for(var t in e)r[t]=d(e[t]);return r}function y(e){if(a(e)&&!i(e)&&!Object.isFrozen(e))if(Object.freeze(e),Array.isArray(e))e.forEach(y);else for(var r in e)y(e[r])}var v=function(e){this.drafts=[],this.parent=e,this.canAutoFreeze=!0,this.patches=null};function b(e){e[o].revoke()}v.prototype.usePatches=function(e){e&&(this.patches=[],this.inversePatches=[],this.patchListener=e)},v.prototype.revoke=function(){this.leave(),this.drafts.forEach(b),this.drafts=null},v.prototype.leave=function(){this===v.current&&(v.current=this.parent)},v.current=null,v.enter=function(){return this.current=new v(this.current)};var g={};function m(e,r){var t=Array.isArray(e),n=A(e);u(n,function(r){!function(e,r,t){var n=g[r];n?n.enumerable=t:g[r]=n={configurable:!0,enumerable:t,get:function(){return function(e,r){E(e);var t=O(w(e),r);if(e.finalizing)return t;if(t===O(e.base,r)&&a(t))return j(e),e.copy[r]=m(t,e);return t}(this[o],r)},set:function(e){!function(e,r,t){if(E(e),e.assigned[r]=!0,!e.modified){if(h(t,O(w(e),r)))return;z(e),j(e)}e.copy[r]=t}(this[o],r,e)}};Object.defineProperty(e,r,n)}(n,r,t||p(e,r))});var i,f,s,c=r?r.scope:v.current;return i=n,f=o,s={scope:c,modified:!1,finalizing:!1,finalized:!1,assigned:{},parent:r,base:e,draft:n,copy:null,revoke:P,revoked:!1},Object.defineProperty(i,f,{value:s,enumerable:!1,writable:!0}),c.drafts.push(n),n}function P(){this.revoked=!0}function w(e){return e.copy||e.base}function O(e,r){var t=e[o];if(t&&!t.finalizing){t.finalizing=!0;var n=e[r];return t.finalizing=!1,n}return e[r]}function z(e){e.modified||(e.modified=!0,e.parent&&z(e.parent))}function j(e){e.copy||(e.copy=A(e.base))}function A(e){var r=e&&e[o];if(r){r.finalizing=!0;var t=c(r.draft,!0);return r.finalizing=!1,t}return c(e)}function E(e){if(!0===e.revoked)throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+JSON.stringify(w(e)))}function D(e){for(var r=e.length-1;r>=0;r--){var t=e[r][o];t.modified||(Array.isArray(t.base)?k(t)&&z(t):F(t)&&z(t))}}function F(e){for(var r=e.base,t=e.draft,n=Object.keys(t),i=n.length-1;i>=0;i--){var a=n[i],f=r[a];if(void 0===f&&!l(r,a))return!0;var s=t[a],c=s&&s[o];if(c?c.base!==f:!h(s,f))return!0}return n.length!==Object.keys(r).length}function k(e){var r=e.draft;if(r.length!==e.base.length)return!0;var t=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!t||t.get)}var x=Object.freeze({willFinalize:function(e,r,t){e.drafts.forEach(function(e){e[o].finalizing=!0}),t?i(r)&&r[o].scope===e&&D(e.drafts):(e.patches&&function e(r){if(r&&"object"==typeof r){var t=r[o];if(t){var n=t.base,i=t.draft,a=t.assigned;if(Array.isArray(r)){if(k(t)){if(z(t),a.length=!0,i.length<n.length)for(var f=i.length;f<n.length;f++)a[f]=!1;else for(var s=n.length;s<i.length;s++)a[s]=!0;for(var c=0;c<i.length;c++)void 0===a[c]&&e(i[c])}}else Object.keys(i).forEach(function(r){void 0!==n[r]||l(n,r)?a[r]||e(i[r]):(a[r]=!0,z(t))}),Object.keys(n).forEach(function(e){void 0!==i[e]||l(i,e)||(a[e]=!1,z(t))})}}}(e.drafts[0]),D(e.drafts))},createProxy:m});function R(e,r){var t=r?r.scope:v.current,n={scope:t,modified:!1,finalized:!1,assigned:{},parent:r,base:e,draft:null,drafts:{},copy:null,revoke:null},o=Array.isArray(e)?Proxy.revocable([n],I):Proxy.revocable(n,S),i=o.revoke,a=o.proxy;return n.draft=a,n.revoke=i,t.drafts.push(a),a}var S={get:function(e,r){if(r===o)return e;var t=e.drafts;if(!e.modified&&l(t,r))return t[r];var n=N(e)[r];if(e.finalized||!a(n))return n;if(e.modified){if(n!==_(e.base,r))return n;t=e.copy}return t[r]=R(n,e)},has:function(e,r){return r in N(e)},ownKeys:function(e){return Reflect.ownKeys(N(e))},set:function(e,r,t){if(!e.modified){var n=_(e.base,r),o=t?h(n,t)||t===e.drafts[r]:h(n,t)&&r in e.base;if(o)return!0;T(e)}return e.assigned[r]=!0,e.copy[r]=t,!0},deleteProperty:function(e,r){void 0!==_(e.base,r)||r in e.base?(e.assigned[r]=!1,T(e)):e.assigned[r]&&delete e.assigned[r];e.copy&&delete e.copy[r];return!0},getOwnPropertyDescriptor:function(e,r){var t=N(e),n=Reflect.getOwnPropertyDescriptor(t,r);n&&(n.writable=!0,n.configurable=!Array.isArray(t)||"length"!==r);return n},defineProperty:function(){throw new Error("Object.defineProperty() cannot be used on an Immer draft")},getPrototypeOf:function(e){return Object.getPrototypeOf(e.base)},setPrototypeOf:function(){throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft")}},I={};function N(e){return e.copy||e.base}function _(e,r){var t=e[o],n=Reflect.getOwnPropertyDescriptor(t?N(t):e,r);return n&&n.value}function T(e){e.modified||(e.modified=!0,e.copy=f(c(e.base),e.drafts),e.drafts=null,e.parent&&T(e.parent))}u(S,function(e,r){I[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}}),I.deleteProperty=function(e,r){if(isNaN(parseInt(r)))throw new Error("Immer only supports deleting array indices");return S.deleteProperty.call(this,e[0],r)},I.set=function(e,r,t){if("length"!==r&&isNaN(parseInt(r)))throw new Error("Immer only supports setting array indices and the 'length' property");return S.set.call(this,e[0],r,t)};var C=Object.freeze({willFinalize:function(){},createProxy:R});function U(e,r,t,n){Array.isArray(e.base)?function(e,r,t,n){var o,i,a=e.base,f=e.copy,s=e.assigned;f.length<a.length&&(a=(o=[f,a])[0],f=o[1],t=(i=[n,t])[0],n=i[1]);var c=f.length-a.length,u=0;for(;a[u]===f[u]&&u<a.length;)++u;var p=a.length;for(;p>u&&a[p-1]===f[p+c-1];)--p;for(var l=u;l<p;++l)if(s[l]&&f[l]!==a[l]){var h=r.concat([l]);t.push({op:"replace",path:h,value:f[l]}),n.push({op:"replace",path:h,value:a[l]})}for(var d=t.length,y=p+c-1;y>=p;--y){var v=r.concat([y]);t[d+y-p]={op:"add",path:v,value:f[y]},n.push({op:"remove",path:v})}}(e,r,t,n):function(e,r,t,n){var o=e.base,i=e.copy;u(e.assigned,function(e,a){var f=o[e],s=i[e],c=a?e in o?"replace":"add":"remove";if(f!==s||"replace"!==c){var u=r.concat(e);t.push("remove"===c?{op:c,path:u}:{op:c,path:u,value:s}),n.push("add"===c?{op:"remove",path:u}:"remove"===c?{op:"add",path:u,value:f}:{op:"replace",path:u,value:f})}})}(e,r,t,n)}var W=function(e,r){for(var t=0,n=r;t<n.length;t+=1){var o=n[t],i=o.path,a=o.op,f=d(o.value);if(!i.length)throw new Error("Illegal state");for(var s=e,c=0;c<i.length-1;c++)if(!(s=s[i[c]])||"object"!=typeof s)throw new Error("Cannot apply patch, path doesn't resolve: "+i.join("/"));var u=i[i.length-1];switch(a){case"replace":s[u]=f;break;case"add":Array.isArray(s)?s.splice(u,0,f):s[u]=f;break;case"remove":Array.isArray(s)?s.splice(u,1):delete s[u];break;default:throw new Error("Unsupported patch operation: "+a)}}return e};var K={useProxies:"undefined"!=typeof Proxy&&"undefined"!=typeof Reflect,autoFreeze:"undefined"!=typeof process?"production"!==process.env.NODE_ENV:"verifyMinified"===function(){}.name,onAssign:null,onDelete:null,onCopy:null},M=function(e){f(this,K,e),this.setUseProxies(this.useProxies),this.produce=this.produce.bind(this)};M.prototype.produce=function(e,r,n){var o,i=this;if("function"==typeof e&&"function"!=typeof r){var f=r;r=e;var s=this;return function(e){var t=this;void 0===e&&(e=f);for(var n=[],o=arguments.length-1;o-- >0;)n[o]=arguments[o+1];return s.produce(e,function(e){return r.call.apply(r,[t,e].concat(n))})}}if("function"!=typeof r)throw new Error("The first or second argument to `produce` must be a function");if(void 0!==n&&"function"!=typeof n)throw new Error("The third argument to `produce` must be a function or undefined");if(a(e)){var c=v.enter(),u=this.createProxy(e),p=!0;try{o=r(u),p=!1}finally{p?c.revoke():c.leave()}return o instanceof Promise?o.then(function(e){return c.usePatches(n),i.processResult(e,c)},function(e){throw c.revoke(),e}):(c.usePatches(n),this.processResult(o,c))}if((o=r(e))!==t)return void 0===o&&(o=e),this.maybeFreeze(o,!0),o},M.prototype.produceWithPatches=function(e,r,t){var n,o,i=this;if("function"==typeof e)return function(r){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];return i.produceWithPatches(r,function(r){return e.apply(void 0,[r].concat(t))})};if(t)throw new Error("A patch listener cannot be passed to produceWithPatches");return[this.produce(e,r,function(e,r){n=e,o=r}),n,o]},M.prototype.createDraft=function(e){if(!a(e))throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object");var r=v.enter(),t=this.createProxy(e);return t[o].isManual=!0,r.leave(),t},M.prototype.finishDraft=function(e,r){var t=e&&e[o];if(!t||!t.isManual)throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`");if(t.finalized)throw new Error("The given draft is already finalized");var n=t.scope;return n.usePatches(r),this.processResult(void 0,n)},M.prototype.setAutoFreeze=function(e){this.autoFreeze=e},M.prototype.setUseProxies=function(e){this.useProxies=e,f(this,e?C:x)},M.prototype.applyPatches=function(e,r){var t;for(t=r.length-1;t>=0;t--){var n=r[t];if(0===n.path.length&&"replace"===n.op){e=n.value;break}}return i(e)?W(e,r):this.produce(e,function(e){return W(e,r.slice(t+1))})},M.prototype.processResult=function(e,r){var n=r.drafts[0],i=void 0!==e&&e!==n;if(this.willFinalize(r,e,i),i){if(n[o].modified)throw r.revoke(),new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.");a(e)&&(e=this.finalize(e,null,r),this.maybeFreeze(e)),r.patches&&(r.patches.push({op:"replace",path:[],value:e}),r.inversePatches.push({op:"replace",path:[],value:n[o].base}))}else e=this.finalize(n,[],r);return r.revoke(),r.patches&&r.patchListener(r.patches,r.inversePatches),e!==t?e:void 0},M.prototype.finalize=function(e,r,t){var n=this,i=e[o];if(!i)return Object.isFrozen(e)?e:this.finalizeTree(e,null,t);if(i.scope!==t)return e;if(!i.modified)return this.maybeFreeze(i.base,!0),i.base;if(!i.finalized){if(i.finalized=!0,this.finalizeTree(i.draft,r,t),this.onDelete)if(this.useProxies){var a=i.assigned;for(var f in a)a[f]||this.onDelete(i,f)}else{var s=i.base,c=i.copy;u(s,function(e){l(c,e)||n.onDelete(i,e)})}this.onCopy&&this.onCopy(i),this.autoFreeze&&t.canAutoFreeze&&Object.freeze(i.copy),r&&t.patches&&U(i,r,t.patches,t.inversePatches)}return i.copy},M.prototype.finalizeTree=function(e,r,t){var n=this,f=e[o];f&&(this.useProxies||(f.copy=c(f.draft,!0)),e=f.copy);var s=!!r&&!!t.patches,l=function(o,c,d){if(c===d)throw Error("Immer forbids circular references");var y=!!f&&d===e;if(i(c)){var v=y&&s&&!f.assigned[o]?r.concat(o):null;if(i(c=n.finalize(c,v,t))&&(t.canAutoFreeze=!1),Array.isArray(d)||p(d,o)?d[o]=c:Object.defineProperty(d,o,{value:c}),y&&c===f.base[o])return}else{if(y&&h(c,f.base[o]))return;a(c)&&!Object.isFrozen(c)&&(u(c,l),n.maybeFreeze(c))}y&&n.onAssign&&n.onAssign(f,o,c)};return u(e,l),e},M.prototype.maybeFreeze=function(e,r){void 0===r&&(r=!1),this.autoFreeze&&!i(e)&&(r?y(e):Object.freeze(e))};var L=new M,$=L.produce,J=L.produceWithPatches.bind(L),V=L.setAutoFreeze.bind(L),q=L.setUseProxies.bind(L),B=L.applyPatches.bind(L),G=L.createDraft.bind(L),H=L.finishDraft.bind(L);e.Immer=M,e.applyPatches=B,e.createDraft=G,e.default=$,e.finishDraft=H,e.immerable=n,e.isDraft=i,e.isDraftable=a,e.nothing=t,e.original=function(e){if(e&&e[o])return e[o].base},e.produce=$,e.produceWithPatches=J,e.setAutoFreeze=V,e.setUseProxies=q,Object.defineProperty(e,"__esModule",{value:!0})});
//# sourceMappingURL=immer.umd.js.map
{
"name": "immer",
"version": "4.0.0-beta.0",
"description": "Create your next immutable state by mutating the current one",
"main": "dist/immer.js",
"umd:main": "dist/immer.umd.js",
"module": "dist/immer.module.js",
"jsnext:main": "dist/immer.module.js",
"react-native": "dist/immer.module.js",
"types": "./dist/immer.d.ts",
"scripts": {
"test": "jest",
"test:perf": "NODE_ENV=production yarn-or-npm build && cd __performance_tests__ && babel-node add-data.js && babel-node todo.js && babel-node incremental.js",
"test:flow": "yarn-or-npm flow check __tests__/flow",
"test:dts": "tsc -p __tests__/tsconfig.json --noEmit",
"coveralls": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage",
"build": "rimraf dist/ && yarn-or-npm bili && yarn-or-npm typed",
"typed": "cpx 'src/immer.{d.ts,js.flow}' dist -v"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"repository": {
"type": "git",
"url": "https://github.com/immerjs/immer.git"
},
"keywords": [
"immutable",
"mutable",
"copy-on-write"
],
"author": "Michel Weststrate",
"license": "MIT",
"bugs": {
"url": "https://github.com/immerjs/immer/issues"
},
"homepage": "https://github.com/immerjs/immer#readme",
"files": [
"dist",
"src"
],
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.3.4",
"@babel/node": "^7.2.2",
"@babel/plugin-external-helpers": "^7.2.0",
"@babel/preset-env": "^7.3.4",
"@types/jest": "^24.0.11",
"babel-jest": "^24.4.0",
"babel-preset-modern-browsers": "^13.1.0",
"bili": "^4.7.0",
"coveralls": "^3.0.0",
"cpx": "^1.5.0",
"cross-env": "^5.1.3",
"deep-freeze": "^0.0.1",
"expect": "^24.7.1",
"flow-bin": "^0.68.0",
"husky": "^1.2.0",
"immutable": "^3.8.2",
"jest": "^24.7.1",
"lodash": "^4.17.4",
"lodash.clonedeep": "^4.5.0",
"prettier": "1.17.0",
"pretty-quick": "^1.8.0",
"regenerator-runtime": "^0.11.1",
"rimraf": "^2.6.2",
"seamless-immutable": "^7.1.3",
"spec.ts": "^1.1.0",
"typescript": "3.4.3",
"yarn-or-npm": "^2.0.4"
},
"jest": {
"moduleFileExtensions": [
"js"
],
"testURL": "http://localhost",
"transform": {
"\\.js$": "babel-jest"
}
}
"name": "immer",
"version": "4.0.0",
"description": "Create your next immutable state by mutating the current one",
"main": "dist/immer.js",
"umd:main": "dist/immer.umd.js",
"unpkg": "dist/immer.umd.js",
"jsdelivr": "dist/immer.umd.js",
"module": "dist/immer.module.js",
"jsnext:main": "dist/immer.module.js",
"react-native": "dist/immer.module.js",
"types": "./dist/immer.d.ts",
"scripts": {
"test": "jest",
"test:perf": "NODE_ENV=production yarn-or-npm build && cd __performance_tests__ && babel-node add-data.js && babel-node todo.js && babel-node incremental.js",
"test:flow": "yarn-or-npm flow check __tests__/flow",
"test:dts": "tsc -p __tests__/tsconfig.json --noEmit",
"watch": "jest --watch",
"coveralls": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/.bin/coveralls && rm -rf ./coverage",
"build": "rimraf dist/ && yarn-or-npm bili && yarn-or-npm typed",
"typed": "cpx 'src/immer.{d.ts,js.flow}' dist -v"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"repository": {
"type": "git",
"url": "https://github.com/immerjs/immer.git"
},
"keywords": [
"immutable",
"mutable",
"copy-on-write"
],
"author": "Michel Weststrate",
"license": "MIT",
"bugs": {
"url": "https://github.com/immerjs/immer/issues"
},
"homepage": "https://github.com/immerjs/immer#readme",
"files": [
"dist",
"src"
],
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.3.4",
"@babel/node": "^7.2.2",
"@babel/plugin-external-helpers": "^7.2.0",
"@babel/preset-env": "^7.3.4",
"@types/jest": "^24.0.11",
"babel-jest": "^24.4.0",
"babel-preset-modern-browsers": "^13.1.0",
"bili": "^4.7.0",
"coveralls": "^3.0.0",
"cpx": "^1.5.0",
"cross-env": "^5.1.3",
"deep-freeze": "^0.0.1",
"expect": "^24.7.1",
"flow-bin": "^0.68.0",
"husky": "^1.2.0",
"immutable": "^3.8.2",
"jest": "^24.7.1",
"lodash": "^4.17.4",
"lodash.clonedeep": "^4.5.0",
"prettier": "1.17.0",
"pretty-quick": "^1.8.0",
"regenerator-runtime": "^0.11.1",
"rimraf": "^2.6.2",
"seamless-immutable": "^7.1.3",
"spec.ts": "^1.1.0",
"typescript": "3.4.3",
"yarn-or-npm": "^2.0.4"
},
"jest": {
"moduleFileExtensions": [
"js"
],
"testURL": "http://localhost",
"transform": {
"\\.js$": "babel-jest"
}
}
}

@@ -9,3 +9,3 @@ <img src="images/immer-logo.png" height="200px" align="right"/>

Winner of the "breakthrough of the year" [open source award](https://osawards.com/react/) in 2019
Winner of the "Breakthrough of the year" [React open source award](https://osawards.com/react/) and "Most impactful contribution" [JavaScript open source award](https://osawards.com/javascript/) in 2019

@@ -18,16 +18,16 @@ ### [Release notes](https://github.com/immerjs/immer/releases)

- NPM: `npm install immer`
- Yarn: `yarn add immer`
- CDN: Exposed global is `immer`
- Unpkg: `<script src="https://unpkg.com/immer/dist/immer.umd.js"></script>`
- JSDelivr: `<script src="https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js"></script>`
- NPM: `npm install immer`
- Yarn: `yarn add immer`
- CDN: Exposed global is `immer`
- Unpkg: `<script src="https://unpkg.com/immer/dist/immer.umd.js"></script>`
- JSDelivr: `<script src="https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js"></script>`
---
- Egghead lesson covering all of immer (7m): [Simplify creating immutable data trees with Immer](https://egghead.io/lessons/redux-simplify-creating-immutable-data-trees-with-immer)
- Introduction blogpost: [Immer: Immutability the easy way](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3)
- Egghead lesson covering all of immer (7m): [Simplify creating immutable data trees with Immer](https://egghead.io/lessons/redux-simplify-creating-immutable-data-trees-with-immer)
- Introduction blogpost: [Immer: Immutability the easy way](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3)
Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way. It is based on the [_copy-on-write_](https://en.wikipedia.org/wiki/Copy-on-write) mechanism.
The basic idea is that you will apply all your changes to a temporarily _draftState_, which is a proxy of the _currentState_. Once all your mutations are completed, Immer will produce the _nextState_ based on the mutations to the draft state. This means that you can interact with your data by simply modifying it while keeping all the benefits of immutable data.
The basic idea is that you will apply all your changes to a temporary _draftState_, which is a proxy of the _currentState_. Once all your mutations are completed, Immer will produce the _nextState_ based on the mutations to the draft state. This means that you can interact with your data by simply modifying it while keeping all the benefits of immutable data.

@@ -42,8 +42,8 @@ ![immer-hd.png](images/hd/immer.png)

- Blog: [The Rise of Immer in React](https://www.netlify.com/blog/2018/09/12/the-rise-of-immer-in-react/)
- Blog: by Workday Prism on why they picked Immer to manage immutable state [The Search for a Strongly-Typed, Immutable State](https://medium.com/workday-engineering/workday-prism-analytics-the-search-for-a-strongly-typed-immutable-state-a09f6768b2b5)
- Blog: [Immutability in React and Redux: The Complete Guide](https://daveceddia.com/react-redux-immutability-guide/)
- Video tutorial: [Using Immer with React.setState](https://codedaily.io/screencasts/86/Immutable-Data-with-Immer-and-React-setState)
- [Talk](https://www.youtube.com/watch?v=-gJbS7YjcSo) + [slides](http://immer.surge.sh/) on Immer at React Finland 2018 by Michel Weststrate
- [ForwardJS 2019: Immutability is Changing - From Immutable.js to Immer](https://www.youtube.com/watch?v=bFuRvcAEiHg&feature=youtu.be) by [shawn swyx wang](https://twitter.com/swyx/)
- Blog: [The Rise of Immer in React](https://www.netlify.com/blog/2018/09/12/the-rise-of-immer-in-react/)
- Blog: by Workday Prism on why they picked Immer to manage immutable state [The Search for a Strongly-Typed, Immutable State](https://medium.com/workday-engineering/workday-prism-analytics-the-search-for-a-strongly-typed-immutable-state-a09f6768b2b5)
- Blog: [Immutability in React and Redux: The Complete Guide](https://daveceddia.com/react-redux-immutability-guide/)
- Video tutorial: [Using Immer with React.setState](https://codedaily.io/screencasts/86/Immutable-Data-with-Immer-and-React-setState)
- [Talk](https://www.youtube.com/watch?v=-gJbS7YjcSo) + [slides](http://immer.surge.sh/) on Immer at React Finland 2018 by Michel Weststrate
- [ForwardJS 2019: Immutability is Changing - From Immutable.js to Immer](https://www.youtube.com/watch?v=bFuRvcAEiHg&feature=youtu.be) by [shawn swyx wang](https://twitter.com/swyx/)

@@ -64,15 +64,15 @@ ## API

const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
]
const nextState = produce(baseState, draftState => {
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
})

@@ -101,9 +101,9 @@ ```

- Immutability with normal JavaScript objects and arrays. No new APIs to learn!
- Strongly typed, no string based paths selectors etc.
- Structural sharing out of the box
- Object freezing out of the box
- Deep updates are a breeze
- Boilerplate reduction. Less noise, more concise code.
- Small [![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js)
- Immutability with normal JavaScript objects and arrays. No new APIs to learn!
- Strongly typed, no string based paths selectors etc.
- Structural sharing out of the box
- Object freezing out of the box
- Deep updates are a breeze
- Boilerplate reduction. Less noise, more concise code.
- Small [![size](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js?compression=gzip)](http://img.badgesize.io/https://cdn.jsdelivr.net/npm/immer/dist/immer.umd.js)

@@ -120,14 +120,14 @@ Read further to see all these benefits explained.

const byId = (state, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
return {
...state,
...action.products.reduce((obj, product) => {
obj[product.id] = product
return obj
}, {})
}
default:
return state
}
switch (action.type) {
case RECEIVE_PRODUCTS:
return {
...state,
...action.products.reduce((obj, product) => {
obj[product.id] = product
return obj
}, {})
}
default:
return state
}
}

@@ -142,10 +142,10 @@ ```

const byId = (state, action) =>
produce(state, draft => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
}
})
produce(state, draft => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
}
})
```

@@ -168,8 +168,8 @@

onBirthDayClick1 = () => {
this.setState(prevState => ({
user: {
...prevState.user,
age: prevState.user.age + 1
}
}))
this.setState(prevState => ({
user: {
...prevState.user,
age: prevState.user.age + 1
}
}))
}

@@ -182,7 +182,7 @@

onBirthDayClick2 = () => {
this.setState(
produce(draft => {
draft.user.age += 1
})
)
this.setState(
produce(draft => {
draft.user.age += 1
})
)
}

@@ -200,3 +200,3 @@ ```

const mapper = produce((draft, index) => {
draft.index = index
draft.index = index
})

@@ -212,13 +212,12 @@

```javascript
import produce from 'immer'
import produce from "immer"
const byId = produce((draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
})
}
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
}
})

@@ -235,14 +234,14 @@ ```

const byId = produce(
(draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
}
},
{
1: {id: 1, name: "product-1"}
}
(draft, action) => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
return
}
},
{
1: {id: 1, name: "product-1"}
}
)

@@ -272,5 +271,5 @@ ```

- To exchange incremental updates with other parties, for example over websockets
- For debugging / traces, to see precisely how state is changed over time
- As basis for undo/redo or as an approach to replay changes on a slightly different state tree
- To exchange incremental updates with other parties, for example over websockets
- For debugging / traces, to see precisely how state is changed over time
- As basis for undo/redo or as an approach to replay changes on a slightly different state tree

@@ -283,4 +282,4 @@ To help with replaying patches, `applyPatches` comes in handy. Here is an example how patches could be used to record the incremental updates and (inverse) apply them:

let state = {
name: "Micheal",
age: 32
name: "Micheal",
age: 32
}

@@ -297,11 +296,11 @@

fork = produce(
fork,
draft => {
draft.age = 33
},
// The third argument to produce is a callback to which the patches will be fed
(patches, inversePatches) => {
changes.push(...patches)
inverseChanges.push(...inversePatches)
}
fork,
draft => {
draft.age = 33
},
// The third argument to produce is a callback to which the patches will be fed
(patches, inversePatches) => {
changes.push(...patches)
inverseChanges.push(...inversePatches)
}
)

@@ -312,3 +311,3 @@

state = produce(state, draft => {
draft.name = "Michel"
draft.name = "Michel"
})

@@ -321,4 +320,4 @@

expect(state).toEqual({
name: "Michel", // changed by the server
age: 33 // changed by the wizard
name: "Michel", // changed by the server
age: 33 // changed by the wizard
})

@@ -329,4 +328,4 @@

expect(state).toEqual({
name: "Michel", // Not reverted
age: 32 // Reverted
name: "Michel", // Not reverted
age: 32 // Reverted
})

@@ -339,8 +338,8 @@ ```

[
{
"op": "replace",
"path": ["profile"],
"value": {"name": "Veria", "age": 5}
},
{"op": "remove", "path": ["tags", 3]}
{
"op": "replace",
"path": ["profile"],
"value": {"name": "Veria", "age": 5}
},
{"op": "remove", "path": ["tags", 3]}
]

@@ -351,7 +350,48 @@ ```

[
{"op": "replace", "path": ["profile"], "value": {"name": "Noa", "age": 6}},
{"op": "add", "path": ["tags", 3], "value": "kiddo"}
{"op": "replace", "path": ["profile"], "value": {"name": "Noa", "age": 6}},
{"op": "add", "path": ["tags", 3], "value": "kiddo"}
]
```
### `produceWithPatches`
Instead of setting up a patch listener, an easier way to obtain the patches is to use `produceWithPatches`, which has the same signature as `produce`, except that it doesn't return just the next state, but a tuple consisting of `[nextState, patches, inversePatches]`. Like `produce`, `produceWithPatches` supports currying as well.
```javascript
import {produceWithPatches} from "immer"
const [nextState, patches, inversePatches] = produceWithPatches(
{
age: 33
},
draft => {
draft.age++
}
)
```
Which produces:
```javascript
;[
{
age: 34
},
[
{
op: "replace",
path: ["age"],
value: 34
}
],
[
{
op: "replace",
path: ["age"],
value: 33
}
]
]
```
For a more in-depth study, see [Distributing patches and rebasing actions using Immer](https://medium.com/@mweststrate/distributing-state-changes-using-snapshots-patches-and-actions-part-2-2f50d8363988)

@@ -369,8 +409,8 @@

const user = {
name: "michel",
todos: []
name: "michel",
todos: []
}
const loadedUser = await produce(user, async function(draft) {
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
})

@@ -389,4 +429,4 @@ ```

const user = {
name: "michel",
todos: []
name: "michel",
todos: []
}

@@ -411,32 +451,32 @@

const userReducer = produce((draft, action) => {
switch (action.type) {
case "renameUser":
// OK: we modify the current state
draft.users[action.payload.id].name = action.payload.name
return draft // same as just 'return'
case "loadUsers":
// OK: we return an entirely new state
return action.payload
case "adduser-1":
// NOT OK: This doesn't do change the draft nor return a new state!
// It doesn't modify the draft (it just redeclares it)
// In fact, this just doesn't do anything at all
draft = {users: [...draft.users, action.payload]}
return
case "adduser-2":
// NOT OK: modifying draft *and* returning a new state
draft.userCount += 1
return {users: [...draft.users, action.payload]}
case "adduser-3":
// OK: returning a new state. But, unnecessary complex and expensive
return {
userCount: draft.userCount + 1,
users: [...draft.users, action.payload]
}
case "adduser-4":
// OK: the immer way
draft.userCount += 1
draft.users.push(action.payload)
return
}
switch (action.type) {
case "renameUser":
// OK: we modify the current state
draft.users[action.payload.id].name = action.payload.name
return draft // same as just 'return'
case "loadUsers":
// OK: we return an entirely new state
return action.payload
case "adduser-1":
// NOT OK: This doesn't do change the draft nor return a new state!
// It doesn't modify the draft (it just redeclares it)
// In fact, this just doesn't do anything at all
draft = {users: [...draft.users, action.payload]}
return
case "adduser-2":
// NOT OK: modifying draft *and* returning a new state
draft.userCount += 1
return {users: [...draft.users, action.payload]}
case "adduser-3":
// OK: returning a new state. But, unnecessary complex and expensive
return {
userCount: draft.userCount + 1,
users: [...draft.users, action.payload]
}
case "adduser-4":
// OK: the immer way
draft.userCount += 1
draft.users.push(action.payload)
return
}
})

@@ -453,3 +493,3 @@ ```

produce({}, draft => {
// don't do anything
// don't do anything
})

@@ -462,4 +502,4 @@ ```

produce({}, draft => {
// Try to return undefined from the producer
return undefined
// Try to return undefined from the producer
return undefined
})

@@ -476,3 +516,3 @@ ```

const state = {
hello: "world"
hello: "world"
}

@@ -511,5 +551,7 @@

```js
import {original} from "immer"
const baseState = {users: [{name: "Richie"}]}
const nextState = produce(baseState, draftState => {
original(draftState.users) // is === baseState.users
original(draftState.users) // is === baseState.users
})

@@ -525,5 +567,5 @@ ```

const nextState = produce(baseState, draft => {
isDraft(draft) // => true
isDraft(draft.users) // => true
isDraft(draft.users[0]) // => true
isDraft(draft) // => true
isDraft(draft.users) // => true
isDraft(draft.users[0]) // => true
})

@@ -537,2 +579,4 @@ isDraft(nextState) // => false

_⚠️ If auto freezing is enabled, recipes are not entirely side-effect free: Any plain object or array that ends up in the produced result, will be frozen, even when these objects were not frozen before the start of the producer! ⚠️_
## Immer on older JavaScript environments?

@@ -560,3 +604,3 @@

Plain objects, arrays, `Map` objects, and `Set` objects are always drafted by Immer.
Plain objects and arrays are always drafted by Immer.

@@ -569,7 +613,7 @@ Every other object must use the `immerable` symbol to mark itself as compatible with Immer. When one of these objects is mutated within a producer, its prototype is preserved between copies.

class Foo {
[immerable] = true // Option 1
[immerable] = true // Option 1
constructor() {
this[immerable] = true // Option 2
}
constructor() {
this[immerable] = true // Option 2
}
}

@@ -584,2 +628,36 @@

Built-in classes like `Map` and `Set` are not supported. As a workaround, you should clone them before mutating them in a producer:
```js
const state = {
set: new Set(),
map: new Map()
}
const nextState = produce(state, draft => {
// Don't use any Set methods, as that mutates the instance!
draft.set.add("foo") // ❌
// 1. Instead, clone the set (just once)
const newSet = new Set(draft.set) // ✅
// 2. Mutate the clone (just in this producer)
newSet.add("foo")
// 3. Update the draft with the new set
draft.set = newSet
// Similarly, don't use any Map methods.
draft.map.set("foo", "bar") // ❌
// 1. Instead, clone the map (just once)
const newMap = new Map(draft.map) // ✅
// 2. Mutate it
newMap.set("foo", "bar")
// 3. Update the draft
draft.map = newMap
})
```
## TypeScript or Flow

@@ -595,3 +673,3 @@

interface State {
readonly x: number
readonly x: number
}

@@ -601,8 +679,8 @@

const state: State = {
x: 0
x: 0
}
const newState = produce(state, draft => {
// `x` can be modified here
draft.x++
// `x` can be modified here
draft.x++
})

@@ -621,3 +699,3 @@

interface State {
readonly x: number
readonly x: number
}

@@ -627,8 +705,8 @@

const state: State = {
x: 0
x: 0
}
const increment = produce((draft: Draft<State>, inc: number) => {
// `x` can be modified here
draft.x += inc
// `x` can be modified here
draft.x += inc
})

@@ -654,14 +732,15 @@

- [react-copy-write](https://github.com/aweary/react-copy-write) _Immutable state with a mutable API_
- [redux-starter-kit](https://github.com/markerikson/redux-starter-kit) _A simple set of tools to make using Redux easier_
- [immer based handleActions](https://gist.github.com/kitze/fb65f527803a93fb2803ce79a792fff8) _Boilerplate free actions for Redux_
- [redux-box](https://github.com/anish000kumar/redux-box) _Modular and easy-to-grasp redux based state management, with least boilerplate_
- [quick-redux](https://github.com/jeffreyyoung/quick-redux) _tools to make redux development quicker and easier_
- [bey](https://github.com/jamiebuilds/bey) _Simple immutable state for React using Immer_
- [immer-wieder](https://github.com/drcmda/immer-wieder#readme) _State management lib that combines React 16 Context and immer for Redux semantics_
- [robodux](https://github.com/neurosnap/robodux) _flexible way to reduce redux boilerplate_
- [immer-reducer](https://github.com/epeli/immer-reducer) _Type-safe and terse Redux reducers with Typescript_
- [redux-ts-utils](https://github.com/knpwrs/redux-ts-utils) _Everything you need to create type-safe applications with Redux with a strong emphasis on simplicity_
- [react-state-tree](https://github.com/suchipi/react-state-tree) _Drop-in replacement for useState that persists your state into a redux-like state tree_
- ... and [many more](https://www.npmjs.com/browse/depended/immer)
- [react-copy-write](https://github.com/aweary/react-copy-write) _Immutable state with a mutable API_
- [redux-starter-kit](https://github.com/markerikson/redux-starter-kit) _A simple set of tools to make using Redux easier_
- [immer based handleActions](https://gist.github.com/kitze/fb65f527803a93fb2803ce79a792fff8) _Boilerplate free actions for Redux_
- [redux-box](https://github.com/anish000kumar/redux-box) _Modular and easy-to-grasp redux based state management, with least boilerplate_
- [quick-redux](https://github.com/jeffreyyoung/quick-redux) _tools to make redux development quicker and easier_
- [bey](https://github.com/jamiebuilds/bey) _Simple immutable state for React using Immer_
- [immer-wieder](https://github.com/drcmda/immer-wieder#readme) _State management lib that combines React 16 Context and immer for Redux semantics_
- [robodux](https://github.com/neurosnap/robodux) _flexible way to reduce redux boilerplate_
- [immer-reducer](https://github.com/epeli/immer-reducer) _Type-safe and terse React (useReducer()) and Redux reducers with Typescript_
- [redux-ts-utils](https://github.com/knpwrs/redux-ts-utils) _Everything you need to create type-safe applications with Redux with a strong emphasis on simplicity_
- [react-state-tree](https://github.com/suchipi/react-state-tree) _Drop-in replacement for useState that persists your state into a redux-like state tree_
- [redux-immer](https://github.com/salvoravida/redux-immer) _is used to create an equivalent function of Redux combineReducers that works with `immer` state. Like `redux-immutable` but for `immer`_
- ... and [many more](https://www.npmjs.com/browse/depended/immer)

@@ -681,4 +760,4 @@ ## How does Immer work?

const todosObj = {
id1: {done: false, body: "Take out the trash"},
id2: {done: false, body: "Check Email"}
id1: {done: false, body: "Take out the trash"},
id2: {done: false, body: "Check Email"}
}

@@ -688,3 +767,3 @@

const addedTodosObj = produce(todosObj, draft => {
draft["id3"] = {done: false, body: "Buy bananas"}
draft["id3"] = {done: false, body: "Buy bananas"}
})

@@ -694,3 +773,3 @@

const deletedTodosObj = produce(todosObj, draft => {
delete draft["id1"]
delete draft["id1"]
})

@@ -700,3 +779,3 @@

const updatedTodosObj = produce(todosObj, draft => {
draft["id1"].done = true
draft["id1"].done = true
})

@@ -706,4 +785,4 @@

const todosArray = [
{id: "id1", done: false, body: "Take out the trash"},
{id: "id2", done: false, body: "Check Email"}
{id: "id1", done: false, body: "Take out the trash"},
{id: "id2", done: false, body: "Check Email"}
]

@@ -713,3 +792,3 @@

const addedTodosArray = produce(todosArray, draft => {
draft.push({id: "id3", done: false, body: "Buy bananas"})
draft.push({id: "id3", done: false, body: "Buy bananas"})
})

@@ -719,5 +798,5 @@

const deletedTodosArray = produce(todosArray, draft => {
draft.splice(draft.findIndex(todo => todo.id === "id1"), 1)
// or (slower):
// return draft.filter(todo => todo.id !== "id1")
draft.splice(draft.findIndex(todo => todo.id === "id1"), 1)
// or (slower):
// return draft.filter(todo => todo.id !== "id1")
})

@@ -727,3 +806,3 @@

const updatedTodosArray = produce(todosArray, draft => {
draft[draft.findIndex(todo => todo.id === "id1")].done = true
draft[draft.findIndex(todo => todo.id === "id1")].done = true
})

@@ -742,6 +821,6 @@ ```

- Immer with proxies is roughly speaking twice to three times slower as a handwritten reducer (the above test case is worst case, see `yarn test:perf` for more tests). This is in practice negligible.
- Immer is roughly as fast as ImmutableJS. However, the _immutableJS + toJS_ makes clear the cost that often needs to be paid later; converting the immutableJS objects back to plain objects, to be able to pass them to components, over the network etc... (And there is also the upfront cost of converting data received from e.g. the server to immutable JS)
- Generating patches doesn't significantly slow immer down
- The ES5 fallback implementation is roughly twice as slow as the proxy implementation, in some cases worse.
- Immer with proxies is roughly speaking twice to three times slower as a handwritten reducer (the above test case is worst case, see `yarn test:perf` for more tests). This is in practice negligible.
- Immer is roughly as fast as ImmutableJS. However, the _immutableJS + toJS_ makes clear the cost that often needs to be paid later; converting the immutableJS objects back to plain objects, to be able to pass them to components, over the network etc... (And there is also the upfront cost of converting data received from e.g. the server to immutable JS)
- Generating patches doesn't significantly slow immer down
- The ES5 fallback implementation is roughly twice as slow as the proxy implementation, in some cases worse.

@@ -764,6 +843,2 @@ ## Migration

**Immer 3.\* -> 4.0**
`Maps` and `Sets` are now supported by `immer` and, therefore, immutable. Your current code, where you kept immutability of `Maps` and `Sets` on your own, should keep working as before in simple cases. However, consider covering it with a sufficient number of unit tests to avoid any unexpected outcome.
## FAQ

@@ -770,0 +845,0 @@

export const NOTHING =
typeof Symbol !== "undefined"
? Symbol("immer-nothing")
: {["immer-nothing"]: true}
typeof Symbol !== "undefined"
? Symbol("immer-nothing")
: {["immer-nothing"]: true}
export const DRAFTABLE =
typeof Symbol !== "undefined"
? Symbol.for("immer-draftable")
: "__$immer_draftable"
typeof Symbol !== "undefined" && Symbol.for
? Symbol.for("immer-draftable")
: "__$immer_draftable"
export const DRAFT_STATE =
typeof Symbol !== "undefined" ? Symbol.for("immer-state") : "__$immer_state"
typeof Symbol !== "undefined" && Symbol.for
? Symbol.for("immer-state")
: "__$immer_state"
export function isDraft(value) {
return !!value && !!value[DRAFT_STATE]
return !!value && !!value[DRAFT_STATE]
}
export function isDraftable(value) {
if (!value || typeof value !== "object") return false
if (Array.isArray(value)) return true
const proto = Object.getPrototypeOf(value)
if (!proto || proto === Object.prototype) return true
if (isMap(value) || isSet(value)) return true
return !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE]
if (!value) return false
return (
isPlainObject(value) || !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE]
)
}
export function original(value) {
if (value && value[DRAFT_STATE]) {
return value[DRAFT_STATE].base
}
// otherwise return undefined
export function isPlainObject(value) {
if (!value || typeof value !== "object") return false
if (Array.isArray(value)) return true
const proto = Object.getPrototypeOf(value)
return !proto || proto === Object.prototype
}
// We use Maps as `drafts` for Sets, not Objects
// See proxy.js
export function assignSet(target, override) {
override.forEach(value => {
// When we add new drafts we have to remove their originals if present
const prev = original(value)
if (prev) target.delete(prev)
target.add(value)
})
return target
export function original(value) {
if (value && value[DRAFT_STATE]) {
return value[DRAFT_STATE].base
}
// otherwise return undefined
}
// We use Maps as `drafts` for Maps, not Objects
// See proxy.js
export function assignMap(target, override) {
override.forEach((value, key) => target.set(key, value))
return target
}
export const assign =
Object.assign ||
((target, ...overrides) => {
overrides.forEach(override =>
Object.keys(override).forEach(key => (target[key] = override[key]))
)
return target
})
Object.assign ||
function assign(target, value) {
for (let key in value) {
if (has(value, key)) {
target[key] = value[key]
}
}
return target
}
export const ownKeys =
typeof Reflect !== "undefined" && Reflect.ownKeys
? Reflect.ownKeys
: typeof Object.getOwnPropertySymbols !== "undefined"
? obj =>
Object.getOwnPropertyNames(obj).concat(
Object.getOwnPropertySymbols(obj)
)
: Object.getOwnPropertyNames
typeof Reflect !== "undefined" && Reflect.ownKeys
? Reflect.ownKeys
: typeof Object.getOwnPropertySymbols !== "undefined"
? obj =>
Object.getOwnPropertyNames(obj).concat(
Object.getOwnPropertySymbols(obj)
)
: Object.getOwnPropertyNames
export function shallowCopy(base, invokeGetters = false) {
if (Array.isArray(base)) return base.slice()
if (isMap(base)) return new Map(base)
if (isSet(base)) return new Set(base)
const clone = Object.create(Object.getPrototypeOf(base))
ownKeys(base).forEach(key => {
if (key === DRAFT_STATE) {
return // Never copy over draft state.
}
const desc = Object.getOwnPropertyDescriptor(base, key)
let {value} = desc
if (desc.get) {
if (!invokeGetters) {
throw new Error("Immer drafts cannot have computed properties")
}
value = desc.get.call(base)
}
if (desc.enumerable) {
clone[key] = value
} else {
Object.defineProperty(clone, key, {
value,
writable: true,
configurable: true
})
}
})
return clone
if (Array.isArray(base)) return base.slice()
const clone = Object.create(Object.getPrototypeOf(base))
ownKeys(base).forEach(key => {
if (key === DRAFT_STATE) {
return // Never copy over draft state.
}
const desc = Object.getOwnPropertyDescriptor(base, key)
let {value} = desc
if (desc.get) {
if (!invokeGetters) {
throw new Error("Immer drafts cannot have computed properties")
}
value = desc.get.call(base)
}
if (desc.enumerable) {
clone[key] = value
} else {
Object.defineProperty(clone, key, {
value,
writable: true,
configurable: true
})
}
})
return clone
}
export function each(obj, iter) {
if (Array.isArray(obj) || isMap(obj) || isSet(obj)) {
obj.forEach((entry, index) => iter(index, entry, obj))
} else {
ownKeys(obj).forEach(key => iter(key, obj[key], obj))
}
export function each(value, cb) {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) cb(i, value[i], value)
} else {
ownKeys(value).forEach(key => cb(key, value[key], value))
}
}
export function isEnumerable(base, prop) {
const desc = Object.getOwnPropertyDescriptor(base, prop)
return !!desc && desc.enumerable
const desc = Object.getOwnPropertyDescriptor(base, prop)
return !!desc && desc.enumerable
}
export function has(thing, prop) {
return isMap(thing)
? thing.has(prop)
: Object.prototype.hasOwnProperty.call(thing, prop)
return Object.prototype.hasOwnProperty.call(thing, prop)
}
export function get(thing, prop) {
return isMap(thing) ? thing.get(prop) : thing[prop]
}
export function is(x, y) {
// From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js
if (x === y) {
return x !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
// From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js
if (x === y) {
return x !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
export function isMap(target) {
return target instanceof Map
export function clone(obj) {
if (!isDraftable(obj)) return obj
if (Array.isArray(obj)) return obj.map(clone)
const cloned = Object.create(Object.getPrototypeOf(obj))
for (const key in obj) cloned[key] = clone(obj[key])
return cloned
}
export function isSet(target) {
return target instanceof Set
export function deepFreeze(obj) {
if (!isDraftable(obj) || isDraft(obj) || Object.isFrozen(obj)) return
Object.freeze(obj)
if (Array.isArray(obj)) obj.forEach(deepFreeze)
else for (const key in obj) deepFreeze(obj[key])
}
"use strict"
import {
each,
has,
is,
isDraft,
isDraftable,
isEnumerable,
shallowCopy,
DRAFT_STATE
each,
has,
is,
isDraft,
isDraftable,
isEnumerable,
shallowCopy,
DRAFT_STATE
} from "./common"

@@ -19,52 +19,52 @@ import {ImmerScope} from "./scope"

export function willFinalize(scope, result, isReplaced) {
scope.drafts.forEach(draft => {
draft[DRAFT_STATE].finalizing = true
})
if (!isReplaced) {
if (scope.patches) {
markChangesRecursively(scope.drafts[0])
}
// This is faster when we don't care about which attributes changed.
markChangesSweep(scope.drafts)
}
// When a child draft is returned, look for changes.
else if (isDraft(result) && result[DRAFT_STATE].scope === scope) {
markChangesSweep(scope.drafts)
}
scope.drafts.forEach(draft => {
draft[DRAFT_STATE].finalizing = true
})
if (!isReplaced) {
if (scope.patches) {
markChangesRecursively(scope.drafts[0])
}
// This is faster when we don't care about which attributes changed.
markChangesSweep(scope.drafts)
}
// When a child draft is returned, look for changes.
else if (isDraft(result) && result[DRAFT_STATE].scope === scope) {
markChangesSweep(scope.drafts)
}
}
export function createProxy(base, parent) {
const isArray = Array.isArray(base)
const draft = clonePotentialDraft(base)
each(draft, prop => {
proxyProperty(draft, prop, isArray || isEnumerable(base, prop))
})
const isArray = Array.isArray(base)
const draft = clonePotentialDraft(base)
each(draft, prop => {
proxyProperty(draft, prop, isArray || isEnumerable(base, prop))
})
// See "proxy.js" for property documentation.
const scope = parent ? parent.scope : ImmerScope.current
const state = {
scope,
modified: false,
finalizing: false, // es5 only
finalized: false,
assigned: {},
parent,
base,
draft,
copy: null,
revoke,
revoked: false // es5 only
}
// See "proxy.js" for property documentation.
const scope = parent ? parent.scope : ImmerScope.current
const state = {
scope,
modified: false,
finalizing: false, // es5 only
finalized: false,
assigned: {},
parent,
base,
draft,
copy: null,
revoke,
revoked: false // es5 only
}
createHiddenProperty(draft, DRAFT_STATE, state)
scope.drafts.push(draft)
return draft
createHiddenProperty(draft, DRAFT_STATE, state)
scope.drafts.push(draft)
return draft
}
function revoke() {
this.revoked = true
this.revoked = true
}
function latest(state) {
return state.copy || state.base
function source(state) {
return state.copy || state.base
}

@@ -74,82 +74,82 @@

function peek(draft, prop) {
const state = draft[DRAFT_STATE]
if (state && !state.finalizing) {
state.finalizing = true
const value = draft[prop]
state.finalizing = false
return value
}
return draft[prop]
const state = draft[DRAFT_STATE]
if (state && !state.finalizing) {
state.finalizing = true
const value = draft[prop]
state.finalizing = false
return value
}
return draft[prop]
}
function get(state, prop) {
assertUnrevoked(state)
const value = peek(latest(state), prop)
if (state.finalizing) return value
// Create a draft if the value is unmodified.
if (value === peek(state.base, prop) && isDraftable(value)) {
prepareCopy(state)
return (state.copy[prop] = createProxy(value, state))
}
return value
assertUnrevoked(state)
const value = peek(source(state), prop)
if (state.finalizing) return value
// Create a draft if the value is unmodified.
if (value === peek(state.base, prop) && isDraftable(value)) {
prepareCopy(state)
return (state.copy[prop] = createProxy(value, state))
}
return value
}
function set(state, prop, value) {
assertUnrevoked(state)
state.assigned[prop] = true
if (!state.modified) {
if (is(value, peek(latest(state), prop))) return
markChanged(state)
prepareCopy(state)
}
state.copy[prop] = value
assertUnrevoked(state)
state.assigned[prop] = true
if (!state.modified) {
if (is(value, peek(source(state), prop))) return
markChanged(state)
prepareCopy(state)
}
state.copy[prop] = value
}
function markChanged(state) {
if (!state.modified) {
state.modified = true
if (state.parent) markChanged(state.parent)
}
if (!state.modified) {
state.modified = true
if (state.parent) markChanged(state.parent)
}
}
function prepareCopy(state) {
if (!state.copy) state.copy = clonePotentialDraft(state.base)
if (!state.copy) state.copy = clonePotentialDraft(state.base)
}
function clonePotentialDraft(base) {
const state = base && base[DRAFT_STATE]
if (state) {
state.finalizing = true
const draft = shallowCopy(state.draft, true)
state.finalizing = false
return draft
}
return shallowCopy(base)
const state = base && base[DRAFT_STATE]
if (state) {
state.finalizing = true
const draft = shallowCopy(state.draft, true)
state.finalizing = false
return draft
}
return shallowCopy(base)
}
function proxyProperty(draft, prop, enumerable) {
let desc = descriptors[prop]
if (desc) {
desc.enumerable = enumerable
} else {
descriptors[prop] = desc = {
configurable: true,
enumerable,
get() {
return get(this[DRAFT_STATE], prop)
},
set(value) {
set(this[DRAFT_STATE], prop, value)
}
}
}
Object.defineProperty(draft, prop, desc)
let desc = descriptors[prop]
if (desc) {
desc.enumerable = enumerable
} else {
descriptors[prop] = desc = {
configurable: true,
enumerable,
get() {
return get(this[DRAFT_STATE], prop)
},
set(value) {
set(this[DRAFT_STATE], prop, value)
}
}
}
Object.defineProperty(draft, prop, desc)
}
function assertUnrevoked(state) {
if (state.revoked === true)
throw new Error(
"Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " +
JSON.stringify(latest(state))
)
if (state.revoked === true)
throw new Error(
"Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " +
JSON.stringify(source(state))
)
}

@@ -159,108 +159,108 @@

function markChangesSweep(drafts) {
// The natural order of drafts in the `scope` array is based on when they
// were accessed. By processing drafts in reverse natural order, we have a
// better chance of processing leaf nodes first. When a leaf node is known to
// have changed, we can avoid any traversal of its ancestor nodes.
for (let i = drafts.length - 1; i >= 0; i--) {
const state = drafts[i][DRAFT_STATE]
if (!state.modified) {
if (Array.isArray(state.base)) {
if (hasArrayChanges(state)) markChanged(state)
} else if (hasObjectChanges(state)) markChanged(state)
}
}
// The natural order of drafts in the `scope` array is based on when they
// were accessed. By processing drafts in reverse natural order, we have a
// better chance of processing leaf nodes first. When a leaf node is known to
// have changed, we can avoid any traversal of its ancestor nodes.
for (let i = drafts.length - 1; i >= 0; i--) {
const state = drafts[i][DRAFT_STATE]
if (!state.modified) {
if (Array.isArray(state.base)) {
if (hasArrayChanges(state)) markChanged(state)
} else if (hasObjectChanges(state)) markChanged(state)
}
}
}
function markChangesRecursively(object) {
if (!object || typeof object !== "object") return
const state = object[DRAFT_STATE]
if (!state) return
const {base, draft, assigned} = state
if (!Array.isArray(object)) {
// Look for added keys.
Object.keys(draft).forEach(key => {
// The `undefined` check is a fast path for pre-existing keys.
if (base[key] === undefined && !has(base, key)) {
assigned[key] = true
markChanged(state)
} else if (!assigned[key]) {
// Only untouched properties trigger recursion.
markChangesRecursively(draft[key])
}
})
// Look for removed keys.
Object.keys(base).forEach(key => {
// The `undefined` check is a fast path for pre-existing keys.
if (draft[key] === undefined && !has(draft, key)) {
assigned[key] = false
markChanged(state)
}
})
} else if (hasArrayChanges(state)) {
markChanged(state)
assigned.length = true
if (draft.length < base.length) {
for (let i = draft.length; i < base.length; i++) assigned[i] = false
} else {
for (let i = base.length; i < draft.length; i++) assigned[i] = true
}
for (let i = 0; i < draft.length; i++) {
// Only untouched indices trigger recursion.
if (assigned[i] === undefined) markChangesRecursively(draft[i])
}
}
if (!object || typeof object !== "object") return
const state = object[DRAFT_STATE]
if (!state) return
const {base, draft, assigned} = state
if (!Array.isArray(object)) {
// Look for added keys.
Object.keys(draft).forEach(key => {
// The `undefined` check is a fast path for pre-existing keys.
if (base[key] === undefined && !has(base, key)) {
assigned[key] = true
markChanged(state)
} else if (!assigned[key]) {
// Only untouched properties trigger recursion.
markChangesRecursively(draft[key])
}
})
// Look for removed keys.
Object.keys(base).forEach(key => {
// The `undefined` check is a fast path for pre-existing keys.
if (draft[key] === undefined && !has(draft, key)) {
assigned[key] = false
markChanged(state)
}
})
} else if (hasArrayChanges(state)) {
markChanged(state)
assigned.length = true
if (draft.length < base.length) {
for (let i = draft.length; i < base.length; i++) assigned[i] = false
} else {
for (let i = base.length; i < draft.length; i++) assigned[i] = true
}
for (let i = 0; i < draft.length; i++) {
// Only untouched indices trigger recursion.
if (assigned[i] === undefined) markChangesRecursively(draft[i])
}
}
}
function hasObjectChanges(state) {
const {base, draft} = state
const {base, draft} = state
// Search for added keys and changed keys. Start at the back, because
// non-numeric keys are ordered by time of definition on the object.
const keys = Object.keys(draft)
for (let i = keys.length - 1; i >= 0; i--) {
const key = keys[i]
const baseValue = base[key]
// The `undefined` check is a fast path for pre-existing keys.
if (baseValue === undefined && !has(base, key)) {
return true
}
// Once a base key is deleted, future changes go undetected, because its
// descriptor is erased. This branch detects any missed changes.
else {
const value = draft[key]
const state = value && value[DRAFT_STATE]
if (state ? state.base !== baseValue : !is(value, baseValue)) {
return true
}
}
}
// Search for added keys and changed keys. Start at the back, because
// non-numeric keys are ordered by time of definition on the object.
const keys = Object.keys(draft)
for (let i = keys.length - 1; i >= 0; i--) {
const key = keys[i]
const baseValue = base[key]
// The `undefined` check is a fast path for pre-existing keys.
if (baseValue === undefined && !has(base, key)) {
return true
}
// Once a base key is deleted, future changes go undetected, because its
// descriptor is erased. This branch detects any missed changes.
else {
const value = draft[key]
const state = value && value[DRAFT_STATE]
if (state ? state.base !== baseValue : !is(value, baseValue)) {
return true
}
}
}
// At this point, no keys were added or changed.
// Compare key count to determine if keys were deleted.
return keys.length !== Object.keys(base).length
// At this point, no keys were added or changed.
// Compare key count to determine if keys were deleted.
return keys.length !== Object.keys(base).length
}
function hasArrayChanges(state) {
const {draft} = state
if (draft.length !== state.base.length) return true
// See #116
// If we first shorten the length, our array interceptors will be removed.
// If after that new items are added, result in the same original length,
// those last items will have no intercepting property.
// So if there is no own descriptor on the last position, we know that items were removed and added
// N.B.: splice, unshift, etc only shift values around, but not prop descriptors, so we only have to check
// the last one
const descriptor = Object.getOwnPropertyDescriptor(draft, draft.length - 1)
// descriptor can be null, but only for newly created sparse arrays, eg. new Array(10)
if (descriptor && !descriptor.get) return true
// For all other cases, we don't have to compare, as they would have been picked up by the index setters
return false
const {draft} = state
if (draft.length !== state.base.length) return true
// See #116
// If we first shorten the length, our array interceptors will be removed.
// If after that new items are added, result in the same original length,
// those last items will have no intercepting property.
// So if there is no own descriptor on the last position, we know that items were removed and added
// N.B.: splice, unshift, etc only shift values around, but not prop descriptors, so we only have to check
// the last one
const descriptor = Object.getOwnPropertyDescriptor(draft, draft.length - 1)
// descriptor can be null, but only for newly created sparse arrays, eg. new Array(10)
if (descriptor && !descriptor.get) return true
// For all other cases, we don't have to compare, as they would have been picked up by the index setters
return false
}
function createHiddenProperty(target, prop, value) {
Object.defineProperty(target, prop, {
value: value,
enumerable: false,
writable: true
})
Object.defineProperty(target, prop, {
value: value,
enumerable: false,
writable: true
})
}
type Tail<T extends any[]> = ((...t: T) => any) extends ((
_: any,
...tail: infer TT
_: any,
...tail: infer TT
) => any)
? TT
: []
? TT
: []
/** Object types that should never be mapped */
type AtomicObject =
| Function
| Map<any, any>
| WeakMap<any, any>
| Set<any>
| WeakSet<any>
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String
| Function
| Map<any, any>
| WeakMap<any, any>
| Set<any>
| WeakSet<any>
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String
export type Draft<T> = T extends AtomicObject
? T
: T extends object
? {-readonly [K in keyof T]: Draft<T[K]>}
: T
? T
: T extends object
? {-readonly [K in keyof T]: Draft<T[K]>}
: T
/** Convert a mutable type into a readonly type */
export type Immutable<T> = T extends AtomicObject
? T
: T extends object
? {readonly [K in keyof T]: Immutable<T[K]>}
: T
? T
: T extends object
? {readonly [K in keyof T]: Immutable<T[K]>}
: T
export interface Patch {
op: "replace" | "remove" | "add"
path: (string | number)[]
value?: any
op: "replace" | "remove" | "add"
path: (string | number)[]
value?: any
}

@@ -48,6 +48,6 @@

export type Produced<Base, Return> = Return extends void
? Base
: Return extends Promise<infer Result>
? Promise<Result extends void ? Base : FromNothing<Result>>
: FromNothing<Return>
? Base
: Return extends Promise<infer Result>
? Promise<Result extends void ? Base : FromNothing<Result>>
: FromNothing<Return>

@@ -74,36 +74,36 @@ /**

export interface IProduce {
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => Produced<Base, ReturnType<Recipe>>
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return,
listener?: PatchListener
): Produced<Base, Return>
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return,
listener?: PatchListener
): Produced<Base, Return>
}

@@ -114,6 +114,49 @@

/**
* Like `produce`, but instead of just returning the new state,
* a tuple is returned with [nextState, patches, inversePatches]
*
* Like produce, this function supports currying
*/
export interface IProduceWithPatches {
/** Curried producer */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe
): <Base extends Immutable<T>>(
base: Base,
...rest: Tail<Params>
) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
// ^ by making the returned type generic, the actual type of the passed in object is preferred
// over the type used in the recipe. However, it does have to satisfy the immutable version used in the recipe
// Note: the type of S is the widened version of T, so it can have more props than T, but that is technically actually correct!
/** Curried producer with initial state */
<
Recipe extends (...args: any[]) => any,
Params extends any[] = Parameters<Recipe>,
T = Params[0]
>(
recipe: Recipe,
initialState: Immutable<T>
): <Base extends Immutable<T>>(
base?: Base,
...rest: Tail<Params>
) => [Produced<Base, ReturnType<Recipe>>, Patch[], Patch[]]
/** Normal producer */
<Base, D = Draft<Base>, Return = void>(
base: Base,
recipe: (draft: D) => Return
): [Produced<Base, Return>, Patch[], Patch[]]
}
export const produceWithPatches: IProduceWithPatches
/** Use a class type for `nothing` so its type is unique */
declare class Nothing {
// This lets us do `Exclude<T, Nothing>`
private _: any
// This lets us do `Exclude<T, Nothing>`
private _: any
}

@@ -184,61 +227,61 @@

export class Immer {
constructor(config: {
useProxies?: boolean
autoFreeze?: boolean
onAssign?: (
state: ImmerState,
prop: string | number,
value: unknown
) => void
onDelete?: (state: ImmerState, prop: string | number) => void
onCopy?: (state: ImmerState) => void
})
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
produce: IProduce
/**
* When true, `produce` will freeze the copies it creates.
*/
readonly autoFreeze: boolean
/**
* When true, drafts are ES2015 proxies.
*/
readonly useProxies: boolean
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is disabled in production.
*/
setAutoFreeze(autoFreeze: boolean): void
/**
* Pass true to use the ES2015 `Proxy` class when creating drafts, which is
* always faster than using ES5 proxies.
*
* By default, feature detection is used, so calling this is rarely necessary.
*/
setUseProxies(useProxies: boolean): void
constructor(config: {
useProxies?: boolean
autoFreeze?: boolean
onAssign?: (
state: ImmerState,
prop: string | number,
value: unknown
) => void
onDelete?: (state: ImmerState, prop: string | number) => void
onCopy?: (state: ImmerState) => void
})
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
produce: IProduce
/**
* When true, `produce` will freeze the copies it creates.
*/
readonly autoFreeze: boolean
/**
* When true, drafts are ES2015 proxies.
*/
readonly useProxies: boolean
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is disabled in production.
*/
setAutoFreeze(autoFreeze: boolean): void
/**
* Pass true to use the ES2015 `Proxy` class when creating drafts, which is
* always faster than using ES5 proxies.
*
* By default, feature detection is used, so calling this is rarely necessary.
*/
setUseProxies(useProxies: boolean): void
}
export interface ImmerState<T = any> {
parent?: ImmerState
base: T
copy: T
assigned: {[prop: string]: boolean; [index: number]: boolean}
parent?: ImmerState
base: T
copy: T
assigned: {[prop: string]: boolean; [index: number]: boolean}
}

@@ -248,6 +291,6 @@

declare global {
interface Set<T> {}
interface Map<K, V> {}
interface WeakSet<T> {}
interface WeakMap<K extends object, V> {}
interface Set<T> {}
interface Map<K, V> {}
interface WeakSet<T> {}
interface WeakMap<K extends object, V> {}
}

@@ -5,16 +5,13 @@ import * as legacyProxy from "./es5"

import {
assign,
each,
get,
has,
is,
isDraft,
isDraftable,
isEnumerable,
isMap,
shallowCopy,
DRAFT_STATE,
NOTHING,
isSet,
original
assign,
each,
has,
is,
isDraft,
isDraftable,
isEnumerable,
shallowCopy,
DRAFT_STATE,
NOTHING,
deepFreeze
} from "./common"

@@ -26,289 +23,304 @@ import {ImmerScope} from "./scope"

const configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
autoFreeze:
typeof process !== "undefined"
? process.env.NODE_ENV !== "production"
: verifyMinified.name === "verifyMinified",
onAssign: null,
onDelete: null,
onCopy: null
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
autoFreeze:
typeof process !== "undefined"
? process.env.NODE_ENV !== "production"
: verifyMinified.name === "verifyMinified",
onAssign: null,
onDelete: null,
onCopy: null
}
export class Immer {
constructor(config) {
assign(this, configDefaults, config)
this.setUseProxies(this.useProxies)
this.produce = this.produce.bind(this)
}
produce(base, recipe, patchListener) {
// curried invocation
if (typeof base === "function" && typeof recipe !== "function") {
const defaultBase = recipe
recipe = base
constructor(config) {
assign(this, configDefaults, config)
this.setUseProxies(this.useProxies)
this.produce = this.produce.bind(this)
}
produce(base, recipe, patchListener) {
// curried invocation
if (typeof base === "function" && typeof recipe !== "function") {
const defaultBase = recipe
recipe = base
const self = this
return function curriedProduce(base = defaultBase, ...args) {
return self.produce(base, draft => recipe.call(this, draft, ...args)) // prettier-ignore
}
}
const self = this
return function curriedProduce(base = defaultBase, ...args) {
return self.produce(base, draft => recipe.call(this, draft, ...args)) // prettier-ignore
}
}
// prettier-ignore
{
if (typeof recipe !== "function") {
throw new Error("The first or second argument to `produce` must be a function")
}
if (patchListener !== undefined && typeof patchListener !== "function") {
throw new Error("The third argument to `produce` must be a function or undefined")
}
}
// prettier-ignore
{
if (typeof recipe !== "function") {
throw new Error("The first or second argument to `produce` must be a function")
}
if (patchListener !== undefined && typeof patchListener !== "function") {
throw new Error("The third argument to `produce` must be a function or undefined")
}
}
let result
let result
// Only plain objects, arrays, and "immerable classes" are drafted.
if (isDraftable(base)) {
const scope = ImmerScope.enter()
const proxy = this.createProxy(base)
let hasError = true
try {
result = recipe(proxy)
hasError = false
} finally {
// finally instead of catch + rethrow better preserves original stack
if (hasError) scope.revoke()
else scope.leave()
}
if (result instanceof Promise) {
return result.then(
result => {
scope.usePatches(patchListener)
return this.processResult(result, scope)
},
error => {
scope.revoke()
throw error
}
)
}
scope.usePatches(patchListener)
return this.processResult(result, scope)
} else {
result = recipe(base)
if (result === undefined) return base
return result !== NOTHING ? result : undefined
}
}
createDraft(base) {
if (!isDraftable(base)) {
throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object") // prettier-ignore
}
const scope = ImmerScope.enter()
const proxy = this.createProxy(base)
proxy[DRAFT_STATE].isManual = true
scope.leave()
return proxy
}
finishDraft(draft, patchListener) {
const state = draft && draft[DRAFT_STATE]
if (!state || !state.isManual) {
throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`") // prettier-ignore
}
if (state.finalized) {
throw new Error("The given draft is already finalized") // prettier-ignore
}
const {scope} = state
scope.usePatches(patchListener)
return this.processResult(undefined, scope)
}
setAutoFreeze(value) {
this.autoFreeze = value
}
setUseProxies(value) {
this.useProxies = value
assign(this, value ? modernProxy : legacyProxy)
}
applyPatches(base, patches) {
// Mutate the base state when a draft is passed.
if (isDraft(base)) {
return applyPatches(base, patches)
}
// Otherwise, produce a copy of the base state.
return this.produce(base, draft => applyPatches(draft, patches))
}
/** @internal */
processResult(result, scope) {
const baseDraft = scope.drafts[0]
const isReplaced = result !== undefined && result !== baseDraft
this.willFinalize(scope, result, isReplaced)
if (isReplaced) {
if (baseDraft[DRAFT_STATE].modified) {
scope.revoke()
throw new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.") // prettier-ignore
}
if (isDraftable(result)) {
// Finalize the result in case it contains (or is) a subset of the draft.
result = this.finalize(result, null, scope)
}
if (scope.patches) {
scope.patches.push({
op: "replace",
path: [],
value: result
})
scope.inversePatches.push({
op: "replace",
path: [],
value: baseDraft[DRAFT_STATE].base
})
}
} else {
// Finalize the base draft.
result = this.finalize(baseDraft, [], scope)
}
scope.revoke()
if (scope.patches) {
scope.patchListener(scope.patches, scope.inversePatches)
}
return result !== NOTHING ? result : undefined
}
/**
* @internal
* Finalize a draft, returning either the unmodified base state or a modified
* copy of the base state.
*/
finalize(draft, path, scope) {
const state = draft[DRAFT_STATE]
if (!state) {
if (Object.isFrozen(draft)) return draft
return this.finalizeTree(draft, null, scope)
}
// Never finalize drafts owned by another scope.
if (state.scope !== scope) {
return draft
}
if (!state.modified) {
return state.base
}
if (!state.finalized) {
state.finalized = true
this.finalizeTree(state.draft, path, scope)
// Only plain objects, arrays, and "immerable classes" are drafted.
if (isDraftable(base)) {
const scope = ImmerScope.enter()
const proxy = this.createProxy(base)
let hasError = true
try {
result = recipe(proxy)
hasError = false
} finally {
// finally instead of catch + rethrow better preserves original stack
if (hasError) scope.revoke()
else scope.leave()
}
if (result instanceof Promise) {
return result.then(
result => {
scope.usePatches(patchListener)
return this.processResult(result, scope)
},
error => {
scope.revoke()
throw error
}
)
}
scope.usePatches(patchListener)
return this.processResult(result, scope)
} else {
result = recipe(base)
if (result === NOTHING) return undefined
if (result === undefined) result = base
this.maybeFreeze(result, true)
return result
}
}
produceWithPatches(arg1, arg2, arg3) {
if (typeof arg1 === "function") {
const self = this
return (state, ...args) =>
this.produceWithPatches(state, draft => arg1(draft, ...args))
}
// non-curried form
if (arg3)
throw new Error("A patch listener cannot be passed to produceWithPatches")
let patches, inversePatches
const nextState = this.produce(arg1, arg2, (p, ip) => {
patches = p
inversePatches = ip
})
return [nextState, patches, inversePatches]
}
createDraft(base) {
if (!isDraftable(base)) {
throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object") // prettier-ignore
}
const scope = ImmerScope.enter()
const proxy = this.createProxy(base)
proxy[DRAFT_STATE].isManual = true
scope.leave()
return proxy
}
finishDraft(draft, patchListener) {
const state = draft && draft[DRAFT_STATE]
if (!state || !state.isManual) {
throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`") // prettier-ignore
}
if (state.finalized) {
throw new Error("The given draft is already finalized") // prettier-ignore
}
const {scope} = state
scope.usePatches(patchListener)
return this.processResult(undefined, scope)
}
setAutoFreeze(value) {
this.autoFreeze = value
}
setUseProxies(value) {
this.useProxies = value
assign(this, value ? modernProxy : legacyProxy)
}
applyPatches(base, patches) {
// If a patch replaces the entire state, take that replacement as base
// before applying patches
let i
for (i = patches.length - 1; i >= 0; i--) {
const patch = patches[i]
if (patch.path.length === 0 && patch.op === "replace") {
base = patch.value
break
}
}
// We cannot really delete anything inside of a Set. We can only replace the whole Set.
if (this.onDelete && !isSet(state.base)) {
// The `assigned` object is unreliable with ES5 drafts.
if (this.useProxies) {
const {assigned} = state
each(assigned, (prop, exists) => {
if (!exists) this.onDelete(state, prop)
})
} else {
// TODO: Figure it out for Maps and Sets if we need to support ES5
const {base, copy} = state
each(base, prop => {
if (!has(copy, prop)) this.onDelete(state, prop)
})
}
}
if (this.onCopy) {
this.onCopy(state)
}
if (isDraft(base)) {
// N.B: never hits if some patch a replacement, patches are never drafts
return applyPatches(base, patches)
}
// Otherwise, produce a copy of the base state.
return this.produce(base, draft =>
applyPatches(draft, patches.slice(i + 1))
)
}
/** @internal */
processResult(result, scope) {
const baseDraft = scope.drafts[0]
const isReplaced = result !== undefined && result !== baseDraft
this.willFinalize(scope, result, isReplaced)
if (isReplaced) {
if (baseDraft[DRAFT_STATE].modified) {
scope.revoke()
throw new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.") // prettier-ignore
}
if (isDraftable(result)) {
// Finalize the result in case it contains (or is) a subset of the draft.
result = this.finalize(result, null, scope)
this.maybeFreeze(result)
}
if (scope.patches) {
scope.patches.push({
op: "replace",
path: [],
value: result
})
scope.inversePatches.push({
op: "replace",
path: [],
value: baseDraft[DRAFT_STATE].base
})
}
} else {
// Finalize the base draft.
result = this.finalize(baseDraft, [], scope)
}
scope.revoke()
if (scope.patches) {
scope.patchListener(scope.patches, scope.inversePatches)
}
return result !== NOTHING ? result : undefined
}
/**
* @internal
* Finalize a draft, returning either the unmodified base state or a modified
* copy of the base state.
*/
finalize(draft, path, scope) {
const state = draft[DRAFT_STATE]
if (!state) {
if (Object.isFrozen(draft)) return draft
return this.finalizeTree(draft, null, scope)
}
// Never finalize drafts owned by another scope.
if (state.scope !== scope) {
return draft
}
if (!state.modified) {
this.maybeFreeze(state.base, true)
return state.base
}
if (!state.finalized) {
state.finalized = true
this.finalizeTree(state.draft, path, scope)
// At this point, all descendants of `state.copy` have been finalized,
// so we can be sure that `scope.canAutoFreeze` is accurate.
if (this.autoFreeze && scope.canAutoFreeze) {
Object.freeze(state.copy)
}
if (this.onDelete) {
// The `assigned` object is unreliable with ES5 drafts.
if (this.useProxies) {
const {assigned} = state
for (const prop in assigned) {
if (!assigned[prop]) this.onDelete(state, prop)
}
} else {
const {base, copy} = state
each(base, prop => {
if (!has(copy, prop)) this.onDelete(state, prop)
})
}
}
if (this.onCopy) {
this.onCopy(state)
}
if (path && scope.patches) {
generatePatches(
state,
path,
scope.patches,
scope.inversePatches
)
}
}
return state.copy
}
/**
* @internal
* Finalize all drafts in the given state tree.
*/
finalizeTree(root, rootPath, scope) {
const state = root[DRAFT_STATE]
if (state) {
if (!this.useProxies) {
// Create the final copy, with added keys and without deleted keys.
state.copy = shallowCopy(state.draft, true)
}
root = state.copy
}
// At this point, all descendants of `state.copy` have been finalized,
// so we can be sure that `scope.canAutoFreeze` is accurate.
if (this.autoFreeze && scope.canAutoFreeze) {
Object.freeze(state.copy)
}
const needPatches = !!rootPath && !!scope.patches
const finalizeProperty = (prop, value, parent) => {
if (value === parent) {
throw Error("Immer forbids circular references")
}
if (path && scope.patches) {
generatePatches(state, path, scope.patches, scope.inversePatches)
}
}
return state.copy
}
/**
* @internal
* Finalize all drafts in the given state tree.
*/
finalizeTree(root, rootPath, scope) {
const state = root[DRAFT_STATE]
if (state) {
if (!this.useProxies) {
// Create the final copy, with added keys and without deleted keys.
state.copy = shallowCopy(state.draft, true)
}
root = state.copy
}
// In the `finalizeTree` method, only the `root` object may be a draft.
const isDraftProp = !!state && parent === root
const isSetMember = isSet(parent)
const needPatches = !!rootPath && !!scope.patches
const finalizeProperty = (prop, value, parent) => {
if (value === parent) {
throw Error("Immer forbids circular references")
}
if (isDraft(value)) {
const path =
isDraftProp &&
needPatches &&
!isSetMember && // Set objects are atomic since they have no keys.
!has(state.assigned, prop) // Skip deep patches for assigned keys.
? rootPath.concat(prop)
: null
// In the `finalizeTree` method, only the `root` object may be a draft.
const isDraftProp = !!state && parent === root
// Drafts owned by `scope` are finalized here.
value = this.finalize(value, path, scope)
replace(parent, prop, value)
if (isDraft(value)) {
const path =
isDraftProp && needPatches && !state.assigned[prop]
? rootPath.concat(prop)
: null
// Drafts from another scope must prevent auto-freezing.
if (isDraft(value)) {
scope.canAutoFreeze = false
}
// Drafts owned by `scope` are finalized here.
value = this.finalize(value, path, scope)
// Unchanged drafts are never passed to the `onAssign` hook.
if (isDraftProp && value === get(state.base, prop)) return
}
// Unchanged draft properties are ignored.
else if (isDraftProp && is(value, get(state.base, prop))) {
return
}
// Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
else if (isDraftable(value) && !Object.isFrozen(value)) {
each(value, finalizeProperty)
}
// Drafts from another scope must prevent auto-freezing.
if (isDraft(value)) {
scope.canAutoFreeze = false
}
// We cannot really assign anything inside of a Set. We can only replace the whole Set.
if (isDraftProp && this.onAssign && !isSetMember) {
this.onAssign(state, prop, value)
}
}
// Preserve non-enumerable properties.
if (Array.isArray(parent) || isEnumerable(parent, prop)) {
parent[prop] = value
} else {
Object.defineProperty(parent, prop, {value})
}
each(root, finalizeProperty)
return root
}
}
// Unchanged drafts are never passed to the `onAssign` hook.
if (isDraftProp && value === state.base[prop]) return
}
// Unchanged draft properties are ignored.
else if (isDraftProp && is(value, state.base[prop])) {
return
}
// Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
else if (isDraftable(value) && !Object.isFrozen(value)) {
each(value, finalizeProperty)
this.maybeFreeze(value)
}
function replace(parent, prop, value) {
if (isMap(parent)) {
parent.set(prop, value)
} else if (isSet(parent)) {
// In this case, the `prop` is actually a draft.
parent.delete(prop)
parent.add(value)
} else if (Array.isArray(parent) || isEnumerable(parent, prop)) {
// Preserve non-enumerable properties.
parent[prop] = value
} else {
Object.defineProperty(parent, prop, {
value,
writable: true,
configurable: true
})
}
if (isDraftProp && this.onAssign) {
this.onAssign(state, prop, value)
}
}
each(root, finalizeProperty)
return root
}
maybeFreeze(value, deep = false) {
if (this.autoFreeze && !isDraft(value)) {
if (deep) deepFreeze(value)
else Object.freeze(value)
}
}
}

@@ -28,2 +28,8 @@ import {Immer} from "./immer"

/**
* Like `produce`, but `produceWithPatches` always returns a tuple
* [nextState, patches, inversePatches] (instead of just the next state)
*/
export const produceWithPatches = immer.produceWithPatches.bind(immer)
/**
* Pass true to automatically freeze all copies created by Immer.

@@ -67,9 +73,9 @@ *

export {
original,
isDraft,
isDraftable,
NOTHING as nothing,
DRAFTABLE as immerable
original,
isDraft,
isDraftable,
NOTHING as nothing,
DRAFTABLE as immerable
} from "./common"
export {Immer}

@@ -1,203 +0,129 @@

import {get, each, isMap, isSet, has} from "./common"
import {each, clone} from "./common"
import {createDraft} from "./immer"
export function generatePatches(state, basePath, patches, inversePatches) {
const generatePatchesFn = Array.isArray(state.base)
? generateArrayPatches
: isSet(state.base)
? generateSetPatches
: generatePatchesFromAssigned
generatePatchesFn(state, basePath, patches, inversePatches)
Array.isArray(state.base)
? generateArrayPatches(state, basePath, patches, inversePatches)
: generateObjectPatches(state, basePath, patches, inversePatches)
}
function generateArrayPatches(state, basePath, patches, inversePatches) {
let {base, copy, assigned} = state
let {base, copy, assigned} = state
// Reduce complexity by ensuring `base` is never longer.
if (copy.length < base.length) {
;[base, copy] = [copy, base]
;[patches, inversePatches] = [inversePatches, patches]
}
// Reduce complexity by ensuring `base` is never longer.
if (copy.length < base.length) {
;[base, copy] = [copy, base]
;[patches, inversePatches] = [inversePatches, patches]
}
const delta = copy.length - base.length
const delta = copy.length - base.length
// Find the first replaced index.
let start = 0
while (base[start] === copy[start] && start < base.length) {
++start
}
// Find the first replaced index.
let start = 0
while (base[start] === copy[start] && start < base.length) {
++start
}
// Find the last replaced index. Search from the end to optimize splice patches.
let end = base.length
while (end > start && base[end - 1] === copy[end + delta - 1]) {
--end
}
// Find the last replaced index. Search from the end to optimize splice patches.
let end = base.length
while (end > start && base[end - 1] === copy[end + delta - 1]) {
--end
}
// Process replaced indices.
for (let i = start; i < end; ++i) {
if (assigned[i] && copy[i] !== base[i]) {
const path = basePath.concat([i])
patches.push({
op: "replace",
path,
value: copy[i]
})
inversePatches.push({
op: "replace",
path,
value: base[i]
})
}
}
// Process replaced indices.
for (let i = start; i < end; ++i) {
if (assigned[i] && copy[i] !== base[i]) {
const path = basePath.concat([i])
patches.push({
op: "replace",
path,
value: copy[i]
})
inversePatches.push({
op: "replace",
path,
value: base[i]
})
}
}
const useRemove = end != base.length
const replaceCount = patches.length
const replaceCount = patches.length
// Process added indices.
for (let i = end + delta - 1; i >= end; --i) {
const path = basePath.concat([i])
patches[replaceCount + i - end] = {
op: "add",
path,
value: copy[i]
}
if (useRemove) {
inversePatches.push({
op: "remove",
path
})
}
}
// One "replace" patch reverses all non-splicing "add" patches.
if (!useRemove) {
inversePatches.push({
op: "replace",
path: basePath.concat(["length"]),
value: base.length
})
}
// Process added indices.
for (let i = end + delta - 1; i >= end; --i) {
const path = basePath.concat([i])
patches[replaceCount + i - end] = {
op: "add",
path,
value: copy[i]
}
inversePatches.push({
op: "remove",
path
})
}
}
// This is used for both Map objects and normal objects.
function generatePatchesFromAssigned(state, basePath, patches, inversePatches) {
const {base, copy} = state
each(state.assigned, (key, assignedValue) => {
const origValue = get(base, key)
const value = get(copy, key)
const op = !assignedValue
? "remove"
: has(base, key)
? "replace"
: "add"
if (origValue === value && op === "replace") return
const path = basePath.concat(key)
patches.push(op === "remove" ? {op, path} : {op, path, value})
inversePatches.push(
op === "add"
? {op: "remove", path}
: op === "remove"
? {op: "add", path, value: origValue}
: {op: "replace", path, value: origValue}
)
})
function generateObjectPatches(state, basePath, patches, inversePatches) {
const {base, copy} = state
each(state.assigned, (key, assignedValue) => {
const origValue = base[key]
const value = copy[key]
const op = !assignedValue ? "remove" : key in base ? "replace" : "add"
if (origValue === value && op === "replace") return
const path = basePath.concat(key)
patches.push(op === "remove" ? {op, path} : {op, path, value})
inversePatches.push(
op === "add"
? {op: "remove", path}
: op === "remove"
? {op: "add", path, value: origValue}
: {op: "replace", path, value: origValue}
)
})
}
function generateSetPatches(state, basePath, patches, inversePatches) {
let {base, copy} = state
export const applyPatches = (draft, patches) => {
for (const patch of patches) {
const {path, op} = patch
const value = clone(patch.value) // used to clone patch to ensure original patch is not modified, see #411
let i = 0
for (const value of base) {
if (!copy.has(value)) {
const path = basePath.concat([i])
patches.push({
op: "remove",
path,
value
})
inversePatches.unshift({
op: "add",
path,
value
})
}
i++
}
i = 0
for (const value of copy) {
if (!base.has(value)) {
const path = basePath.concat([i])
patches.push({
op: "add",
path,
value
})
inversePatches.unshift({
op: "remove",
path,
value
})
}
i++
}
}
if (!path.length) throw new Error("Illegal state")
export function applyPatches(draft, patches) {
for (let i = 0; i < patches.length; i++) {
const patch = patches[i]
const {path} = patch
if (path.length === 0 && patch.op === "replace") {
draft = patch.value
} else {
let base = draft
for (let i = 0; i < path.length - 1; i++) {
base = get(base, path[i])
if (!base || typeof base !== "object")
throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")) // prettier-ignore
}
const key = path[path.length - 1]
let base = draft
for (let i = 0; i < path.length - 1; i++) {
base = base[path[i]]
if (!base || typeof base !== "object")
throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")) // prettier-ignore
}
const replace = (key, value) => {
if (isMap(base)) {
base.set(key, value)
return
}
if (isSet(base)) {
throw new Error('Sets cannot have "replace" patches.')
}
base[key] = value
}
const add = (key, value) =>
Array.isArray(base)
? base.splice(key, 0, value)
: isMap(base)
? base.set(key, value)
: isSet(base)
? base.add(value)
: (base[key] = value)
const remove = (key, value) =>
Array.isArray(base)
? base.splice(key, 1)
: isMap(base)
? base.delete(key)
: isSet(base)
? base.delete(value)
: delete base[key]
const key = path[path.length - 1]
switch (op) {
case "replace":
// if value is an object, then it's assigned by reference
// in the following add or remove ops, the value field inside the patch will also be modifyed
// so we use value from the cloned patch
base[key] = value
break
case "add":
if (Array.isArray(base)) {
// TODO: support "foo/-" paths for appending to an array
base.splice(key, 0, value)
} else {
base[key] = value
}
break
case "remove":
if (Array.isArray(base)) {
base.splice(key, 1)
} else {
delete base[key]
}
break
default:
throw new Error("Unsupported patch operation: " + op)
}
}
switch (patch.op) {
case "replace":
replace(key, patch.value)
break
case "add":
add(key, patch.value)
break
case "remove":
remove(key, patch.value)
break
default:
throw new Error("Unsupported patch operation: " + patch.op)
}
}
}
return draft
return draft
}
"use strict"
import {
assign,
each,
has,
is,
isDraftable,
isDraft,
isMap,
isSet,
shallowCopy,
DRAFT_STATE,
assignMap,
assignSet,
original
assign,
each,
has,
is,
isDraftable,
isDraft,
shallowCopy,
DRAFT_STATE
} from "./common"

@@ -22,395 +17,171 @@ import {ImmerScope} from "./scope"

/**
* Returns a new draft of the `base` object.
*
* The second argument is the parent draft-state (used internally).
*/
export function createProxy(base, parent) {
const scope = parent ? parent.scope : ImmerScope.current
const state = {
// Track which produce call this is associated with.
scope,
// True for both shallow and deep changes.
modified: false,
// Used during finalization.
finalized: false,
// Track which properties have been assigned (true) or deleted (false).
assigned: {},
// The parent draft state.
parent,
// The base state.
base,
// The base proxy.
draft: null,
// Any property proxies.
drafts: {},
// The base copy with any updated values.
copy: null,
// Called by the `produce` function.
revoke: null
}
const scope = parent ? parent.scope : ImmerScope.current
const state = {
// Track which produce call this is associated with.
scope,
// True for both shallow and deep changes.
modified: false,
// Used during finalization.
finalized: false,
// Track which properties have been assigned (true) or deleted (false).
assigned: {},
// The parent draft state.
parent,
// The base state.
base,
// The base proxy.
draft: null,
// Any property proxies.
drafts: {},
// The base copy with any updated values.
copy: null,
// Called by the `produce` function.
revoke: null
}
let target = state
let traps = objectTraps
if (Array.isArray(base)) {
target = [state]
traps = arrayTraps
}
// Map drafts must support object keys, so we use Map objects to track changes.
else if (isMap(base)) {
traps = mapTraps
state.drafts = new Map()
state.assigned = new Map()
}
// Set drafts use a Map object to track which of its values are drafted.
// And we don't need the "assigned" property, because Set objects have no keys.
else if (isSet(base)) {
traps = setTraps
state.drafts = new Map()
}
const {revoke, proxy} = Array.isArray(base)
? // [state] is used for arrays, to make sure the proxy is array-ish and not violate invariants,
// although state itself is an object
Proxy.revocable([state], arrayTraps)
: Proxy.revocable(state, objectTraps)
const {revoke, proxy} = Proxy.revocable(target, traps)
state.draft = proxy
state.revoke = revoke
state.draft = proxy
state.revoke = revoke
scope.drafts.push(proxy)
return proxy
scope.drafts.push(proxy)
return proxy
}
/**
* Object drafts
*/
const objectTraps = {
get(state, prop) {
if (prop === DRAFT_STATE) return state
let {drafts} = state
// Check for existing draft in unmodified state.
if (!state.modified && has(drafts, prop)) {
return drafts[prop]
}
const value = latest(state)[prop]
if (state.finalized || !isDraftable(value)) {
return value
}
// Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek(state.base, prop)) return value
// Store drafts on the copy (when one exists).
drafts = state.copy
}
return (drafts[prop] = createProxy(value, state))
},
has(state, prop) {
return prop in latest(state)
},
ownKeys(state) {
return Reflect.ownKeys(latest(state))
},
set(state, prop, value) {
if (!state.modified) {
const baseValue = peek(state.base, prop)
// Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
const isUnchanged = value
? is(baseValue, value) || value === state.drafts[prop]
: is(baseValue, value) && prop in state.base
if (isUnchanged) return true
markChanged(state)
}
state.assigned[prop] = true
state.copy[prop] = value
return true
},
deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false
markChanged(state)
}
if (state.copy) delete state.copy[prop]
return true
},
// Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
getOwnPropertyDescriptor(state, prop) {
const owner = latest(state)
const desc = Reflect.getOwnPropertyDescriptor(owner, prop)
if (desc) {
desc.writable = true
desc.configurable = !Array.isArray(owner) || prop !== "length"
}
return desc
},
defineProperty() {
throw new Error("Object.defineProperty() cannot be used on an Immer draft") // prettier-ignore
},
getPrototypeOf(state) {
return Object.getPrototypeOf(state.base)
},
setPrototypeOf() {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft") // prettier-ignore
}
get,
has(target, prop) {
return prop in source(target)
},
ownKeys(target) {
return Reflect.ownKeys(source(target))
},
set,
deleteProperty,
getOwnPropertyDescriptor,
defineProperty() {
throw new Error("Object.defineProperty() cannot be used on an Immer draft") // prettier-ignore
},
getPrototypeOf(target) {
return Object.getPrototypeOf(target.base)
},
setPrototypeOf() {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft") // prettier-ignore
}
}
/**
* Array drafts
*/
const arrayTraps = {}
each(objectTraps, (key, fn) => {
arrayTraps[key] = function() {
arguments[0] = arguments[0][0]
return fn.apply(this, arguments)
}
arrayTraps[key] = function() {
arguments[0] = arguments[0][0]
return fn.apply(this, arguments)
}
})
arrayTraps.deleteProperty = function(state, prop) {
if (isNaN(parseInt(prop))) {
throw new Error("Immer only supports deleting array indices") // prettier-ignore
}
return objectTraps.deleteProperty.call(this, state[0], prop)
if (isNaN(parseInt(prop))) {
throw new Error("Immer only supports deleting array indices") // prettier-ignore
}
return objectTraps.deleteProperty.call(this, state[0], prop)
}
arrayTraps.set = function(state, prop, value) {
if (prop !== "length" && isNaN(parseInt(prop))) {
throw new Error("Immer only supports setting array indices and the 'length' property") // prettier-ignore
}
return objectTraps.set.call(this, state[0], prop, value)
if (prop !== "length" && isNaN(parseInt(prop))) {
throw new Error("Immer only supports setting array indices and the 'length' property") // prettier-ignore
}
return objectTraps.set.call(this, state[0], prop, value)
}
// Used by Map and Set drafts
const reflectTraps = makeReflectTraps([
"ownKeys",
"has",
"set",
"deleteProperty",
"defineProperty",
"getOwnPropertyDescriptor",
"preventExtensions",
"isExtensible",
"getPrototypeOf"
])
// returns the object we should be reading the current value from, which is base, until some change has been made
function source(state) {
return state.copy || state.base
}
/**
* Map drafts
*/
// Access a property without creating an Immer draft.
function peek(draft, prop) {
const state = draft[DRAFT_STATE]
const desc = Reflect.getOwnPropertyDescriptor(
state ? source(state) : draft,
prop
)
return desc && desc.value
}
const mapTraps = makeTrapsForGetters({
[DRAFT_STATE]: state => state,
size: state => latest(state).size,
has: state => key => latest(state).has(key),
set: state => (key, value) => {
const values = latest(state)
if (!values.has(key) || values.get(key) !== value) {
markChanged(state)
state.assigned.set(key, true)
state.copy.set(key, value)
}
return state.draft
},
delete: state => key => {
if (latest(state).has(key)) {
markChanged(state)
state.assigned.set(key, false)
return state.copy.delete(key)
}
return false
},
clear: state => () => {
markChanged(state)
state.assigned = new Map()
for (const key of latest(state).keys()) {
state.assigned.set(key, false)
}
return state.copy.clear()
},
forEach: (state, _, receiver) => (cb, thisArg) =>
latest(state).forEach((_, key, map) => {
const value = receiver.get(key)
cb.call(thisArg, value, key, map)
}),
get: state => key => {
const drafts = state[state.modified ? "copy" : "drafts"]
if (drafts.has(key)) {
return drafts.get(key)
}
function get(state, prop) {
if (prop === DRAFT_STATE) return state
let {drafts} = state
const value = latest(state).get(key)
if (state.finalized || !isDraftable(value)) {
return value
}
// Check for existing draft in unmodified state.
if (!state.modified && has(drafts, prop)) {
return drafts[prop]
}
const draft = createProxy(value, state)
drafts.set(key, draft)
return draft
},
keys: state => () => latest(state).keys(),
values: iterateMapValues,
entries: iterateMapValues,
[Symbol.iterator]: iterateMapValues
})
const value = source(state)[prop]
if (state.finalized || !isDraftable(value)) {
return value
}
/** Map.prototype.values _-or-_ Map.prototype.entries */
function iterateMapValues(state, prop, receiver) {
const isEntries = prop !== "values"
return () => {
const iterator = latest(state)[Symbol.iterator]()
return makeIterable(() => {
const result = iterator.next()
if (!result.done) {
const [key] = result.value
const value = receiver.get(key)
result.value = isEntries ? [key, value] : value
}
return result
})
}
}
// Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek(state.base, prop)) return value
// Store drafts on the copy (when one exists).
drafts = state.copy
}
/**
* Set drafts
*/
const setTraps = makeTrapsForGetters({
[DRAFT_STATE]: state => state,
size: state => latest(state).size,
has: state => key => latest(state).has(key),
add: state => value => {
if (!latest(state).has(value)) {
markChanged(state)
state.copy.add(value)
}
return state.draft
},
delete: state => value => {
markChanged(state)
return state.copy.delete(value)
},
clear: state => () => {
markChanged(state)
return state.copy.clear()
},
forEach: state => (cb, thisArg) => {
const iterator = iterateSetValues(state)()
let result = iterator.next()
while (!result.done) {
cb.call(thisArg, result.value, result.value, state.draft)
result = iterator.next()
}
},
keys: iterateSetValues,
values: iterateSetValues,
entries: iterateSetValues,
[Symbol.iterator]: iterateSetValues
})
function iterateSetValues(state, prop) {
const isEntries = prop === "entries"
return () => {
const iterator = latest(state)[Symbol.iterator]()
return makeIterable(() => {
const result = iterator.next()
if (!result.done) {
const value = wrapSetValue(state, result.value)
result.value = isEntries ? [value, value] : value
}
return result
})
}
return (drafts[prop] = createProxy(value, state))
}
function wrapSetValue(state, value) {
const key = original(value) || value
let draft = state.drafts.get(key)
if (!draft) {
if (state.finalized || !isDraftable(value)) {
return value
}
draft = createProxy(value, state)
state.drafts.set(key, draft)
if (state.modified) {
state.copy.add(draft)
}
}
return draft
function set(state, prop, value) {
if (!state.modified) {
const baseValue = peek(state.base, prop)
// Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
const isUnchanged = value
? is(baseValue, value) || value === state.drafts[prop]
: is(baseValue, value) && prop in state.base
if (isUnchanged) return true
markChanged(state)
}
state.assigned[prop] = true
state.copy[prop] = value
return true
}
/**
* Helpers
*/
// Retrieve the latest values of the draft.
function latest(state) {
return state.copy || state.base
function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false
markChanged(state)
} else if (state.assigned[prop]) {
// if an originally not assigned property was deleted
delete state.assigned[prop]
}
if (state.copy) delete state.copy[prop]
return true
}
// Access a property without creating an Immer draft.
function peek(draft, prop) {
const state = draft[DRAFT_STATE]
const desc = Reflect.getOwnPropertyDescriptor(
state ? latest(state) : draft,
prop
)
return desc && desc.value
// Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
function getOwnPropertyDescriptor(state, prop) {
const owner = source(state)
const desc = Reflect.getOwnPropertyDescriptor(owner, prop)
if (desc) {
desc.writable = true
desc.configurable = !Array.isArray(owner) || prop !== "length"
}
return desc
}
function markChanged(state) {
if (!state.modified) {
state.modified = true
const {base, drafts, parent} = state
const copy = shallowCopy(base)
if (isSet(base)) {
// Note: The `drafts` property is preserved for Set objects, since
// we need to keep track of which values are drafted.
assignSet(copy, drafts)
} else {
// Merge nested drafts into the copy.
if (isMap(base)) assignMap(copy, drafts)
else assign(copy, drafts)
state.drafts = null
}
state.copy = copy
if (parent) {
markChanged(parent)
}
}
if (!state.modified) {
state.modified = true
state.copy = assign(shallowCopy(state.base), state.drafts)
state.drafts = null
if (state.parent) markChanged(state.parent)
}
}
function makeIterable(next) {
let self
return (self = {
[Symbol.iterator]: () => self,
next
})
}
/** Create traps that all use the `Reflect` API on the `latest(state)` */
function makeReflectTraps(names) {
return names.reduce((traps, name) => {
traps[name] = (state, ...args) => Reflect[name](latest(state), ...args)
return traps
}, {})
}
function makeTrapsForGetters(getters) {
return {
...reflectTraps,
get(state, prop, receiver) {
return getters.hasOwnProperty(prop)
? getters[prop](state, prop, receiver)
: Reflect.get(state, prop, receiver)
},
setPrototypeOf(state) {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft") // prettier-ignore
}
}
}

@@ -5,30 +5,30 @@ import {DRAFT_STATE} from "./common"

export class ImmerScope {
constructor(parent) {
this.drafts = []
this.parent = parent
constructor(parent) {
this.drafts = []
this.parent = parent
// Whenever the modified draft contains a draft from another scope, we
// need to prevent auto-freezing so the unowned draft can be finalized.
this.canAutoFreeze = true
// Whenever the modified draft contains a draft from another scope, we
// need to prevent auto-freezing so the unowned draft can be finalized.
this.canAutoFreeze = true
// To avoid prototype lookups:
this.patches = null
}
usePatches(patchListener) {
if (patchListener) {
this.patches = []
this.inversePatches = []
this.patchListener = patchListener
}
}
revoke() {
this.leave()
this.drafts.forEach(revoke)
this.drafts = null // Make draft-related methods throw.
}
leave() {
if (this === ImmerScope.current) {
ImmerScope.current = this.parent
}
}
// To avoid prototype lookups:
this.patches = null
}
usePatches(patchListener) {
if (patchListener) {
this.patches = []
this.inversePatches = []
this.patchListener = patchListener
}
}
revoke() {
this.leave()
this.drafts.forEach(revoke)
this.drafts = null // Make draft-related methods throw.
}
leave() {
if (this === ImmerScope.current) {
ImmerScope.current = this.parent
}
}
}

@@ -38,7 +38,7 @@

ImmerScope.enter = function() {
return (this.current = new ImmerScope(this.current))
return (this.current = new ImmerScope(this.current))
}
function revoke(draft) {
draft[DRAFT_STATE].revoke()
draft[DRAFT_STATE].revoke()
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc