mutative
Version:
A JavaScript library for efficient immutable updates
1,452 lines (1,429 loc) • 56.5 kB
JavaScript
'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) => {
*