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

immer

Package Overview
Dependencies
Maintainers
1
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 1.4.0 to 1.5.0

4

changelog.md
# Changelog
### 1.5.0
* Added support for patches, through [#168](https://github.com/mweststrate/immer/pull/168). See the [patches](https://github.com/mweststrate/immer#patches) section for details
### 1.4.0

@@ -4,0 +8,0 @@

@@ -13,2 +13,10 @@ // Mapped type to remove readonly modifiers from state

export interface Patch {
op: "replace" | "remove" | "add",
path: (string|number)[]
value?: any
}
export type PatchListener = (patches: Patch[], inversePatches: Patch[]) => void
export interface IProduce {

@@ -30,3 +38,4 @@ /**

currentState: S,
recipe: (this: Draft<S>, draftState: Draft<S>) => void | S
recipe: (this: Draft<S>, draftState: Draft<S>) => void | S,
listener?: PatchListener
): S

@@ -104,1 +113,3 @@

export function setUseProxies(useProxies: boolean): void
export function applyPatches<S>(state: S, patches: Patch[]): S

@@ -5,2 +5,88 @@ 'use strict';

function generatePatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
if (patches) if (Array.isArray(baseValue)) generateArrayPatches(state, basepath, patches, inversePatches, baseValue, resultValue);else generateObjectPatches(state, basepath, patches, inversePatches, baseValue, resultValue);
}
function generateArrayPatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
var shared = Math.min(baseValue.length, resultValue.length);
for (var i = 0; i < shared; i++) {
if (state.assigned[i] && baseValue[i] !== resultValue[i]) {
var path = basepath.concat(i);
patches.push({ op: "replace", path: path, value: resultValue[i] });
inversePatches.push({ op: "replace", path: path, value: baseValue[i] });
}
}
if (shared < resultValue.length) {
// stuff was added
for (var _i = shared; _i < resultValue.length; _i++) {
var _path = basepath.concat(_i);
patches.push({ op: "add", path: _path, value: resultValue[_i] });
}
inversePatches.push({
op: "replace",
path: basepath.concat("length"),
value: baseValue.length
});
} else if (shared < baseValue.length) {
// stuff was removed
patches.push({
op: "replace",
path: basepath.concat("length"),
value: resultValue.length
});
for (var _i2 = shared; _i2 < baseValue.length; _i2++) {
var _path2 = basepath.concat(_i2);
inversePatches.push({ op: "add", path: _path2, value: baseValue[_i2] });
}
}
}
function generateObjectPatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
each(state.assigned, function (key, assignedValue) {
var origValue = baseValue[key];
var value = resultValue[key];
var op = !assignedValue ? "remove" : key in baseValue ? "replace" : "add";
if (origValue === baseValue && op === "replace") return;
var path = basepath.concat(key);
patches.push(op === "remove" ? { op: op, path: path } : { op: op, path: path, value: value });
inversePatches.push(op === "add" ? { op: "remove", path: path } : op === "remove" ? { op: "add", path: path, value: origValue } : { op: "replace", path: path, value: origValue });
});
}
function applyPatches(draft, patches) {
var _loop = function _loop(i) {
var patch = patches[i];
if (patch.path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var path = patch.path.slice();
var key = path.pop();
var base = path.reduce(function (current, part) {
if (!current) throw new Error("Cannot apply patch, path doesn't resolve: " + patch.path.join("/"));
return current[part];
}, draft);
if (!base) throw new Error("Cannot apply patch, path doesn't resolve: " + patch.path.join("/"));
switch (patch.op) {
case "replace":
case "add":
// TODO: add support is not extensive, it does not support insertion or `-` atm!
base[key] = patch.value;
break;
case "remove":
if (Array.isArray(base)) {
if (key === base.length - 1) base.length -= 1;else throw new Error("Remove can only remove the last key of an array, index: " + key + ", length: " + base.length);
} else delete base[key];
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
}
};
for (var i = 0; i < patches.length; i++) {
_loop(i);
}
return draft;
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {

@@ -94,3 +180,3 @@ return typeof obj;

// given a base object, returns it if unmodified, or return the changed cloned if modified
function finalize(base) {
function finalize(base, path, patches, inversePatches) {
if (isProxy(base)) {

@@ -101,3 +187,5 @@ var state = base[PROXY_STATE];

state.finalized = true;
return finalizeObject(useProxies ? state.copy : state.copy = shallowCopy(base), state);
var result = finalizeObject(useProxies ? state.copy : state.copy = shallowCopy(base), state, path, patches, inversePatches);
generatePatches(state, path, patches, inversePatches, state.base, result);
return result;
} else {

@@ -111,6 +199,11 @@ return state.base;

function finalizeObject(copy, state) {
function finalizeObject(copy, state, path, patches, inversePatches) {
var base = state.base;
each(copy, function (prop, value) {
if (value !== base[prop]) copy[prop] = finalize(value);
if (value !== base[prop]) {
// if there was an assignment on this property, we don't need to generate
// patches for the subtree
var _generatePatches = patches && !has(state.assigned, prop);
copy[prop] = finalize(value, _generatePatches && path.concat(prop), _generatePatches && patches, inversePatches);
}
});

@@ -177,3 +270,4 @@ return freeze(copy);

return {
modified: false,
modified: false, // this tree is modified (either this object or one of it's children)
assigned: {}, // true: value was assigned to these props, false: was removed
finalized: false,

@@ -209,2 +303,4 @@ parent: parent,

function set$1(state, prop, value) {
// TODO: optimize
state.assigned[prop] = true;
if (!state.modified) {

@@ -219,2 +315,3 @@ if (prop in state.base && is(state.base[prop], value) || has(state.proxies, prop) && state.proxies[prop] === value) return true;

function deleteProperty(state, prop) {
state.assigned[prop] = false;
markChanged(state);

@@ -247,5 +344,5 @@ delete state.copy[prop];

// creates a proxy for plain objects / arrays
function createProxy(parentState, base) {
function createProxy(parentState, base, key) {
if (isProxy(base)) throw new Error("Immer bug. Plz report.");
var state = createState(parentState, base);
var state = createState(parentState, base, key);
var proxy = Array.isArray(base) ? Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps);

@@ -256,3 +353,3 @@ proxies.push(proxy);

function produceProxy(baseState, producer) {
function produceProxy(baseState, producer, patchListener) {
if (isProxy(baseState)) {

@@ -265,2 +362,4 @@ // See #100, don't nest producers

proxies = [];
var patches = patchListener && [];
var inversePatches = patchListener && [];
try {

@@ -282,4 +381,8 @@ // create proxy for root

result = finalize(_returnValue);
if (patches) {
patches.push({ op: "replace", path: [], value: result });
inversePatches.push({ op: "replace", path: [], value: baseState });
}
} else {
result = finalize(rootProxy);
result = finalize(rootProxy, [], patches, inversePatches);
}

@@ -290,2 +393,3 @@ // revoke all proxies

});
patchListener && patchListener(patches, inversePatches);
return result;

@@ -305,2 +409,3 @@ } finally {

modified: false,
assigned: {}, // true: value was assigned to these props, false: was removed
hasCopy: false,

@@ -335,2 +440,3 @@ parent: parent,

assertUnfinished(state);
state.assigned[prop] = true; // optimization; skip this if there is no listener
if (!state.modified) {

@@ -389,3 +495,3 @@ if (is(source$1(state)[prop], value)) return;

// which it is not already know that they are changed (that is, only object for which no known key was changed)
function markChanges() {
function markChangesSweep() {
// intentionally we process the proxies in reverse order;

@@ -404,2 +510,53 @@ // ideally we start by processing leafs in the tree, because if a child has changed, we don't have to check the parent anymore

function markChangesRecursively(object) {
if (!object || (typeof object === "undefined" ? "undefined" : _typeof(object)) !== "object") return;
var state = object[PROXY_STATE];
if (!state) return;
var proxy = state.proxy,
base = state.base;
if (Array.isArray(object)) {
if (hasArrayChanges(state)) {
markChanged$1(state);
state.assigned.length = true;
if (proxy.length < base.length) for (var i = proxy.length; i < base.length; i++) {
state.assigned[i] = false;
} else for (var _i = base.length; _i < proxy.length; _i++) {
state.assigned[_i] = true;
}each(proxy, function (index, child) {
if (!state.assigned[index]) markChangesRecursively(child);
});
}
} else {
var _diffKeys = diffKeys(base, proxy),
added = _diffKeys.added,
removed = _diffKeys.removed;
if (added.length > 0 || removed.length > 0) markChanged$1(state);
each(added, function (_, key) {
state.assigned[key] = true;
});
each(removed, function (_, key) {
state.assigned[key] = false;
});
each(proxy, function (key, child) {
if (!state.assigned[key]) markChangesRecursively(child);
});
}
}
function diffKeys(from, to) {
// TODO: optimize
var a = Object.keys(from);
var b = Object.keys(to);
return {
added: b.filter(function (key) {
return a.indexOf(key) === -1;
}),
removed: a.filter(function (key) {
return b.indexOf(key) === -1;
})
};
}
function hasObjectChanges(state) {

@@ -429,3 +586,3 @@ var baseKeys = Object.keys(state.base);

function produceEs5(baseState, producer) {
function produceEs5(baseState, producer, patchListener) {
if (isProxy(baseState)) {

@@ -438,2 +595,4 @@ // See #100, don't nest producers

states = [];
var patches = patchListener && [];
var inversePatches = patchListener && [];
try {

@@ -448,5 +607,2 @@ // create proxy for root

});
// find and mark all changes (for parts not done yet)
// TODO: store states by depth, to be able guarantee processing leaves first
markChanges();
var result = void 0;

@@ -458,3 +614,11 @@ // check whether the draft was modified and/or a value was returned

result = finalize(_returnValue);
} else result = finalize(rootProxy);
if (patches) {
patches.push({ op: "replace", path: [], value: result });
inversePatches.push({ op: "replace", path: [], value: baseState });
}
} else {
if (patchListener) markChangesRecursively(rootProxy);
markChangesSweep(); // this one is more efficient if we don't need to know which attributes have changed
result = finalize(rootProxy, [], patches, inversePatches);
}
// make sure all proxies become unusable

@@ -464,2 +628,3 @@ each(states, function (_, state) {

});
patchListener && patchListener(patches, inversePatches);
return result;

@@ -504,7 +669,8 @@ } finally {

* @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 produces here
* @returns {any} a new state, or the base state if nothing was modified
*/
function produce(baseState, producer) {
function produce(baseState, producer, patchListener) {
// prettier-ignore
if (arguments.length !== 1 && arguments.length !== 2) throw new Error("produce expects 1 or 2 arguments, got " + arguments.length);
if (arguments.length < 1 || arguments.length > 3) throw new Error("produce expects 1 to 3 arguments, got " + arguments.length);

@@ -534,2 +700,3 @@ // curried invocation

if (typeof producer !== "function") throw new Error("if first argument is not a function, the second argument to produce should be a function");
if (patchListener !== undefined && typeof patchListener !== "function") throw new Error("the third argument of a producer should not be set or a function");
}

@@ -544,9 +711,12 @@

if (!isProxyable(baseState)) throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got " + (typeof baseState === "undefined" ? "undefined" : _typeof(baseState)) + ": \"" + baseState + "\"");
return getUseProxies() ? produceProxy(baseState, producer) : produceEs5(baseState, producer);
return getUseProxies() ? produceProxy(baseState, producer, patchListener) : produceEs5(baseState, producer, patchListener);
}
var applyPatches$1 = produce(applyPatches);
exports.produce = produce;
exports['default'] = produce;
exports.applyPatches = applyPatches$1;
exports.setAutoFreeze = setAutoFreeze;
exports.setUseProxies = setUseProxies;
//# sourceMappingURL=immer.js.map

@@ -0,1 +1,87 @@

function generatePatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
if (patches) if (Array.isArray(baseValue)) generateArrayPatches(state, basepath, patches, inversePatches, baseValue, resultValue);else generateObjectPatches(state, basepath, patches, inversePatches, baseValue, resultValue);
}
function generateArrayPatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
var shared = Math.min(baseValue.length, resultValue.length);
for (var i = 0; i < shared; i++) {
if (state.assigned[i] && baseValue[i] !== resultValue[i]) {
var path = basepath.concat(i);
patches.push({ op: "replace", path: path, value: resultValue[i] });
inversePatches.push({ op: "replace", path: path, value: baseValue[i] });
}
}
if (shared < resultValue.length) {
// stuff was added
for (var _i = shared; _i < resultValue.length; _i++) {
var _path = basepath.concat(_i);
patches.push({ op: "add", path: _path, value: resultValue[_i] });
}
inversePatches.push({
op: "replace",
path: basepath.concat("length"),
value: baseValue.length
});
} else if (shared < baseValue.length) {
// stuff was removed
patches.push({
op: "replace",
path: basepath.concat("length"),
value: resultValue.length
});
for (var _i2 = shared; _i2 < baseValue.length; _i2++) {
var _path2 = basepath.concat(_i2);
inversePatches.push({ op: "add", path: _path2, value: baseValue[_i2] });
}
}
}
function generateObjectPatches(state, basepath, patches, inversePatches, baseValue, resultValue) {
each(state.assigned, function (key, assignedValue) {
var origValue = baseValue[key];
var value = resultValue[key];
var op = !assignedValue ? "remove" : key in baseValue ? "replace" : "add";
if (origValue === baseValue && op === "replace") return;
var path = basepath.concat(key);
patches.push(op === "remove" ? { op: op, path: path } : { op: op, path: path, value: value });
inversePatches.push(op === "add" ? { op: "remove", path: path } : op === "remove" ? { op: "add", path: path, value: origValue } : { op: "replace", path: path, value: origValue });
});
}
function applyPatches(draft, patches) {
var _loop = function _loop(i) {
var patch = patches[i];
if (patch.path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var path = patch.path.slice();
var key = path.pop();
var base = path.reduce(function (current, part) {
if (!current) throw new Error("Cannot apply patch, path doesn't resolve: " + patch.path.join("/"));
return current[part];
}, draft);
if (!base) throw new Error("Cannot apply patch, path doesn't resolve: " + patch.path.join("/"));
switch (patch.op) {
case "replace":
case "add":
// TODO: add support is not extensive, it does not support insertion or `-` atm!
base[key] = patch.value;
break;
case "remove":
if (Array.isArray(base)) {
if (key === base.length - 1) base.length -= 1;else throw new Error("Remove can only remove the last key of an array, index: " + key + ", length: " + base.length);
} else delete base[key];
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
}
};
for (var i = 0; i < patches.length; i++) {
_loop(i);
}
return draft;
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {

@@ -89,3 +175,3 @@ return typeof obj;

// given a base object, returns it if unmodified, or return the changed cloned if modified
function finalize(base) {
function finalize(base, path, patches, inversePatches) {
if (isProxy(base)) {

@@ -96,3 +182,5 @@ var state = base[PROXY_STATE];

state.finalized = true;
return finalizeObject(useProxies ? state.copy : state.copy = shallowCopy(base), state);
var result = finalizeObject(useProxies ? state.copy : state.copy = shallowCopy(base), state, path, patches, inversePatches);
generatePatches(state, path, patches, inversePatches, state.base, result);
return result;
} else {

@@ -106,6 +194,11 @@ return state.base;

function finalizeObject(copy, state) {
function finalizeObject(copy, state, path, patches, inversePatches) {
var base = state.base;
each(copy, function (prop, value) {
if (value !== base[prop]) copy[prop] = finalize(value);
if (value !== base[prop]) {
// if there was an assignment on this property, we don't need to generate
// patches for the subtree
var _generatePatches = patches && !has(state.assigned, prop);
copy[prop] = finalize(value, _generatePatches && path.concat(prop), _generatePatches && patches, inversePatches);
}
});

@@ -172,3 +265,4 @@ return freeze(copy);

return {
modified: false,
modified: false, // this tree is modified (either this object or one of it's children)
assigned: {}, // true: value was assigned to these props, false: was removed
finalized: false,

@@ -204,2 +298,4 @@ parent: parent,

function set$1(state, prop, value) {
// TODO: optimize
state.assigned[prop] = true;
if (!state.modified) {

@@ -214,2 +310,3 @@ if (prop in state.base && is(state.base[prop], value) || has(state.proxies, prop) && state.proxies[prop] === value) return true;

function deleteProperty(state, prop) {
state.assigned[prop] = false;
markChanged(state);

@@ -242,5 +339,5 @@ delete state.copy[prop];

// creates a proxy for plain objects / arrays
function createProxy(parentState, base) {
function createProxy(parentState, base, key) {
if (isProxy(base)) throw new Error("Immer bug. Plz report.");
var state = createState(parentState, base);
var state = createState(parentState, base, key);
var proxy = Array.isArray(base) ? Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps);

@@ -251,3 +348,3 @@ proxies.push(proxy);

function produceProxy(baseState, producer) {
function produceProxy(baseState, producer, patchListener) {
if (isProxy(baseState)) {

@@ -260,2 +357,4 @@ // See #100, don't nest producers

proxies = [];
var patches = patchListener && [];
var inversePatches = patchListener && [];
try {

@@ -277,4 +376,8 @@ // create proxy for root

result = finalize(_returnValue);
if (patches) {
patches.push({ op: "replace", path: [], value: result });
inversePatches.push({ op: "replace", path: [], value: baseState });
}
} else {
result = finalize(rootProxy);
result = finalize(rootProxy, [], patches, inversePatches);
}

@@ -285,2 +388,3 @@ // revoke all proxies

});
patchListener && patchListener(patches, inversePatches);
return result;

@@ -300,2 +404,3 @@ } finally {

modified: false,
assigned: {}, // true: value was assigned to these props, false: was removed
hasCopy: false,

@@ -330,2 +435,3 @@ parent: parent,

assertUnfinished(state);
state.assigned[prop] = true; // optimization; skip this if there is no listener
if (!state.modified) {

@@ -384,3 +490,3 @@ if (is(source$1(state)[prop], value)) return;

// which it is not already know that they are changed (that is, only object for which no known key was changed)
function markChanges() {
function markChangesSweep() {
// intentionally we process the proxies in reverse order;

@@ -399,2 +505,53 @@ // ideally we start by processing leafs in the tree, because if a child has changed, we don't have to check the parent anymore

function markChangesRecursively(object) {
if (!object || (typeof object === "undefined" ? "undefined" : _typeof(object)) !== "object") return;
var state = object[PROXY_STATE];
if (!state) return;
var proxy = state.proxy,
base = state.base;
if (Array.isArray(object)) {
if (hasArrayChanges(state)) {
markChanged$1(state);
state.assigned.length = true;
if (proxy.length < base.length) for (var i = proxy.length; i < base.length; i++) {
state.assigned[i] = false;
} else for (var _i = base.length; _i < proxy.length; _i++) {
state.assigned[_i] = true;
}each(proxy, function (index, child) {
if (!state.assigned[index]) markChangesRecursively(child);
});
}
} else {
var _diffKeys = diffKeys(base, proxy),
added = _diffKeys.added,
removed = _diffKeys.removed;
if (added.length > 0 || removed.length > 0) markChanged$1(state);
each(added, function (_, key) {
state.assigned[key] = true;
});
each(removed, function (_, key) {
state.assigned[key] = false;
});
each(proxy, function (key, child) {
if (!state.assigned[key]) markChangesRecursively(child);
});
}
}
function diffKeys(from, to) {
// TODO: optimize
var a = Object.keys(from);
var b = Object.keys(to);
return {
added: b.filter(function (key) {
return a.indexOf(key) === -1;
}),
removed: a.filter(function (key) {
return b.indexOf(key) === -1;
})
};
}
function hasObjectChanges(state) {

@@ -424,3 +581,3 @@ var baseKeys = Object.keys(state.base);

function produceEs5(baseState, producer) {
function produceEs5(baseState, producer, patchListener) {
if (isProxy(baseState)) {

@@ -433,2 +590,4 @@ // See #100, don't nest producers

states = [];
var patches = patchListener && [];
var inversePatches = patchListener && [];
try {

@@ -443,5 +602,2 @@ // create proxy for root

});
// find and mark all changes (for parts not done yet)
// TODO: store states by depth, to be able guarantee processing leaves first
markChanges();
var result = void 0;

@@ -453,3 +609,11 @@ // check whether the draft was modified and/or a value was returned

result = finalize(_returnValue);
} else result = finalize(rootProxy);
if (patches) {
patches.push({ op: "replace", path: [], value: result });
inversePatches.push({ op: "replace", path: [], value: baseState });
}
} else {
if (patchListener) markChangesRecursively(rootProxy);
markChangesSweep(); // this one is more efficient if we don't need to know which attributes have changed
result = finalize(rootProxy, [], patches, inversePatches);
}
// make sure all proxies become unusable

@@ -459,2 +623,3 @@ each(states, function (_, state) {

});
patchListener && patchListener(patches, inversePatches);
return result;

@@ -499,7 +664,8 @@ } finally {

* @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 produces here
* @returns {any} a new state, or the base state if nothing was modified
*/
function produce(baseState, producer) {
function produce(baseState, producer, patchListener) {
// prettier-ignore
if (arguments.length !== 1 && arguments.length !== 2) throw new Error("produce expects 1 or 2 arguments, got " + arguments.length);
if (arguments.length < 1 || arguments.length > 3) throw new Error("produce expects 1 to 3 arguments, got " + arguments.length);

@@ -529,2 +695,3 @@ // curried invocation

if (typeof producer !== "function") throw new Error("if first argument is not a function, the second argument to produce should be a function");
if (patchListener !== undefined && typeof patchListener !== "function") throw new Error("the third argument of a producer should not be set or a function");
}

@@ -539,7 +706,9 @@

if (!isProxyable(baseState)) throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got " + (typeof baseState === "undefined" ? "undefined" : _typeof(baseState)) + ": \"" + baseState + "\"");
return getUseProxies() ? produceProxy(baseState, producer) : produceEs5(baseState, producer);
return getUseProxies() ? produceProxy(baseState, producer, patchListener) : produceEs5(baseState, producer, patchListener);
}
export { produce, setAutoFreeze, setUseProxies };
var applyPatches$1 = produce(applyPatches);
export { produce, applyPatches$1 as applyPatches, setAutoFreeze, setUseProxies };
export default produce;
//# sourceMappingURL=immer.module.js.map

2

dist/immer.umd.js

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

var e,r;e=this,r=function(e){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n="undefined"!=typeof Symbol?Symbol("immer-proxy-state"):"__$immer_state",t="An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.";var o=!("undefined"!=typeof process&&"production"===process.env.NODE_ENV||"verifyMinified"!==function(){}.name),i="undefined"!=typeof Proxy;function f(e){return!!e&&!!e[n]}function u(e){if(!e)return!1;if("object"!==(void 0===e?"undefined":r(e)))return!1;if(Array.isArray(e))return!0;var n=Object.getPrototypeOf(e);return null===n||n===Object.prototype}function a(e){return o&&Object.freeze(e),e}var c=Object.assign||function(e,r){for(var n in r)p(r,n)&&(e[n]=r[n]);return e};function s(e){if(Array.isArray(e))return e.slice();var r=void 0===e.__proto__?Object.create(null):{};return c(r,e)}function d(e,r){if(Array.isArray(e))for(var n=0;n<e.length;n++)r(n,e[n]);else for(var t in e)r(t,e[t])}function p(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function y(e){if(f(e)){var r=e[n];return!0===r.modified?!0===r.finalized?r.copy:(r.finalized=!0,t=i?r.copy:r.copy=s(e),o=r.base,d(t,function(e,r){r!==o[e]&&(t[e]=y(r))}),a(t)):r.base}var t,o;return function e(r){if(!u(r))return;if(Object.isFrozen(r))return;d(r,function(n,t){f(t)?r[n]=y(t):e(t)});a(r)}(e),e}function l(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}var v=null,b={get:function(e,r){if(r===n)return e;if(e.modified){var t=e.copy[r];return t===e.base[r]&&u(t)?e.copy[r]=w(e,t):t}if(p(e.proxies,r))return e.proxies[r];var o=e.base[r];return!f(o)&&u(o)?e.proxies[r]=w(e,o):o},has:function(e,r){return r in h(e)},ownKeys:function(e){return Reflect.ownKeys(h(e))},set:function(e,r,n){if(!e.modified){if(r in e.base&&l(e.base[r],n)||p(e.proxies,r)&&e.proxies[r]===n)return!0;g(e)}return e.copy[r]=n,!0},deleteProperty:function(e,r){return g(e),delete e.copy[r],!0},getOwnPropertyDescriptor:function(e,r){var n=e.modified?e.copy:p(e.proxies,r)?e.proxies:e.base,t=Reflect.getOwnPropertyDescriptor(n,r);!t||Array.isArray(n)&&"length"===r||(t.configurable=!0);return t},defineProperty:function(){throw new Error("Immer does not support defining properties on draft objects.")},setPrototypeOf:function(){throw new Error("Immer does not support `setPrototypeOf()`.")}},m={};function h(e){return!0===e.modified?e.copy:e.base}function g(e){e.modified||(e.modified=!0,e.copy=s(e.base),Object.assign(e.copy,e.proxies),e.parent&&g(e.parent))}function w(e,r){if(f(r))throw new Error("Immer bug. Plz report.");var n={modified:!1,finalized:!1,parent:e,base:r,copy:void 0,proxies:{}},t=Array.isArray(r)?Proxy.revocable([n],m):Proxy.revocable(n,b);return v.push(t),t.proxy}d(b,function(e,r){m[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}});var O={},j=null;function x(e){return e.hasCopy?e.copy:e.base}function P(e){e.modified||(e.modified=!0,e.parent&&P(e.parent))}function A(e){e.hasCopy||(e.hasCopy=!0,e.copy=s(e.base))}function E(e,r){var t=s(r);d(r,function(e){var r;Object.defineProperty(t,""+e,O[r=""+e]||(O[r]={configurable:!0,enumerable:!0,get:function(){return function(e,r){z(e);var n=x(e)[r];return!e.finalizing&&n===e.base[r]&&u(n)?(A(e),e.copy[r]=E(e,n)):n}(this[n],r)},set:function(e){!function(e,r,n){if(z(e),!e.modified){if(l(x(e)[r],n))return;P(e),A(e)}e.copy[r]=n}(this[n],r,e)}}))});var o,i,f,a={modified:!1,hasCopy:!1,parent:e,base:r,proxy:t,copy:void 0,finished:!1,finalizing:!1,finalized:!1};return o=t,i=n,f=a,Object.defineProperty(o,i,{value:f,enumerable:!1,writable:!0}),j.push(a),t}function z(e){if(!0===e.finished)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(e.copy||e.base))}function _(e){var r=e.proxy;if(r.length!==e.base.length)return!0;var n=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!n||n.get)}function S(e,o){if(f(e)){var i=o.call(e,e);return void 0===i?e:i}var u=j;j=[];try{var a=E(void 0,e),c=o.call(a,a);d(j,function(e,r){r.finalizing=!0}),function(){for(var e=j.length-1;e>=0;e--){var n=j[e];!1===n.modified&&(Array.isArray(n.base)?_(n)&&P(n):(t=n,o=Object.keys(t.base),i=Object.keys(t.proxy),function(e,n){if(l(e,n))return!0;if("object"!==(void 0===e?"undefined":r(e))||null===e||"object"!==(void 0===n?"undefined":r(n))||null===n)return!1;var t=Object.keys(e),o=Object.keys(n);if(t.length!==o.length)return!1;for(var i=0;i<t.length;i++)if(!hasOwnProperty.call(n,t[i])||!l(e[t[i]],n[t[i]]))return!1;return!0}(o,i)||P(n)))}var t,o,i}();var s=void 0;if(void 0!==c&&c!==a){if(a[n].modified)throw new Error(t);s=y(c)}else s=y(a);return d(j,function(e,r){r.finished=!0}),s}finally{j=u}}function k(e,o){if(1!==arguments.length&&2!==arguments.length)throw new Error("produce expects 1 or 2 arguments, got "+arguments.length);if("function"==typeof e){if("function"==typeof o)throw new Error("if first argument is a function (curried invocation), the second argument to produce cannot be a function");var a=o,c=e;return function(){var e=arguments;return k(void 0===e[0]&&void 0!==a?a:e[0],function(r){return e[0]=r,c.apply(r,e)})}}if("function"!=typeof o)throw new Error("if first argument is not a function, the second argument to produce should be a function");if("object"!==(void 0===e?"undefined":r(e))||null===e){var s=o(e);return void 0===s?e:s}if(!u(e))throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got "+(void 0===e?"undefined":r(e))+': "'+e+'"');return i?function(e,r){if(f(e)){var o=r.call(e,e);return void 0===o?e:o}var i=v;v=[];try{var u=w(void 0,e),a=r.call(u,u),c=void 0;if(void 0!==a&&a!==u){if(u[n].modified)throw new Error(t);c=y(a)}else c=y(u);return d(v,function(e,r){return r.revoke()}),c}finally{v=i}}(e,o):S(e,o)}e.produce=k,e.default=k,e.setAutoFreeze=function(e){o=e},e.setUseProxies=function(e){i=e},Object.defineProperty(e,"__esModule",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(e.immer={});
var e,r;e=this,r=function(e){"use strict";function r(e,r,n,t,o,i){var a,f,u,c,p;n&&(Array.isArray(o)?function(e,r,n,t,o,i){for(var a=Math.min(o.length,i.length),f=0;f<a;f++)if(e.assigned[f]&&o[f]!==i[f]){var u=r.concat(f);n.push({op:"replace",path:u,value:i[f]}),t.push({op:"replace",path:u,value:o[f]})}if(a<i.length){for(var c=a;c<i.length;c++){var p=r.concat(c);n.push({op:"add",path:p,value:i[c]})}t.push({op:"replace",path:r.concat("length"),value:o.length})}else if(a<o.length){n.push({op:"replace",path:r.concat("length"),value:i.length});for(var s=a;s<o.length;s++){var d=r.concat(s);t.push({op:"add",path:d,value:o[s]})}}}(e,r,n,t,o,i):(a=r,f=n,u=t,c=o,p=i,d(e.assigned,function(e,r){var n=c[e],t=p[e],o=r?e in c?"replace":"add":"remove";if(n!==c||"replace"!==o){var i=a.concat(e);f.push("remove"===o?{op:o,path:i}:{op:o,path:i,value:t}),u.push("add"===o?{op:"remove",path:i}:"remove"===o?{op:"add",path:i,value:n}:{op:"replace",path:i,value:n})}})))}var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t="undefined"!=typeof Symbol?Symbol("immer-proxy-state"):"__$immer_state",o="An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.";var i=!("undefined"!=typeof process&&"production"===process.env.NODE_ENV||"verifyMinified"!==function(){}.name),a="undefined"!=typeof Proxy;function f(e){return!!e&&!!e[t]}function u(e){if(!e)return!1;if("object"!==(void 0===e?"undefined":n(e)))return!1;if(Array.isArray(e))return!0;var r=Object.getPrototypeOf(e);return null===r||r===Object.prototype}function c(e){return i&&Object.freeze(e),e}var p=Object.assign||function(e,r){for(var n in r)l(r,n)&&(e[n]=r[n]);return e};function s(e){if(Array.isArray(e))return e.slice();var r=void 0===e.__proto__?Object.create(null):{};return p(r,e)}function d(e,r){if(Array.isArray(e))for(var n=0;n<e.length;n++)r(n,e[n]);else for(var t in e)r(t,e[t])}function l(e,r){return Object.prototype.hasOwnProperty.call(e,r)}function h(e,n,o,i){if(f(e)){var p=e[t];if(!0===p.modified){if(!0===p.finalized)return p.copy;p.finalized=!0;var v=(y=a?p.copy:p.copy=s(e),b=n,m=o,w=i,O=(g=p).base,d(y,function(e,r){if(r!==O[e]){var n=m&&!l(g.assigned,e);y[e]=h(r,n&&b.concat(e),n&&m,w)}}),c(y));return r(p,n,o,i,p.base,v),v}return p.base}var y,g,b,m,w,O;return function e(r){if(!u(r))return;if(Object.isFrozen(r))return;d(r,function(n,t){f(t)?r[n]=h(t):e(t)});c(r)}(e),e}function v(e,r){return e===r?0!==e||1/e==1/r:e!=e&&r!=r}var y=null,g={get:function(e,r){if(r===t)return e;if(e.modified){var n=e.copy[r];return n===e.base[r]&&u(n)?e.copy[r]=O(e,n):n}if(l(e.proxies,r))return e.proxies[r];var o=e.base[r];return!f(o)&&u(o)?e.proxies[r]=O(e,o):o},has:function(e,r){return r in m(e)},ownKeys:function(e){return Reflect.ownKeys(m(e))},set:function(e,r,n){if(e.assigned[r]=!0,!e.modified){if(r in e.base&&v(e.base[r],n)||l(e.proxies,r)&&e.proxies[r]===n)return!0;w(e)}return e.copy[r]=n,!0},deleteProperty:function(e,r){return e.assigned[r]=!1,w(e),delete e.copy[r],!0},getOwnPropertyDescriptor:function(e,r){var n=e.modified?e.copy:l(e.proxies,r)?e.proxies:e.base,t=Reflect.getOwnPropertyDescriptor(n,r);!t||Array.isArray(n)&&"length"===r||(t.configurable=!0);return t},defineProperty:function(){throw new Error("Immer does not support defining properties on draft objects.")},setPrototypeOf:function(){throw new Error("Immer does not support `setPrototypeOf()`.")}},b={};function m(e){return!0===e.modified?e.copy:e.base}function w(e){e.modified||(e.modified=!0,e.copy=s(e.base),Object.assign(e.copy,e.proxies),e.parent&&w(e.parent))}function O(e,r,n){if(f(r))throw new Error("Immer bug. Plz report.");var t={modified:!1,assigned:{},finalized:!1,parent:e,base:r,copy:void 0,proxies:{}},o=Array.isArray(r)?Proxy.revocable([t],b):Proxy.revocable(t,g);return y.push(o),o.proxy}d(g,function(e,r){b[e]=function(){return arguments[0]=arguments[0][0],r.apply(this,arguments)}});var j={},x=null;function A(e){return e.hasCopy?e.copy:e.base}function P(e){e.modified||(e.modified=!0,e.parent&&P(e.parent))}function E(e){e.hasCopy||(e.hasCopy=!0,e.copy=s(e.base))}function k(e,r){var n=s(r);d(r,function(e){var r;Object.defineProperty(n,""+e,j[r=""+e]||(j[r]={configurable:!0,enumerable:!0,get:function(){return function(e,r){z(e);var n=A(e)[r];return!e.finalizing&&n===e.base[r]&&u(n)?(E(e),e.copy[r]=k(e,n)):n}(this[t],r)},set:function(e){!function(e,r,n){if(z(e),e.assigned[r]=!0,!e.modified){if(v(A(e)[r],n))return;P(e),E(e)}e.copy[r]=n}(this[t],r,e)}}))});var o,i,a,f={modified:!1,assigned:{},hasCopy:!1,parent:e,base:r,proxy:n,copy:void 0,finished:!1,finalizing:!1,finalized:!1};return o=n,i=t,a=f,Object.defineProperty(o,i,{value:a,enumerable:!1,writable:!0}),x.push(f),n}function z(e){if(!0===e.finished)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(e.copy||e.base))}function _(e){if(e&&"object"===(void 0===e?"undefined":n(e))){var r=e[t];if(r){var o,i,a,f,u=r.proxy,c=r.base;if(Array.isArray(e)){if(S(r)){if(P(r),r.assigned.length=!0,u.length<c.length)for(var p=u.length;p<c.length;p++)r.assigned[p]=!1;else for(var s=c.length;s<u.length;s++)r.assigned[s]=!0;d(u,function(e,n){r.assigned[e]||_(n)})}}else{var l=(o=c,i=u,a=Object.keys(o),{added:(f=Object.keys(i)).filter(function(e){return-1===a.indexOf(e)}),removed:a.filter(function(e){return-1===f.indexOf(e)})}),h=l.added,v=l.removed;(h.length>0||v.length>0)&&P(r),d(h,function(e,n){r.assigned[n]=!0}),d(v,function(e,n){r.assigned[n]=!1}),d(u,function(e,n){r.assigned[e]||_(n)})}}}}function S(e){var r=e.proxy;if(r.length!==e.base.length)return!0;var n=Object.getOwnPropertyDescriptor(r,r.length-1);return!(!n||n.get)}function C(e,r,i){if(f(e)){var a=r.call(e,e);return void 0===a?e:a}var u=x;x=[];var c=i&&[],p=i&&[];try{var s=k(void 0,e),l=r.call(s,s);d(x,function(e,r){r.finalizing=!0});var y=void 0;if(void 0!==l&&l!==s){if(s[t].modified)throw new Error(o);y=h(l),c&&(c.push({op:"replace",path:[],value:y}),p.push({op:"replace",path:[],value:e}))}else i&&_(s),function(){for(var e=x.length-1;e>=0;e--){var r=x[e];!1===r.modified&&(Array.isArray(r.base)?S(r)&&P(r):(t=r,o=Object.keys(t.base),i=Object.keys(t.proxy),function(e,r){if(v(e,r))return!0;if("object"!==(void 0===e?"undefined":n(e))||null===e||"object"!==(void 0===r?"undefined":n(r))||null===r)return!1;var t=Object.keys(e),o=Object.keys(r);if(t.length!==o.length)return!1;for(var i=0;i<t.length;i++)if(!hasOwnProperty.call(r,t[i])||!v(e[t[i]],r[t[i]]))return!1;return!0}(o,i)||P(r)))}var t,o,i}(),y=h(s,[],c,p);return d(x,function(e,r){r.finished=!0}),i&&i(c,p),y}finally{x=u}}function D(e,r,i){if(arguments.length<1||arguments.length>3)throw new Error("produce expects 1 to 3 arguments, got "+arguments.length);if("function"==typeof e){if("function"==typeof r)throw new Error("if first argument is a function (curried invocation), the second argument to produce cannot be a function");var c=r,p=e;return function(){var e=arguments;return D(void 0===e[0]&&void 0!==c?c:e[0],function(r){return e[0]=r,p.apply(r,e)})}}if("function"!=typeof r)throw new Error("if first argument is not a function, the second argument to produce should be a function");if(void 0!==i&&"function"!=typeof i)throw new Error("the third argument of a producer should not be set or a function");if("object"!==(void 0===e?"undefined":n(e))||null===e){var s=r(e);return void 0===s?e:s}if(!u(e))throw new Error("the first argument to an immer producer should be a primitive, plain object or array, got "+(void 0===e?"undefined":n(e))+': "'+e+'"');return a?function(e,r,n){if(f(e)){var i=r.call(e,e);return void 0===i?e:i}var a=y;y=[];var u=n&&[],c=n&&[];try{var p=O(void 0,e),s=r.call(p,p),l=void 0;if(void 0!==s&&s!==p){if(p[t].modified)throw new Error(o);l=h(s),u&&(u.push({op:"replace",path:[],value:l}),c.push({op:"replace",path:[],value:e}))}else l=h(p,[],u,c);return d(y,function(e,r){return r.revoke()}),n&&n(u,c),l}finally{y=a}}(e,r,i):C(e,r,i)}var I=D(function(e,r){for(var n=function(n){var t=r[n];if(0===t.path.length&&"replace"===t.op)e=t.value;else{var o=t.path.slice(),i=o.pop(),a=o.reduce(function(e,r){if(!e)throw new Error("Cannot apply patch, path doesn't resolve: "+t.path.join("/"));return e[r]},e);if(!a)throw new Error("Cannot apply patch, path doesn't resolve: "+t.path.join("/"));switch(t.op){case"replace":case"add":a[i]=t.value;break;case"remove":if(Array.isArray(a)){if(i!==a.length-1)throw new Error("Remove can only remove the last key of an array, index: "+i+", length: "+a.length);a.length-=1}else delete a[i];break;default:throw new Error("Unsupported patch operation: "+t.op)}}},t=0;t<r.length;t++)n(t);return e});e.produce=D,e.default=D,e.applyPatches=I,e.setAutoFreeze=function(e){i=e},e.setUseProxies=function(e){a=e},Object.defineProperty(e,"__esModule",{value:!0})},"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(e.immer={});
//# sourceMappingURL=immer.umd.js.map
{
"name": "immer",
"version": "1.4.0",
"version": "1.5.0",
"description": "Create your next immutable state by mutating the current one",

@@ -14,3 +14,3 @@ "main": "dist/immer.js",

"test": "jest",
"test:perf": "NODE_ENV=production yarn-or-npm build && cd __preformance_tests__ && babel-node add-data.js && babel-node todo.js && babel-node incremental.js",
"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",

@@ -17,0 +17,0 @@ "coveralls": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",

@@ -223,2 +223,83 @@ # Immer

## Patches
During the run of a producer, Immer can record all the patches that would replay the changes made by the reducer.
This is a very powerful tool if you want to fork your state temporarily, and replay the changes to the original.
Patches are useful in few scenarios:
* 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 approach to replay changes on a slightly different state tree
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:
```javascript
import produce, {applyPatches} from "immer"
let state = {
name: "Micheal",
age: 32
}
// Let's assume the user is in a wizard, and we don't know whether
// his changes should be end up in the base state ultimately or not...
let fork = state
// all the changes the user made in the wizard
let changes = []
// the inverse of all the changes made in the wizard
let inverseChanges = []
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)
}
)
// In the mean time, our original state is replaced, as, for example,
// some changes were received from the server
state = produce(state, draft => {
draft.name = "Michel"
})
// When the wizard finishes (successfully) we can replay the changes that were in the fork onto the *new* state!
state = applyPatches(state, changes)
// state now contains the changes from both code paths!
expect(state).toEqual({
name: "Michel", // changed by the server
age: 33 // changed by the wizard
})
// Finally, even after finishing the wizard, the user might change his mind and undo his changes...
state = applyPatches(state, inverseChanges)
expect(state).toEqual({
name: "Michel", // Not reverted
age: 32 // Reverted
})
```
The generated patches are similar (but not the same) to the [RFC-6902 JSON patch standard](http://tools.ietf.org/html/rfc6902), except that the `path` property is an array, rather than a string.
This makes processing patches easier. If you want to normalize to the official specification, `patch.path = patch.path.join("/")` should do the trick. Anyway, this is what a bunch of patches and their inverse could look like:
```json
[
{ "op": "replace", "path": ["profile"], "value": { "name": "Veria", "age": 5 }},
{ "op": "remove", "path": ["tags", 3] }
]
```
```json
[
{ "op": "replace", "path": ["profile"], "value": { "name": "Noa", "age": 6 }},
{ "op": "add", "path": ["tags", 3], "value": "kiddo"},
]
```
## Auto freezing

@@ -356,2 +437,3 @@

* [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_
* ... and [many more](https://www.npmjs.com/browse/depended/immer)

@@ -417,17 +499,14 @@

Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 100.000 todo items, and updates 10.000 of them. _Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree.
Here is a [simple benchmark](__performance_tests__/todo.js) on the performance of Immer. This test takes 50,000 todo items, and updates 5,000 of them. _Freeze_ indicates that the state tree has been frozen after producing it. This is a _development_ best practice, as it prevents developers from accidentally modifying the state tree.
These tests were executed on Node 8.4.0. Use `yarn test:perf` to reproduce them locally.
These tests were executed on Node 9.3.0. Use `yarn test:perf` to reproduce them locally.
![performance.png](images/performance.png)
Some observations:
Most important observation:
* From `immer` perspective, this benchmark is a _worst case_ scenario, because the root collection it has to proxy is really large relatively to the rest of the data set.
* The _mutate_, and _deepclone, mutate_ benchmarks establish a baseline on how expensive changing the data is, without immutability (or structural sharing in the deep clone case).
* The _reducer_ and _naive reducer_ are implemented in typical Redux style reducers. The "smart" implementation slices the collection first, and then maps and freezes only the relevant todos. The "naive" implementation just maps over and processes the entire collection.
* Immer with proxies is roughly speaking twice as slow as a hand written reducer. This is in practice negligible.
* Immer with proxies is roughly speaking twice to three times slower as a hand written reducer (the above test case is worst case, se `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)
* The ES5 implementation of Immer is significantly slower. For most reducers this won't matter, but reducers that process large amounts of data might benefit from not (or only partially) using an Immer producer. Luckily, Immer is fully opt-in.
* The peaks in the _frozen_ versions of _just mutate_, _deepclone_ and _naive reducer_ come from the fact that they recursively freeze the full state tree, while the other test cases only freeze the modified parts of the tree.
* 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.

@@ -434,0 +513,0 @@ ## FAQ

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

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