Socket
Socket
Sign inDemoInstall

dpdm

Package Overview
Dependencies
64
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.6.1 to 3.7.0

328

lib/bin/dpdm.js

@@ -16,168 +16,176 @@ #!/usr/bin/env node

const utils_1 = require("../utils");
const argv = yargs_1.default
.strict()
.usage('$0 [options] files...')
.option('context', {
type: 'string',
desc: 'the context directory to shorten path, default is current directory',
})
.option('extensions', {
alias: 'ext',
type: 'string',
desc: 'comma separated extensions to resolve',
default: utils_1.defaultOptions.extensions.filter(Boolean).join(','),
})
.option('js', {
type: 'string',
desc: 'comma separated extensions indicate the file is js like',
default: utils_1.defaultOptions.js.join(','),
})
.option('include', {
type: 'string',
desc: 'included filenames regexp in string, default includes all files',
default: utils_1.defaultOptions.include.source,
})
.option('exclude', {
type: 'string',
desc: 'excluded filenames regexp in string, set as empty string to include all files',
default: utils_1.defaultOptions.exclude.source,
})
.option('output', {
alias: 'o',
type: 'string',
desc: 'output json to file',
})
.option('tree', {
type: 'boolean',
desc: 'print tree to stdout',
default: true,
})
.option('circular', {
type: 'boolean',
desc: 'print circular to stdout',
default: true,
})
.option('warning', {
type: 'boolean',
desc: 'print warning to stdout',
default: true,
})
.option('tsconfig', {
type: 'string',
desc: 'the tsconfig path, which is used for resolve path alias, default is tsconfig.json if it exists in context directory',
})
.option('transform', {
type: 'boolean',
desc: 'transform typescript modules to javascript before analyze, it allows you to omit types dependency in typescript',
default: utils_1.defaultOptions.transform,
alias: 'T',
})
.option('exit-code', {
type: 'string',
desc: 'exit with specified code, the value format is CASE:CODE, `circular` is the only supported CASE, ' +
'CODE should be a integer between 0 and 128. ' +
'For example: `dpdm --exit-code circular:1` the program will exit with code 1 if circular dependency found.',
})
.option('progress', {
type: 'boolean',
desc: 'show progress bar',
default: process.stdout.isTTY && !process.env.CI,
})
.alias('h', 'help')
.wrap(Math.min(yargs_1.default.terminalWidth(), 100)).argv;
if (argv._.length === 0) {
yargs_1.default.showHelp();
console.log('\nMissing entry file');
process.exit(1);
}
const exitCases = new Set(['circular']);
const exitCodes = [];
if (argv['exit-code']) {
argv['exit-code'].split(',').forEach((c) => {
const [label, code] = c.split(':');
if (!code || !isFinite(+code)) {
throw new TypeError(`exit code should be a number`);
function main() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const argv = yield yargs_1.default
.strict()
.usage('$0 [options] files...')
.option('context', {
type: 'string',
desc: 'the context directory to shorten path, default is current directory',
})
.option('extensions', {
alias: 'ext',
type: 'string',
desc: 'comma separated extensions to resolve',
default: utils_1.defaultOptions.extensions.filter(Boolean).join(','),
})
.option('js', {
type: 'string',
desc: 'comma separated extensions indicate the file is js like',
default: utils_1.defaultOptions.js.join(','),
})
.option('include', {
type: 'string',
desc: 'included filenames regexp in string, default includes all files',
default: utils_1.defaultOptions.include.source,
})
.option('exclude', {
type: 'string',
desc: 'excluded filenames regexp in string, set as empty string to include all files',
default: utils_1.defaultOptions.exclude.source,
})
.option('output', {
alias: 'o',
type: 'string',
desc: 'output json to file',
})
.option('tree', {
type: 'boolean',
desc: 'print tree to stdout',
default: true,
})
.option('circular', {
type: 'boolean',
desc: 'print circular to stdout',
default: true,
})
.option('warning', {
type: 'boolean',
desc: 'print warning to stdout',
default: true,
})
.option('tsconfig', {
type: 'string',
desc: 'the tsconfig path, which is used for resolve path alias, default is tsconfig.json if it exists in context directory',
})
.option('transform', {
type: 'boolean',
desc: 'transform typescript modules to javascript before analyze, it allows you to omit types dependency in typescript',
default: utils_1.defaultOptions.transform,
alias: 'T',
})
.option('exit-code', {
type: 'string',
desc: 'exit with specified code, the value format is CASE:CODE, `circular` is the only supported CASE, ' +
'CODE should be a integer between 0 and 128. ' +
'For example: `dpdm --exit-code circular:1` the program will exit with code 1 if circular dependency found.',
})
.option('progress', {
type: 'boolean',
desc: 'show progress bar',
default: process.stdout.isTTY && !process.env.CI,
})
.alias('h', 'help')
.wrap(Math.min(yargs_1.default.terminalWidth(), 100)).argv;
if (argv._.length === 0) {
yargs_1.default.showHelp();
console.log('\nMissing entry file');
process.exit(1);
}
if (!exitCases.has(label)) {
throw new TypeError(`unsupported exit case "${label}"`);
const exitCases = new Set(['circular']);
const exitCodes = [];
if (argv['exit-code']) {
argv['exit-code'].split(',').forEach((c) => {
const [label, code] = c.split(':');
if (!code || !isFinite(+code)) {
throw new TypeError(`exit code should be a number`);
}
if (!exitCases.has(label)) {
throw new TypeError(`unsupported exit case "${label}"`);
}
exitCodes.push([label, +code]);
});
}
exitCodes.push([label, +code]);
const o = ora_1.default('Start analyzing dependencies...').start();
let total = 0;
let ended = 0;
let current = '';
const context = argv.context || process.cwd();
function onProgress(event, target) {
switch (event) {
case 'start':
total += 1;
current = path_1.default.relative(context, target);
break;
case 'end':
ended += 1;
break;
}
if (argv.progress) {
o.text = `[${ended}/${total}] Analyzing ${current}...`;
o.render();
}
}
const options = {
context,
extensions: argv.extensions.split(','),
js: argv.js.split(','),
include: new RegExp(argv.include || '.*'),
exclude: new RegExp(argv.exclude || '$.'),
tsconfig: argv.tsconfig,
transform: argv.transform,
onProgress,
};
const files = argv._.filter((t) => typeof t === 'string');
parser_1.parseDependencyTree(files, options)
.then((tree) => tslib_1.__awaiter(this, void 0, void 0, function* () {
o.succeed(`[${ended}/${total}] Analyze done!`);
const entriesDeep = yield Promise.all(files.map((g) => utils_1.glob(g)));
const entries = yield Promise.all(Array()
.concat(...entriesDeep)
.map((name) => utils_1.simpleResolver(options.context, path_1.default.join(options.context, name), options.extensions).then((id) => (id ? path_1.default.relative(options.context, id) : name))));
const circulars = utils_1.parseCircular(tree);
if (argv.output) {
yield fs_extra_1.default.outputJSON(argv.output, { entries, tree, circulars }, { spaces: 2 });
}
if (argv.tree) {
console.log(chalk_1.default.bold('• Dependencies Tree'));
console.log(utils_1.prettyTree(tree, entries));
console.log('');
}
if (argv.circular) {
console.log(chalk_1.default.bold.red('• Circular Dependencies'));
if (circulars.length === 0) {
console.log(chalk_1.default.bold.green(' ✅ Congratulations, no circular dependency were found in your project.'));
}
else {
console.log(utils_1.prettyCircular(circulars));
}
console.log('');
}
if (argv.warning) {
console.log(chalk_1.default.bold.yellow('• Warnings'));
console.log(utils_1.prettyWarning(utils_1.parseWarnings(tree)));
console.log('');
}
for (const [label, code] of exitCodes) {
switch (label) {
case 'circular':
if (circulars.length > 0) {
process.exit(code);
}
}
}
}))
.catch((e) => {
o.fail();
console.error(e.stack || e);
process.exit(1);
});
});
}
const o = ora_1.default('Start analyzing dependencies...').start();
let total = 0;
let ended = 0;
let current = '';
const context = argv.context || process.cwd();
function onProgress(event, target) {
switch (event) {
case 'start':
total += 1;
current = path_1.default.relative(context, target);
break;
case 'end':
ended += 1;
break;
}
if (argv.progress) {
o.text = `[${ended}/${total}] Analyzing ${current}...`;
o.render();
}
}
const options = {
context,
extensions: argv.extensions.split(','),
js: argv.js.split(','),
include: new RegExp(argv.include || '.*'),
exclude: new RegExp(argv.exclude || '$.'),
tsconfig: argv.tsconfig,
transform: argv.transform,
onProgress,
};
const files = argv._.filter((t) => typeof t === 'string');
parser_1.parseDependencyTree(files, options)
.then((tree) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
o.succeed(`[${ended}/${total}] Analyze done!`);
const entriesDeep = yield Promise.all(files.map((g) => utils_1.glob(g)));
const entries = yield Promise.all(Array()
.concat(...entriesDeep)
.map((name) => utils_1.simpleResolver(options.context, path_1.default.join(options.context, name), options.extensions).then((id) => (id ? path_1.default.relative(options.context, id) : name))));
const circulars = utils_1.parseCircular(tree);
if (argv.output) {
yield fs_extra_1.default.outputJSON(argv.output, { entries, tree, circulars }, { spaces: 2 });
}
if (argv.tree) {
console.log(chalk_1.default.bold('• Dependencies Tree'));
console.log(utils_1.prettyTree(tree, entries));
console.log('');
}
if (argv.circular) {
console.log(chalk_1.default.bold.red('• Circular Dependencies'));
if (circulars.length === 0) {
console.log(chalk_1.default.bold.green(' ✅ Congratulations, no circular dependency were found in your project.'));
}
else {
console.log(utils_1.prettyCircular(circulars));
}
console.log('');
}
if (argv.warning) {
console.log(chalk_1.default.bold.yellow('• Warnings'));
console.log(utils_1.prettyWarning(utils_1.parseWarnings(tree)));
console.log('');
}
for (const [label, code] of exitCodes) {
switch (label) {
case 'circular':
if (circulars.length > 0) {
process.exit(code);
}
}
}
}))
.catch((e) => {
o.fail();
console.error(e.stack || e);
main().catch((e) => {
console.error(e);
process.exit(1);
});
//# sourceMappingURL=dpdm.js.map

@@ -22,3 +22,3 @@ "use strict";

include: /.*/,
exclude: /\/node_modules\//,
exclude: /node_modules/,
tsconfig: void 0,

@@ -25,0 +25,0 @@ transform: false,

@@ -16,4 +16,4 @@ "use strict";

// dependents on yarn.lock
const pkg = yield utils_1.simpleResolver(__dirname, 'base', ext);
const deepPkg = yield utils_1.simpleResolver(path_1.dirname(pkg), 'define-property', ext);
const pkg = yield utils_1.simpleResolver(__dirname, 'expect', ext);
const deepPkg = yield utils_1.simpleResolver(path_1.dirname(pkg), 'ansi-styles', ext);
const notFound = yield utils_1.simpleResolver(__dirname, './utils.tsx', ext);

@@ -23,4 +23,4 @@ expect([local, index, pkg, deepPkg, notFound]).toEqual([

path_1.join(__dirname, 'index.ts'),
path_1.join(__dirname, '../node_modules/base/index.js'),
path_1.join(__dirname, '../node_modules/base/node_modules/define-property/index.js'),
path_1.join(__dirname, '../node_modules/expect/build/index.js'),
path_1.join(__dirname, '../node_modules/expect/node_modules/ansi-styles/index.js'),
null,

@@ -27,0 +27,0 @@ ]);

{
"name": "dpdm",
"version": "3.6.1",
"version": "3.7.0",
"description": "Analyze circular dependencies in your JavaScript/TypeScript projects.",

@@ -44,3 +44,4 @@ "keywords": [

"start": "npm run clean && npm run build:esm -- --watch",
"test": "jest"
"test": "jest",
"prepare": "husky install"
},

@@ -53,30 +54,25 @@ "repository": {

"devDependencies": {
"@types/jest": "^26.0.23",
"husky": "^4.3.7",
"jest": "^26.6.3",
"lint-staged": "^11.0.0",
"@types/jest": "^26.0.24",
"husky": "^7.0.1",
"jest": "^27.0.6",
"lint-staged": "^11.1.1",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.0",
"rollup": "^2.47.0",
"ts-jest": "^26.5.6",
"ts-node": "^9.1.1"
"prettier": "^2.3.2",
"rollup": "^2.53.3",
"ts-jest": "^27.0.4",
"ts-node": "^10.1.0"
},
"dependencies": {
"@types/fs-extra": "^9.0.11",
"@types/glob": "^7.1.3",
"@types/yargs": "^16.0.1",
"@types/fs-extra": "^9.0.12",
"@types/glob": "^7.1.4",
"@types/yargs": "^17.0.2",
"chalk": "^4.1.1",
"fs-extra": "^10.0.0",
"glob": "^7.1.7",
"ora": "^5.4.0",
"tslib": "^2.2.0",
"typescript": "^4.2.4",
"ora": "^5.4.1",
"tslib": "^2.3.0",
"typescript": "^4.3.5",
"yargs": "^17.0.1"
},
"cliVersion": "8.8.4",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {

@@ -83,0 +79,0 @@ "*.{js,jsx,ts,tsx,json,css,less,scss,md}": [

@@ -25,195 +25,199 @@ #!/usr/bin/env node

const argv = yargs
.strict()
.usage('$0 [options] files...')
.option('context', {
type: 'string',
desc: 'the context directory to shorten path, default is current directory',
})
.option('extensions', {
alias: 'ext',
type: 'string',
desc: 'comma separated extensions to resolve',
default: defaultOptions.extensions.filter(Boolean).join(','),
})
.option('js', {
type: 'string',
desc: 'comma separated extensions indicate the file is js like',
default: defaultOptions.js.join(','),
})
.option('include', {
type: 'string',
desc: 'included filenames regexp in string, default includes all files',
default: defaultOptions.include.source,
})
.option('exclude', {
type: 'string',
desc:
'excluded filenames regexp in string, set as empty string to include all files',
default: defaultOptions.exclude.source,
})
.option('output', {
alias: 'o',
type: 'string',
desc: 'output json to file',
})
.option('tree', {
type: 'boolean',
desc: 'print tree to stdout',
default: true,
})
.option('circular', {
type: 'boolean',
desc: 'print circular to stdout',
default: true,
})
.option('warning', {
type: 'boolean',
desc: 'print warning to stdout',
default: true,
})
.option('tsconfig', {
type: 'string',
desc:
'the tsconfig path, which is used for resolve path alias, default is tsconfig.json if it exists in context directory',
})
.option('transform', {
type: 'boolean',
desc:
'transform typescript modules to javascript before analyze, it allows you to omit types dependency in typescript',
default: defaultOptions.transform,
alias: 'T',
})
.option('exit-code', {
type: 'string',
desc:
'exit with specified code, the value format is CASE:CODE, `circular` is the only supported CASE, ' +
'CODE should be a integer between 0 and 128. ' +
'For example: `dpdm --exit-code circular:1` the program will exit with code 1 if circular dependency found.',
})
.option('progress', {
type: 'boolean',
desc: 'show progress bar',
default: process.stdout.isTTY && !process.env.CI,
})
.alias('h', 'help')
.wrap(Math.min(yargs.terminalWidth(), 100)).argv;
async function main() {
const argv = await yargs
.strict()
.usage('$0 [options] files...')
.option('context', {
type: 'string',
desc: 'the context directory to shorten path, default is current directory',
})
.option('extensions', {
alias: 'ext',
type: 'string',
desc: 'comma separated extensions to resolve',
default: defaultOptions.extensions.filter(Boolean).join(','),
})
.option('js', {
type: 'string',
desc: 'comma separated extensions indicate the file is js like',
default: defaultOptions.js.join(','),
})
.option('include', {
type: 'string',
desc: 'included filenames regexp in string, default includes all files',
default: defaultOptions.include.source,
})
.option('exclude', {
type: 'string',
desc: 'excluded filenames regexp in string, set as empty string to include all files',
default: defaultOptions.exclude.source,
})
.option('output', {
alias: 'o',
type: 'string',
desc: 'output json to file',
})
.option('tree', {
type: 'boolean',
desc: 'print tree to stdout',
default: true,
})
.option('circular', {
type: 'boolean',
desc: 'print circular to stdout',
default: true,
})
.option('warning', {
type: 'boolean',
desc: 'print warning to stdout',
default: true,
})
.option('tsconfig', {
type: 'string',
desc: 'the tsconfig path, which is used for resolve path alias, default is tsconfig.json if it exists in context directory',
})
.option('transform', {
type: 'boolean',
desc: 'transform typescript modules to javascript before analyze, it allows you to omit types dependency in typescript',
default: defaultOptions.transform,
alias: 'T',
})
.option('exit-code', {
type: 'string',
desc:
'exit with specified code, the value format is CASE:CODE, `circular` is the only supported CASE, ' +
'CODE should be a integer between 0 and 128. ' +
'For example: `dpdm --exit-code circular:1` the program will exit with code 1 if circular dependency found.',
})
.option('progress', {
type: 'boolean',
desc: 'show progress bar',
default: process.stdout.isTTY && !process.env.CI,
})
.alias('h', 'help')
.wrap(Math.min(yargs.terminalWidth(), 100)).argv;
if (argv._.length === 0) {
yargs.showHelp();
console.log('\nMissing entry file');
process.exit(1);
}
if (argv._.length === 0) {
yargs.showHelp();
console.log('\nMissing entry file');
process.exit(1);
}
const exitCases = new Set(['circular']);
const exitCodes: [string, number][] = [];
if (argv['exit-code']) {
argv['exit-code'].split(',').forEach((c) => {
const [label, code] = c.split(':');
if (!code || !isFinite(+code)) {
throw new TypeError(`exit code should be a number`);
}
if (!exitCases.has(label)) {
throw new TypeError(`unsupported exit case "${label}"`);
}
exitCodes.push([label, +code]);
});
}
const exitCases = new Set(['circular']);
const exitCodes: [string, number][] = [];
if (argv['exit-code']) {
argv['exit-code'].split(',').forEach((c) => {
const [label, code] = c.split(':');
if (!code || !isFinite(+code)) {
throw new TypeError(`exit code should be a number`);
}
if (!exitCases.has(label)) {
throw new TypeError(`unsupported exit case "${label}"`);
}
exitCodes.push([label, +code]);
});
}
const o = ora('Start analyzing dependencies...').start();
const o = ora('Start analyzing dependencies...').start();
let total = 0;
let ended = 0;
let current = '';
let total = 0;
let ended = 0;
let current = '';
const context = argv.context || process.cwd();
const context = argv.context || process.cwd();
function onProgress(event: 'start' | 'end', target: string) {
switch (event) {
case 'start':
total += 1;
current = path.relative(context, target);
break;
case 'end':
ended += 1;
break;
function onProgress(event: 'start' | 'end', target: string) {
switch (event) {
case 'start':
total += 1;
current = path.relative(context, target);
break;
case 'end':
ended += 1;
break;
}
if (argv.progress) {
o.text = `[${ended}/${total}] Analyzing ${current}...`;
o.render();
}
}
if (argv.progress) {
o.text = `[${ended}/${total}] Analyzing ${current}...`;
o.render();
}
}
const options: ParseOptions = {
context,
extensions: argv.extensions.split(','),
js: argv.js.split(','),
include: new RegExp(argv.include || '.*'),
exclude: new RegExp(argv.exclude || '$.'),
tsconfig: argv.tsconfig,
transform: argv.transform,
onProgress,
};
const options: ParseOptions = {
context,
extensions: argv.extensions.split(','),
js: argv.js.split(','),
include: new RegExp(argv.include || '.*'),
exclude: new RegExp(argv.exclude || '$.'),
tsconfig: argv.tsconfig,
transform: argv.transform,
onProgress,
};
const files = argv._.filter((t) => typeof t === 'string') as string[];
const files = argv._.filter((t) => typeof t === 'string') as string[];
parseDependencyTree(files, options)
.then(async (tree) => {
o.succeed(`[${ended}/${total}] Analyze done!`);
const entriesDeep = await Promise.all(files.map((g) => glob(g)));
const entries = await Promise.all(
Array<string>()
.concat(...entriesDeep)
.map((name) =>
simpleResolver(
options.context!,
path.join(options.context!, name),
options.extensions,
).then((id) => (id ? path.relative(options.context!, id) : name)),
),
);
const circulars = parseCircular(tree);
if (argv.output) {
await fs.outputJSON(
argv.output,
{ entries, tree, circulars },
{ spaces: 2 },
parseDependencyTree(files, options)
.then(async (tree) => {
o.succeed(`[${ended}/${total}] Analyze done!`);
const entriesDeep = await Promise.all(files.map((g) => glob(g)));
const entries = await Promise.all(
Array<string>()
.concat(...entriesDeep)
.map((name) =>
simpleResolver(
options.context!,
path.join(options.context!, name),
options.extensions,
).then((id) => (id ? path.relative(options.context!, id) : name)),
),
);
}
if (argv.tree) {
console.log(chalk.bold('• Dependencies Tree'));
console.log(prettyTree(tree, entries));
console.log('');
}
if (argv.circular) {
console.log(chalk.bold.red('• Circular Dependencies'));
if (circulars.length === 0) {
console.log(
chalk.bold.green(
' ✅ Congratulations, no circular dependency were found in your project.',
),
const circulars = parseCircular(tree);
if (argv.output) {
await fs.outputJSON(
argv.output,
{ entries, tree, circulars },
{ spaces: 2 },
);
} else {
console.log(prettyCircular(circulars));
}
console.log('');
}
if (argv.warning) {
console.log(chalk.bold.yellow('• Warnings'));
console.log(prettyWarning(parseWarnings(tree)));
console.log('');
}
for (const [label, code] of exitCodes) {
switch (label) {
case 'circular':
if (circulars.length > 0) {
process.exit(code);
}
if (argv.tree) {
console.log(chalk.bold('• Dependencies Tree'));
console.log(prettyTree(tree, entries));
console.log('');
}
}
})
.catch((e: Error) => {
o.fail();
console.error(e.stack || e);
process.exit(1);
});
if (argv.circular) {
console.log(chalk.bold.red('• Circular Dependencies'));
if (circulars.length === 0) {
console.log(
chalk.bold.green(
' ✅ Congratulations, no circular dependency were found in your project.',
),
);
} else {
console.log(prettyCircular(circulars));
}
console.log('');
}
if (argv.warning) {
console.log(chalk.bold.yellow('• Warnings'));
console.log(prettyWarning(parseWarnings(tree)));
console.log('');
}
for (const [label, code] of exitCodes) {
switch (label) {
case 'circular':
if (circulars.length > 0) {
process.exit(code);
}
}
}
})
.catch((e: Error) => {
o.fail();
console.error(e.stack || e);
process.exit(1);
});
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

@@ -15,4 +15,4 @@ /*!

// dependents on yarn.lock
const pkg = await simpleResolver(__dirname, 'base', ext);
const deepPkg = await simpleResolver(dirname(pkg!), 'define-property', ext);
const pkg = await simpleResolver(__dirname, 'expect', ext);
const deepPkg = await simpleResolver(dirname(pkg!), 'ansi-styles', ext);
const notFound = await simpleResolver(__dirname, './utils.tsx', ext);

@@ -22,6 +22,6 @@ expect([local, index, pkg, deepPkg, notFound]).toEqual([

join(__dirname, 'index.ts'),
join(__dirname, '../node_modules/base/index.js'),
join(__dirname, '../node_modules/expect/build/index.js'),
join(
__dirname,
'../node_modules/base/node_modules/define-property/index.js',
'../node_modules/expect/node_modules/ansi-styles/index.js',
),

@@ -28,0 +28,0 @@ null,

@@ -23,3 +23,3 @@ /*!

include: /.*/,
exclude: /\/node_modules\//,
exclude: /node_modules/,
tsconfig: void 0,

@@ -26,0 +26,0 @@ transform: false,

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

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

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc