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

module-grapher

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

module-grapher - npm Package Compare versions

Comparing version 0.10.3 to 0.10.4

.npmignore

44

lib/dependency-resolver.js

@@ -133,3 +133,3 @@ var identifier = require('./identifier'),

// for `foo`.
var indexId = module.identifier.toDirIdentifier();
var indexId = identifier.fromDirIdentifier(module.identifier);

@@ -146,5 +146,3 @@ // Looks to see if there already exists a module with this id.

err = new Error(msg);
err.file = module.fullPath;
err.longDesc = err.toString() + '\n in ' + file;
err.toString = function() { return err.longDesc; };
_addFileToError(err, module.fullPath);
cont(err);

@@ -242,7 +240,8 @@ } else {

parserOutput.forEach(function(arg) {
var err, ident;
// Check if the call to require has arguments.
var ident;
if (!arg) {
err = new TypeError('Empty require call');
} else if (arg[0] != 'string') { // Check if the identifier is a string.
throw _createTypeError('Empty require call', file);
}
if (arg[0] != 'string') {
// If dynamic identifiers are allowed just log.

@@ -256,3 +255,4 @@ // Dynamic identifiers might be useful to require modules

// Get actual source code to throw more meaningful errors.
err = new TypeError('Cannot resolve dynamic module identifiers: ' + this.parser.astToSrcCode(arg));
var msg = 'Cannot resolve dynamic module identifiers: ' + this.parser.astToSrcCode(arg);
throw _createTypeError(msg, file);
}

@@ -265,3 +265,3 @@ } else {

if (!ident.isValid()) {
err = new TypeError('Invalid module identifier: ' + ident);
throw _createTypeError('Invalid module identifier: ' + ident, file);
}

@@ -272,13 +272,7 @@ // Try resoving the identifer.

} catch(e) {
err = e;
_addFileToError(e, file);
throw e;
}
}
if (err) {
err.file = file;
err.longDesc = err.toString() + '\n in ' + file;
err.toString = function() { return err.longDesc; };
throw err;
}
if (ident) {

@@ -297,1 +291,15 @@ results.push(ident);

}
function _createTypeError(msg, file) {
var err = new TypeError(msg);
_addFileToError(err, file);
Error.captureStackTrace(err, _createTypeError);
return err;
}
function _addFileToError(err, file) {
err.file = file;
err.longDesc = err.toString() + '\n in ' + file;
err.toString = function() { return err.longDesc; };
Error.captureStackTrace(err, _addFileToError);
}

@@ -88,9 +88,2 @@ exports.Identifier = Identifier;

p.toDirIdentifier = toDirIdentifier;
function toDirIdentifier() {
var terms = this.toArray();
terms.push('index');
return createIdentifier(terms);
}
p.toString = toString;

@@ -106,2 +99,9 @@ function toString() {

return new Identifier(terms);
}
exports.fromDirIdentifier = fromDirIdentifier;
function fromDirIdentifier(ident) {
var terms = ident.toArray();
terms.push('index');
return createIdentifier(terms);
}

@@ -25,2 +25,5 @@ exports.createPackage = createPackage;

p.main = null;
p.paths = null;
p.extensions = null;
p.allowDirModules = false
p._modules = null;

@@ -31,6 +34,13 @@

this.descriptorFileData = data;
var directories = data && data.directories;
var directories = data && data.directories,
custom = data && data.modulr;
this.lib = (directories && directories.lib) || './lib';
this.main = data.main || './index';
if (custom) {
this.paths = custom.paths || null;
this.extensions = custom.extensions || null;
this.allowDirModules = !!custom.allowDirModules;
}
}

@@ -37,0 +47,0 @@

@@ -129,4 +129,4 @@ /*

// Iterate over each paths supplied in `config`.
serialize.forEach(this.config.paths, function(currentPath, checkNextPath) {
var p = path.resolve(self.config.root, currentPath, id),
serialize.forEach(this.paths, function(currentPath, checkNextPath) {
var p = path.resolve(self.root, currentPath, id),
descriptorFile = path.join(p, 'package.json');

@@ -153,3 +153,3 @@ fs.readFile(descriptorFile, 'utf8', function(err, data) {

pkgInst.fullPath = p;
pkgInst.relativePath = path.relative(self.config.root, p);
pkgInst.relativePath = path.relative(self.root, p);
pkgInst.descriptorFile = descriptorFile;

@@ -156,0 +156,0 @@ pkgInst.setDescriptorFileData(data);

var path = require('path'),
pathHelpers = require('./path-helpers'),
fs = require('fs'),

@@ -14,31 +13,36 @@ serialize = require('async-it').serial;

function SrcResolver(config) {
this.config = this.normalizeConfig(config);
this.allowDirModules = config.allowDirModules;
this.setExtensions(config.extensions);
this.setPaths(config.paths);
this.setRoot(config.root);
}
(function(p) {
p.config = null;
p.allowDirModules = null;
p.extensions = null;
p.paths = null;
p.root = null;
p.normalizeConfig = normalizeConfig;
function normalizeConfig(input) {
input = input || {};
var output = {},
exts = input.extensions || ['.js', '.coffee'];
p.setExtensions = setExtensions;
function setExtensions(extensions) {
extensions = extensions || ['.js', '.coffee'];
this.extensions = extensions.map(function(ext) {
ext = ext || '';
return ext.indexOf('.') === 0 ? ext : '.' + ext;
});
}
for (var prop in input) {
output[prop] = input[prop];
p.setPaths = setPaths;
function setPaths(paths) {
this.paths = (paths || ['.']).slice(0); // clone it
if (this.paths.indexOf('.') < 0) {
this.paths.push('.');
}
output.extensions = this.normalizeExtensions(exts);
output.paths = (output.paths || ['.']).slice(0); // clone it
if (output.paths.indexOf('.') < 0) { output.paths.push('.'); }
output.root = input.root || process.cwd();
return output;
}
p.normalizeExtensions = normalizeExtensions;
function normalizeExtensions(extensions) {
return extensions.map(pathHelpers.normalizeExt);
p.setRoot = setRoot;
function setRoot(root) {
this.root = root || process.cwd();
}
p.resolve = resolve;

@@ -54,4 +58,4 @@ function resolve(module, callback) {

// Iterate over each paths supplied in `config`.
serialize.forEach(this.config.paths, function(currentPath, checkNextPath) {
var resolvedPath = path.resolve(self.config.root, currentPath, relativePath);
serialize.forEach(this.paths, function(currentPath, checkNextPath) {
var resolvedPath = path.resolve(self.root, currentPath, relativePath);
self.resolveExtension(resolvedPath, module, function(p, isDir) {

@@ -75,10 +79,10 @@ if (p) {

var err = new Error('Cannot find module: ' + module),
exts = '.[' + self.config.extensions.map(function(e) { return e.substring(1); }).join('|') + ']';
exts = '.[' + self.extensions.map(function(e) { return e.substring(1); }).join('|') + ']';
err.file = module.lastRequiredBy ? module.lastRequiredBy.fullPath : '@'; // firebug convention
err.longDesc = err.toString() + '\n in ' + err.file + '\nTried looking for it in the following files:';
self.config.paths.forEach(function(searchPath) {
err.longDesc +='\n ' + path.resolve(self.config.root, searchPath, relativePath + exts);
if (self.config.allowDirModules) {
err.longDesc +='\n ' + path.resolve(self.config.root, searchPath, relativePath, 'index' + exts);
self.paths.forEach(function(searchPath) {
err.longDesc +='\n ' + path.resolve(self.root, searchPath, relativePath + exts);
if (self.allowDirModules) {
err.longDesc +='\n ' + path.resolve(self.root, searchPath, relativePath, 'index' + exts);
}

@@ -94,3 +98,3 @@ });

var self = this;
serialize.forEach(this.config.extensions, function(ext, checkNextExtension) {
serialize.forEach(this.extensions, function(ext, checkNextExtension) {
var p = fullPath + ext;

@@ -101,6 +105,6 @@ fs.stat(p, function(err, stats) {

module.fullPath = p;
module.relativePath = path.relative(self.config.root, p);
module.relativePath = path.relative(self.root, p);
module.ext = ext;
callback(p, false);
} else if (self.config.allowDirModules) {
} else if (self.allowDirModules) {
// look for [modName]/index[.ext]

@@ -112,3 +116,3 @@ p = path.join(fullPath, 'index') + ext;

module.fullPath = p;
module.relativePath = path.relative(self.config.root, p);
module.relativePath = path.relative(self.root, p);
module.ext = ext;

@@ -115,0 +119,0 @@ callback(p, true);

@@ -5,3 +5,3 @@ {

"main": "./main",
"version": "0.10.3",
"version": "0.10.4",
"dependencies": {

@@ -38,3 +38,4 @@ "async-it": ">=0.2.0",

"node": "*"
}
}
},
"optionalDependencies": {}
}

@@ -0,148 +1,121 @@

var assert = require('./custom-asserts');
var identifier = require('../lib/identifier'),
create = identifier.create;
exports.testIsValidReturnsTrueForValidIdentifiers = function(assert) {
assert.strictEqual(true, create('foo').isValid());
assert.strictEqual(true, create('./foo').isValid());
assert.strictEqual(true, create('../foo').isValid());
assert.strictEqual(true, create('./foo/../B-A-R/../1_2_3').isValid());
assert.done();
}
suite('Identifier', function() {
test("require('identifier').fromDirIdentifier returns a clone of the identifier with index added to it.", function() {
var ident = create('./foo/bar');
var dirIdent = identifier.fromDirIdentifier(ident);
assert.notStrictEqual(ident, dirIdent);
assert.deepEqual(['.', 'foo', 'bar', 'index'], dirIdent.terms);
});
exports.testIsValidReturnsFalseForInvalidIdentifiers = function(assert) {
assert.strictEqual(false, create('.../foo').isValid());
assert.strictEqual(false, create('foo.js').isValid());
assert.strictEqual(false, create('f@@').isValid());
assert.done();
};
test("Identifier#isValid returns true for valid identifiers", function() {
assert.isTrue(create('foo').isValid());
assert.isTrue(create('./foo').isValid());
assert.isTrue(create('../foo').isValid());
assert.isTrue(create('./foo/../B-A-R/../1_2_3').isValid());
});
exports.testIsRelativeReturnsTrueForRelativeIdentifiers = function(assert) {
assert.strictEqual(true, create('../foo').isRelative());
assert.strictEqual(true, create('./foo').isRelative());
assert.strictEqual(true, create('../../foo').isRelative());
assert.done();
};
test("Identifier#isValid returns false for invalid identifiers", function() {
assert.isFalse(create('.../foo').isValid());
assert.isFalse(create('foo.js').isValid());
assert.isFalse(create('f@@').isValid());
});
exports.testIsRelativeReturnsFalseForTopLevelIdentifiers = function(assert) {
assert.strictEqual(false, create('foo').isRelative());
assert.strictEqual(false, create('foo/bar/baz').isRelative());
assert.strictEqual(false, create('foo/../../foo').isRelative());
assert.done();
};
test("Identifier#isRelative returns true for relative identifiers", function() {
assert.isTrue(create('../foo').isRelative());
assert.isTrue(create('./foo').isRelative());
assert.isTrue(create('../../foo').isRelative());
});
exports.testIsTopLevelReturnsTrueForTopLevelIdentifiers = function(assert) {
assert.strictEqual(true, create('foo').isTopLevel());
assert.done();
};
test("Identifier#isRelative returns false for top-level identifiers", function() {
assert.isFalse(create('foo').isRelative());
assert.isFalse(create('foo/bar/baz').isRelative());
assert.isFalse(create('foo/../../foo').isRelative());
});
exports.testIsTopLevelReturnsFalseForRelativeIdentifiers = function(assert) {
assert.strictEqual(false, create('./foo').isTopLevel());
assert.done();
};
test("Identifier#isTopLevel returns true for top-level identifiers", function() {
assert.isTrue(create('foo').isTopLevel());
});
exports.testToArrayReturnsAnArrayOfTerms = function(assert) {
assert.deepEqual(['.', 'foo', 'bar'], create('./foo/bar').toArray());
assert.done();
};
test("Identifier#isTopLevel returns false for relative identifiers", function() {
assert.isFalse(create('./foo').isTopLevel());
});
exports.testToArrayReturnsClonedTerms = function(assert) {
var ident = create('./foo/bar')
assert.notEqual(ident.terms, ident.toArray());
assert.done();
};
test("Identifier#toArray returns an array of terms", function() {
assert.deepEqual(['.', 'foo', 'bar'], create('./foo/bar').toArray());
assert.deepEqual(['foo', 'bar'], create('foo/bar').toArray());
});
exports.testToStringReturnsACleanedUpVersionOfTheIdentifier = function(assert) {
var str = './foo//bar';
assert.notEqual(str, create(str).toString());
assert.equal('./foo/bar', create(str).toString());
assert.done();
};
test("Identifier#toArray returns cloned terms", function() {
var ident = create('./foo/bar')
assert.notStrictEqual(ident.toArray(), ident.toArray());
});
test("Identifier#toString returns cleaned up version of the identifier", function() {
var str = './foo//bar';
assert.notEqual(str, create(str).toString());
assert.equal('./foo/bar', create(str).toString());
});
/*
test("Identifier#clone returns a new Identifier object.", function() {
var ident = create('./foo/bar')
assert.ok(ident.clone().constructor === ident.constructor);
assert.notStrictEqual(ident, ident.clone());
});
exports.Identifier = Identifier;
function Identifier(terms) {
if (typeof terms == 'string') {
terms = terms.split('/');
}
this.terms = terms.filter(function(t) { return t; });
}
test("Identifier#clone returns a copy of the identifier with equal but not identical terms.", function() {
var ident = create('./foo/bar')
assert.deepEqual(ident.terms, ident.clone().terms);
assert.notStrictEqual(ident.terms, ident.clone().terms);
});
(function(p) {
var TERM_REGEXP = /^([a-zA-Z0-9-_$]+|\.\.?)$/;
test("Identifier#getDirTerms returns the terms up to the modules dir", function() {
assert.deepEqual(['.', 'foo'], create('./foo/bar').getDirTerms());
assert.deepEqual(['foo'], create('foo/bar').getDirTerms());
assert.deepEqual(['.'], create('./bar').getDirTerms());
assert.deepEqual([], create('bar').getDirTerms());
});
test("When the module is a dir module, Identifier#getDirTerms returns the terms up to the module itself", function() {
assert.deepEqual(['.', 'foo', 'bar'], create('./foo/bar').getDirTerms(true));
assert.deepEqual(['bar'], create('bar').getDirTerms(true));
});
p.resolve = resolve;
function resolve(otherIdentifier, isDir) {
if (this.isTopLevel()) {
return this.clone();
}
test("Identifier#resolve returns an new, top-level identifier when resolved with a top-level identifier", function() {
var ident = create('./bar').resolve(create('foo/baz/foo-bar'));
assert.strictEqual(ident.constructor, identifier.Identifier);
assert.ok(ident.isTopLevel());
assert.strictEqual('foo/baz/bar', ident.toString());
});
var otherTerms = otherIdentifier ? otherIdentifier.getDirTerms(isDir) : [],
terms = this.resolveTerms(otherTerms);
return createIdentifier(terms);
}
test("Identifier#resolve returns an new, top-level identifier when resolved with a relative identifier", function() {
var ident = create('./bar').resolve(create('./foo/bar/../foo-bar'));
assert.strictEqual(ident.constructor, identifier.Identifier);
assert.ok(ident.isTopLevel());
assert.strictEqual('foo/bar', ident.toString());
});
p.resolveTerms = resolveTerms;
function resolveTerms(terms) {
var output = [], term;
if (terms && this.isRelative()) {
terms = terms.slice(0);
} else {
terms = [];
}
terms.push.apply(terms, this.terms);
for (var i = 0, length = terms.length; i < length; i++) {
term = terms[i];
switch (term) {
case '':
case '.':
continue;
case '..':
if (output.length) {
output.pop();
} else {
throw new RangeError('Out of bounds identifier: ' + this);
}
break;
default:
output.push(term);
}
}
return output;
}
test("Identifier#resolve returns an new, top-level identifier when resolved without a resolver", function() {
var ident = create('./bar').resolve();
assert.strictEqual(ident.constructor, identifier.Identifier);
assert.ok(ident.isTopLevel());
assert.strictEqual('bar', ident.toString());
});
p.clone = clone;
function clone() {
return createIdentifier(this.toArray());
}
test("Identifier#resolve throws an out of bound error if resolving the identifier is outside of the current root dir.", function() {
assert.throws(function() { create('../bar').resolve(null) }, 'RangeError');
});
p.getDirTerms = getDirTerms;
function getDirTerms(isDir) {
var terms = this.terms,
length = isDir ? terms.length : terms.length - 1;
return terms.slice(0, length);
}
test("Identifier#resolve always resolves top-level identifiers to themselves", function() {
assert.strictEqual('bar', create('bar').resolve(null).toString());
assert.strictEqual('bar', create('bar').resolve(create('foo/bar/baz')).toString());
});
p.toDirIdentifier = toDirIdentifier;
function toDirIdentifier() {
var terms = this.toArray();
terms.push('index');
return createIdentifier(terms);
}
test("Identifier#resolveTerms", function() {
p.toString = toString;
function toString() {
return this.terms.join('/');
}
})(Identifier.prototype);
});
});
exports.createIdentifier = createIdentifier;
exports.create = createIdentifier;
function createIdentifier(terms) {
return new Identifier(terms);
}
*/
var module = require('../lib/module'),
identifier = require('../lib/identifier');
exports.setUp = function(cb) {
this.identifier = identifier.create('foo');
this.module = module.createModule(this.identifier);
cb()
};
var assert = require('./custom-asserts');
exports["test Module#resolve arguments"] = function(assert) {
var self = this;
this.module.resolve({
resolveModule: function(m, callback) {
assert.equal(m, self.module, 'First argument is the module itself.');
assert.equal(typeof callback, 'function');
callback();
suite('Module', function() {
test("Module#setIdentifier sets module.identifier and module.id", function() {
var m = module.create(identifier.create('foo')),
ident = identifier.create('bar');
m.setIdentifier(ident);
assert.strictEqual(ident, m.identifier);
assert.strictEqual('bar', m.id);
});
test("Module#setIdentifier throws an error when passed a relative identifier", function() {
var m = module.create(identifier.create('foo'));
assert.throws(function() {
m.setIdentifier(identifier.create('./bar'));
}, TypeError);
});
test("Module#resolveIdentifier resolves the identifier relative to itself.", function() {
var m = module.create(identifier.create('foo/bar')),
ident = identifier.create('./baz');
assert.strictEqual(identifier.Identifier, m.resolveIdentifier(ident).constructor);
assert.strictEqual('foo/baz', m.resolveIdentifier(ident).toString());
m._isDir = true;
assert.strictEqual('foo/bar/baz', m.resolveIdentifier(ident).toString(), "Module is a dir module");
});
test("Module#getHashCode returns an md5 hash of the raw src code", function() {
var m = module.create(identifier.create('bar'));
m.raw = "foo";
assert.strictEqual("acbd18db4cc2f85cedef654fccc4a4d8", m.getHashCode());
});
test("Module#getHashCode returns an md5 hash of the raw src code", function() {
});
});
(function(p) {
p.missing = false;
p._dependencies = null;
p._directDependencies = null;
p._requirers = null;
p.lastRequiredBy = null;
p._hashCode = null;
p.raw = '';
p._src = '';
p.ast = null;
p.searchPath = null;
p.fullPath = null;
p.relativePath = null;
p.ext = null;
p.mtime = null;
p._size = 0;
p._totalSize = 0;
p._totalLoc = 0;
p._totalSloc = 0;
p._isDir = false;
p.duplicateOf = null;
p._duplicates = null;
p.dirModule = null;
p.indexModule = null;
p.compile = compile;
function compile(compiler) {
this._src = compiler.compile(this.raw);
}
p.parseSrc = parseSrc;
function parseSrc(parser, isDir) {
var result = parser.parse(this.getSrc(), this, isDir);
this.ast = result.ast;
return result;
}
p.getSrc = getSrc;
function getSrc() {
if (this.indexModule) {
return 'module.exports = require("' + this.indexModule.id + '");';
}
}, assert.done);
};
return this._src ? this._src : this.raw;
}
exports.testResolveMethod = function(assert) {
var self = this;
this.module.resolve({
resolveModule: function(m, callback) {
assert.equal(m, self.module, 'First argument is the module itself.');
assert.equal(typeof callback, 'function');
callback();
p.resolve = resolve;
function resolve(resolver, callback) {
var self = this;
if (resolver && this._resolver === resolver) {
process.nextTick(function() {
callback(null, self._isDir);
});
} else {
this._resolver = resolver;
resolver.resolveModule(this, function(err, isDir) {
self._isDir = isDir;
callback(err, isDir);
});
}
}, assert.done);
};
}
p.addDependency = addDependency;
function addDependency(m) {
var id = m.id,
deps = this.getDirectDependencies();
if (!(id in deps)) {
deps[id] = m;
m.addRequirer(this);
}
}
p.clearDependencies = clearDependencies;
function clearDependencies() {
this._directDependencies = null;
this._dependencies = null;
}
p.addRequirer = addRequirer;
function addRequirer(m) {
var id = m.id,
reqs = this.getRequirers();
if (!(id in reqs)) { reqs[id] = m; }
this.lastRequiredBy = m;
}
p.clearRequirers = clearRequirers;
function clearRequirers() {
this._requirers = null;
this.lastRequiredBy = null;
}
p.getDirectDependencies = getDirectDependencies;
function getDirectDependencies() {
return (this._directDependencies = this._directDependencies || {});
}
p.getDependencies = getDependencies;
function getDependencies() {
if (!this._dependencies) {
var deps = this._dependencies = {},
stack = [this],
children,
child,
m;
while (stack.length) {
m = stack.pop();
children = m.getDirectDependencies();
for (var id in children) {
if (!(id in deps)) {
child = children[id];
deps[id] = child;
stack.push(child);
}
}
}
}
return this._dependencies;
}
p.pointTo = pointTo;
function pointTo(m) {
this.markAsDuplicateOf(m);
this.clearDependencies();
this.addDependency(m);
m.dirModule = this;
this.indexModule = m;
this._isDir = true;
m._isDir = false;
}
p.markAsDuplicateOf = markAsDuplicateOf;
function markAsDuplicateOf(m) {
if (this._duplicates) {
// Transfer duplicates to master
var dups = this.getDuplicates();
for (var id in dups) {
m.addDuplicate(dups[id]);
}
// Remove them.
this._duplicates = null;
}
m.addDuplicate(this);
}
p.addDuplicate = addDuplicate;
function addDuplicate(m) {
var id = m.id,
dups = this.getDuplicates();
if (!(id in dups)) {
dups[id] = m;
m.duplicateOf = this;
}
}
p.getDuplicates = getDuplicates;
function getDuplicates() {
return (this._duplicates = this._duplicates || {});
}
p.isEqual = isEqual;
function isEqual(m) {
return this.raw === m.raw;
}
p.clone = clone;
function clone() {
var clone = createModule(this.identifier);
for (var prop in this) {
clone[prop] = this[prop];
}
// shallow clone referenced objects.
clone._dependencies = _cloneObj(this._dependencies);
clone._directDependencies = _cloneObj(this._directDependencies);
clone._requirers = _cloneObj(this._requirers);
return clone;
}
function _cloneObj(obj) {
if (!obj) { return obj; }
var clone = {};
for (var prop in obj) {
clone[prop] = obj[prop];
}
return clone;
}
p.getRequirers = getRequirers;
function getRequirers() {
return (this._requirers = this._requirers || {});
}
p.getSize = getSize;
function getSize() {
return (this._size = this._size || Buffer.byteLength(this.getSrc()));
}
p.getLoc = getLoc;
function getLoc() {
return this.raw.split('\n').length;
}
p.getSloc = getSloc;
function getSloc() {
return this.raw.split(/\n\s*/).length;
}
p.getTotalSize = _makeSummingMethod('Size');
p.getTotalLoc = _makeSummingMethod('Loc');
p.getTotalSloc = _makeSummingMethod('Sloc');
function _makeSummingMethod(prop) {
var cacheName = '_total' + prop,
methodName = 'get' + prop;
return function() {
if (!this[cacheName]) {
var sum = this[methodName](),
deps = this.getDependencies();
for (var id in deps) {
sum += deps[id][methodName]();
}
this[cacheName] = sum;
}
return this[cacheName];
}
}
p.toString = toString;
function toString() {
return this.id;
}
});
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