UNPKG

mutative

Version:

A JavaScript library for efficient immutable updates

1,452 lines (1,429 loc) 56.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var Operation = { Remove: 'remove', Replace: 'replace', Add: 'add' }; // Don't use `Symbol()` just for 3rd party access the draft var PROXY_DRAFT = /*#__PURE__*/Symbol["for"]('__MUTATIVE_PROXY_DRAFT__'); var RAW_RETURN_SYMBOL = /*#__PURE__*/Symbol('__MUTATIVE_RAW_RETURN_SYMBOL__'); var iteratorSymbol = Symbol.iterator; var dataTypes = { mutable: 'mutable', immutable: 'immutable' }; var internal = {}; function latest(proxyDraft) { var _proxyDraft$copy; return (_proxyDraft$copy = proxyDraft.copy) != null ? _proxyDraft$copy : proxyDraft.original; } /** * Check if the value is a draft */ function isDraft(target) { return !!getProxyDraft(target); } function getProxyDraft(value) { if (typeof value !== 'object') return null; return value == null ? void 0 : value[PROXY_DRAFT]; } function getValue(value) { var _proxyDraft$copy2; var proxyDraft = getProxyDraft(value); return proxyDraft ? (_proxyDraft$copy2 = proxyDraft.copy) != null ? _proxyDraft$copy2 : proxyDraft.original : value; } /** * Check if a value is draftable */ function isDraftable(value, options) { if (!value || typeof value !== 'object') return false; var markResult; return Object.getPrototypeOf(value) === Object.prototype || Array.isArray(value) || value instanceof Map || value instanceof Set || !!(options != null && options.mark) && ((markResult = options.mark(value, dataTypes)) === dataTypes.immutable || typeof markResult === 'function'); } function getPath(target, path) { if (path === void 0) { path = []; } if (Object.hasOwnProperty.call(target, 'key')) { // check if the parent is a draft and the original value is not equal to the current value var proxyDraft = getProxyDraft(get(target.parent.copy, target.key)); if (proxyDraft !== null && (proxyDraft == null ? void 0 : proxyDraft.original) !== target.original) { return null; } path.push(target.parent.type === 3 /* Set */ ? Array.from(target.parent.setMap.keys()).indexOf(target.key) : target.key); } if (target.parent) { return getPath(target.parent, path); } return path.reverse(); } function getType(target) { if (Array.isArray(target)) return 1 /* Array */; if (target instanceof Map) return 2 /* Map */; if (target instanceof Set) return 3 /* Set */; return 0 /* Object */; } function get(target, key) { return getType(target) === 2 /* Map */ ? target.get(key) : target[key]; } function set(target, key, value) { var type = getType(target); if (type === 2 /* Map */) { target.set(key, value); } else { target[key] = value; } } function peek(target, key) { var state = getProxyDraft(target); var source = state ? latest(state) : target; return source[key]; } function isEqual(x, y) { if (x === y) { return x !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; } } function revokeProxy(proxyDraft) { if (!proxyDraft) return; while (proxyDraft.finalities.revoke.length > 0) { var revoke = proxyDraft.finalities.revoke.pop(); revoke(); } } // handle JSON Pointer path with spec https://www.rfc-editor.org/rfc/rfc6901 function escapePath(path, pathAsArray) { return pathAsArray ? path : [''].concat(path).map(function (_item) { var item = "" + _item; if (item.indexOf('/') === -1 && item.indexOf('~') === -1) return item; return item.replace(/~/g, '~0').replace(/\//g, '~1'); }).join('/'); } function unescapePath(path) { if (Array.isArray(path)) return path; return path.split('/').map(function (_item) { return _item.replace(/~1/g, '/').replace(/~0/g, '~'); }).slice(1); } function strictCopy(target) { var descriptors = Object.getOwnPropertyDescriptors(target); Reflect.ownKeys(descriptors).forEach(function (key) { var desc = descriptors[key]; // for freeze if (!desc.writable) { desc.writable = true; desc.configurable = true; } if (desc.get || desc.set) descriptors[key] = { configurable: true, writable: true, enumerable: desc.enumerable, value: target[key] }; }); return Object.create(Object.getPrototypeOf(target), descriptors); } function shallowCopy(original, options) { var markResult; if (Array.isArray(original)) { return Array.prototype.concat.call(original); } else if (original instanceof Set) { return new Set(original.values()); } else if (original instanceof Map) { return new Map(original); } else if (options != null && options.mark && (markResult = options.mark(original, dataTypes), markResult !== undefined) && markResult !== dataTypes.mutable) { if (markResult === dataTypes.immutable) { return strictCopy(original); } else if (typeof markResult === 'function') { if ( (options.enablePatches || options.enableAutoFreeze)) { throw new Error("You can't use mark and patches or auto freeze together."); } return markResult(); } throw new Error("Unsupported mark result: " + markResult); } else if (typeof original === 'object' && Object.getPrototypeOf(original) === Object.prototype) { // For best performance with shallow copies, // don't use `Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));` by default. var copy = {}; Object.keys(original).forEach(function (key) { copy[key] = original[key]; }); return copy; } else { throw new Error("Please check mark() to ensure that it is a stable marker draftable function."); } } function ensureShallowCopy(target) { if (target.copy) return; target.copy = shallowCopy(target.original, target.options); } function deepClone(target) { if (!isDraftable(target)) return getValue(target); if (Array.isArray(target)) return target.map(deepClone); if (target instanceof Map) return new Map(Array.from(target.entries()).map(function (_ref) { var k = _ref[0], v = _ref[1]; return [k, deepClone(v)]; })); if (target instanceof Set) return new Set(Array.from(target).map(deepClone)); var copy = Object.create(Object.getPrototypeOf(target)); for (var key in target) copy[key] = deepClone(target[key]); return copy; } function cloneIfNeeded(target) { return isDraft(target) ? deepClone(target) : target; } function markChanged(proxyDraft) { var _proxyDraft$assignedM; proxyDraft.assignedMap = (_proxyDraft$assignedM = proxyDraft.assignedMap) != null ? _proxyDraft$assignedM : new Map(); if (!proxyDraft.operated) { proxyDraft.operated = true; if (proxyDraft.parent) { markChanged(proxyDraft.parent); } } } function throwFrozenError() { throw new Error('Cannot modify frozen object'); } function deepFreeze(target, subKey, updatedValues, stack, keys) { { var _updatedValues, _stack, _keys; updatedValues = (_updatedValues = updatedValues) != null ? _updatedValues : new WeakMap(); stack = (_stack = stack) != null ? _stack : []; keys = (_keys = keys) != null ? _keys : []; var value = updatedValues.has(target) ? updatedValues.get(target) : target; if (stack.length > 0) { var index = stack.indexOf(value); if (value && typeof value === 'object' && index !== -1) { if (stack[0] === value) { throw new Error("Forbids circular reference"); } throw new Error("Forbids circular reference: ~/" + keys.slice(0, index).map(function (key, index) { if (typeof key === 'symbol') return "[" + key.toString() + "]"; var parent = stack[index]; if (typeof key === 'object' && (parent instanceof Map || parent instanceof Set)) return Array.from(parent.keys()).indexOf(key); return key; }).join('/')); } stack.push(value); keys.push(subKey); } else { stack.push(value); } } if (Object.isFrozen(target) || isDraft(target)) { { stack.pop(); keys.pop(); } return; } var type = getType(target); switch (type) { case 2 /* Map */: for (var _iterator = _createForOfIteratorHelperLoose(target), _step; !(_step = _iterator()).done;) { var _step$value = _step.value, key = _step$value[0], _value = _step$value[1]; deepFreeze(key, key, updatedValues, stack, keys); deepFreeze(_value, key, updatedValues, stack, keys); } target.set = target.clear = target["delete"] = throwFrozenError; break; case 3 /* Set */: for (var _iterator2 = _createForOfIteratorHelperLoose(target), _step2; !(_step2 = _iterator2()).done;) { var _value2 = _step2.value; deepFreeze(_value2, _value2, updatedValues, stack, keys); } target.add = target.clear = target["delete"] = throwFrozenError; break; case 1 /* Array */: Object.freeze(target); var _index = 0; for (var _iterator3 = _createForOfIteratorHelperLoose(target), _step3; !(_step3 = _iterator3()).done;) { var _value3 = _step3.value; deepFreeze(_value3, _index, updatedValues, stack, keys); _index += 1; } break; default: Object.freeze(target); // ignore non-enumerable or symbol properties Object.keys(target).forEach(function (name) { var value = target[name]; deepFreeze(value, name, updatedValues, stack, keys); }); } { stack.pop(); keys.pop(); } } function has(target, key) { return target instanceof Map ? target.has(key) : Object.prototype.hasOwnProperty.call(target, key); } function getDescriptor(target, key) { if (key in target) { var prototype = Reflect.getPrototypeOf(target); while (prototype) { var descriptor = Reflect.getOwnPropertyDescriptor(prototype, key); if (descriptor) return descriptor; prototype = Reflect.getPrototypeOf(prototype); } } return; } function forEach(target, iter) { var type = getType(target); if (type === 0 /* Object */) { Reflect.ownKeys(target).forEach(function (key) { iter(key, target[key], target); }); } else if (type === 1 /* Array */) { var index = 0; for (var _iterator = _createForOfIteratorHelperLoose(target), _step; !(_step = _iterator()).done;) { var entry = _step.value; iter(index, entry, target); index += 1; } } else { target.forEach(function (entry, index) { return iter(index, entry, target); }); } } function handleValue(target, handledSet) { if (isDraft(target) || !isDraftable(target) || handledSet.has(target) || Object.isFrozen(target)) return; var isSet = target instanceof Set; var setMap = isSet ? new Map() : undefined; handledSet.add(target); forEach(target, function (key, value) { if (isDraft(value)) { var _proxyDraft$assignedM; var proxyDraft = getProxyDraft(value); ensureShallowCopy(proxyDraft); // A draft where a child node has been changed, or assigned a value var updatedValue = (_proxyDraft$assignedM = proxyDraft.assignedMap) != null && _proxyDraft$assignedM.size || proxyDraft.operated ? proxyDraft.copy : proxyDraft.original; // final update value set(isSet ? setMap : target, key, updatedValue); } else { handleValue(value, handledSet); } }); if (setMap) { var _set = target; var values = Array.from(_set); _set.clear(); values.forEach(function (value) { _set.add(setMap.has(value) ? setMap.get(value) : value); }); } } function finalizeAssigned(proxyDraft, key) { // handle the draftable assigned values, and the value is not a draft var copy = proxyDraft.type === 3 /* Set */ ? proxyDraft.setMap : proxyDraft.copy; if (proxyDraft.finalities.revoke.length > 1 && proxyDraft.assignedMap.get(key) && copy) { handleValue(get(copy, key), proxyDraft.finalities.handledSet); } } function finalizeSetValue(target) { if (target.type === 3 /* Set */ && target.copy) { target.copy.clear(); target.setMap.forEach(function (value) { target.copy.add(getValue(value)); }); } } function finalizePatches(target, generatePatches, patches, inversePatches) { var shouldFinalize = target.operated && target.assignedMap && target.assignedMap.size > 0 && !target.finalized; if (shouldFinalize) { if (patches && inversePatches) { var basePath = getPath(target); if (basePath) { generatePatches(target, basePath, patches, inversePatches); } } target.finalized = true; } } function markFinalization(target, key, value, generatePatches) { var proxyDraft = getProxyDraft(value); if (proxyDraft) { // !case: assign the draft value if (!proxyDraft.callbacks) { proxyDraft.callbacks = []; } proxyDraft.callbacks.push(function (patches, inversePatches) { var copy = target.type === 3 /* Set */ ? target.setMap : target.copy; if (isEqual(get(copy, key), value)) { var updatedValue = proxyDraft.original; if (proxyDraft.copy) { updatedValue = proxyDraft.copy; } finalizeSetValue(target); finalizePatches(target, generatePatches, patches, inversePatches); if ( target.options.enableAutoFreeze) { var _target$options$updat; target.options.updatedValues = (_target$options$updat = target.options.updatedValues) != null ? _target$options$updat : new WeakMap(); target.options.updatedValues.set(updatedValue, proxyDraft.original); } // final update value set(copy, key, updatedValue); } }); if (target.options.enableAutoFreeze) { // !case: assign the draft value in cross draft tree if (proxyDraft.finalities !== target.finalities) { target.options.enableAutoFreeze = false; } } } if (isDraftable(value, target.options)) { // !case: assign the non-draft value target.finalities.draft.push(function () { var copy = target.type === 3 /* Set */ ? target.setMap : target.copy; if (isEqual(get(copy, key), value)) { finalizeAssigned(target, key); } }); } } function generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray) { var original = proxyState.original, assignedMap = proxyState.assignedMap, options = proxyState.options; var copy = proxyState.copy; if (copy.length < original.length) { var _ref = [copy, original]; original = _ref[0]; copy = _ref[1]; var _ref2 = [inversePatches, patches]; patches = _ref2[0]; inversePatches = _ref2[1]; } for (var index = 0; index < original.length; index += 1) { if (assignedMap.get(index.toString()) && copy[index] !== original[index]) { var _path = basePath.concat([index]); var path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Replace, path: path, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(copy[index]) }); inversePatches.push({ op: Operation.Replace, path: path, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(original[index]) }); } } for (var _index = original.length; _index < copy.length; _index += 1) { var _path2 = basePath.concat([_index]); var _path3 = escapePath(_path2, pathAsArray); patches.push({ op: Operation.Add, path: _path3, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(copy[_index]) }); } if (original.length < copy.length) { // https://www.rfc-editor.org/rfc/rfc6902#appendix-A.4 // For performance, here we only generate an operation that replaces the length of the array, // which is inconsistent with JSON Patch specification var _options$enablePatche = options.enablePatches.arrayLengthAssignment, arrayLengthAssignment = _options$enablePatche === void 0 ? true : _options$enablePatche; if (arrayLengthAssignment) { var _path4 = basePath.concat(['length']); var _path5 = escapePath(_path4, pathAsArray); inversePatches.push({ op: Operation.Replace, path: _path5, value: original.length }); } else { for (var _index2 = copy.length; original.length < _index2; _index2 -= 1) { var _path6 = basePath.concat([_index2 - 1]); var _path7 = escapePath(_path6, pathAsArray); inversePatches.push({ op: Operation.Remove, path: _path7 }); } } } } function generatePatchesFromAssigned(_ref3, basePath, patches, inversePatches, pathAsArray) { var original = _ref3.original, copy = _ref3.copy, assignedMap = _ref3.assignedMap; assignedMap.forEach(function (assignedValue, key) { var originalValue = get(original, key); var value = cloneIfNeeded(get(copy, key)); var op = !assignedValue ? Operation.Remove : has(original, key) ? Operation.Replace : Operation.Add; if (isEqual(originalValue, value) && op === Operation.Replace) return; var _path = basePath.concat(key); var path = escapePath(_path, pathAsArray); patches.push(op === Operation.Remove ? { op: op, path: path } : { op: op, path: path, value: value }); inversePatches.push(op === Operation.Add ? { op: Operation.Remove, path: path } : op === Operation.Remove ? { op: Operation.Add, path: path, value: originalValue } : { op: Operation.Replace, path: path, value: originalValue }); }); } function generateSetPatches(_ref4, basePath, patches, inversePatches, pathAsArray) { var original = _ref4.original, copy = _ref4.copy; var index = 0; original.forEach(function (value) { if (!copy.has(value)) { var _path = basePath.concat([index]); var path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Remove, path: path, value: value }); inversePatches.unshift({ op: Operation.Add, path: path, value: value }); } index += 1; }); index = 0; copy.forEach(function (value) { if (!original.has(value)) { var _path = basePath.concat([index]); var path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Add, path: path, value: value }); inversePatches.unshift({ op: Operation.Remove, path: path, value: value }); } index += 1; }); } function generatePatches(proxyState, basePath, patches, inversePatches) { var _proxyState$options$e = proxyState.options.enablePatches.pathAsArray, pathAsArray = _proxyState$options$e === void 0 ? true : _proxyState$options$e; switch (proxyState.type) { case 0 /* Object */: case 2 /* Map */: return generatePatchesFromAssigned(proxyState, basePath, patches, inversePatches, pathAsArray); case 1 /* Array */: return generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray); case 3 /* Set */: return generateSetPatches(proxyState, basePath, patches, inversePatches, pathAsArray); } } var readable = false; var checkReadable = function checkReadable(value, options, ignoreCheckDraftable) { if (ignoreCheckDraftable === void 0) { ignoreCheckDraftable = false; } if (typeof value === 'object' && value !== null && (!isDraftable(value, options) || ignoreCheckDraftable) && !readable) { throw new Error("Strict mode: Mutable data cannot be accessed directly, please use 'unsafe(callback)' wrap."); } }; /** * `unsafe(callback)` to access mutable data directly in strict mode. * * ## Example * * ```ts * import { create, unsafe } from '../index'; * * class Foobar { * bar = 1; * } * * const baseState = { foobar: new Foobar() }; * const state = create( * baseState, * (draft) => { * unsafe(() => { * draft.foobar.bar = 2; * }); * }, * { * strict: true, * } * ); * * expect(state).toBe(baseState); * expect(state.foobar).toBe(baseState.foobar); * expect(state.foobar.bar).toBe(2); * ``` */ function unsafe(callback) { readable = true; var result; try { result = callback(); } finally { readable = false; } return result; } var _mapHandler; var mapHandler = (_mapHandler = { get size() { var current = latest(getProxyDraft(this)); return current.size; }, has: function has(key) { return latest(getProxyDraft(this)).has(key); }, set: function set(key, value) { var target = getProxyDraft(this); var source = latest(target); if (!source.has(key) || !isEqual(source.get(key), value)) { ensureShallowCopy(target); markChanged(target); target.assignedMap.set(key, true); target.copy.set(key, value); markFinalization(target, key, value, generatePatches); } return this; }, "delete": function _delete(key) { if (!this.has(key)) { return false; } var target = getProxyDraft(this); ensureShallowCopy(target); markChanged(target); if (target.original.has(key)) { target.assignedMap.set(key, false); } else { target.assignedMap["delete"](key); } target.copy["delete"](key); return true; }, clear: function clear() { var target = getProxyDraft(this); if (!this.size) return; ensureShallowCopy(target); markChanged(target); target.assignedMap = new Map(); for (var _iterator = _createForOfIteratorHelperLoose(target.original), _step; !(_step = _iterator()).done;) { var _step$value = _step.value, key = _step$value[0]; target.assignedMap.set(key, false); } target.copy.clear(); }, forEach: function forEach(callback, thisArg) { var _this = this; var target = getProxyDraft(this); latest(target).forEach(function (_value, _key) { callback.call(thisArg, _this.get(_key), _key, _this); }); }, get: function get(key) { var target = getProxyDraft(this); var value = latest(target).get(key); var mutable = (target.options.mark == null ? void 0 : target.options.mark(value, dataTypes)) === dataTypes.mutable; if (target.options.strict) { checkReadable(value, target.options, mutable); } if (mutable) { return value; } if (target.finalized || !isDraftable(value, target.options)) { return value; } // drafted or reassigned if (value !== target.original.get(key)) { return value; } var draft = internal.createDraft({ original: value, parentDraft: target, key: key, finalities: target.finalities, options: target.options }); ensureShallowCopy(target); target.copy.set(key, draft); return draft; }, keys: function keys() { return latest(getProxyDraft(this)).keys(); }, values: function values() { var _this2 = this, _ref; var iterator = this.keys(); return _ref = {}, _ref[iteratorSymbol] = function () { return _this2.values(); }, _ref.next = function next() { var result = iterator.next(); if (result.done) return result; var value = _this2.get(result.value); return { done: false, value: value }; }, _ref; }, entries: function entries() { var _this3 = this, _ref2; var iterator = this.keys(); return _ref2 = {}, _ref2[iteratorSymbol] = function () { return _this3.entries(); }, _ref2.next = function next() { var result = iterator.next(); if (result.done) return result; var value = _this3.get(result.value); return { done: false, value: [result.value, value] }; }, _ref2; } }, _mapHandler[iteratorSymbol] = function () { return this.entries(); }, _mapHandler); var mapHandlerKeys = /*#__PURE__*/Reflect.ownKeys(mapHandler); var _setHandler; var getNextIterator = function getNextIterator(target, iterator, _ref) { var isValuesIterator = _ref.isValuesIterator; return function () { var result = iterator.next(); if (result.done) return result; var key = result.value; var value = target.setMap.get(key); var currentDraft = getProxyDraft(value); var mutable = (target.options.mark == null ? void 0 : target.options.mark(value, dataTypes)) === dataTypes.mutable; if (target.options.strict) { checkReadable(key, target.options, mutable); } if (!mutable && !currentDraft && isDraftable(key, target.options) && !target.finalized && target.original.has(key)) { // draft a draftable original set item var proxy = internal.createDraft({ original: key, parentDraft: target, key: key, finalities: target.finalities, options: target.options }); target.setMap.set(key, proxy); value = proxy; } else if (currentDraft) { // drafted value = currentDraft.proxy; } return { done: false, value: isValuesIterator ? value : [value, value] }; }; }; var setHandler = (_setHandler = { get size() { var target = getProxyDraft(this); return target.setMap.size; }, has: function has(value) { var target = getProxyDraft(this); // reassigned or non-draftable values if (target.setMap.has(value)) return true; ensureShallowCopy(target); var valueProxyDraft = getProxyDraft(value); // drafted if (valueProxyDraft && target.setMap.has(valueProxyDraft.original)) return true; return false; }, add: function add(value) { var target = getProxyDraft(this); if (!this.has(value)) { ensureShallowCopy(target); markChanged(target); target.assignedMap.set(value, true); target.setMap.set(value, value); markFinalization(target, value, value, generatePatches); } return this; }, "delete": function _delete(value) { if (!this.has(value)) { return false; } var target = getProxyDraft(this); ensureShallowCopy(target); markChanged(target); var valueProxyDraft = getProxyDraft(value); if (valueProxyDraft && target.setMap.has(valueProxyDraft.original)) { // delete drafted target.assignedMap.set(valueProxyDraft.original, false); return target.setMap["delete"](valueProxyDraft.original); } if (!valueProxyDraft && target.setMap.has(value)) { // non-draftable values target.assignedMap.set(value, false); } else { // reassigned target.assignedMap["delete"](value); } // delete reassigned or non-draftable values return target.setMap["delete"](value); }, clear: function clear() { if (!this.size) return; var target = getProxyDraft(this); ensureShallowCopy(target); markChanged(target); for (var _iterator = _createForOfIteratorHelperLoose(target.original), _step; !(_step = _iterator()).done;) { var value = _step.value; target.assignedMap.set(value, false); } target.setMap.clear(); }, values: function values() { var _this = this, _ref2; var target = getProxyDraft(this); ensureShallowCopy(target); var iterator = target.setMap.keys(); return _ref2 = {}, _ref2[Symbol.iterator] = function () { return _this.values(); }, _ref2.next = getNextIterator(target, iterator, { isValuesIterator: true }), _ref2; }, entries: function entries() { var _this2 = this, _ref3; var target = getProxyDraft(this); ensureShallowCopy(target); var iterator = target.setMap.keys(); return _ref3 = {}, _ref3[Symbol.iterator] = function () { return _this2.entries(); }, _ref3.next = getNextIterator(target, iterator, { isValuesIterator: false }), _ref3; }, keys: function keys() { return this.values(); } }, _setHandler[iteratorSymbol] = function () { return this.values(); }, _setHandler.forEach = function forEach(callback, thisArg) { var iterator = this.values(); var result = iterator.next(); while (!result.done) { callback.call(thisArg, result.value, result.value, this); result = iterator.next(); } }, _setHandler); var setHandlerKeys = /*#__PURE__*/Reflect.ownKeys(setHandler); var proxyHandler = { get: function get(target, key, receiver) { if (key === PROXY_DRAFT) return target; var markResult; if (target.options.mark) { // handle `Uncaught TypeError: Method get Map.prototype.size called on incompatible receiver #<Map>` // or `Uncaught TypeError: Method get Set.prototype.size called on incompatible receiver #<Set>` var _value = key === 'size' && (target.original instanceof Map || target.original instanceof Set) ? Reflect.get(target.original, key) : Reflect.get(target.original, key, receiver); markResult = target.options.mark(_value, dataTypes); if (markResult === dataTypes.mutable) { if (target.options.strict) { checkReadable(_value, target.options, true); } return _value; } } var source = latest(target); if (source instanceof Map && mapHandlerKeys.includes(key)) { if (key === 'size') { return Object.getOwnPropertyDescriptor(mapHandler, 'size').get.call(target.proxy); } var handle = mapHandler[key]; if (handle) { return handle.bind(target.proxy); } } if (source instanceof Set && setHandlerKeys.includes(key)) { if (key === 'size') { return Object.getOwnPropertyDescriptor(setHandler, 'size').get.call(target.proxy); } var _handle = setHandler[key]; if (_handle) { return _handle.bind(target.proxy); } } if (!has(source, key)) { var _desc$get; var desc = getDescriptor(source, key); return desc ? "value" in desc ? desc.value : // !case: support for getter (_desc$get = desc.get) == null ? void 0 : _desc$get.call(target.proxy) : undefined; } var value = source[key]; if (target.options.strict) { checkReadable(value, target.options); } if (target.finalized || !isDraftable(value, target.options)) { return value; } // Ensure that the assigned values are not drafted if (value === peek(target.original, key)) { ensureShallowCopy(target); target.copy[key] = createDraft({ original: target.original[key], parentDraft: target, key: target.type === 1 /* Array */ ? Number(key) : key, finalities: target.finalities, options: target.options }); // !case: support for custom shallow copy function if (typeof markResult === 'function') { var subProxyDraft = getProxyDraft(target.copy[key]); ensureShallowCopy(subProxyDraft); // Trigger a custom shallow copy to update to a new copy markChanged(subProxyDraft); return subProxyDraft.copy; } return target.copy[key]; } return value; }, set: function set(target, key, value) { if (target.type === 3 /* Set */ || target.type === 2 /* Map */) { throw new Error("Map/Set draft does not support any property assignment."); } if (target.type === 1 /* Array */ && key !== 'length' && isNaN(Number(key))) { throw new Error("Only supports setting array indices and the 'length' property."); } var desc = getDescriptor(latest(target), key); if (desc != null && desc.set) { // !case: cover the case of setter desc.set.call(target.proxy, value); return true; } var current = peek(latest(target), key); var currentProxyDraft = getProxyDraft(current); if (currentProxyDraft && isEqual(currentProxyDraft.original, value)) { var _target$assignedMap; // !case: ignore the case of assigning the original draftable value to a draft target.copy[key] = value; target.assignedMap = (_target$assignedMap = target.assignedMap) != null ? _target$assignedMap : new Map(); target.assignedMap.set(key, false); return true; } // !case: handle new props with value 'undefined' if (isEqual(value, current) && (value !== undefined || has(target.original, key))) return true; ensureShallowCopy(target); markChanged(target); if (has(target.original, key) && isEqual(value, target.original[key])) { // !case: handle the case of assigning the original non-draftable value to a draft target.assignedMap["delete"](key); } else { target.assignedMap.set(key, true); } target.copy[key] = value; markFinalization(target, key, value, generatePatches); return true; }, has: function has(target, key) { return key in latest(target); }, ownKeys: function ownKeys(target) { return Reflect.ownKeys(latest(target)); }, getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, key) { var source = latest(target); var descriptor = Reflect.getOwnPropertyDescriptor(source, key); if (!descriptor) return descriptor; return { writable: true, configurable: target.type !== 1 /* Array */ || key !== 'length', enumerable: descriptor.enumerable, value: source[key] }; }, getPrototypeOf: function getPrototypeOf(target) { return Reflect.getPrototypeOf(target.original); }, setPrototypeOf: function setPrototypeOf() { throw new Error("Cannot call 'setPrototypeOf()' on drafts"); }, defineProperty: function defineProperty() { throw new Error("Cannot call 'defineProperty()' on drafts"); }, deleteProperty: function deleteProperty(target, key) { if (target.type === 1 /* Array */) { return proxyHandler.set.call(this, target, key, undefined, target.proxy); } if (peek(target.original, key) !== undefined || key in target.original) { // !case: delete an existing key ensureShallowCopy(target); markChanged(target); target.assignedMap.set(key, false); } else { var _target$assignedMap2; target.assignedMap = (_target$assignedMap2 = target.assignedMap) != null ? _target$assignedMap2 : new Map(); // The original non-existent key has been deleted target.assignedMap["delete"](key); } if (target.copy) delete target.copy[key]; return true; } }; function createDraft(createDraftOptions) { var original = createDraftOptions.original, parentDraft = createDraftOptions.parentDraft, key = createDraftOptions.key, finalities = createDraftOptions.finalities, options = createDraftOptions.options; var type = getType(original); var proxyDraft = { type: type, finalized: false, parent: parentDraft, original: original, copy: null, proxy: null, finalities: finalities, options: options, // Mapping of draft Set items to their corresponding draft values. setMap: type === 3 /* Set */ ? new Map(original.entries()) : undefined }; // !case: undefined as a draft map key if (key || 'key' in createDraftOptions) { proxyDraft.key = key; } var _Proxy$revocable = Proxy.revocable(type === 1 /* Array */ ? Object.assign([], proxyDraft) : proxyDraft, proxyHandler), proxy = _Proxy$revocable.proxy, revoke = _Proxy$revocable.revoke; finalities.revoke.push(revoke); proxyDraft.proxy = proxy; if (parentDraft) { var target = parentDraft; target.finalities.draft.push(function (patches, inversePatches) { var _oldProxyDraft$callba; var oldProxyDraft = getProxyDraft(proxy); // if target is a Set draft, `setMap` is the real Set copies proxy mapping. var copy = target.type === 3 /* Set */ ? target.setMap : target.copy; var draft = get(copy, key); var proxyDraft = getProxyDraft(draft); if (proxyDraft) { // assign the updated value to the copy object var updatedValue = proxyDraft.original; if (proxyDraft.operated) { updatedValue = getValue(draft); } finalizeSetValue(proxyDraft); finalizePatches(proxyDraft, generatePatches, patches, inversePatches); if ( target.options.enableAutoFreeze) { var _target$options$updat; target.options.updatedValues = (_target$options$updat = target.options.updatedValues) != null ? _target$options$updat : new WeakMap(); target.options.updatedValues.set(updatedValue, proxyDraft.original); } // final update value set(copy, key, updatedValue); } // !case: handle the deleted key (_oldProxyDraft$callba = oldProxyDraft.callbacks) == null ? void 0 : _oldProxyDraft$callba.forEach(function (callback) { callback(patches, inversePatches); }); }); } else { // !case: handle the root draft var _target = getProxyDraft(proxy); _target.finalities.draft.push(function (patches, inversePatches) { finalizeSetValue(_target); finalizePatches(_target, generatePatches, patches, inversePatches); }); } return proxy; } internal.createDraft = createDraft; function finalizeDraft(result, returnedValue, patches, inversePatches, enableAutoFreeze) { var _proxyDraft$original; var proxyDraft = getProxyDraft(result); var original = (_proxyDraft$original = proxyDraft == null ? void 0 : proxyDraft.original) != null ? _proxyDraft$original : result; var hasReturnedValue = !!returnedValue.length; if (proxyDraft != null && proxyDraft.operated) { while (proxyDraft.finalities.draft.length > 0) { var finalize = proxyDraft.finalities.draft.pop(); finalize(patches, inversePatches); } } var state = hasReturnedValue ? returnedValue[0] : proxyDraft ? proxyDraft.operated ? proxyDraft.copy : proxyDraft.original : result; if (proxyDraft) revokeProxy(proxyDraft); if (enableAutoFreeze) { deepFreeze(state, state, proxyDraft == null ? void 0 : proxyDraft.options.updatedValues); } return [state, patches && hasReturnedValue ? [{ op: Operation.Replace, path: [], value: returnedValue[0] }] : patches, inversePatches && hasReturnedValue ? [{ op: Operation.Replace, path: [], value: original }] : inversePatches]; } function draftify(baseState, options) { var finalities = { draft: [], revoke: [], handledSet: new WeakSet() }; var patches; var inversePatches; if (options.enablePatches) { patches = []; inversePatches = []; } var isMutable = (options.mark == null ? void 0 : options.mark(baseState, dataTypes)) === dataTypes.mutable || !isDraftable(baseState, options); var draft = isMutable ? baseState : createDraft({ original: baseState, parentDraft: null, finalities: finalities, options: options }); return [draft, function (returnedValue) { if (returnedValue === void 0) { returnedValue = []; } var _finalizeDraft = finalizeDraft(draft, returnedValue, patches, inversePatches, options.enableAutoFreeze), finalizedState = _finalizeDraft[0], finalizedPatches = _finalizeDraft[1], finalizedInversePatches = _finalizeDraft[2]; return options.enablePatches ? [finalizedState, finalizedPatches, finalizedInversePatches] : finalizedState; }]; } function handleReturnValue(options) { var rootDraft = options.rootDraft, value = options.value, _options$useRawReturn = options.useRawReturn, useRawReturn = _options$useRawReturn === void 0 ? false : _options$useRawReturn, _options$isRoot = options.isRoot, isRoot = _options$isRoot === void 0 ? true : _options$isRoot; forEach(value, function (key, item, source) { var proxyDraft = getProxyDraft(item); // just handle the draft which is created by the same rootDraft if (proxyDraft && rootDraft && proxyDraft.finalities === rootDraft.finalities) { options.isContainDraft = true; var currentValue = proxyDraft.original; // final update value, but just handle return value if (source instanceof Set) { var arr = Array.from(source); source.clear(); arr.forEach(function (_item) { return source.add(key === _item ? currentValue : _item); }); } else { set(source, key, currentValue); } } else if (typeof item === 'object' && item !== null) { options.value = item; options.isRoot = false; handleReturnValue(options); } }); if ( isRoot) { if (!options.isContainDraft) console.warn("The return value does not contain any draft, please use 'rawReturn()' to wrap the return value to improve performance."); if (useRawReturn) { console.warn("The return value contains drafts, please don't use 'rawReturn()' to wrap the return value."); } } } function getCurrent(target) { var proxyDraft = getProxyDraft(target); if (!isDraftable(target, proxyDraft == null ? void 0 : proxyDraft.options)) return target; var type = getType(target); if (proxyDraft && !proxyDraft.operated) return proxyDraft.original; if (proxyDraft) proxyDraft.finalized = true; var currentValue; try { currentValue = type === 2 /* Map */ ? new Map(target) : type === 3 /* Set */ ? Array.from(proxyDraft.setMap.values()) : shallowCopy(target, proxyDraft == null ? void 0 : proxyDraft.options); } finally { if (proxyDraft) proxyDraft.finalized = false; } forEach(currentValue, function (key, value) { if (proxyDraft && isEqual(get(proxyDraft.original, key), value)) return; set(currentValue, key, getCurrent(value)); }); return type === 3 /* Set */ ? new Set(currentValue) : currentValue; } /** * `current(draft)` to get current state in the draft mutation function. * * ## Example * * ```ts * import { create, current } from '../index'; * * const baseState = { foo: { bar: 'str' }, arr: [] }; * const state = create( * baseState, * (draft) => { * draft.foo.bar = 'str2'; * expect(current(draft.foo)).toEqual({ bar: 'str2' }); * }, * ); * ``` */ function current(target) { if (!isDraft(target)) { throw new Error("current() is only used for Draft, parameter: " + target); } return getCurrent(target); } /** * `makeCreator(options)` to make a creator function. * * ## Example * * ```ts * import { makeCreator } from '../index'; * * const baseState = { foo: { bar: 'str' }, arr: [] }; * const create = makeCreator({ enableAutoFreeze: true }); * const state = create( * baseState, * (draft) => { * draft.foo.bar = 'str2'; * }, * ); * * expect(state).toEqual({ foo: { bar: 'str2' }, arr: [] }); * expect(state).not.toBe(baseState); * expect(state.foo).not.toBe(baseState.foo); * expect(state.arr).toBe(baseState.arr); * expect(Object.isFrozen(state)).toBeTruthy(); * ``` */ var makeCreator = function makeCreator(arg) { if ( arg !== undefined && Object.prototype.toString.call(arg) !== '[object Object]') { throw new Error("Invalid options: " + String(arg) + ", 'options' should be an object."); } return function create(arg0, arg1, arg2) { var _options$enablePatche, _options$strict, _options$enableAutoFr; if (typeof arg0 === 'function' && typeof arg1 !== 'function') { return function (base) { var _this = this; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return create(base, function (draft) { return arg0.call.apply(arg0, [_this, draft].concat(args)); }, arg1); }; } var base = arg0; var mutate = arg1; var options = arg2; if (typeof arg1 !== 'function') { options = arg1; } if ( options !== undefined && Object.prototype.toString.call(options) !== '[object Object]') { throw new Error("Invalid options: " + options + ", 'options' should be an object."); } options = _extends({}, arg, options); var state = isDraft(base) ? current(base) : base; var mark = Array.isArray(options.mark) ? function (value, types) { for (var _iterator = _createForOfIteratorHelperLoose(options.mark), _step; !(_step = _iterator()).done;) { var _mark = _step.value; if ( typeof _mark !== 'function') { throw new Error("Invalid mark: " + _mark + ", 'mark' should be a function."); } var _result = _mark(value, types); if (_result) { return _result; } } return; } : options.mark; var enablePatches = (_options$enablePatche = options.enablePatches) != null ? _options$enablePatche : false; var strict = (_options$strict = options.strict) != null ? _options$strict : false; var enableAutoFreeze = (_options$enableAutoFr = options.enableAutoFreeze) != null ? _options$enableAutoFr : false; var _options = { enableAutoFreeze: enableAutoFreeze, mark: mark, strict: strict, enablePatches: enablePatches }; if (!isDraftable(state, _options) && typeof state === 'object' && state !== null) { throw new Error("Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable."); } var _draftify = draftify(state, _options), draft = _draftify[0], finalize = _draftify[1]; if (typeof arg1 !== 'function') { if (!isDraftable(state, _options)) { throw new Error("Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable."); } return [draft, finalize]; } var result; try { result = mutate(draft); } catch (error) { revokeProxy(getProxyDraft(draft)); throw error; } var returnValue = function returnValue(value) { var proxyDraft = getProxyDraft(draft); if (!isDraft(value)) { if (value !== undefined && !isEqual(value, draft) && proxyDraft != null && proxyDraft.operated) { throw new Error("Either the value is returned as a new non-draft value, or only the draft is modified without returning any value."); } var rawReturnValue = value == null ? void 0 : value[RAW_RETURN_SYMBOL]; if (rawReturnValue) { var _value = rawReturnValue[0]; if (_options.strict && typeof value === 'object' && value !== null) { handleReturnValue({ rootDraft: proxyDraft, value: value, useRawReturn: true }); } return finalize([_value]); } if (value !== undefined) { if (typeof value === 'object' && value !== null) { handleReturnValue({ rootDraft: proxyDraft, value: value }); } return finalize([value]); } } if (value === draft || value === undefined) { return finalize([]); } var returnedProxyDraft = getProxyDraft(value); if (_options === returnedProxyDraft.options) { if (returnedProxyDraft.operated) { throw new Error("Cannot return a modified child draft."); } return finalize([current(value)]); } return finalize([value]); }; if (result instanceof Promise) { return result.then(returnValue, function (error) { revokeProxy(getProxyDraft(draft)); throw error; }); } return returnValue(result); }; }; /** * `create(baseState, callback, options)` to create the next state * * ## Example * * ```ts * import { create } from '../index'; * * const baseState = { foo: { bar: 'str' }, arr: [] }; * const state = create( * baseState, * (draft) => { * draft.foo.bar = 'str2'; * }, * ); * * expect(state).toEqual({ foo: { bar: 'str2' }, arr: [] }); * expect(state).not.toBe(baseState); * expect(state.foo).not.toBe(baseState.foo); * expect(state.arr).toBe(baseState.arr); * ``` */ var create = /*#__PURE__*/makeCreator(); /** * `apply(state, patches)` to apply patches to state * * ## Example * * ```ts * import { create, apply } from '../index'; * * const baseState = { foo: { bar: 'str' }, arr: [] }; * const [state, patches] = create( * baseState, * (draft) => { *