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

solid-js

Package Overview
Dependencies
Maintainers
1
Versions
463
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

solid-js - npm Package Compare versions

Comparing version 0.3.4 to 0.3.5

test/state.spec.js

278

dist/solid.js

@@ -1,49 +0,33 @@

import S$1 from 's-js';
import S from 's-js';
function comparer(v, k, b, isArray, path, r) {
let index;
const newPath = path.concat([k]);
if (isArray && v != null && typeof v === 'object'
&& k != (index = b.findIndex(i => i && (i === v || (v.id != null && i.id === v.id) || (v._id != null && i._id === v._id))))) {
return r.push(newPath.concat([index > -1 ? b[index] : v]));
}
return r.push.apply(r, diff(v, b[k], newPath));
}
const SNODE = Symbol('solid-node'),
SPROXY = Symbol('solid-proxy');
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); }
function diff(a, b, path = []) {
let i, k, l, len, v;
const r = [];
if (!isWrappable(a) || (b == null)) {
if (a !== b) {
r.push(path.concat([a]));
}
} else if (Array.isArray(a)) {
for (k = i = 0, len = a.length; i < len; k = ++i) {
v = a[k];
if ((b != null ? b[k] : void 0) !== v) comparer(v, k, b, true, path, r);
}
if ((b != null ? b.length : void 0) > a.length) {
l = a.length;
while (l < b.length) {
r.push(path.concat([l, void 0]));
l++;
const proxyTraps = {
get(target, property) {
if (property === '_state') return target;
const value = target[property],
wrappable = isWrappable(value);
if (S.isListening() && typeof value !== 'function') {
let nodes, node;
if (wrappable && (nodes = getDataNodes(value))) {
node = nodes._self || (nodes._self = S.makeDataNode());
node.current();
}
nodes = getDataNodes(target);
node = nodes[property] || (nodes[property] = S.makeDataNode());
node.current();
}
} else {
for (k in a) {
v = a[k];
if ((b != null ? b[k] : void 0) !== v)
comparer(v, k, b, false, path, r);
}
for (k in b) {
v = b[k];
if (!(k in a))
r.push(path.concat([k, void 0]));
}
}
return r;
}
return wrappable ? wrap(value) : value;
},
set() { return true; },
deleteProperty() { return true; }
};
function wrap(value) { return value[SPROXY] || (value[SPROXY] = new Proxy(value, proxyTraps)); }
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); }
function unwrap(item) {

@@ -71,52 +55,2 @@ let result, unwrapped, v;

// export observable
function observable(input) {
if (Symbol.observable in input) return input[Symbol.observable]();
return {
subscribe(observer) {
if (!(observer instanceof Object) || observer == null) {
throw new TypeError('Expected the observer to be an object.');
}
observer = observer.next || observer;
let complete = false;
S.on(input, function next() {
if (complete) return;
observer(input());
});
return {
unsubscribe() { complete = true; }
};
},
[Symbol.observable]() { return this; }
};
}
const SNODE = Symbol('solid-node'),
SPROXY = Symbol('solid-proxy');
const proxyTraps = {
get(target, property) {
if (property === '_state') return target;
const value = target[property],
wrappable = isWrappable(value);
if (S$1.isListening() && typeof value !== 'function') {
let nodes, node;
if (wrappable && (nodes = getDataNodes(value))) {
node = nodes._self || (nodes._self = S$1.makeDataNode());
node.current();
}
nodes = getDataNodes(target);
node = nodes[property] || (nodes[property] = S$1.makeDataNode());
node.current();
}
return wrappable ? wrap(value) : value;
},
set() { return true; },
deleteProperty() { return true; }
};
function wrap(value) { return value[SPROXY] || (value[SPROXY] = new Proxy(value, proxyTraps)); }
function getDataNodes(target) {

@@ -128,11 +62,9 @@ let nodes = target[SNODE];

function setProperty(state, property, value) {
function setProperty(state, property, value, force) {
value = unwrap(value);
if (state[property] === value) return;
if (!force && state[property] === value) return;
const notify = Array.isArray(state) || !(property in state);
if (value === void 0) {
delete state[property];
if (Array.isArray(state)) state.length -= 1;
}
else state[property] = value;
} else state[property] = value;
let nodes = getDataNodes(state), node;

@@ -151,3 +83,3 @@ (node = nodes[property]) && node.next();

function updatePath(current, path, traversed = [], replace) {
function updatePath(current, path, traversed = []) {
if (path.length === 1) {

@@ -157,8 +89,4 @@ let value = path[0];

value = value(wrap(current), traversed);
// deep map
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i += 1)
updatePath(current, value[i], traversed, true);
return;
}
// reconciled
if (value === undefined) return;
}

@@ -175,7 +103,7 @@ return mergeState(current, value);

for (let i = 0; i < part.length; i++)
updatePath(current, [part[i]].concat(path), traversed.concat([part[i]]), replace);
updatePath(current, [part[i]].concat(path), traversed.concat([part[i]]));
} else if (isArray && partType === 'function') {
// Ex. update('data', i => i.id === 42, 'label', l => l + ' !!!');
for (let i = 0; i < current.length; i++)
if (part(current[i], i)) updatePath(current[i], path.slice(0), traversed.concat([i]), replace);
if (part(current[i], i)) updatePath(current[i], path.slice(0), traversed.concat([i]));
} else if (isArray && partType === 'object') {

@@ -185,7 +113,7 @@ // Ex. update('data', { from: 3, to: 12, by: 2 }, 'label', l => l + ' !!!');

for (let i = from; i <= to; i += by)
updatePath(current[i], path.slice(0), traversed.concat([i]), replace);
updatePath(current[i], path.slice(0), traversed.concat([i]));
} else if (isArray && part === '*') {
// Ex. update('data', '*', 'label', l => l + ' !!!');
for (let i = 0; i < current.length; i++)
updatePath(current, [i].concat(path), traversed.concat([i]), replace);
updatePath(current, [i].concat(path), traversed.concat([i]));
} else if (path.length === 1) {

@@ -197,6 +125,6 @@ let value = path[0];

}
if (!replace && isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value))
if (isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value))
return mergeState(current[part], value);
return setProperty(current, part, value);
} else updatePath(current[part], path, traversed.concat([part]), replace);
} else updatePath(current[part], path, traversed.concat([part]));
}

@@ -211,3 +139,3 @@

const args = arguments;
S$1.freeze(() => {
S.freeze(() => {
if (Array.isArray(args[0])) {

@@ -221,9 +149,93 @@ for (let i = 0; i < args.length; i += 1)

function reconcile() {
const path = Array.prototype.slice.call(arguments, 0, -1),
const DEFAULT = 'default',
MERGE = 'merge',
FORCE = 'force';
function applyState(target, parent, property, mode, key) {
let previous = parent[property], force = mode === FORCE;
if (!force && target === previous) return;
if (!isWrappable(target) || (previous == null)) {
return (force || target !== previous) && setProperty(parent, property, target, force);
}
if (Array.isArray(target)) {
if (target.length && previous.length && (mode === DEFAULT
|| (key && mode === MERGE && target[0][key] != null))) {
// skip common prefix and suffix
let i, j, start, end, newEnd, item, newIndicesNext, keyVal,
temp = new Array(target.length),
newIndices = new Map();
for (start = 0, end = Math.min(previous.length, target.length); start < end && (previous[start] === target[start] || key && previous[start][key] === target[start][key]); start++)
applyState(target[start], previous, start, mode, key);
for (end = previous.length - 1, newEnd = target.length - 1; end >= 0 && newEnd >= 0 && (previous[end] === target[newEnd] || key && previous[end][key] === target[newEnd][key]); end--, newEnd--)
temp[newEnd] = previous[end];
// prepare a map of all indices in target
newIndicesNext = new Array(newEnd + 1);
for (j = newEnd; j >= start; j--) {
item = target[j];
keyVal = key ? item[key] : item;
i = newIndices.get(keyVal);
newIndicesNext[j] = i === undefined ? -1 : i;
newIndices.set(keyVal, j);
}
// step through all old items to check reuse
for (i = start; i <= end; i++) {
item = previous[i];
keyVal = key ? item[key] : item;
j = newIndices.get(keyVal);
if (j !== undefined && j !== -1) {
temp[j] = mapped[i];
j = newIndicesNext[j];
newIndices.set(keyVal, j);
}
}
// set all the new values
for (j = start; j < target.length; j++) {
if (temp.hasOwnProperty(j)) {
setProperty(previous, j, temp[j]);
applyState(target[j], previous, j, mode, key);
}
else setProperty(previous, j, target[j]);
}
} else {
for (let i = 0, len = target.length; i < len; i++) {
applyState(target[i], previous, i, mode, key);
}
}
if (previous.length > target.length) setProperty(previous, 'length', target.length);
return;
}
const targetKeys = Object.keys(target);
for (let i = 0, len = targetKeys.length; i < len; i++) {
applyState(target[targetKeys[i]], previous, targetKeys[i], mode, key);
}
const previousKeys = Object.keys(previous);
for (let i = 0, len = previousKeys.length; i < len; i++) {
if (target[previousKeys[i]] === undefined) setProperty(previous, previousKeys[i], undefined);
}
}
// Diff method for setState
function reconcile(path, options = {}) {
let value;
if (Array.isArray(path)) {
value = path.pop();
} else if (typeof path === 'object') {
value = path;
path = undefined;
} else {
path = Array.prototype.slice.call(arguments, 0, -1),
value = arguments[arguments.length - 1];
options = {};
}
const mode = options.mode !== undefined ? options.mode : DEFAULT,
key = options.key !== undefined ? options.key : 'id';
return state => {
state = unwrap(state);
for (let i = 0; i < path.length; i += 1) state = state[path[i]];
return diff(value, state, path);
if (path) {
for (let i = 0; i < path.length - 1; i += 1) state = state[path[i]];
applyState(value, state, path[path.length - 1], mode, key);
} else applyState(value, { state }, 'state', mode, key);
}

@@ -233,3 +245,3 @@ }

function useSignal(value, comparator) {
const d = S$1.makeDataNode(value);
const d = S.makeDataNode(value);
let setter;

@@ -252,11 +264,33 @@ if (comparator) {

function useMemo(fn, seed) { return S$1(fn, seed); }
function useMemo(fn, seed) { return S(fn, seed); }
function useEffect(fn, deps, defer) {
if (!deps) return S$1.makeComputationNode(fn);
S$1.on(deps, fn, undefined, defer);
if (!deps) return S.makeComputationNode(fn);
S.on(deps, fn, undefined, defer);
}
const { root, cleanup: useCleanup, sample, freeze } = S$1;
// export observable
function observable(input) {
if (Symbol.observable in input) return input[Symbol.observable]();
return {
subscribe(observer) {
if (!(observer instanceof Object) || observer == null) {
throw new TypeError('Expected the observer to be an object.');
}
observer = observer.next || observer;
let complete = false;
S.on(input, function next() {
if (complete) return;
observer(input());
});
return {
unsubscribe() { complete = true; }
};
},
[Symbol.observable]() { return this; }
};
}
export { root, useCleanup, sample, freeze, unwrap, observable, useState, reconcile, useSignal, useMemo, useEffect };
const { root, cleanup: useCleanup, sample, freeze } = S;
export { root, useCleanup, sample, freeze, useState, unwrap, reconcile, useSignal, useMemo, useEffect, observable };

@@ -25,6 +25,22 @@ # State

This can be used to do deep diffs by producing the list of changes to apply from a new State value. This is useful when pulling in immutable data trees from stores to ensure the least amount of mutations to your state. It can also be used to replace the all keys on the base state object if no path is provided as it does both positive and negative diff.
This can be used to do deep diffs by applying the changes from a new State value. This is useful when pulling in immutable data trees from stores to ensure the least amount of mutations to your state. It can also be used to replace the all keys on the base state object if no path is provided as it does both positive and negative diff.
```js
setState(reconcile('users', store.get('users')))
```
```
If you pass as array you can configure the diff algorithm with an options object:
```js
setState(reconcile(
['users', store.get('users')],
{
mode: 'force' // type of comparison - default: 'default'
key: '_id' // does a keyed comparison on arrays with key - default: 'id'
}
))
```
<b>Modes</b>
* default: tries to detect array position changes by ref when not keyed
* merge: overwrites rather than detects array position changes when not keyed
* force: updates everything assuming 100% dirty

@@ -7,50 +7,34 @@ 'use strict';

var S$1 = _interopDefault(require('s-js'));
var S = _interopDefault(require('s-js'));
function comparer(v, k, b, isArray, path, r) {
let index;
const newPath = path.concat([k]);
if (isArray && v != null && typeof v === 'object'
&& k != (index = b.findIndex(i => i && (i === v || (v.id != null && i.id === v.id) || (v._id != null && i._id === v._id))))) {
return r.push(newPath.concat([index > -1 ? b[index] : v]));
}
return r.push.apply(r, diff(v, b[k], newPath));
}
const SNODE = Symbol('solid-node'),
SPROXY = Symbol('solid-proxy');
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); }
function diff(a, b, path = []) {
let i, k, l, len, v;
const r = [];
if (!isWrappable(a) || (b == null)) {
if (a !== b) {
r.push(path.concat([a]));
}
} else if (Array.isArray(a)) {
for (k = i = 0, len = a.length; i < len; k = ++i) {
v = a[k];
if ((b != null ? b[k] : void 0) !== v) comparer(v, k, b, true, path, r);
}
if ((b != null ? b.length : void 0) > a.length) {
l = a.length;
while (l < b.length) {
r.push(path.concat([l, void 0]));
l++;
const proxyTraps = {
get(target, property) {
if (property === '_state') return target;
const value = target[property],
wrappable = isWrappable(value);
if (S.isListening() && typeof value !== 'function') {
let nodes, node;
if (wrappable && (nodes = getDataNodes(value))) {
node = nodes._self || (nodes._self = S.makeDataNode());
node.current();
}
nodes = getDataNodes(target);
node = nodes[property] || (nodes[property] = S.makeDataNode());
node.current();
}
} else {
for (k in a) {
v = a[k];
if ((b != null ? b[k] : void 0) !== v)
comparer(v, k, b, false, path, r);
}
for (k in b) {
v = b[k];
if (!(k in a))
r.push(path.concat([k, void 0]));
}
}
return r;
}
return wrappable ? wrap(value) : value;
},
set() { return true; },
deleteProperty() { return true; }
};
function wrap(value) { return value[SPROXY] || (value[SPROXY] = new Proxy(value, proxyTraps)); }
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); }
function unwrap(item) {

@@ -78,52 +62,2 @@ let result, unwrapped, v;

// export observable
function observable(input) {
if (Symbol.observable in input) return input[Symbol.observable]();
return {
subscribe(observer) {
if (!(observer instanceof Object) || observer == null) {
throw new TypeError('Expected the observer to be an object.');
}
observer = observer.next || observer;
let complete = false;
S.on(input, function next() {
if (complete) return;
observer(input());
});
return {
unsubscribe() { complete = true; }
};
},
[Symbol.observable]() { return this; }
};
}
const SNODE = Symbol('solid-node'),
SPROXY = Symbol('solid-proxy');
const proxyTraps = {
get(target, property) {
if (property === '_state') return target;
const value = target[property],
wrappable = isWrappable(value);
if (S$1.isListening() && typeof value !== 'function') {
let nodes, node;
if (wrappable && (nodes = getDataNodes(value))) {
node = nodes._self || (nodes._self = S$1.makeDataNode());
node.current();
}
nodes = getDataNodes(target);
node = nodes[property] || (nodes[property] = S$1.makeDataNode());
node.current();
}
return wrappable ? wrap(value) : value;
},
set() { return true; },
deleteProperty() { return true; }
};
function wrap(value) { return value[SPROXY] || (value[SPROXY] = new Proxy(value, proxyTraps)); }
function getDataNodes(target) {

@@ -135,11 +69,9 @@ let nodes = target[SNODE];

function setProperty(state, property, value) {
function setProperty(state, property, value, force) {
value = unwrap(value);
if (state[property] === value) return;
if (!force && state[property] === value) return;
const notify = Array.isArray(state) || !(property in state);
if (value === void 0) {
delete state[property];
if (Array.isArray(state)) state.length -= 1;
}
else state[property] = value;
} else state[property] = value;
let nodes = getDataNodes(state), node;

@@ -158,3 +90,3 @@ (node = nodes[property]) && node.next();

function updatePath(current, path, traversed = [], replace) {
function updatePath(current, path, traversed = []) {
if (path.length === 1) {

@@ -164,8 +96,4 @@ let value = path[0];

value = value(wrap(current), traversed);
// deep map
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i += 1)
updatePath(current, value[i], traversed, true);
return;
}
// reconciled
if (value === undefined) return;
}

@@ -182,7 +110,7 @@ return mergeState(current, value);

for (let i = 0; i < part.length; i++)
updatePath(current, [part[i]].concat(path), traversed.concat([part[i]]), replace);
updatePath(current, [part[i]].concat(path), traversed.concat([part[i]]));
} else if (isArray && partType === 'function') {
// Ex. update('data', i => i.id === 42, 'label', l => l + ' !!!');
for (let i = 0; i < current.length; i++)
if (part(current[i], i)) updatePath(current[i], path.slice(0), traversed.concat([i]), replace);
if (part(current[i], i)) updatePath(current[i], path.slice(0), traversed.concat([i]));
} else if (isArray && partType === 'object') {

@@ -192,7 +120,7 @@ // Ex. update('data', { from: 3, to: 12, by: 2 }, 'label', l => l + ' !!!');

for (let i = from; i <= to; i += by)
updatePath(current[i], path.slice(0), traversed.concat([i]), replace);
updatePath(current[i], path.slice(0), traversed.concat([i]));
} else if (isArray && part === '*') {
// Ex. update('data', '*', 'label', l => l + ' !!!');
for (let i = 0; i < current.length; i++)
updatePath(current, [i].concat(path), traversed.concat([i]), replace);
updatePath(current, [i].concat(path), traversed.concat([i]));
} else if (path.length === 1) {

@@ -204,6 +132,6 @@ let value = path[0];

}
if (!replace && isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value))
if (isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value))
return mergeState(current[part], value);
return setProperty(current, part, value);
} else updatePath(current[part], path, traversed.concat([part]), replace);
} else updatePath(current[part], path, traversed.concat([part]));
}

@@ -218,3 +146,3 @@

const args = arguments;
S$1.freeze(() => {
S.freeze(() => {
if (Array.isArray(args[0])) {

@@ -228,9 +156,93 @@ for (let i = 0; i < args.length; i += 1)

function reconcile() {
const path = Array.prototype.slice.call(arguments, 0, -1),
const DEFAULT = 'default',
MERGE = 'merge',
FORCE = 'force';
function applyState(target, parent, property, mode, key) {
let previous = parent[property], force = mode === FORCE;
if (!force && target === previous) return;
if (!isWrappable(target) || (previous == null)) {
return (force || target !== previous) && setProperty(parent, property, target, force);
}
if (Array.isArray(target)) {
if (target.length && previous.length && (mode === DEFAULT
|| (key && mode === MERGE && target[0][key] != null))) {
// skip common prefix and suffix
let i, j, start, end, newEnd, item, newIndicesNext, keyVal,
temp = new Array(target.length),
newIndices = new Map();
for (start = 0, end = Math.min(previous.length, target.length); start < end && (previous[start] === target[start] || key && previous[start][key] === target[start][key]); start++)
applyState(target[start], previous, start, mode, key);
for (end = previous.length - 1, newEnd = target.length - 1; end >= 0 && newEnd >= 0 && (previous[end] === target[newEnd] || key && previous[end][key] === target[newEnd][key]); end--, newEnd--)
temp[newEnd] = previous[end];
// prepare a map of all indices in target
newIndicesNext = new Array(newEnd + 1);
for (j = newEnd; j >= start; j--) {
item = target[j];
keyVal = key ? item[key] : item;
i = newIndices.get(keyVal);
newIndicesNext[j] = i === undefined ? -1 : i;
newIndices.set(keyVal, j);
}
// step through all old items to check reuse
for (i = start; i <= end; i++) {
item = previous[i];
keyVal = key ? item[key] : item;
j = newIndices.get(keyVal);
if (j !== undefined && j !== -1) {
temp[j] = mapped[i];
j = newIndicesNext[j];
newIndices.set(keyVal, j);
}
}
// set all the new values
for (j = start; j < target.length; j++) {
if (temp.hasOwnProperty(j)) {
setProperty(previous, j, temp[j]);
applyState(target[j], previous, j, mode, key);
}
else setProperty(previous, j, target[j]);
}
} else {
for (let i = 0, len = target.length; i < len; i++) {
applyState(target[i], previous, i, mode, key);
}
}
if (previous.length > target.length) setProperty(previous, 'length', target.length);
return;
}
const targetKeys = Object.keys(target);
for (let i = 0, len = targetKeys.length; i < len; i++) {
applyState(target[targetKeys[i]], previous, targetKeys[i], mode, key);
}
const previousKeys = Object.keys(previous);
for (let i = 0, len = previousKeys.length; i < len; i++) {
if (target[previousKeys[i]] === undefined) setProperty(previous, previousKeys[i], undefined);
}
}
// Diff method for setState
function reconcile(path, options = {}) {
let value;
if (Array.isArray(path)) {
value = path.pop();
} else if (typeof path === 'object') {
value = path;
path = undefined;
} else {
path = Array.prototype.slice.call(arguments, 0, -1),
value = arguments[arguments.length - 1];
options = {};
}
const mode = options.mode !== undefined ? options.mode : DEFAULT,
key = options.key !== undefined ? options.key : 'id';
return state => {
state = unwrap(state);
for (let i = 0; i < path.length; i += 1) state = state[path[i]];
return diff(value, state, path);
if (path) {
for (let i = 0; i < path.length - 1; i += 1) state = state[path[i]];
applyState(value, state, path[path.length - 1], mode, key);
} else applyState(value, { state }, 'state', mode, key);
}

@@ -240,3 +252,3 @@ }

function useSignal(value, comparator) {
const d = S$1.makeDataNode(value);
const d = S.makeDataNode(value);
let setter;

@@ -259,11 +271,33 @@ if (comparator) {

function useMemo(fn, seed) { return S$1(fn, seed); }
function useMemo(fn, seed) { return S(fn, seed); }
function useEffect(fn, deps, defer) {
if (!deps) return S$1.makeComputationNode(fn);
S$1.on(deps, fn, undefined, defer);
if (!deps) return S.makeComputationNode(fn);
S.on(deps, fn, undefined, defer);
}
const { root, cleanup: useCleanup, sample, freeze } = S$1;
// export observable
function observable(input) {
if (Symbol.observable in input) return input[Symbol.observable]();
return {
subscribe(observer) {
if (!(observer instanceof Object) || observer == null) {
throw new TypeError('Expected the observer to be an object.');
}
observer = observer.next || observer;
let complete = false;
S.on(input, function next() {
if (complete) return;
observer(input());
});
return {
unsubscribe() { complete = true; }
};
},
[Symbol.observable]() { return this; }
};
}
const { root, cleanup: useCleanup, sample, freeze } = S;
exports.root = root;

@@ -273,5 +307,4 @@ exports.useCleanup = useCleanup;

exports.freeze = freeze;
exports.useState = useState;
exports.unwrap = unwrap;
exports.observable = observable;
exports.useState = useState;
exports.reconcile = reconcile;

@@ -281,1 +314,2 @@ exports.useSignal = useSignal;

exports.useEffect = useEffect;
exports.observable = observable;
{
"name": "solid-js",
"description": "A declarative JavaScript library for building user interfaces.",
"version": "0.3.4",
"version": "0.3.5",
"author": "Ryan Carniato",

@@ -27,5 +27,5 @@ "license": "MIT",

"jest": "~23.6.0",
"rollup": "^1.0.0",
"rollup": "^1.1.0",
"rollup-plugin-node-resolve": "^4.0.0"
}
}
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