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

prom-client

Package Overview
Dependencies
Maintainers
1
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

prom-client - npm Package Compare versions

Comparing version 2.0.2 to 3.0.0

lib/validation.js

23

example/server.js

@@ -8,22 +8,33 @@ 'use strict';

var Histogram = require('../lib/histogram');
var h = new Histogram('test_histogram', 'Example of a histogram');
var h = new Histogram('test_histogram', 'Example of a histogram', [ 'code' ]);
var Counter = require('../lib/counter');
var c = new Counter('test_counter', 'Example of a counter', { labels: { 'code': 200 }});
var c = new Counter('test_counter', 'Example of a counter', [ 'code' ]);
var Gauge = require('../lib/gauge');
var g = new Gauge('test_gauge', 'Example of a gauge');
var g = new Gauge('test_gauge', 'Example of a gauge', [ 'method', 'code' ]);
setInterval(function() {
h.observe(Math.random());
}, 100);
h.labels('200').observe(Math.random());
}, 1000);
setInterval(function() {
c.inc();
c.inc({ code: 200 });
}, 5000);
setInterval(function() {
c.inc({ code: 400 });
}, 2000);
setInterval(function() {
c.inc();
}, 2000);
setInterval(function() {
g.set({ method: 'get', code: 200 }, Math.random());
g.set(Math.random());
g.labels('post', '300').inc();
}, 100);
server.get('/metrics', function(req, res) {

@@ -30,0 +41,0 @@ res.end(register.metrics());

@@ -8,3 +8,10 @@ /**

var isNumber = require('./util').isNumber;
var getProperties = require('./util').getPropertiesFromObj;
var createValue = require('./util').incValue;
var validateLabels = require('./validation').validateLabel;
var validateMetricName = require('./validation').validateMetricName;
var validateLabelNames = require('./validation').validateLabelName;
var getLabels = require('./util').getLabels;
/**

@@ -14,6 +21,6 @@ * Counter

* @param {string} help - Help description for the metric
* @param {object} obj - Configuration
* @param {array} labels - Labels
* @constructor
*/
function Counter(name, help, obj) {
function Counter(name, help, labels) {
if(!help) {

@@ -25,8 +32,14 @@ throw new Error('Missing mandatory help parameter');

}
obj = obj || {};
if(!validateMetricName(name)) {
throw new Error('Invalid metric name');
}
if(!validateLabelNames(labels)) {
throw new Error('Invalid label name');
}
this.name = name;
this.values = [{
value: 0,
labels: obj.labels
}];
this.hashMap = {};
this.labelNames = (labels || []);
this.help = help;

@@ -38,13 +51,11 @@ register.registerMetric(this);

* Increment counter
* @param {object} labels - What label you want to be incremented
* @param {float} value - Value to increment, if omitted increment with 1
* @returns {void}
*/
Counter.prototype.inc = function(value) {
if(value && !isNumber(value)) {
throw new Error('Value is not a valid number', value);
Counter.prototype.inc = function(labels, value) {
if(isNumber(labels)) {
return inc.call(this, null)(labels);
}
if(value < 0) {
throw new Error('It is not possible to decrease a counter');
}
this.values[0].value = this.values[0].value += value || 1;
return inc.call(this, labels)(value);
};

@@ -57,6 +68,30 @@

type: type,
values: this.values
values: getProperties(this.hashMap)
};
};
Counter.prototype.labels = function() {
var labels = getLabels(this.labelNames, arguments);
return {
inc: inc.call(this, labels)
};
};
var inc = function(labels) {
var that = this;
return function(value) {
if(value && !isNumber(value)) {
throw new Error('Value is not a valid number', value);
}
if(value < 0) {
throw new Error('It is not possible to decrease a counter');
}
labels = labels || {};
validateLabels(that.labelNames, labels);
that.hashMap = createValue(that.hashMap, value || 1, labels);
};
};
module.exports = Counter;

@@ -8,4 +8,11 @@ /**

var type = 'gauge';
var objectHash = require('object-hash');
var isNumber = require('./util').isNumber;
var createValue = require('./util').setValue;
var getProperties = require('./util').getPropertiesFromObj;
var getLabels = require('./util').getLabels;
var validateMetricName = require('./validation').validateMetricName;
var validateLabels = require('./validation').validateLabel;
var validateLabelNames = require('./validation').validateLabelName;

@@ -16,6 +23,6 @@ /**

* @param {string} help - Help for the metric
* @param {object} obj - Configuration
* @param {array} labels - Array with strings, all label keywords supported
* @constructor
*/
function Gauge(name, help, obj) {
function Gauge(name, help, labels) {
if(!help) {

@@ -27,8 +34,13 @@ throw new Error('Missing mandatory help parameter');

}
obj = obj || {};
if(!validateMetricName(name)) {
throw new Error('Invalid metric name');
}
if(!validateLabelNames(labels)) {
throw new Error('Invalid label name');
}
this.name = name;
this.values = [{
value: 0,
labels: obj.labels
}];
this.labelNames = labels || [];
this.hashMap = {};
this.help = help;

@@ -40,10 +52,11 @@ register.registerMetric(this);

* Set a gauge to a value
* @param {object} labels - Object with labels and their values
* @param {float} value - Value to set the gauge to, must be positive
* @returns {void}
*/
Gauge.prototype.set = function(value) {
if(!isNumber(value)) {
throw new Error('Value is not a valid number', value);
Gauge.prototype.set = function(labels, value) {
if(isNumber(labels)) {
return set.call(this, null)(labels);
}
this.values[0].value = value;
return set.call(this, labels)(value);
};

@@ -53,16 +66,19 @@

* Increment a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to increment - if omitted, increment with 1
* @returns {void}
*/
Gauge.prototype.inc = function(value) {
this.set(this._getValue() + (value || 1));
Gauge.prototype.inc = function(labels, value) {
inc.call(this, labels)(value);
};
/**
* Decrement a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to decrement - if omitted, decrement with 1
* @returns {void}
*/
Gauge.prototype.dec = function(value) {
this.set(this._getValue() - (value || 1));
Gauge.prototype.dec = function(labels, value) {
dec.call(this, labels)(value);
};

@@ -72,6 +88,7 @@

* Set the gauge to current unix epoch
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {void}
*/
Gauge.prototype.setToCurrentTime = function() {
this.set(new Date().getTime());
Gauge.prototype.setToCurrentTime = function(labels) {
return setToCurrentTime.call(this, labels)();
};

@@ -81,2 +98,3 @@

* Start a timer
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {function} - Invoke this function to set the duration in seconds since you started the timer.

@@ -89,9 +107,4 @@ * @example

*/
Gauge.prototype.startTimer = function() {
var start = new Date();
var gauge = this;
return function() {
var end = new Date();
gauge.set((end - start) / 1000);
};
Gauge.prototype.startTimer = function(labels) {
return startTimer.call(this, labels)();
};

@@ -104,18 +117,84 @@

type: type,
values: this.values
values: getProperties(this.hashMap)
};
};
/**
* Reset the value of the gauge to 0
* @returns {void}
*/
Gauge.prototype.reset = function() {
this.values[0].value = 0;
Gauge.prototype._getValue = function(labels) {
var hash = objectHash(labels || {});
return this.hashMap[hash] ? this.hashMap[hash].value : 0;
};
Gauge.prototype._getValue = function() {
return this.values[0].value;
Gauge.prototype.labels = function() {
var labels = getLabels(this.labelNames, arguments);
return {
inc: inc.call(this, labels),
dec: dec.call(this, labels),
set: set.call(this, labels),
setToCurrentTime: setToCurrentTime.call(this, labels),
startTimer: startTimer.call(this, labels)
};
};
function setToCurrentTime(labels) {
var gauge = this;
return function() {
gauge.set(labels, new Date().getTime());
};
}
function startTimer(labels) {
var gauge = this;
return function() {
var start = new Date();
return function() {
var end = new Date();
gauge.set(labels, (end - start) / 1000);
};
};
}
function dec(labels) {
var gauge = this;
return function(value) {
var data = convertLabelsAndValues(labels, value);
gauge.set(data.labels, gauge._getValue(data.labels) - (data.value || 1));
};
}
function inc(labels) {
var gauge = this;
return function(value) {
var data = convertLabelsAndValues(labels, value);
gauge.set(data.labels, gauge._getValue(data.labels) + (data.value || 1));
};
}
function set(labels) {
var that = this;
return function(value) {
if(!isNumber(value)) {
throw new Error('Value is not a valid number', val);
}
labels = labels || {};
validateLabels(that.labelNames, labels);
that.hashMap = createValue(that.hashMap, value, labels);
};
}
function convertLabelsAndValues(labels, value) {
if(isNumber(labels)) {
return {
value: labels,
labels: {}
};
}
return {
labels: labels,
value: value
};
}
module.exports = Gauge;

@@ -10,2 +10,8 @@ /**

var extend = require('util-extend');
var getProperties = require('./util').getPropertiesFromObj;
var getLabels = require('./util').getLabels;
var createValue = require('./util');
var validateLabels = require('./validation').validateLabel;
var validateMetricName = require('./validation').validateMetricName;
var validateLabelNames = require('./validation').validateLabelName;

@@ -16,24 +22,23 @@ /**

* @param {string} help - Help for the metric
* @param {object} obj - Configuration object
* @param {object|array} labelsOrConf - Either array of label names or config object as a key-value object
* @param {object} conf - Configuration object
* @constructor
*/
function Histogram(name, help, obj) {
if(!help) {
throw new Error('Missing mandatory help parameter');
}
if(!name) {
throw new Error('Missing mandatory name parameter');
}
function Histogram(name, help, labelsOrConf, conf) {
var obj;
var labels = [];
obj = obj || {};
if(obj.labels && obj.labels.le) {
throw new Error('Le is a reserved keyword and can not be used as a custom label');
if(Array.isArray(labelsOrConf)) {
obj = conf || {};
labels = labelsOrConf;
} else {
obj = labelsOrConf || {};
}
var defaultUpperbounds = (obj.buckets || [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]).sort(function(x, y) {
return x - y;
});
validateInput(name, help, labels);
this.name = name;
this.help = help;
this.upperBounds = defaultUpperbounds;
this.upperBounds = configureUpperbounds(obj.buckets);
this.bucketValues = this.upperBounds.reduce(function(acc, upperBound) {

@@ -43,6 +48,9 @@ acc[upperBound] = 0;

}, {});
Object.freeze(this.upperBounds);
this.sum = 0;
this.count = 0;
this.labels = {};
this.hashMap = {};
this.labelNames = labels || [];
register.registerMetric(this);

@@ -53,27 +61,16 @@ }

* Observe a value in histogram
* @param {float} val - Value to observe in the histogram
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to observe in the histogram
* @returns {void}
*/
Histogram.prototype.observe = function(val) {
if(!isNumber(val)) {
throw new Error('Value is not a valid number', val);
}
var b = findBound(this.upperBounds, val);
if(!this.bucketValues.hasOwnProperty(b)) {
return;
}
this.bucketValues[b] += 1;
this.sum += val;
this.count += 1;
Histogram.prototype.observe = function(labels, value) {
observe.call(this, labels || {})(value);
};
Histogram.prototype.get = function() {
var histogram = this;
var values = this.upperBounds.map(function(upperBound) {
var lbls = extend({ le: upperBound }, histogram.labels);
return createValuePair(lbls, histogram.bucketValues[upperBound], histogram.name + '_bucket');
});
values.push(createValuePair({ le: '+Inf' }, this.count, histogram.name + '_bucket'));
values.push(createValuePair({}, this.sum, histogram.name + '_sum'));
values.push(createValuePair({}, this.count, histogram.name + '_count'));
var data = getProperties(this.hashMap);
var values =
data.map(extractBucketValuesForExport(this))
.reduce(addSumAndCountForExport(this), []);
return {

@@ -89,2 +86,3 @@ name: this.name,

* Start a timer that could be used to logging durations
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {function} - Function to invoke when you want to stop the timer and observe the duration in seconds

@@ -97,11 +95,56 @@ * @example

*/
Histogram.prototype.startTimer = function() {
var start = new Date();
Histogram.prototype.startTimer = function(labels) {
return startTimer.call(this, labels)();
};
Histogram.prototype.labels = function() {
var labels = getLabels(this.labelNames, arguments);
return {
observe: observe.call(this, labels),
startTimer: startTimer.call(this, labels)
};
};
function startTimer(labels) {
var histogram = this;
return function() {
var end = new Date();
histogram.observe((end - start) / 1000);
var start = new Date();
return function() {
var end = new Date();
histogram.observe(labels, (end - start) / 1000);
};
};
};
}
function validateInput(name, help, labels) {
if(!help) {
throw new Error('Missing mandatory help parameter');
}
if(!name) {
throw new Error('Missing mandatory name parameter');
}
if(!validateMetricName(name)) {
throw new Error('Invalid metric name');
}
if(!validateLabelNames(labels)) {
throw new Error('Invalid label name');
}
labels.forEach(function(label) {
if(label === 'le') {
throw new Error('le is a reserved label keyword');
}
});
}
function configureUpperbounds(configuredBuckets) {
var defaultBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
return (configuredBuckets || defaultBuckets).sort(sortAscending);
}
function sortAscending(x, y) {
return x - y;
}
function createValuePair(labels, value, metricName) {

@@ -115,3 +158,2 @@ return {

function findBound(upperBounds, value) {

@@ -125,4 +167,84 @@ for(var i = 0; i < upperBounds.length; i++) {

}
//TODO: What to return?
return -1;
}
function observe(labels) {
var histogram = this;
return function(value) {
var labelValuePair = convertLabelsAndValues(labels, value);
validateLabels(histogram.labelNames, labelValuePair.labels);
if(!isNumber(labelValuePair.value)) {
throw new Error('Value is not a valid number', labelValuePair.value);
}
var objectHash = require('object-hash');
var hash = objectHash(labelValuePair.labels);
var valueFromMap = histogram.hashMap[hash];
if(!valueFromMap) {
valueFromMap = createBaseValues(labelValuePair.labels, histogram.bucketValues);
}
var b = findBound(histogram.upperBounds, labelValuePair.value);
valueFromMap.sum += labelValuePair.value;
valueFromMap.count += 1;
if(histogram.bucketValues.hasOwnProperty(b)) {
valueFromMap.bucketValues[b] += 1;
}
histogram.hashMap[hash] = valueFromMap;
};
}
function createBaseValues(labels, bucketValues) {
return {
labels: labels,
bucketValues: bucketValues,
sum: 0,
count: 0
};
}
function convertLabelsAndValues(labels, value) {
if(isNumber(labels)) {
return {
value: labels,
labels: {}
};
}
return {
labels: labels,
value: value
};
}
function extractBucketValuesForExport(histogram) {
return function(bucketData) {
var buckets = histogram.upperBounds.map(createBucketValues(bucketData, histogram));
return { buckets: buckets, data: bucketData };
};
}
function addSumAndCountForExport(histogram) {
return function(acc, d) {
acc = acc.concat(d.buckets);
var infLabel = extend({ le: '+Inf'}, d.data.labels);
acc.push(createValuePair(infLabel, d.data.count, histogram.name + '_bucket'));
acc.push(createValuePair(d.data.labels, d.data.sum, histogram.name + '_sum'));
acc.push(createValuePair(d.data.labels, d.data.count, histogram.name + '_count'));
return acc;
};
}
function createBucketValues(bucket, histogram) {
return function(upperBound) {
var lbls = extend({ le: upperBound }, bucket.labels);
var valuePair = createValuePair(lbls, bucket.bucketValues[upperBound], histogram.name + '_bucket');
return valuePair;
};
}
module.exports = Histogram;

@@ -8,6 +8,8 @@ 'use strict';

var item = metric.get();
var help = ['#', 'HELP', item.name, item.help].join(' ');
var type = ['#', 'TYPE', item.name, item.type].join(' ');
var name = escapeString(item.name);
var help = escapeString(item.help);
var help = ['#', 'HELP', name, help].join(' ');
var type = ['#', 'TYPE', name, item.type].join(' ');
var values = item.values.reduce(function(valAcc, val) {
var values = (item.values || []).reduce(function(valAcc, val) {
var labels = Object.keys(val.labels || {}).map(function(key) {

@@ -32,2 +34,6 @@ return key + '="' + val.labels[key] + '"';

function escapeString(str) {
return str.replace('"', '\\"').replace('\\n', '\\\n').replace('\\', '\\\\');
}
var registerMetric = function registerMetric(metricFn) {

@@ -34,0 +40,0 @@ metrics.push(metricFn);

'use strict';
exports.isNumber = function isNumber(obj) { return !isNaN(parseFloat(obj)); };
var objectHash = require('object-hash');
exports.isNumber = isNumber;
exports.getPropertiesFromObj = function(hashMap) {
var obj = Object.keys(hashMap).map(function(x) {
return hashMap[x];
});
return obj;
};
exports.incValue = function createValue(hashMap, value, labels) {
var hash = hashObject(labels);
if(hashMap[hash]){
hashMap[hash].value += value || 1;
} else {
hashMap[hash] = { value: value || 1, labels: labels || {} };
}
return hashMap;
};
exports.setValue = function setValue(hashMap, value, labels) {
var hash = hashObject(labels);
hashMap[hash] = { value: isNumber(value) ? value : 0, labels: labels || {} };
return hashMap;
};
exports.getLabels = function(labelNames, args) {
if(labelNames.length !== args.length) {
throw new Error('Invalid number of arguments');
}
var argsAsArray = Array.prototype.slice.call(args);
return labelNames.reduce(function(acc, label, index){
acc[label] = argsAsArray[index];
return acc;
}, {});
};
function hashObject(labels) {
return objectHash(labels);
}
function isNumber(obj) { return !isNaN(parseFloat(obj)); };
{
"name": "prom-client",
"version": "2.0.2",
"version": "3.0.0",
"description": "Client for prometheus",

@@ -33,4 +33,5 @@ "main": "index.js",

"dependencies": {
"object-hash": "^0.9.2",
"util-extend": "^1.0.1"
}
}

@@ -13,14 +13,4 @@ # Prometheus client for node.js

All metric types has 2 mandatory parameters, name and help. Other than that, it always takes a configuration object as 3rd parameter. For all types, you can these properties:
All metric types has 2 mandatory parameters, name and help.
**labels**
```
new Client.counter('name', 'help',
{
labels: {
type: 'request'
}
});
```
#### Counter

@@ -92,1 +82,11 @@

#### Labels
All metrics take an array as 3rd parameter that should include all supported label keys. There are 2 ways to add values to the labels
```
var Client = require('prom-client');
var gauge = new Client.gauge('metric_name', 'metric_help', [ 'method', 'statusCode' ]);
gauge.set({ method: 'GET', statusCode: '200' }, 100); // 1st version, Set value 100 with method set to GET and statusCode to 200
gauge.labels('GET', '200').set(100); // 2nd version, Same as above
```

@@ -34,2 +34,29 @@ 'use strict';

});
describe('labels', function() {
beforeEach(function() {
instance = new Counter('gauge_test', 'help', [ 'method', 'endpoint']);
});
it('should 1 value per label', function() {
instance.labels('GET', '/test').inc();
instance.labels('POST', '/test').inc();
var values = instance.get().values;
expect(values).to.have.length(2);
});
it('should throw error if label lengths does not match', function() {
var fn = function() {
instance.labels('GET').inc();
};
expect(fn).to.throw(Error);
});
it('should increment label value with provided value', function() {
instance.labels('GET', '/test').inc(100);
var values = instance.get().values;
expect(values[0].value).to.equal(100);
});
});
});

@@ -17,7 +17,2 @@ 'use strict';

it('should reset a gauge', function() {
instance.reset();
expectValue(0);
});
it('should increase with 1 if no param provided', function() {

@@ -65,2 +60,35 @@ instance.inc();

describe('with labels', function() {
beforeEach(function() {
instance = new Gauge('name', 'help', ['code']);
instance.set({ 'code': '200' }, 20);
});
it('should be able to increment', function() {
instance.labels('200').inc();
expectValue(21);
});
it('should be able to decrement', function() {
instance.labels('200').dec();
expectValue(19);
});
it('should be able to set value', function() {
instance.labels('200').set(500);
expectValue(500);
});
it('should be able to set value to current time', function() {
var clock = sinon.useFakeTimers();
instance.labels('200').setToCurrentTime();
expectValue(new Date().getTime());
clock.restore();
});
it('should be able to start a timer', function(){
var clock = sinon.useFakeTimers();
var end = instance.labels('200').startTimer();
clock.tick(1000);
end();
expectValue(1);
clock.restore();
});
});
function expectValue(val) {

@@ -67,0 +95,0 @@ expect(instance.get().values[0].value).to.equal(val);

@@ -74,6 +74,6 @@ 'use strict';

it('should allow custom labels', function() {
var i = new Histogram('histo', 'help', { labels: { code: 'test' }});
i.observe(1);
var pair = getValueByLabel('test', instance.get().values);
expect(pair).to.exist;
var i = new Histogram('histo', 'help', [ 'code' ]);
i.observe({ code: 'test'}, 1);
var pair = getValueByLeAndLabel(1, 'code', 'test', i.get().values);
expect(pair.value).to.equal(1);
});

@@ -83,3 +83,3 @@

var fn = function() {
new Histogram('name', 'help', { labels: { le: 'test' }});
new Histogram('name', 'help', [ 'le' ]);
};

@@ -89,12 +89,38 @@ expect(fn).to.throw(Error);

it('should not observe value if outside most upper bound', function() {
it('should observe value if outside most upper bound', function() {
instance.observe(100000);
var values = instance.get().values;
var valuesAboveZero = values.reduce(function(acc, v) {
acc += v.value;
return acc;
}, 0);
expect(valuesAboveZero).to.equal(0);
var count = getValueByLabel('+Inf', values, 'le');
expect(count.value).to.equal(1);
});
describe('labels', function() {
beforeEach(function() {
instance = new Histogram('histogram_labels', 'Histogram with labels fn', [ 'method' ]);
});
it('should observe', function() {
instance.labels('get').observe(4);
var res = getValueByLeAndLabel(5, 'method', 'get', instance.get().values);
expect(res.value).to.equal(1);
});
it('should not allow different number of labels', function() {
var fn = function() {
instance.labels('get', '500').observe(4);
};
expect(fn).to.throw(Error);
});
it('should start a timer', function() {
var clock = sinon.useFakeTimers();
var end = instance.labels('get').startTimer();
clock.tick(500);
end();
var res = getValueByLeAndLabel(0.5, 'method', 'get', instance.get().values);
expect(res.value).to.equal(1);
clock.restore();
});
});
function getValueByName(name, values) {

@@ -108,5 +134,5 @@ return values.reduce(function(acc, val) {

}
function getValueByLabel(label, values) {
function getValueByLeAndLabel(le, key, label, values) {
return values.reduce(function(acc, val) {
if(val.labels && val.labels.le === label) {
if(val.labels && val.labels.le === le && val.labels[key] === label) {
acc = val;

@@ -117,2 +143,11 @@ }

}
function getValueByLabel(label, values, key) {
return values.reduce(function(acc, val) {
console.log('val', val.labels[key]);
if(val.labels && val.labels[key || 'le'] === label) {
acc = val;
}
return acc;
}, {});
}
});

@@ -54,2 +54,27 @@ 'use strict';

describe('should escape', function() {
var escapedResult;
beforeEach(function() {
register.registerMetric({
get: function() {
return {
name: 'test_"_\_\n_metric',
help: 'help_help',
type: 'counter'
};
}
});
escapedResult = register.metrics();
});
it('double quote to /"', function() {
expect(escapedResult).to.match(/\\"/);
});
it('backslash to \\\\', function() {
expect(escapedResult).to.match(/\\\\/);
});
it('newline to \\\\n', function() {
// expect(escapedResult).to.match(/\/);
});
});
function getMetric() {

@@ -56,0 +81,0 @@ return {

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