Socket
Socket
Sign inDemoInstall

jest-html-reporter

Package Overview
Dependencies
518
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.0 to 2.2.0

146

dist/main.js

@@ -70,2 +70,11 @@ 'use strict';

const sortAlphabetically = ({ a, b, reversed }) => {
if ((!reversed && a < b) || (reversed && a > b)) {
return -1;
} else if ((!reversed && a > b) || (reversed && a < b)) {
return 1;
}
return 0;
};
module.exports = {

@@ -75,2 +84,3 @@ logMessage,

createHtmlBase,
sortAlphabetically,
};

@@ -82,2 +92,3 @@ });

var utils_3 = utils.createHtmlBase;
var utils_4 = utils.sortAlphabetically;

@@ -90,5 +101,5 @@ var sorting = createCommonjsModule(function (module) {

* 3. Passed
* @param {Object} suiteResults
* @param {Array} suiteResults
*/
const sortSuiteResultsByStatus = (suiteResults) => {
const byStatus = (suiteResults) => {
const pendingSuites = [];

@@ -128,18 +139,97 @@ const failingSuites = [];

/**
* Sorts test suite results
* If sort is undefined or is not a supported value this has no effect
* @param {Object} suiteResults
* @param {String} sort The configured sort
* Sorts by Execution Time | Descending
* @param {Array} suiteResults
*/
const sortSuiteResults = (suiteResults, sort) => {
if (sort === 'status') {
return sortSuiteResultsByStatus(suiteResults);
const byExecutionDesc = (suiteResults) => {
if (suiteResults) {
suiteResults.sort((a, b) =>
(b.perfStats.end - b.perfStats.start) - (a.perfStats.end - a.perfStats.start));
}
return suiteResults;
};
/**
* Sorts by Execution Time | Ascending
* @param {Array} suiteResults
*/
const byExecutionAsc = (suiteResults) => {
if (suiteResults) {
suiteResults.sort((a, b) =>
(a.perfStats.end - a.perfStats.start) - (b.perfStats.end - b.perfStats.start));
}
return suiteResults;
};
/**
* Sorts by Suite filename and Test name | Descending
* @param {Array} suiteResults
*/
const byTitleDesc = (suiteResults) => {
if (suiteResults) {
// Sort Suites
const sorted = suiteResults.sort((a, b) =>
utils.sortAlphabetically({ a: a.testFilePath, b: b.testFilePath, reversed: true }));
// Sort Suite testResults
sorted.forEach((suite) => {
suite.testResults.sort((a, b) =>
utils.sortAlphabetically({
a: a.ancestorTitles.join(' '),
b: b.ancestorTitles.join(' '),
reversed: true,
}));
});
return sorted;
}
return suiteResults;
};
/**
* Sorts by Suite filename and Test name | Ascending
* @param {Array} suiteResults
*/
const byTitleAsc = (suiteResults) => {
if (suiteResults) {
// Sort Suites
const sorted = suiteResults.sort((a, b) =>
utils.sortAlphabetically({ a: a.testFilePath, b: b.testFilePath }));
// Sort Suite testResults
sorted.forEach((suite) => {
suite.testResults.sort((a, b) =>
utils.sortAlphabetically({
a: a.ancestorTitles.join(' '),
b: b.ancestorTitles.join(' '),
}));
});
return sorted;
}
return suiteResults;
};
/**
* Sorts test suite results with the given method
* @param {Object} testData
* @param {String} sortMethod
*/
const sortSuiteResults = ({ testData, sortMethod }) => {
if (sortMethod) {
switch (sortMethod.toLowerCase()) {
case 'status':
return byStatus(testData);
case 'executiondesc':
return byExecutionDesc(testData);
case 'executionasc':
return byExecutionAsc(testData);
case 'titledesc':
return byTitleDesc(testData);
case 'titleasc':
return byTitleAsc(testData);
default:
return testData;
}
}
return testData;
};
module.exports = {
sortSuiteResults,
sortSuiteResultsByStatus,
};

@@ -149,3 +239,2 @@ });

var sorting_1 = sorting.sortSuiteResults;
var sorting_2 = sorting.sortSuiteResultsByStatus;

@@ -246,3 +335,6 @@ class ReportGenerator {

// Apply the configured sorting of test data
const sortedTestData = sorting.sortSuiteResults(data.testResults, this.config.getSort());
const sortedTestData = sorting.sortSuiteResults({
testData: data.testResults,
sortMethod: this.config.getSort(),
});

@@ -286,2 +378,7 @@ // Test Suites

});
// Custom Javascript
const customScript = this.config.getCustomScriptFilepath();
if (customScript) {
htmlOutput.ele('script', { src: customScript });
}
return resolve(htmlOutput);

@@ -344,2 +441,9 @@ });

/**
* Returns the Custom Script path that should be injected into the test report
* @return {String}
*/
const getCustomScriptFilepath = () =>
process.env.JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH || config.customScriptPath || null;
/**
* Returns the configured test report title

@@ -400,2 +504,3 @@ * @return {String}

getStylesheetFilepath,
getCustomScriptFilepath,
getPageTitle,

@@ -417,10 +522,11 @@ getLogo,

var config_6 = config_1.getStylesheetFilepath;
var config_7 = config_1.getPageTitle;
var config_8 = config_1.getLogo;
var config_9 = config_1.shouldIncludeFailureMessages;
var config_10 = config_1.getExecutionTimeWarningThreshold;
var config_11 = config_1.getTheme;
var config_12 = config_1.getDateFormat;
var config_13 = config_1.getSort;
var config_14 = config_1.getExecutionMode;
var config_7 = config_1.getCustomScriptFilepath;
var config_8 = config_1.getPageTitle;
var config_9 = config_1.getLogo;
var config_10 = config_1.shouldIncludeFailureMessages;
var config_11 = config_1.getExecutionTimeWarningThreshold;
var config_12 = config_1.getTheme;
var config_13 = config_1.getDateFormat;
var config_14 = config_1.getSort;
var config_15 = config_1.getExecutionMode;

@@ -427,0 +533,0 @@ var src = createCommonjsModule(function (module) {

2

dist/main.min.js

@@ -1,1 +0,1 @@

"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var fs=_interopDefault(require("fs")),path=_interopDefault(require("path")),mkdirp=_interopDefault(require("mkdirp")),xmlbuilder=_interopDefault(require("xmlbuilder")),dateformat=_interopDefault(require("dateformat")),stripAnsi=_interopDefault(require("strip-ansi"));function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var utils=createCommonjsModule(function(e){e.exports={logMessage:({type:e,msg:t,ignoreConsole:s})=>{const o={default:"%s",success:"%s",error:"%s"},r=o[e]?o[e]:o.default,n=`jest-html-reporter >> ${t}`;return s||console.log(r,n),{logColor:r,logMsg:n}},writeFile:({filePath:e,content:t})=>new Promise((s,o)=>{mkdirp(path.dirname(e),r=>r?o(new Error(`Something went wrong when creating the folder: ${r}`)):fs.writeFile(e,t,t=>t?o(new Error(`Something went wrong when creating the file: ${t}`)):s(e)))}),createHtmlBase:({pageTitle:e,stylesheet:t})=>xmlbuilder.create({html:{head:{meta:{"@charset":"utf-8"},title:{"#text":e},style:{"@type":"text/css","#text":t}}}})}}),utils_1=utils.logMessage,utils_2=utils.writeFile,utils_3=utils.createHtmlBase,sorting=createCommonjsModule(function(e){const t=e=>{const t=[],s=[],o=[];return e.forEach(e=>{const r=[],n=[],i=[];e.testResults.forEach(e=>{"pending"===e.status?r.push(e):"failed"===e.status?n.push(e):i.push(e)}),r.length&&t.push(Object.assign({},e,{testResults:r})),n.length&&s.push(Object.assign({},e,{testResults:n})),i.length&&o.push(Object.assign({},e,{testResults:i}))}),[].concat(t,s,o)};e.exports={sortSuiteResults:(e,s)=>"status"===s?t(e):e,sortSuiteResultsByStatus:t}}),sorting_1=sorting.sortSuiteResults,sorting_2=sorting.sortSuiteResultsByStatus;class ReportGenerator{constructor(e){this.config=e}generate({data:e,ignoreConsole:t}){const s=this.config.getOutputFilepath();return this.getStylesheetContent().then(t=>this.renderHtmlReport({data:e,stylesheet:t})).then(e=>utils.writeFile({filePath:s,content:e})).then(()=>utils.logMessage({type:"success",msg:`Report generated (${s})`,ignoreConsole:t})).catch(e=>utils.logMessage({type:"error",msg:e,ignoreConsole:t}))}getStylesheetContent(){const e=this.config.getStylesheetFilepath();return new Promise((t,s)=>{fs.readFile(e,"utf8",(o,r)=>o?s(new Error(`Could not locate the stylesheet: '${e}': ${o}`)):t(r))})}renderHtmlReport({data:e,stylesheet:t}){return new Promise((s,o)=>{if(!e)return o(new Error("Test data missing or malformed"));const r=this.config.getPageTitle(),n=utils.createHtmlBase({pageTitle:r,stylesheet:t}),i=n.ele("header");i.ele("h1",{id:"title"},r);const a=this.config.getLogo();a&&i.ele("img",{id:"logo",src:a});const l=n.ele("div",{id:"metadata-container"}),c=new Date(e.startTime);return l.ele("div",{id:"timestamp"},`Start: ${dateformat(c,this.config.getDateFormat())}`),l.ele("div",{id:"summary"},`\n\t\t\t\t${e.numTotalTests} tests --\n\t\t\t\t${e.numPassedTests} passed /\n\t\t\t\t${e.numFailedTests} failed /\n\t\t\t\t${e.numPendingTests} pending\n\t\t\t`),sorting.sortSuiteResults(e.testResults,this.config.getSort()).forEach(e=>{if(!e.testResults||e.testResults.length<=0)return;const t=n.ele("div",{class:"suite-info"});t.ele("div",{class:"suite-path"},e.testFilePath);const s=(e.perfStats.end-e.perfStats.start)/1e3;t.ele("div",{class:`suite-time${s>5?" warn":""}`},`${s}s`);const o=n.ele("table",{class:"suite-table",cellspacing:"0",cellpadding:"0"});e.testResults.forEach(e=>{const t=o.ele("tr",{class:e.status});t.ele("td",{class:"suite"},e.ancestorTitles.join(" > "));const s=t.ele("td",{class:"test"},e.title);if(e.failureMessages&&this.config.shouldIncludeFailureMessages()){const t=s.ele("div",{class:"failureMessages"});e.failureMessages.forEach(e=>{t.ele("pre",{class:"failureMsg"},stripAnsi(e))})}t.ele("td",{class:"result"},"passed"===e.status?`${e.status} in ${e.duration/1e3}s`:e.status)})}),s(n)})}}var reportGenerator=ReportGenerator,config_1=createCommonjsModule(function(e){const t={},s=e=>Object.assign(t,e),o=()=>process.env.JEST_HTML_REPORTER_THEME||t.theme||"defaultTheme";e.exports={config:t,setup:()=>{try{const e=fs.readFileSync(path.join(process.cwd(),"jesthtmlreporter.config.json"),"utf8");if(e)return s(JSON.parse(e))}catch(e){}try{const e=fs.readFileSync(path.join(process.cwd(),"package.json"),"utf8");if(e)return s(JSON.parse(e)["jest-html-reporter"])}catch(e){}return t},setConfigData:s,getOutputFilepath:()=>process.env.JEST_HTML_REPORTER_OUTPUT_PATH||t.outputPath||path.join(process.cwd(),"test-report.html"),getStylesheetFilepath:()=>process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH||t.styleOverridePath||path.join(__dirname,`../style/${o()}.css`),getPageTitle:()=>process.env.JEST_HTML_REPORTER_PAGE_TITLE||t.pageTitle||"Test report",getLogo:()=>process.env.JEST_HTML_REPORTER_LOGO||t.logo||null,shouldIncludeFailureMessages:()=>process.env.JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG||t.includeFailureMsg||!1,getExecutionTimeWarningThreshold:()=>process.env.JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD||t.executionTimeWarningThreshold||5,getTheme:o,getDateFormat:()=>process.env.JEST_HTML_REPORTER_DATE_FORMAT||t.dateFormat||"yyyy-mm-dd HH:MM:ss",getSort:()=>process.env.JEST_HTML_REPORTER_SORT||t.sort||"default",getExecutionMode:()=>process.env.JEST_HTML_REPORTER_EXECUTION_MODE||t.executionMode||"testResultsProcessor"}}),config_2=config_1.config,config_3=config_1.setup,config_4=config_1.setConfigData,config_5=config_1.getOutputFilepath,config_6=config_1.getStylesheetFilepath,config_7=config_1.getPageTitle,config_8=config_1.getLogo,config_9=config_1.shouldIncludeFailureMessages,config_10=config_1.getExecutionTimeWarningThreshold,config_11=config_1.getTheme,config_12=config_1.getDateFormat,config_13=config_1.getSort,config_14=config_1.getExecutionMode,src=createCommonjsModule(function(e){config_1.setup();const t=new reportGenerator(config_1);e.exports="reporter"===config_1.getExecutionMode()?class{constructor(e,t){this.jestConfig=e,this.jestOptions=t}onRunComplete(e,s){return this.testResult=s,t.generate({data:s})}}:e=>(t.generate({data:e}),e)});module.exports=src;
"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var fs=_interopDefault(require("fs")),path=_interopDefault(require("path")),mkdirp=_interopDefault(require("mkdirp")),xmlbuilder=_interopDefault(require("xmlbuilder")),dateformat=_interopDefault(require("dateformat")),stripAnsi=_interopDefault(require("strip-ansi"));function createCommonjsModule(e,t){return e(t={exports:{}},t.exports),t.exports}var utils=createCommonjsModule(function(e){e.exports={logMessage:({type:e,msg:t,ignoreConsole:s})=>{const r={default:"%s",success:"%s",error:"%s"},o=r[e]?r[e]:r.default,n=`jest-html-reporter >> ${t}`;return s||console.log(o,n),{logColor:o,logMsg:n}},writeFile:({filePath:e,content:t})=>new Promise((s,r)=>{mkdirp(path.dirname(e),o=>o?r(new Error(`Something went wrong when creating the folder: ${o}`)):fs.writeFile(e,t,t=>t?r(new Error(`Something went wrong when creating the file: ${t}`)):s(e)))}),createHtmlBase:({pageTitle:e,stylesheet:t})=>xmlbuilder.create({html:{head:{meta:{"@charset":"utf-8"},title:{"#text":e},style:{"@type":"text/css","#text":t}}}}),sortAlphabetically:({a:e,b:t,reversed:s})=>!s&&e<t||s&&e>t?-1:!s&&e>t||s&&e<t?1:0}}),utils_1=utils.logMessage,utils_2=utils.writeFile,utils_3=utils.createHtmlBase,utils_4=utils.sortAlphabetically,sorting=createCommonjsModule(function(e){e.exports={sortSuiteResults:({testData:e,sortMethod:t})=>{if(t)switch(t.toLowerCase()){case"status":return(e=>{const t=[],s=[],r=[];return e.forEach(e=>{const o=[],n=[],i=[];e.testResults.forEach(e=>{"pending"===e.status?o.push(e):"failed"===e.status?n.push(e):i.push(e)}),o.length&&t.push(Object.assign({},e,{testResults:o})),n.length&&s.push(Object.assign({},e,{testResults:n})),i.length&&r.push(Object.assign({},e,{testResults:i}))}),[].concat(t,s,r)})(e);case"executiondesc":return(e=>(e&&e.sort((e,t)=>t.perfStats.end-t.perfStats.start-(e.perfStats.end-e.perfStats.start)),e))(e);case"executionasc":return(e=>(e&&e.sort((e,t)=>e.perfStats.end-e.perfStats.start-(t.perfStats.end-t.perfStats.start)),e))(e);case"titledesc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath,reversed:!0}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" "),reversed:!0}))}),t}return e})(e);case"titleasc":return(e=>{if(e){const t=e.sort((e,t)=>utils.sortAlphabetically({a:e.testFilePath,b:t.testFilePath}));return t.forEach(e=>{e.testResults.sort((e,t)=>utils.sortAlphabetically({a:e.ancestorTitles.join(" "),b:t.ancestorTitles.join(" ")}))}),t}return e})(e);default:return e}return e}}}),sorting_1=sorting.sortSuiteResults;class ReportGenerator{constructor(e){this.config=e}generate({data:e,ignoreConsole:t}){const s=this.config.getOutputFilepath();return this.getStylesheetContent().then(t=>this.renderHtmlReport({data:e,stylesheet:t})).then(e=>utils.writeFile({filePath:s,content:e})).then(()=>utils.logMessage({type:"success",msg:`Report generated (${s})`,ignoreConsole:t})).catch(e=>utils.logMessage({type:"error",msg:e,ignoreConsole:t}))}getStylesheetContent(){const e=this.config.getStylesheetFilepath();return new Promise((t,s)=>{fs.readFile(e,"utf8",(r,o)=>r?s(new Error(`Could not locate the stylesheet: '${e}': ${r}`)):t(o))})}renderHtmlReport({data:e,stylesheet:t}){return new Promise((s,r)=>{if(!e)return r(new Error("Test data missing or malformed"));const o=this.config.getPageTitle(),n=utils.createHtmlBase({pageTitle:o,stylesheet:t}),i=n.ele("header");i.ele("h1",{id:"title"},o);const a=this.config.getLogo();a&&i.ele("img",{id:"logo",src:a});const l=n.ele("div",{id:"metadata-container"}),c=new Date(e.startTime);l.ele("div",{id:"timestamp"},`Start: ${dateformat(c,this.config.getDateFormat())}`),l.ele("div",{id:"summary"},`\n\t\t\t\t${e.numTotalTests} tests --\n\t\t\t\t${e.numPassedTests} passed /\n\t\t\t\t${e.numFailedTests} failed /\n\t\t\t\t${e.numPendingTests} pending\n\t\t\t`),sorting.sortSuiteResults({testData:e.testResults,sortMethod:this.config.getSort()}).forEach(e=>{if(!e.testResults||e.testResults.length<=0)return;const t=n.ele("div",{class:"suite-info"});t.ele("div",{class:"suite-path"},e.testFilePath);const s=(e.perfStats.end-e.perfStats.start)/1e3;t.ele("div",{class:`suite-time${s>5?" warn":""}`},`${s}s`);const r=n.ele("table",{class:"suite-table",cellspacing:"0",cellpadding:"0"});e.testResults.forEach(e=>{const t=r.ele("tr",{class:e.status});t.ele("td",{class:"suite"},e.ancestorTitles.join(" > "));const s=t.ele("td",{class:"test"},e.title);if(e.failureMessages&&this.config.shouldIncludeFailureMessages()){const t=s.ele("div",{class:"failureMessages"});e.failureMessages.forEach(e=>{t.ele("pre",{class:"failureMsg"},stripAnsi(e))})}t.ele("td",{class:"result"},"passed"===e.status?`${e.status} in ${e.duration/1e3}s`:e.status)})});const u=this.config.getCustomScriptFilepath();return u&&n.ele("script",{src:u}),s(n)})}}var reportGenerator=ReportGenerator,config_1=createCommonjsModule(function(e){const t={},s=e=>Object.assign(t,e),r=()=>process.env.JEST_HTML_REPORTER_THEME||t.theme||"defaultTheme";e.exports={config:t,setup:()=>{try{const e=fs.readFileSync(path.join(process.cwd(),"jesthtmlreporter.config.json"),"utf8");if(e)return s(JSON.parse(e))}catch(e){}try{const e=fs.readFileSync(path.join(process.cwd(),"package.json"),"utf8");if(e)return s(JSON.parse(e)["jest-html-reporter"])}catch(e){}return t},setConfigData:s,getOutputFilepath:()=>process.env.JEST_HTML_REPORTER_OUTPUT_PATH||t.outputPath||path.join(process.cwd(),"test-report.html"),getStylesheetFilepath:()=>process.env.JEST_HTML_REPORTER_STYLE_OVERRIDE_PATH||t.styleOverridePath||path.join(__dirname,`../style/${r()}.css`),getCustomScriptFilepath:()=>process.env.JEST_HTML_REPORTER_CUSTOM_SCRIPT_PATH||t.customScriptPath||null,getPageTitle:()=>process.env.JEST_HTML_REPORTER_PAGE_TITLE||t.pageTitle||"Test report",getLogo:()=>process.env.JEST_HTML_REPORTER_LOGO||t.logo||null,shouldIncludeFailureMessages:()=>process.env.JEST_HTML_REPORTER_INCLUDE_FAILURE_MSG||t.includeFailureMsg||!1,getExecutionTimeWarningThreshold:()=>process.env.JEST_HTML_REPORTER_EXECUTION_TIME_WARNING_THRESHOLD||t.executionTimeWarningThreshold||5,getTheme:r,getDateFormat:()=>process.env.JEST_HTML_REPORTER_DATE_FORMAT||t.dateFormat||"yyyy-mm-dd HH:MM:ss",getSort:()=>process.env.JEST_HTML_REPORTER_SORT||t.sort||"default",getExecutionMode:()=>process.env.JEST_HTML_REPORTER_EXECUTION_MODE||t.executionMode||"testResultsProcessor"}}),config_2=config_1.config,config_3=config_1.setup,config_4=config_1.setConfigData,config_5=config_1.getOutputFilepath,config_6=config_1.getStylesheetFilepath,config_7=config_1.getCustomScriptFilepath,config_8=config_1.getPageTitle,config_9=config_1.getLogo,config_10=config_1.shouldIncludeFailureMessages,config_11=config_1.getExecutionTimeWarningThreshold,config_12=config_1.getTheme,config_13=config_1.getDateFormat,config_14=config_1.getSort,config_15=config_1.getExecutionMode,src=createCommonjsModule(function(e){config_1.setup();const t=new reportGenerator(config_1);e.exports="reporter"===config_1.getExecutionMode()?class{constructor(e,t){this.jestConfig=e,this.jestOptions=t}onRunComplete(e,s){return this.testResult=s,t.generate({data:s})}}:e=>(t.generate({data:e}),e)});module.exports=src;
{
"name": "jest-html-reporter",
"version": "2.1.0",
"version": "2.2.0",
"description": "Jest test results processor for generating a summary in HTML",

@@ -5,0 +5,0 @@ "main": "dist/main",

@@ -70,2 +70,3 @@ <p align="center">

| `styleOverridePath` | `STRING` | The path to a file containing CSS styles that should override the default styling.* | `null`
| `customScriptPath` | `STRING` | Path to a javascript file that should be injected into the test report | `null`
| `theme` | `STRING` | The name of the reporter themes to use when rendering the report. You can find the available themes in the [Documentation](https://github.com/Hargne/jest-html-reporter/wiki/Test-Report-Themes) | `"defaultTheme"`

@@ -75,3 +76,3 @@ | `logo` | `STRING` | Path to a logo that will be included in the header of the report | `null`

| `dateFormat` | `STRING` | The format in which date/time should be formatted in the test report. Have a look in the [Documentation](https://github.com/Hargne/jest-html-reporter/wiki/Date-Format) for the available date format variables. | `"yyyy-mm-dd HH:MM:ss"`
| `sort` | `STRING` | Sorts the test results with the given method. Available methods are: `"default"`, `"status"` More information can be found in the [Documentation](https://github.com/Hargne/jest-html-reporter/wiki/Sorting-Methods). | `"default"`
| `sort` | `STRING` | Sorts the test results using the given method. Available sorting methods can be found in the [Documentation](https://github.com/Hargne/jest-html-reporter/wiki/Sorting-Methods). | `"default"`
| `executionMode` | `STRING` | Defines the execution mode. Avaiable modes are: `reporter`, `testResultsProcessor` | `"testResultsProcessor"`

@@ -78,0 +79,0 @@

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