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

pactum

Package Overview
Dependencies
Maintainers
1
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pactum - npm Package Compare versions

Comparing version 1.0.13 to 1.0.14

src/helpers/logger.js

6

package.json
{
"name": "pactum",
"version": "1.0.13",
"version": "1.0.14",
"description": "REST API endpoint testing tool with a mock server & compatible with pact.io for contract testing",

@@ -10,3 +10,4 @@ "main": "src/index.js",

"scripts": {
"test": "mocha --timeout 10000 ./test/unit/",
"test": "mocha --timeout 10000 ./test/**/*",
"test:unit": "mocha --timeout 10000 ./test/unit/",
"test:component": "mocha --timeout 10000 ./test/component/"

@@ -41,2 +42,3 @@ },

"phin": "^3.4.1",
"pino": "^5.16.0",
"polka": "^0.5.2"

@@ -43,0 +45,0 @@ },

# pactum
## Introduction
pactum is a REST API Testing Tool that combines the implementation of consumer driven contract library [Pact](https://docs.pact.io) for Javascript.
**pactum** is a REST API Testing Tool that combines the implementation of consumer driven contract library [Pact](https://docs.pact.io) for JavaScript.
## Documentation
* [Pactum](https://github.com/ASaiAnudeep/pactum/wiki)
* [Component Testing](https://github.com/ASaiAnudeep/pactum/wiki/Component-Testing)
* [Mock Server](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server)
## Installation
```
```Shell
npm install --save-dev pactum
npm install --save-dev mocha
```
## Table of contents
## Usage
* [Introduction](#introduction)
* [Installation](#installation)
* [Table of contents](#table-of-contents)
* [Usage](#usage)
* [Getting Started](#getting-started)
* [Basics](#basics)
* [HTTP Request](#http-request)
* [Component Testing](#component-testing)
* [Component Testing with Mock Server](#component-testing-with-mock-server)
* [Mock Interaction](#mock-interaction)
* [Contract Testing with Mock Server](#contract-testing-with-mock-server)
* [Pact Interaction](#pact-interaction)
* [Mock Server](#mock-server)
### Component Testing
## Usage
Running a single component test expectation.
```javascript
```JavaScript
const pactum = require('pactum');

@@ -44,3 +34,3 @@

```bash
```Shell
# mocha is a test framework

@@ -50,46 +40,10 @@ mocha /path/to/test

Running a component test with the help of a mock server & a single mock interaction. If the mock interaction is not exercised, the test will fail.
Learn more about pactum as a component tester [here](https://github.com/ASaiAnudeep/pactum/wiki/Component-Testing)
```javascript
const pactum = require('pactum');
### Contract Testing
before(async () => {
await pactum.mock.start();
});
Running a contract test with the help of a mock server & a single pact interaction.
If the pact interaction is not exercised, the test will fail.
it('GET - one interaction', async () => {
await pactum
.addMockInteraction({
withRequest: {
method: 'GET',
path: '/api/projects/1'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 1,
name: 'fake'
}
}
})
.get('http://localhost:9393/api/projects/1')
.expectStatus(200)
.expectJsonLike({
id: 1,
name: 'fake'
})
.toss();
});
after(async () => {
await pactum.mock.stop();
});
```
Running a component test with the help of a mock server & a single pact interaction. If the mock interaction is not exercised, the test will fail.
```javascript
```JavaScript
const pactum = require('pactum');

@@ -137,501 +91,8 @@

### Mock Server
## Getting Started
Running **pactum** as a standalone mock server.
### Basics
| Method | Description |
| ---------- | -------------------------------------------- |
| get | performs a GET request on the resource |
| expectStatus | expects a status code on the response |
| toss | executes the test case and returns a promise |
Create a javascript file named `test.js`
```javascript
// imports pactum library
const pactum = require('pactum');
// this is a test step in mocha
it('should be a teapot', async () => {
await pactum // pactum returns a promise
.get('http://httpbin.org/status/200') // will fetch a response from the url
.expectStatus(200) // sets an expectation on the response
.toss(); // executes the test case
});
```
Running the test with [mocha](https://mochajs.org/#getting-started)
```bash
mocha /path/to/test.js
```
[^ TOC](#table-of-contents)
### HTTP Request
HTTP requests are messages sent by the client to initiate an action on the server.
| Method | Description |
| -------------------------- | ------------------------------------------------- |
| `get('url')` | this is a HTTP method to be performed on resource |
| `withQuery('postId', '1')` | set of parameters attached to the url |
| `withHeaders({})` | request headers |
| `withBody('Hello')` | request body |
| `withJson({id: 1})` | request json object |
```javascript
const pactum = require('pactum');
// performs a get request with query
it('GET - with query', async () => {
await pactum
.get('https://jsonplaceholder.typicode.com/comments')
.withQuery('postId', 1)
.withQuery('id', 1)
.expectStatus(200)
.toss();
});
// performs a post request with JSON
it('POST', async () => {
await pactum
.post('https://jsonplaceholder.typicode.com/posts')
.withJson({
title: 'foo',
body: 'bar',
userId: 1
})
.expectStatus(201)
.toss();
});
// performs a post request with headers & body
it('POST - with body', async () => {
await pactum
.post('https://jsonplaceholder.typicode.com/posts')
.withHeaders({
"content-type": "application/json"
})
.withBody({
title: 'foo',
body: 'bar',
userId: 1
})
.expectStatus(201)
.toss();
});
```
[^ TOC](#table-of-contents)
#### HTTP Methods
The request method indicates the method to be performed on the resource identified by the given Request-URI.
| Method | Description | Usage |
| -------- | ------------------------------------------ | ------------------------------------- |
| get | performs a GET request on the resource | `await pactum.get('url').toss()` |
| post | performs a POST request on the resource | `await pactum.post('url').toss()` |
| put | performs a PUT request on the resource | `await pactum.put('url').toss()` |
| delete | performs a DELETE request on the resource | `await pactum.delete('url').toss()` |
| patch | performs a PATCH request on the resource | `await pactum.patch('url').toss()` |
| head | performs a HEAD request on the resource | `await pactum.head('url').toss()` |
```javascript
// performs a delete request
it('DELETE', async () => {
await pactum
.delete('https://jsonplaceholder.typicode.com/posts/1')
.expectStatus(200)
.toss();
});
```
[^ TOC](#table-of-contents)
### HTTP Expectations
| Method | Description |
| --------------------------------------- | --------------------------------------------------------------------------- |
| `expectStatus(201)` | check HTTP status |
| `expectHeader('key', 'value')` | check HTTP header key + value (RegExp) |
| `expectHeaderContains('key', 'value')` | check HTTP header key contains partial value (RegExp) |
| `expectBody('value')` | check exact match of body |
| `expectBodyContains('value')` | check body contains the value (RegExp) |
| `expectJson({json})` | check exact match of json |
| `expectJsonLike({json})` | check json contains partial value (RegExp) |
| `expectJsonSchema({schema})` | validate [json-schema](https://json-schema.org/learn/) |
| `expectJsonQuery('path', 'value')` | validate [json-query](https://www.npmjs.com/package/json-query) |
| `expectResponseTime(10)` | check if request completes within a specified duration (ms) |
```javascript
const pactum = require('pactum');
it('GET', async () => {
await pactum
.get('https://jsonplaceholder.typicode.com/posts/1')
.expectStatus(200)
.expectHeader('content-type', 'application/json; charset=utf-8')
.expectHeader('connection', /\w+/)
.expectHeaderContains('content-type', 'application/json')
.expectJson({
"userId": 1,
"id": 1,
"title": "some title",
"body": "some body"
})
.expectJsonLike({
userId: 1,
id: 1
})
.expectJsonLike({
title: "some title",
body: "some body"
})
.expectJsonSchema({
"properties": {
"userId": {
"type": "number"
}
},
"required": ["userId", "id"]
})
.expectJsonSchema({
"properties": {
"title": {
"type": "string"
}
},
"required": ["title", "body"]
})
.expectResponseTime(1000)
.toss();
});
```
[^ TOC](#table-of-contents)
## Component Testing
Read more about testing RESTFull services [here](#http-request)
Component testing is defined as a software testing type, in which the testing is performed on each individual component separately without integrating with other components.
Lets assume you have an order-service which is a RESTFull API service running on port 3000.
Here if the order-service has external dependencies, they are mocked using different library.
```javascript
const pactum = require('pactum');
it('should fetch order details', async () => {
await pactum
// assume you have an order-service running locally on port 3000
.get('http://localhost:3000/api/orders/123')
// set an expectation of 200
.expectStatus(200)
// set an expectation on the response body
.expectJson({
orderId: '123',
price: '3030',
currency: 'INR'
})
// execute the test case
.toss();
});
```
[^ TOC](#table-of-contents)
## Component Testing with Mock Server
Read more about component testing [here](#component-testing)
Lets assume you have an order-service which is a RESTFull API service running on port 3000. Consider if order-service depends on a currency-service to fetch the prices.
Start the order-service & overwrite the endpoint of currency-service to http://localhost:9393
```javascript
const pactum = require('pactum');
before(async () => {
// starts the mock service on port 9393 (port number can be modified)
await pactum.mock.start();
});
it('should fetch order details', async () => {
await pactum
// here we are training the mock server to act as currency-service
// if the order-service fails to make a call to this endpoint then
// the test will fail with - Interaction Not Exercised
// after the execution of this spec, the mock interaction will be removed
.addMockInteraction({
withRequest: {
method: 'GET',
path: '/api/currency/INR'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 'INR',
description: 'Indian Rupee'
}
}
})
.get('http://localhost:3000/api/orders/123')
.expectStatus(200)
.expectJson({
orderId: '123',
price: '3030',
currency: 'INR'
})
.toss();
});
after(async () => {
// stops the mock service on port 9393 (port number can be modified)
await pactum.mock.stop();
});
```
If the order-service interacts with multiple services, we can add multiple mock interactions.
```javascript
const pactum = require('pactum');
before(async () => {
await pactum.mock.start();
});
it('should fetch order details', async () => {
await pactum
// if the order-service fails to make a call to this endpoint then
// the test will fail with - Interaction Not Exercised
.addMockInteraction({
withRequest: {
method: 'GET',
path: '/api/currency/INR'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 'INR',
description: 'Indian Rupee'
}
}
})
// if the order-service fails to make a call to this endpoint then
// the test will fail with - Interaction Not Exercised
.addMockInteraction({
withRequest: {
method: 'GET',
path: '/api/allocations/123'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: [12, 13]
}
})
.get('http://localhost:3000/api/orders/123')
.expectStatus(200)
.expectJson({
orderId: '123',
price: '3030',
currency: 'INR'
})
.toss();
});
after(async () => {
await pactum.mock.stop();
});
```
The scope of each interaction added through `pactum.addMockInteraction()` will be restricted to current spec (`it` block)
To add mock interactions that will be consumed in all the specs use - `pactum.mock.addDefaultMockInteraction()`
```javascript
const pactum = require('pactum');
before(async () => {
await pactum.mock.start();
// adds a default mock interaction
pactum.mock.addDefaultMockInteraction({
withRequest: {
method: 'GET',
path: '/api/currency/INR'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 'INR',
description: 'Indian Rupee'
}
}
})
});
it('should fetch order details for id 123', async () => {
await pactum
.get('http://localhost:3000/api/orders/123')
.expectStatus(200)
.expectJson({
orderId: '123',
price: '3030',
currency: 'INR'
})
.toss();
});
it('should fetch order details for id 124', async () => {
await pactum
.get('http://localhost:3000/api/orders/124')
.expectStatus(200)
.expectJson({
orderId: '124',
price: '5643',
currency: 'INR'
})
.toss();
});
after(async () => {
// removes all default interactions
pactum.mock.removeDefaultInteractions();
await pactum.mock.stop();
});
```
[^ TOC](#table-of-contents)
### Mock Interaction
Methods to add a mock interaction:
* `pactum.addMockInteraction()`
* `pactum.mock.addDefaultMockInteraction()`
| Property | Type | Required | Description |
| ----------------------- | ------- | -------- | -------------------------- |
| id | string | optional | id of the interaction |
| consumer | string | optional | name of the consumer |
| provider | string | optional | name of the provider |
| state | string | optional | state of the provider |
| uponReceiving | string | optional | description of the request |
| withRequest | object | required | request details |
| withRequest.method | string | required | HTTP method |
| withRequest.path | string | required | api path |
| withRequest.headers | object | optional | request headers |
| withRequest.query | object | optional | query parameters |
| withRequest.body | any | optional | request body |
| withRequest.ignoreQuery | boolean | optional | ignore request query |
| withRequest.ignoreBody | boolean | optional | ignore request body |
| willRespondWith | object | required | response details |
| willRespondWith.status | number | required | response status code |
| willRespondWith.headers | object | optional | response headers |
| willRespondWith.body | any | required | response body |
## Contract Testing with Mock Server
Contract testing is a technique for testing an integration point by checking each application in isolation to ensure the messages it sends or receives conform to a shared understanding that is documented in a **contract**.
In a single spec we can use multiple mock interactions & multiple pact interactions.
```javascript
const pactum = require('pactum');
before(async () => {
await pactum.mock.start();
});
it('should fetch order details', async () => {
await pactum
// here we are training the mock server to act as currency-service
// if the order-service fails to make a call to this endpoint then
// the test will fail with - Interaction Not Exercised
// after the execution of this spec, the pact interaction will be removed
.addPactInteraction({
consumer: 'order-service',
provider: 'currency-service',
state: 'there is INR currency',
uponReceiving: 'a request for INR currency',
withRequest: {
method: 'GET',
path: '/api/currency/INR'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 'INR',
description: 'Indian Rupee'
}
}
})
.get('http://localhost:3000/api/orders/123')
.expectStatus(200)
.expectJson({
orderId: '123',
price: '3030',
currency: 'INR'
})
.toss();
});
after(async () => {
await pactum.mock.stop();
});
```
[^ TOC](#table-of-contents)
### Pact Interaction
Methods to add a pact interaction:
* `pactum.addPactInteraction()`
* `pactum.mock.addDefaultPactInteraction()`
| Property | Type | Required | Description |
| ----------------------- | ------- | -------- | -------------------------- |
| id | string | optional | id of the interaction |
| consumer | string | required | name of the consumer |
| provider | string | required | name of the provider |
| state | string | required | state of the provider |
| uponReceiving | string | required | description of the request |
| withRequest | object | required | request details |
| withRequest.method | string | required | HTTP method |
| withRequest.path | string | required | api path |
| withRequest.headers | object | optional | request headers |
| withRequest.query | object | optional | query parameters |
| withRequest.body | any | optional | request body |
| willRespondWith | object | required | response details |
| willRespondWith.status | number | required | response status code |
| willRespondWith.headers | object | optional | response headers |
| willRespondWith.body | any | required | response body |
[^ TOC](#table-of-contents)
## Mock Server
Pactum can also be used as a mock server
```javascript
// The below code will run the pactum server on port 3000
const pactum = require('pactum');
pactum.mock.setDefaultPort(3000);

@@ -641,48 +102,2 @@ pactum.mock.start();

Use `addDefaultMockInteraction()` and `addDefaultPactInteraction()` to add default interactions to the mock server.
To add multiple use `addDefaultMockInteractions()` and `addDefaultPactInteractions()`.
```javascript
// The below code will run the pactum server on port 3000
// with a mock interaction & a pact interaction
const pactum = require('pactum');
pactum.mock.setDefaultPort(3000);
pactum.mock.addDefaultMockInteraction({
withRequest: {
method: 'GET',
path: '/api/projects/1'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 1,
name: 'fake'
}
}
});
pactum.mock.addDefaultPactInteraction({
consumer: 'consumer-service',
provider: 'project-service',
state: 'there is a project with id 1',
uponReceiving: 'a request for project 1',
withRequest: {
method: 'GET',
path: '/api/projects/1'
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/json'
},
body: {
id: 1,
name: 'fake'
}
}
});
pactum.mock.start();
```
Learn more about pactum as a mock server [here](https://github.com/ASaiAnudeep/pactum/wiki/Mock-Server)

@@ -164,12 +164,27 @@ const helper = {

getValidInteraction(req, interactions) {
for (let [id, interaction] of interactions) {
/**
* returns a matching interaction
* @param {object} req - req object
* @param {Map<string, object>} interactions - interactions
*/
getMatchingInteraction(req, interactions) {
const ids = Array.from(interactions.keys());
for (let i = ids.length - 1; i >= 0; i--) {
const interaction = interactions.get(ids[i]);
const isValidMethod = (interaction.withRequest.method === req.method);
if (!isValidMethod) {
continue;
}
const isValidPath = (interaction.withRequest.path === req.path);
if (!isValidPath) {
continue;
}
let isValidQuery = true;
if (Object.keys(req.query).length > 0) {
isValidQuery = this.validateQuery(req.query, interaction.withRequest.query);
if (!interaction.withRequest.ignoreQuery) {
if (Object.keys(req.query).length > 0 || interaction.withRequest.query) {
isValidQuery = this.validateQuery(req.query, interaction.withRequest.query);
}
}
if (interaction.withRequest.ignoreQuery) {
isValidQuery = true;
if (!isValidQuery) {
continue;
}

@@ -181,12 +196,11 @@ let isValidHeaders = true;

let isValidBody = true;
if (typeof req.body === 'object') {
if (Object.keys(req.body).length > 0) {
if (!interaction.withRequest.ignoreBody) {
if (typeof req.body === 'object') {
if (Object.keys(req.body).length > 0) {
isValidBody = this.validateBody(req.body, interaction.withRequest.body);
}
} else if (req.body) {
isValidBody = this.validateBody(req.body, interaction.withRequest.body);
}
} else if (req.body) {
isValidBody = this.validateBody(req.body, interaction.withRequest.body);
}
if (interaction.withRequest.ignoreBody) {
isValidBody = true;
}
if (isValidMethod && isValidPath && isValidQuery && isValidHeaders && isValidBody) {

@@ -193,0 +207,0 @@ return interaction;

const fs = require('fs');
const config = require('../config');
const helper = require('./helper');
const log = require('./logger');
const Interaction = require('../models/interaction');

@@ -69,5 +70,10 @@ const { Contract, PactInteraction } = require('../models/contract');

if (!this.interactionExerciseCounter.has(id)) {
console.log('PACTUM', 'Pact interaction not exercised');
console.log('PACTUM', 'Request', interaction.request);
console.log('PACTUM', 'Response', { status: interaction.response.status, body: interaction.response.body });
log.warn('Pact interaction not exercised');
log.warn({
request: interaction.request,
response: {
status: interaction.response.status,
body: interaction.response.body
}
});
}

@@ -74,0 +80,0 @@ delete interaction.id;

@@ -7,2 +7,3 @@ const polka = require('polka');

const store = require('../helpers/store');
const log = require('../helpers/logger')
const config = require('../config');

@@ -19,2 +20,3 @@

start() {
log.trace(`Starting mock server on port ${config.mock.port}`);
return new Promise((resolve) => {

@@ -27,5 +29,7 @@ if (!this.app) {

this.app.listen(config.mock.port, () => {
console.log('PACTUM mock server is listening on port', config.mock.port);
log.info(`Mock server is listening on port ${config.mock.port}`);
resolve();
});
} else {
log.warn(`Mock server is already running on port ${config.mock.port}`);
}

@@ -36,10 +40,11 @@ });

stop() {
log.trace(`Stopping mock server on port ${config.mock.port}`);
return new Promise((resolve) => {
if (this.app) {
this.app.server.close(() => {
console.log('PACTUM mock server stopped on port', config.mock.port);
log.info(`Mock server stopped on port ${config.mock.port}`);
resolve();
});
} else {
console.log('No PACTUM mock server is running on port', config.mock.port);
log.warn(`Mock server is not running on port ${config.mock.port}`);
resolve();

@@ -102,5 +107,5 @@ }

let interactionExercised = false;
let interaction = helper.getValidInteraction(req, server.pactInteractions);
let interaction = helper.getMatchingInteraction(req, server.pactInteractions);
if (!interaction) {
interaction = helper.getValidInteraction(req, server.mockInteractions);
interaction = helper.getMatchingInteraction(req, server.mockInteractions);
}

@@ -120,8 +125,10 @@ if (interaction) {

if (!interactionExercised) {
console.log();
console.log('PACTUM', 'Interaction not found for');
console.log('PACTUM', req.method, req.path);
console.log('PACTUM', 'Headers -', req.headers);
console.log('PACTUM', 'Query -', req.query);
console.log('PACTUM', 'Body -', req.body);
log.warn('Interaction not found');
log.warn({
method: req.method,
path: req.path,
headers: req.headers,
query: req.query,
body: req.body
});
res.status(404);

@@ -147,3 +154,3 @@ res.send('Interaction Not Found');

} catch (error) {
console.log(`Error saving mock interaction - ${error}`);
log.error(`Error saving mock interaction - ${error}`);
res.status(400);

@@ -165,3 +172,3 @@ res.send({ error: error.message });

} catch (error) {
console.error(`Error fetching mock interaction - ${error}`);
log.error(`Error fetching mock interaction - ${error}`);
res.status(500);

@@ -181,3 +188,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error fetching mock interactions - ${error}`);
log.error(`Error fetching mock interactions - ${error}`);
res.status(500);

@@ -200,3 +207,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error deleting mock interaction - ${error}`);
log.error(`Error deleting mock interaction - ${error}`);
res.status(500);

@@ -213,3 +220,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error deleting mock interactions - ${error}`);
log.error(`Error deleting mock interactions - ${error}`);
res.status(500);

@@ -228,3 +235,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.log(`Error saving pact interaction - ${error}`);
log.error(`Error saving pact interaction - ${error}`);
res.status(400);

@@ -246,3 +253,3 @@ res.send({ error: error.message });

} catch (error) {
console.error(`Error fetching pact interaction - ${error}`);
log.error(`Error fetching pact interaction - ${error}`);
res.status(500);

@@ -262,3 +269,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error fetching pact interactions - ${error}`);
log.error(`Error fetching pact interactions - ${error}`);
res.status(500);

@@ -281,3 +288,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error deleting mock interaction - ${error}`);
log.error(`Error deleting mock interaction - ${error}`);
res.status(500);

@@ -294,3 +301,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.error(`Error deleting pact interactions - ${error}`);
log.error(`Error deleting pact interactions - ${error}`);
res.status(500);

@@ -307,3 +314,3 @@ res.send({ error: `Internal System Error` });

} catch (error) {
console.log(`Error saving pacts - ${error}`);
log.error(`Error saving pacts - ${error}`);
res.status(500);

@@ -345,2 +352,3 @@ res.send({ error: error.message });

if (typeof data === 'object') {
this.res.setHeader('Content-Type', 'application/json');
this.res.end(JSON.stringify(data));

@@ -347,0 +355,0 @@ } else {

@@ -5,2 +5,3 @@ const phin = require('phin');

const helper = require('../helpers/helper');
const log = require('../helpers/logger');
const { PactumRequestError } = require('../helpers/errors');

@@ -486,3 +487,2 @@

if (error.response) {
console.log('Normal', error);
this._response = error.response;

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

err = error
console.log('Error performing request', error);
log.warn('Error performing request', error);
this._response = error;

@@ -495,0 +495,0 @@ }

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