Socket
Socket
Sign inDemoInstall

advanced-results

Package Overview
Dependencies
289
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.3 to 1.0.4

6

dist/middleware/advancedResults.d.ts
/// <reference types="qs" />
import { Model } from 'mongoose';
/**
* @description Full description and doc about this middleware: https://www.npmjs.com/package/advanced-results
* @param model - the model for which the advanced filtering will be done
* @param populate - field(String) or fields(Array) that you want to populate
* @param param - if except the query, you want the advancedResults to be done by certain paramatear from url. Expecting: ['user', 'userId'](the first is the FIELD NAME for which the query from the parametar will refrence to, and the second is the PARAMETAR NAME in the url ('/reviews/:trainingProgramId/dada). Example: Review.find({[param[0]]: req.params[param[1]]}) is same as Review.find({user: userId}))
*/
declare const advancedResults: (model: Model<any>, populate?: string | string[] | undefined, param?: [string, string] | undefined, consoleIt?: Boolean) => (req: import("express-serve-static-core").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs>, res: import("express-serve-static-core").Response<any, number>, next: import("express-serve-static-core").NextFunction) => Promise<any>;
export = advancedResults;
//# sourceMappingURL=advancedResults.d.ts.map

90

dist/middleware/advancedResults.js

@@ -56,27 +56,19 @@ "use strict";

var chalk_1 = __importDefault(require("chalk"));
// Im handling cases for queryes like: ?name[regex]=Bla and NOT ?name[$regex]=Bla, so i can prevent nosql injection. Because im using express-mongo-sanitize and what it does is all cases where there's $ transformers them in something else so no injection can be done
/**
* @description Full description and doc about this middleware: https://www.npmjs.com/package/advanced-results
* @param model - the model for which the advanced filtering will be done
* @param populate - field(String) or fields(Array) that you want to populate
* @param param - if except the query, you want the advancedResults to be done by certain paramatear from url. Expecting: ['user', 'userId'](the first is the FIELD NAME for which the query from the parametar will refrence to, and the second is the PARAMETAR NAME in the url ('/reviews/:trainingProgramId/dada). Example: Review.find({[param[0]]: req.params[param[1]]}) is same as Review.find({user: userId}))
*/
var escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
var advancedResults = function (model, populate, param, consoleIt) {
if (consoleIt === void 0) { consoleIt = false; }
return asyncHandler_1.default(function (req, res, next) { return __awaiter(void 0, void 0, void 0, function () {
var query, qr, possibleValues, reqQuery, removeField, queryString, formatter, filter, filterArr, parsedFilter, qr, object, allQuery, words, wordsString, _i, words_1, word, allQueryArr, finalQuery, fields, sortBy, page, limit, startIndex, endIndex, total, maxDocumentsWithoutFilter, pagination, _a, populate_1, val, results;
return __generator(this, function (_b) {
switch (_b.label) {
var query, qr, possibleValues, reqQuery, removeField, queryString, formatter, filter, filterArr, parsedFilter, qr, object, allQuery, words, wordsString, _i, words_1, word, allQueryArr, finalQuery, newFinalQuery_1, fields, singleQ, fields, sortBy, page, limit, startIndex, endIndex, total, maxDocumentsWithoutFilter, pagination, _a, populate_1, val, results;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
// In case it sends 2 requests for favicon.ico
if (req.url === '/favicon.ico') {
return [2 /*return*/, next()];
return [2, next()];
}
// In a case if someone enters /athletes?bio[regex]=David+Laid, without defining option like ?bio[regex]=David+Lai&bio[options]=i, making the regex case-insensitive by DEFAULT
for (qr in req.query) {
possibleValues = req.query[qr];
if (possibleValues.regex) {
// Make sure it's a string, if it's not a string then it should not have [regex] field inside of it
req.query[qr].regex = escape_string_regexp_1.default(req.query[qr].regex);
if (model.schema.paths[qr].instance === 'String') {
// If there are no options provided in the url by default add i
if (!req.query[qr].options) {

@@ -91,5 +83,7 @@ req.query[qr].options = 'i';

if (possibleValues.in) {
// Splitting if there's in query into an array so the query can be done the proper way $in: ['Hypertrophy', 'Strength']
req.query[qr].in = possibleValues.in.split(',');
}
if (possibleValues.nin) {
req.query[qr].nin = possibleValues.nin.split(',');
}
}

@@ -105,8 +99,12 @@ reqQuery = __assign({}, req.query);

'q',
'or',
];
// Loop over removeField and delete from reqQuery
removeField.forEach(function (param) { return delete reqQuery[param]; });
queryString = JSON.stringify(reqQuery);
// Create operators ($gt, $gte ,etc)
queryString = queryString.replace(/\b(gt|gte|in|lt|lte|eq|regex|options)\b/g, function (match) { return "$" + match; });
queryString = queryString.replace(/\b(gt|gte|in|lt|lte|eq|regex|options|ne|nin)\b/g, function (match, contents, index, fullString) {
var charBeforeMatch = fullString.charAt(index - 1);
if (charBeforeMatch === '$')
return match;
return "$" + match;
});
formatter = new BazeQueryFormatter_1.default(model);

@@ -116,5 +114,3 @@ filter = {};

if (req.query.filter) {
// query.filter is an stringified object so i need to parse it before i do anything
filter = JSON.parse(req.query.filter);
// Check for q query which is the same as all
if (filter['q']) {

@@ -124,3 +120,2 @@ filter.all = filter['q'];

}
// I must handle the all case, i populate the req.query.all with the filter.all value, so in the (if statement) below it fetches and queries by every possible field in the Model
if (filter.all) {

@@ -138,3 +133,2 @@ req.query.all = req.query.all

}
// Delete if it's an empty object
if (filter[qr] instanceof Object &&

@@ -145,3 +139,2 @@ Object.keys(filter[qr]).length <= 0) {

}
// Refresh filter, and make it adapt to the $and syntax with array so like {$and: [{this: sth}]} instead of {this: sth} (Both cases are fine but since i use my custom function this way is easier)
if (filterArr.length > 0) {

@@ -156,4 +149,2 @@ filter = {};

wordsString = '';
// In case all parametar is ?all=dsadas%20 ---> all = 'dsadas '. And when i do .split(' ') it makes 2 words the actual word which is dsadas and the empty space ---> ['dsadas', '']; This is bad so i need to trim the ends and beginning of that string
// In case query is all=Sth+sth
if (req.query.all) {

@@ -164,3 +155,2 @@ req.query.all = req.query.all.trim();

}
// In case query is q=Sth+sth
if (req.query.q) {

@@ -171,30 +161,39 @@ req.query.q = req.query.q.trim();

}
// **NEWEST VERSION**
// For every word that is seperated by space in the url do regex
for (_i = 0, words_1 = words; _i < words_1.length; _i++) {
word = words_1[_i];
// This function validates attribute depening on if it's date/objectId/number or normal string (for normal string uses $regex for others not) and pushes to the provided array
formatter.formatAllAttributes(word);
}
allQueryArr = formatter.queryArray;
// At the end add all words to every possible attribute, it might make sense in some cases like duration: 1 Week instead of just seprate words duration: 1 and duration: Week. wordsString is string version of all words that need to be query; This will happen if there are multiple words
if (words.length > 1) {
// This will concat the new query format for all words the the array inside that object
formatter.formatAllAttributes(wordsString);
allQueryArr = formatter.queryArray;
}
// By default all the queries are with $and, so i need to do convert them with $or since this is general query, it works { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
allQuery.$or = allQueryArr;
}
finalQuery = __assign(__assign(__assign({}, JSON.parse(queryString)), allQuery), filter);
if (req.query.or || req.query.or === '') {
newFinalQuery_1 = { $or: [] };
if (req.query.or.length > 0) {
fields = req.query.or.split(',');
fields.forEach(function (field) {
var _a;
newFinalQuery_1.$or.push((_a = {}, _a[field] = finalQuery[field], _a));
delete finalQuery[field];
});
newFinalQuery_1 = __assign(__assign({}, finalQuery), newFinalQuery_1);
}
else {
for (singleQ in finalQuery) {
newFinalQuery_1.$or.push((_b = {}, _b[singleQ] = finalQuery[singleQ], _b));
}
}
finalQuery = newFinalQuery_1;
}
if (consoleIt) {
console.log(chalk_1.default.green("Advanced Results middleware will find documents for model " + model.modelName + " with query: %O"), JSON.stringify(finalQuery));
}
// Adding also parametar in the final query if there's one, because some of my routes work that way ex. /api/v1/training-programs/:trainingProgramId/days (it fetches all days with that trainingProgramId). Also can be handled with /api/v1/days?trainingProgram=asdasdasd123123asdas (and passed in the filter field above or just like the example in a normal way in url)
if (param && req.params[param[1]]) {
finalQuery[param[0]] = req.params[param[1]];
}
// Finding resource
query = model.find(finalQuery);
// Select field
if (req.query.select) {

@@ -204,3 +203,2 @@ fields = req.query.select.split(',').join(' ');

}
// Sort field
if (req.query.sort) {

@@ -218,10 +216,9 @@ sortBy = req.query.sort.split(',').join(' ');

query = query.skip(startIndex).limit(limit);
return [4 /*yield*/, model.countDocuments(finalQuery)];
return [4, model.countDocuments(finalQuery)];
case 1:
total = _b.sent();
return [4 /*yield*/, model.estimatedDocumentCount()];
total = _c.sent();
return [4, model.estimatedDocumentCount()];
case 2:
maxDocumentsWithoutFilter = _b.sent();
maxDocumentsWithoutFilter = _c.sent();
pagination = {};
// Adding max how many pages are there with the certain filtering (I need this for some pages)
pagination.pages = Math.ceil(total / limit);

@@ -242,8 +239,5 @@ pagination.total = total;

}
// Everytime it will show the current page, it doesn't metter if it has limit or not
pagination.current = page;
pagination.limit = limit;
// Populating the fields
if (populate) {
// In case populate is an array an has multiple populate values inside of it
if (Array.isArray(populate)) {

@@ -259,5 +253,5 @@ for (_a = 0, populate_1 = populate; _a < populate_1.length; _a++) {

}
return [4 /*yield*/, query];
return [4, query];
case 3:
results = _b.sent();
results = _c.sent();
res.advancedResults = {

@@ -272,3 +266,3 @@ success: true,

}
return [2 /*return*/, next()];
return [2, next()];
}

@@ -275,0 +269,0 @@ });

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// * This is asyncHandling for middleware
var asyncHandler = function (fn) { return function (req, res, next) {

@@ -5,0 +4,0 @@ return Promise.resolve(fn(req, res, next)).catch(next);

@@ -10,11 +10,2 @@ import { Model } from 'mongoose';

export default BazeQueryFormatter;
/**
Some Tips about querying all:
// $regex doesn't work on ObjectID and Date, Number and Array with ObjectIds so i just ignore them, I can ignore all arrays which is bad, or i can just ignore if the caster of the array is ObjectId (this is what i do)
Because .paths.subString gives all the paths to embedded objects or array i don't need to do extra looping or recursive functions like i did in the past in the decrepted files
If you want directly change for some reason the paths or subPaths object you need to copy them and not change them like they are in this function ex. const subPaths = {...model.schema.paths}. Since i don't change anything inside them and use them only to build my query i don't need to copy them.
*/
//# sourceMappingURL=BazeQueryFormatter.d.ts.map

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

var escapeStringRegexp = require('escape-string-regexp');
var BazeQueryFormatter = /** @class */ (function () {
var BazeQueryFormatter = (function () {
function BazeQueryFormatter(model) {

@@ -39,3 +39,2 @@ var _this = this;

case 'Array':
// * This is for the array if it's full with object ids. If it's just normal array i skip it because i get it's attributes in subPaths !!
if (_this.paths[path].$embeddedSchemaType.instance === 'ObjectID') {

@@ -48,5 +47,2 @@ if (isObjectIdCustom_1.default(value)) {

case 'Date':
// * Because if i put new Date('3') it will return some date. So i need to make sure it's not just a plain number. Let's say for example 3 or 10 or 15. It needs to be full form like: '2020-10-12'. It would be nice to also allow let's say new Date('2020') only string with number like that but it's not consistant.
// ! IM ALLOWING FOR ANY INTEGER TO TRANSFORM INTO DATE since i'm using $or Operator it won't make difference. (uncomment the if statement if you don't want to allow this case)
// if (!Number.isInteger(+value)) {
var date = new Date(value);

@@ -56,3 +52,2 @@ if (date.toString() !== 'Invalid Date') {

}
// }
return false;

@@ -67,8 +62,2 @@ case 'String':

case 'ObjectID':
// Second way of making sure you query if array is let's say array of ObjectIds
// if (path.includes('.$')) {
// const originalField = path.replace('.$', '');
// continue;
// }
// isValidObjectId returns true for ('test t') for some reason. But when i merge them together into testt it causes no problems
if (isObjectIdCustom_1.default(value)) {

@@ -89,7 +78,5 @@ return _d = {}, _d[path] = value, _d;

case 'Boolean':
// If it's string but it's 'true' or 'false' or just a standard boolean
if (typeof value === 'boolean' ||
value === 'true' ||
value === 'false') {
// value === 'true' will set true if it 'true' string or false if it's otherwise
return _g = {},

@@ -109,3 +96,4 @@ _g[path] = typeof value === 'boolean' ? value : value === 'true',

};
this.paths = __assign(__assign({}, model.schema.paths), model.schema.subpaths);
this.paths = model.schema.paths
? __assign(__assign({}, model.schema.paths), model.schema.subpaths) : {};
this.queryArray = [];

@@ -116,11 +104,2 @@ }

exports.default = BazeQueryFormatter;
/**
Some Tips about querying all:
// $regex doesn't work on ObjectID and Date, Number and Array with ObjectIds so i just ignore them, I can ignore all arrays which is bad, or i can just ignore if the caster of the array is ObjectId (this is what i do)
Because .paths.subString gives all the paths to embedded objects or array i don't need to do extra looping or recursive functions like i did in the past in the decrepted files
If you want directly change for some reason the paths or subPaths object you need to copy them and not change them like they are in this function ex. const subPaths = {...model.schema.paths}. Since i don't change anything inside them and use them only to build my query i don't need to copy them.
*/
//# sourceMappingURL=BazeQueryFormatter.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// * This is asyncHandling for middleware
var asyncHandler = function (fn) { return function (req, res, next) {

@@ -5,0 +4,0 @@ return Promise.resolve(fn(req, res, next)).catch(next);

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

var ObjectId = require('mongoose').Types.ObjectId;
// Casting a string to an objectId and then checking that the original string matches the string value of the objectId
var isObjectId = function (string) {
// If it has the same amount of chars that a ObjectId should have
if (isValidObjectId(string)) {

@@ -10,0 +8,0 @@ return new ObjectId(string) === string;

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

/**
* @description It parses an object so it can be used as a query inside .findOne or find etc etc. Example: **{
social: { id: '5013469412003852', provider: 'facebook' },
test: { tester: { da: 'da', ne: 'ne' }, molam: 'molam' },
} -------------------------------------->
{
'social.id': '5013469412003852',
'social.provider': 'facebook',
'test.tester.da': 'da',
'test.tester.ne': 'ne',
'test.molam': 'molam'
}
* @param {Object} object, the object that will be parsed. Transformed from {sth: {foo: 'queryThis'}} into {'sth.foo': 'queryThis'}
*/
export declare const parseObject: (object: Record<string, any>) => Record<string, any>;
//# sourceMappingURL=mongooseObjectParser.d.ts.map

@@ -5,16 +5,2 @@ "use strict";

var utilities_1 = require("./utilities");
/**
* @description It parses an object so it can be used as a query inside .findOne or find etc etc. Example: **{
social: { id: '5013469412003852', provider: 'facebook' },
test: { tester: { da: 'da', ne: 'ne' }, molam: 'molam' },
} -------------------------------------->
{
'social.id': '5013469412003852',
'social.provider': 'facebook',
'test.tester.da': 'da',
'test.tester.ne': 'ne',
'test.molam': 'molam'
}
* @param {Object} object, the object that will be parsed. Transformed from {sth: {foo: 'queryThis'}} into {'sth.foo': 'queryThis'}
*/
exports.parseObject = function (object) {

@@ -24,3 +10,2 @@ var newObject = {};

if (utilities_1.isObject(object[property])) {
// Recursive
var deepObject = exports.parseObject(object[property]);

@@ -27,0 +12,0 @@ for (var deepAtt in deepObject) {

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

/**
* @param object any value that will be checked whenever it's strict object (not array)
*/
export declare const isObject: (object: any) => boolean;
//# sourceMappingURL=utilities.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isObject = void 0;
/**
* @param object any value that will be checked whenever it's strict object (not array)
*/
exports.isObject = function (object) {

@@ -8,0 +5,0 @@ return (object === Object(object) &&

{
"name": "advanced-results",
"version": "1.0.3",
"version": "1.0.4",
"description": "Advanced query middleware for expressjs and mongoose",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -227,9 +227,6 @@ **Middleware** used for advanced querying Mongoose Documents for a specific Model through GET request using the queries and parameters provided by the URL . **It only works with MongoDB (mongoose) and expressjs**. Furthermore, it includes **pagination**

2. If in the model you have **ObjectId field** that can be populated you can pass that field as the **second argument** in this function and res.advancedResults will **return the matching documents with that populated field**.
**Example**. **advancedResults(User, 'blogs')** OR if you have **multiple fields** that you want to populate you do **advancedResults(User, ['blogs', 'books'])**
Examples that you can pass as second argument:
- advancedResults(User, 'books')
- advancedResults(User, ['books', 'blogs'])
- advancedResults(User, [
{ path: 'books', select: 'title content' },
{ path: 'blogs', select: 'title content' }]) - It will populate books and blogs paths and select specific fields (in this case title and content for both ObjectIds)
**Example**. **advancedResults(User, 'blogs')** OR if you have **multiple fields** that you want to populate you do **advancedResults(User, ['blogs', 'books'])**
Examples that you can pass as second argument: - advancedResults(User, 'books') - advancedResults(User, ['books', 'blogs']) - advancedResults(User, [
{ path: 'books', select: 'title content' },
{ path: 'blogs', select: 'title content' }]) - It will populate books and blogs paths and select specific fields (in this case title and content for both ObjectIds)
3. The third argument is specific argument and is used to handle the case where we have a route that has parameter inside of it: example.com/posts/:userId. So the third argument ALWAYS is array with length of 2. The first element of that array is the **field NAME** and the second is the **parameter NAME**. **Example**.

@@ -250,2 +247,4 @@ If lets say model Post has an users field inside of it that is of type ObjectId you pass the ['users', 'userId] ---> it will create additional filter in this case if the parameter's userId = 1234567afd (example.com/posts/123456afd), object will be {users: 1234567afd}.

**NOTE**: Only the options specified bellow work for this middleware (if you want additional options please open up a issue so i can add them)
#### Field Specific Operators

@@ -258,2 +257,3 @@

| **in** | selects the documents where the value of a field equals any value in the specified array , ex. ?field[in]=value1,value2 |
| **nin** | selects the documents where the value of a field is different from any value in the specified array , ex. ?field[nin]=value1,value2 |
| **regex** | provides regular expression capabilities for pattern matching strings in queries. By default is in-case sensitive but can be changed by the \$options operator, ex. ?field[regex]=sth) |

@@ -266,6 +266,7 @@ | **options** | this only works with regex. By default is case-insensitivite ex. ?field[regex]=val&field[options]=i |

| **eq** | example.com?field[eq]=5 |
| **ne** | example.com?field[ne]=5 |
<br />
2. Other operators work however you need to pass $ for example, if you want to pass operator $ne. You need to do example.com?field[$ne]=value. All operators HERE: https://docs.mongodb.com/manual/reference/operator/
2. Other operators work however you need to pass $ for example, if you want to pass operator $ne. You need to do example.com?field[$ne]=value. All operators HERE: https://docs.mongodb.com/manual/reference/operator/. **IMPORTANT:** If you are using any mongo/express sanitizer library, adding or passing of $ne directly inside the URL MIGHT NOT work because of the sanitazation those middlewares do. However this middleware should work correctly for the options specified above without the dollar ($) sign.

@@ -276,10 +277,11 @@ #### Query operations

| Operations | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **select** | selects single or multiple fields from a document, example.com?select=field or multiple example.com?select=field1,field2 |
| **sort** | Default field: createdAt, example.com?sort=field. **Field can be ?sort=name (ASC Order) or ?sort=-name (DSC Order)** or **?sort=randomNum,-name** (sorting multiple fields) |
| **limit** | **default: 6**, used for **limiting documents** affects pagination object inside res.advancedResults, example.com?limit=5 |
| **page** | **default: 1**, the current page (if there are multiple pages), this goes together with ?limit, and depending on the ?page and ?limit values it calculates and displays the current pagination properties inside res.advancedResults, ex. example.com?page=2 |
| **all** or **q** | used for searching trough all fields of provided model for a certain value provided in the URL, ex. example.com?all=value or multiple words example.com?all=value1+value2 |
| **filter** | more complex than the other 2, it requires additional parsing before sending request to backend to work. Short explanation: filter is an object that first needs to be JSON.stringified then using library like query-parser, parsed INTO URL then sent to the backend. [Check this additional explanation for usage](https://github.com/Blagoj5/baze-packages/tree/main/advanced-results/MoreDocsAndExamples) |
| Operations | Description |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **select** | selects single or multiple fields from a document, example.com?select=field or multiple example.com?select=field1,field2 |
| **sort** | Default field: createdAt, example.com?sort=field. **Field can be ?sort=name (ASC Order) or ?sort=-name (DSC Order)** or **?sort=randomNum,-name** (sorting multiple fields) |
| **limit** | **default: 6**, used for **limiting documents** affects pagination object inside res.advancedResults, example.com?limit=5 |
| **page** | **default: 1**, the current page (if there are multiple pages), this goes together with ?limit, and depending on the ?page and ?limit values it calculates and displays the current pagination properties inside res.advancedResults, ex. example.com?page=2 |
| **all** or **q** | used for searching trough all fields of provided model for a certain value provided in the URL, ex. example.com?all=value or multiple words example.com?all=value1+value2 |
| **or** | special property that handles the case if you want to do logical OR instead of the defaul AND in a query. Ex. example.com?name[regex]=Tester&randomNum=42 - **This will return only the documents where the randomNum is 42 and the name has Tester**, however with the new field if you specify example.com?name[regex]=Tester&randomNum=42&or=name,randomNum - **It will execute $or operation on those fields, in this case on randomNum and name, so this query will return all the documents where either the name is Tester or the random num is 42**. If you leave the **?or OR &or** query empty it will group all queries from the URL into a $or operator, ex. example.com?name=Tester&randomNum=42&randomField=something&or - all 3 fields will be inside $or operator, however if you specify &or=randomNum,name, the query will look {randomField: 'something', $or: {name: 'Tester, randomNum: 42}} |
| **filter** | more complex than the other 2, it requires additional parsing before sending request to backend to work. Short explanation: filter is an object that first needs to be JSON.stringified then using library like query-parser, parsed INTO URL then sent to the backend. [Check this additional explanation for usage](https://github.com/Blagoj5/baze-packages/tree/main/advanced-results/MoreDocsAndExamples) |

@@ -286,0 +288,0 @@ #### Result: res.advancedResults explained in Details

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

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