New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@mrtujiawei/bin

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mrtujiawei/bin - npm Package Compare versions

Comparing version 1.3.3 to 1.3.4

657

dist/index.js

@@ -1,52 +0,134 @@

#!/bin/env node
'use strict';
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
var commander = require('commander');
var path = require('path');
var https = require('https');
var fs = require('fs');
var Koa = require('koa');
var Static = require('koa-static');
var Router = require('koa-router');
var cors = require('@koa/cors');
var bodyParser = require('koa-bodyparser');
var nodeUtils = require('@mrtujiawei/node-utils');
var http = require('http');
var utils = require('@mrtujiawei/utils');
/***/ 415:
/***/ ((module) => {
/******************************************************************************
Copyright (c) Microsoft Corporation.
module.exports = {"i8":"1.3.3"};
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
/***/ })
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
;// CONCATENATED MODULE: external "commander"
const external_commander_namespaceObject = require("commander");;
;// CONCATENATED MODULE: external "path"
const external_path_namespaceObject = require("path");;
var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_namespaceObject);
;// CONCATENATED MODULE: external "https"
const external_https_namespaceObject = require("https");;
var external_https_default = /*#__PURE__*/__webpack_require__.n(external_https_namespaceObject);
;// CONCATENATED MODULE: external "fs"
const external_fs_namespaceObject = require("fs");;
var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_namespaceObject);
;// CONCATENATED MODULE: external "koa"
const external_koa_namespaceObject = require("koa");;
var external_koa_default = /*#__PURE__*/__webpack_require__.n(external_koa_namespaceObject);
;// CONCATENATED MODULE: external "koa-static"
const external_koa_static_namespaceObject = require("koa-static");;
var external_koa_static_default = /*#__PURE__*/__webpack_require__.n(external_koa_static_namespaceObject);
;// CONCATENATED MODULE: external "koa-router"
const external_koa_router_namespaceObject = require("koa-router");;
var external_koa_router_default = /*#__PURE__*/__webpack_require__.n(external_koa_router_namespaceObject);
;// CONCATENATED MODULE: external "@koa/cors"
const cors_namespaceObject = require("@koa/cors");;
var cors_default = /*#__PURE__*/__webpack_require__.n(cors_namespaceObject);
;// CONCATENATED MODULE: external "koa-bodyparser"
const external_koa_bodyparser_namespaceObject = require("koa-bodyparser");;
var external_koa_bodyparser_default = /*#__PURE__*/__webpack_require__.n(external_koa_bodyparser_namespaceObject);
;// CONCATENATED MODULE: external "@mrtujiawei/node-utils"
const node_utils_namespaceObject = require("@mrtujiawei/node-utils");;
;// CONCATENATED MODULE: external "http"
const external_http_namespaceObject = require("http");;
var external_http_default = /*#__PURE__*/__webpack_require__.n(external_http_namespaceObject);
;// CONCATENATED MODULE: external "@mrtujiawei/utils"
const utils_namespaceObject = require("@mrtujiawei/utils");;
;// CONCATENATED MODULE: ./packages/bin/src/utils/index.ts
const logger = utils.Logger.getLogger('@mrtujiawei/bin');
logger.setLevel(utils.Logger.LOG_LEVEL.ALL);
logger.subscribe((content) => {
console.log(content.getFormattedMessage());
const logger = utils_namespaceObject.Logger.getLogger('@mrtujiawei/bin');
logger.setLevel(utils_namespaceObject.Logger.LOG_LEVEL.ALL);
logger.subscribe(content => {
console.log(content.getFormattedMessage());
});
class InvalidAgrumentsError extends Error {
constructor(message = 'Invalid Arguments') {
super(message);
}
constructor(message = 'Invalid Arguments') {
super(message);
}
}
/**

@@ -56,16 +138,18 @@ * @param url - http或https开头的url

*/
const getDomain = (url) => {
checkUrl(url);
const match = url.match(/^http[s]\:\/\/[^/]+/) || [];
return match[0] || '';
const getDomain = url => {
checkUrl(url);
const match = url.match(/^http[s]\:\/\/[^/]+/) || [];
return match[0] || '';
};
/**
* 是否是m3u8的注释行
*/
const isComment = (line) => {
if (!line) {
return false;
}
return line.startsWith('#');
const isComment = line => {
if (!line) {
return false;
}
return line.startsWith('#');
};
/**

@@ -76,8 +160,9 @@ * 判断是否是完整的url, http开头

*/
const isCompleteUrl = (url) => {
if (!url) {
return false;
}
return url.startsWith('http');
const isCompleteUrl = url => {
if (!url) {
return false;
}
return url.startsWith('http');
};
/**

@@ -87,221 +172,228 @@ * 判断是否是完整的url, http开头

*/
const checkUrl = (url) => {
if (!isCompleteUrl(url)) {
throw new InvalidAgrumentsError();
}
const checkUrl = url => {
if (!isCompleteUrl(url)) {
throw new InvalidAgrumentsError();
}
};
function sendRequest(options, body, secure) {
return __awaiter(this, void 0, void 0, function* () {
let sender = secure ? https : http;
return new Promise((resolve, reject) => {
sender
.request(options, (res) => {
let data = [];
res.on('data', (chunk) => {
data.push(chunk.toString());
});
res.on('end', () => {
resolve(data.join(''));
});
})
.end(JSON.stringify(body))
.on('timeout', () => {
logger.info('请求超时');
reject(new Error('请求超时'));
})
.on('error', (err) => {
console.log({ err });
reject(err);
});
});
async function sendRequest(options, body, secure) {
let sender = secure ? (external_https_default()) : (external_http_default());
return new Promise((resolve, reject) => {
sender.request(options, res => {
let data = [];
res.on('data', chunk => {
data.push(chunk.toString());
});
res.on('end', () => {
resolve(data.join(''));
});
}).end(JSON.stringify(body)).on('timeout', () => {
logger.info('请求超时');
reject(new Error('请求超时'));
}).on('error', err => {
console.log({
err
});
reject(err);
});
});
}
;// CONCATENATED MODULE: ./packages/bin/src/impl/server.ts
function server(options) {
const app = new Koa();
const router = new Router();
options.dir.forEach((dir) => {
logger.info(`static dir: ${dir}`);
app.use(Static(dir));
});
app
.use(cors())
.use(bodyParser())
.use(router.routes())
.use((context, next) => {
const req = context.req;
const path = context.path;
const method = req.method;
const querystring = context.querystring;
logger.trace(`${method} ${path}${querystring ? `?${querystring}` : ''}`);
return next();
})
.use(router.allowedMethods());
// 需要开代理
if (options.target) {
app.use((context, next) => __awaiter(this, void 0, void 0, function* () {
var _a;
const { request } = context;
const prefixReg = new RegExp(`^${options.prefix}`);
if (prefixReg.test(request.url)) {
if (options.rewrite) {
request.url = request.url.replace(prefixReg, '');
}
const url = new URL(options.target);
let requestOptions = {
method: request.method,
path: path.resolve(url.pathname, request.url),
hostname: url.hostname,
headers: {
'Content-Type': 'application/json',
},
};
const skipKeys = new Set([
'host',
'referer',
'accept-encoding',
'Content-Length',
]);
for (const key in request.headers) {
if (skipKeys.has(key)) {
continue;
}
requestOptions.headers[key] = request.headers[key];
}
try {
const data = yield sendRequest(requestOptions, request.body, (_a = options.target) === null || _a === void 0 ? void 0 : _a.startsWith('https'));
context.body = data;
}
catch (e) {
context.body = e;
}
}
else {
yield next();
}
}));
}
if (options.https) {
// 文件查找有点小问题,需要根据打包后的文件去找
const httpsOptions = {
key: fs.readFileSync(path.resolve(__dirname, '../../../../pem/private_key.pem')),
cert: fs.readFileSync(path.resolve(__dirname, '../../../../pem/ca-cert.pem')),
};
https
.createServer(httpsOptions, app.callback())
.listen(options.port, () => {
const ips = nodeUtils.getIps();
logger.info('Server is running at:');
ips.forEach((ip) => {
logger.info(`https://${ip}:${options.port}`);
});
});
}
else {
app.listen(options.port, () => {
const ips = nodeUtils.getIps();
logger.info('Server is running at:');
ips.forEach((ip) => {
logger.info(`http://${ip}:${options.port}`);
});
});
}
}
const app = new (external_koa_default())();
const router = new (external_koa_router_default())();
options.dir.forEach(dir => {
logger.info(`static dir: ${dir}`);
app.use(external_koa_static_default()(dir));
});
app.use(cors_default()()).use(external_koa_bodyparser_default()()).use(router.routes()).use((context, next) => {
const req = context.req;
const path = context.path;
const method = req.method;
const querystring = context.querystring;
logger.trace(`${method} ${path}${querystring ? `?${querystring}` : ''}`);
return next();
}).use(router.allowedMethods());
const getDownloader = (domain, relativePrefix, dir) => {
const lock = new utils.Lock(4);
return (url) => __awaiter(void 0, void 0, void 0, function* () {
if (!url || isComment(url)) {
return;
// 需要开代理
if (options.target) {
app.use(async (context, next) => {
const {
request
} = context;
const prefixReg = new RegExp(`^${options.prefix}`);
if (prefixReg.test(request.url)) {
if (options.rewrite) {
request.url = request.url.replace(prefixReg, '');
}
if (!isCompleteUrl(url)) {
if (url.startsWith('/')) {
url = domain + url;
}
else {
url = relativePrefix + url;
}
const url = new URL(options.target);
let requestOptions = {
method: request.method,
path: external_path_default().resolve(url.pathname, request.url),
hostname: url.hostname,
headers: {
'Content-Type': 'application/json'
}
};
const skipKeys = new Set(['host', 'referer', 'accept-encoding', 'Content-Length']);
for (const key in request.headers) {
if (skipKeys.has(key)) {
continue;
}
requestOptions.headers[key] = request.headers[key];
}
const fileName = nodeUtils.getFileName(url);
try {
yield lock.lock();
let tsData = yield nodeUtils.request(url);
let path = nodeUtils.pathResolve(dir, fileName);
yield nodeUtils.writeFile(path, tsData);
logger.trace(`下载成功 ${fileName}`);
const data = await sendRequest(requestOptions, request.body, options.target?.startsWith('https'));
context.body = data;
} catch (e) {
context.body = e;
}
catch (e) {
logger.error(`下载失败: ${fileName}`);
logger.error(`失败地址: ${url}`);
logger.error('' + e);
}
finally {
lock.unlock();
}
} else {
await next();
}
});
}
if (options.https) {
// 文件查找有点小问题,需要根据打包后的文件去找
const httpsOptions = {
key: external_fs_default().readFileSync(external_path_default().resolve(__dirname, '../pem/private_key.pem')),
cert: external_fs_default().readFileSync(external_path_default().resolve(__dirname, '../pem/ca-cert.pem'))
};
external_https_default().createServer(httpsOptions, app.callback()).listen(options.port, () => {
const ips = (0,node_utils_namespaceObject.getIps)();
logger.info('Server is running at:');
ips.forEach(ip => {
logger.info(`https://${ip}:${options.port}`);
});
});
} else {
app.listen(options.port, () => {
const ips = (0,node_utils_namespaceObject.getIps)();
logger.info('Server is running at:');
ips.forEach(ip => {
logger.info(`http://${ip}:${options.port}`);
});
});
}
}
;// CONCATENATED MODULE: ./packages/bin/src/m3u8-dowmload.ts
const getDownloader = (domain, relativePrefix, dir) => {
const lock = new utils_namespaceObject.Lock(4);
return async url => {
if (!url || isComment(url)) {
return;
}
if (!isCompleteUrl(url)) {
if (url.startsWith('/')) {
url = domain + url;
} else {
url = relativePrefix + url;
}
}
const fileName = (0,node_utils_namespaceObject.getFileName)(url);
try {
await lock.lock();
let tsData = await (0,node_utils_namespaceObject.request)(url);
let path = (0,node_utils_namespaceObject.pathResolve)(dir, fileName);
await (0,node_utils_namespaceObject.writeFile)(path, tsData);
logger.trace(`下载成功 ${fileName}`);
} catch (e) {
logger.error(`下载失败: ${fileName}`);
logger.error(`失败地址: ${url}`);
logger.error('' + e);
} finally {
lock.unlock();
}
};
};
/**
* 额外的参数只支持 -dir 指定下载目录
*/
const download = (m3u8, params) => __awaiter(void 0, void 0, void 0, function* () {
const domain = getDomain(m3u8);
const relativePrefix = m3u8.replace(/[^/]*$/, '');
const buffers = yield nodeUtils.request(m3u8);
const m3u8List = buffers.join('');
// 处理特殊的m3u8内容
const playList = m3u8List.split(/\r?\n/);
const dir = nodeUtils.pathResolve(nodeUtils.getCwd(), params.dir || m3u8.split(/[/.]/).slice(-2, -1)[0]);
yield nodeUtils.rmdir(dir);
logger.trace('删除目录 ' + dir + ' 成功');
yield nodeUtils.mkdir(dir);
logger.trace('创建目录 ' + dir + ' 成功');
const filePlayList = playList.map((line) => {
const url = line;
if (!url || isComment(url)) {
return line;
}
return `/${nodeUtils.getFileName(url)}`;
});
yield nodeUtils.writeFile(nodeUtils.pathResolve(dir, 'index.m3u8'), filePlayList.join('\n'));
logger.trace('写入index.m3u8成功');
const downloader = getDownloader(domain, relativePrefix, dir);
playList.forEach((url) => {
downloader(url);
});
});
const download = async (m3u8, params) => {
const domain = getDomain(m3u8);
const relativePrefix = m3u8.replace(/[^/]*$/, '');
const buffers = await (0,node_utils_namespaceObject.request)(m3u8);
const m3u8List = buffers.join('');
// 处理特殊的m3u8内容
const playList = m3u8List.split(/\r?\n/);
const dir = (0,node_utils_namespaceObject.pathResolve)((0,node_utils_namespaceObject.getCwd)(), params.dir || m3u8.split(/[/.]/).slice(-2, -1)[0]);
await (0,node_utils_namespaceObject.rmdir)(dir);
logger.trace('删除目录 ' + dir + ' 成功');
await (0,node_utils_namespaceObject.mkdir)(dir);
logger.trace('创建目录 ' + dir + ' 成功');
const filePlayList = playList.map(line => {
const url = line;
if (!url || isComment(url)) {
return line;
}
return `/${(0,node_utils_namespaceObject.getFileName)(url)}`;
});
await (0,node_utils_namespaceObject.writeFile)((0,node_utils_namespaceObject.pathResolve)(dir, 'index.m3u8'), filePlayList.join('\n'));
logger.trace('写入index.m3u8成功');
const downloader = getDownloader(domain, relativePrefix, dir);
playList.forEach(url => {
downloader(url);
});
};
/* harmony default export */ const m3u8_dowmload = (download);
;// CONCATENATED MODULE: ./packages/bin/src/push-all.ts
/**
* 自动提交一个文件夹下的所有项目代码
* @filename: push-all.js
* @author: Mr Prince
* @date: 2022年01月04日 星期日 20时05分36秒
*/
/**
* 提交单个git仓库
*/
const push = (cwd) => __awaiter(void 0, void 0, void 0, function* () {
const options = {
cwd,
encoding: 'utf8',
};
logger.info(`${cwd} 开始提交`);
try {
logger.info('git add .');
yield nodeUtils.exec('git add .', options);
logger.info(`git commit -m 'feat(all):自动提交'`);
yield nodeUtils.exec(`git commit -m 'feat(all):自动提交'`, options);
logger.info('git push');
yield nodeUtils.exec('git push', options);
logger.info(`${cwd} 提交成功`);
}
catch (e) {
logger.error('提交终止' + e);
}
});
const push = async cwd => {
const options = {
cwd,
encoding: 'utf8'
};
logger.info(`${cwd} 开始提交`);
try {
logger.info('git add .');
await (0,node_utils_namespaceObject.exec)('git add .', options);
logger.info(`git commit -m 'feat(all):自动提交'`);
await (0,node_utils_namespaceObject.exec)(`git commit -m 'feat(all):自动提交'`, options);
logger.info('git push');
await (0,node_utils_namespaceObject.exec)('git push', options);
logger.info(`${cwd} 提交成功`);
} catch (e) {
logger.error('提交终止' + e);
}
};
/**
* 提交指定目录下的所有git仓库
*/
const pushAll = () => __awaiter(void 0, void 0, void 0, function* () {
const cwd = nodeUtils.getCwd();
const dirs = yield nodeUtils.getDirs(cwd);
for (let i = 0; i < dirs.length; i++) {
const dir = nodeUtils.pathResolve(cwd, dirs[i]);
yield push(dir);
}
});
const pushAll = async () => {
const cwd = (0,node_utils_namespaceObject.getCwd)();
const dirs = await (0,node_utils_namespaceObject.getDirs)(cwd);
for (let i = 0; i < dirs.length; i++) {
const dir = (0,node_utils_namespaceObject.pathResolve)(cwd, dirs[i]);
await push(dir);
}
};
/* harmony default export */ const push_all = (pushAll);
;// CONCATENATED MODULE: ./packages/bin/src/index.ts
#!/bin/env node
/**

@@ -312,40 +404,31 @@ * @filename /home/tujiawei/github/package/packages/bin/src/index.ts

*/
const program = new commander.Command();
program
.name('t')
.description('')
.version(require('../package.json').version);
program
.command('download-m3u8')
.description('下载m3u8文件')
.argument('<url>', 'index.m3u8')
.option('--dir 指定下载目录')
.action((url, options) => {
return download(url, options);
const program = new external_commander_namespaceObject.Command();
program.name('t').description('').version(__webpack_require__(415)/* .version */ .i8);
program.command('download-m3u8').description('下载m3u8文件').argument('<url>', 'index.m3u8').option('--dir 指定下载目录').action((url, options) => {
return m3u8_dowmload(url, options);
});
program
.command('pushAll')
.description('提交指定目录下的所有git仓库')
.action(() => {
pushAll();
program.command('pushAll').description('提交指定目录下的所有git仓库').action(() => {
push_all();
});
program
.command('server')
.description('静态文件服务,支持https,请求代理')
.option('-s, --https', '开启https', false)
.option('-p, --port <port>', '监听端口号', '3000')
.option('-d, --dir <directory...>', '项目根目录', [process.cwd()])
.option('-t, --target <url>', '代理目录地址')
.option('--prefix <prefix>', '需要代理的路径前缀', '/api')
.option('--rewrite', '是否移除前缀', false)
.action((options) => {
options.port = Number(options.port);
for (let i = 0; i < options.dir.length; i++) {
if (!path.isAbsolute(options.dir[i])) {
options.dir[i] = path.resolve(process.cwd(), options.dir[i]);
}
program.command('server').description('静态文件服务,支持https,请求代理').option('-s, --https', '开启https', false).option('-p, --port <port>', '监听端口号', '3000').option('-d, --dir <directory...>', '项目根目录', [process.cwd()]).option('-t, --target <url>', '代理目录地址').option('--prefix <prefix>', '需要代理的路径前缀', '/api').option('--rewrite', '是否移除前缀', false).action(options => {
options.port = Number(options.port);
for (let i = 0; i < options.dir.length; i++) {
if (!external_path_default().isAbsolute(options.dir[i])) {
options.dir[i] = external_path_default().resolve(process.cwd(), options.dir[i]);
}
server(options);
}
server(options);
});
program.parse(process.argv);
//# sourceMappingURL=index.js.map
})();
var __webpack_export_target__ = exports;
for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i];
if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true });
/******/ })()
;
{
"name": "@mrtujiawei/bin",
"version": "1.3.3",
"version": "1.3.4",
"description": "我的命令行工具",

@@ -37,3 +37,9 @@ "files": [

"author": "屠佳伟",
"license": "MIT"
"license": "MIT",
"compileOptions": {
"input": "src/index.ts",
"outputDir": "dist",
"outputPath": "dist/index.js",
"format": "cjs"
}
}
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