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

@hcaptcha/react-hcaptcha

Package Overview
Dependencies
Maintainers
4
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hcaptcha/react-hcaptcha - npm Package Compare versions

Comparing version 0.2.2 to 0.3.0-alpha

.github/workflows/ci.yml

244

dist/index.js

@@ -1,18 +0,36 @@

'use strict';
"use strict";
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var React = require('react');
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
// Borrowed from https://github.com/ai/nanoid/blob/3.0.2/non-secure/index.js
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var React = require('react'); // Borrowed from https://github.com/ai/nanoid/blob/3.0.2/non-secure/index.js
// This alphabet uses `A-Za-z0-9_-` symbols. A genetic algorithm helped
// optimize the gzip compression for this alphabet.
var urlAlphabet = 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW';

@@ -22,6 +40,6 @@

var size = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 21;
var id = ''; // A compact alternative for `for (var i = 0; i < step; i++)`.
var id = '';
// A compact alternative for `for (var i = 0; i < step; i++)`.
var i = size;
while (i--) {

@@ -31,10 +49,10 @@ // `| 0` is more compact and faster than `Math.floor()`.

}
return id;
};
}; // Create script to init hCaptcha
// Create script to init hCaptcha
var onLoadListeners = [];
var captchaScriptCreated = false;
var captchaScriptCreated = false; // Generate hCaptcha API Script
// Generate hCaptcha API Script
var CaptchaScript = function CaptchaScript(hl, reCaptchaCompat) {

@@ -53,5 +71,7 @@ // Create global onload callback

script.async = true;
if (hl) {
script.src += '&hl=' + hl;
script.src += "&hl=".concat(hl);
}
if (reCaptchaCompat === false) {

@@ -64,33 +84,30 @@ script.src += '&recaptchacompat=off';

var HCaptcha = function (_React$Component) {
var HCaptcha = /*#__PURE__*/function (_React$Component) {
_inherits(HCaptcha, _React$Component);
var _super = _createSuper(HCaptcha);
function HCaptcha(props) {
var _this;
_classCallCheck(this, HCaptcha);
var _this = _possibleConstructorReturn(this, (HCaptcha.__proto__ || Object.getPrototypeOf(HCaptcha)).call(this, props));
_this = _super.call(this, props);
var _props$id = props.id,
id = _props$id === undefined ? null : _props$id;
id = _props$id === void 0 ? null : _props$id; // API Methods
// API Methods
_this.renderCaptcha = _this.renderCaptcha.bind(_assertThisInitialized(_this));
_this.resetCaptcha = _this.resetCaptcha.bind(_assertThisInitialized(_this));
_this.removeCaptcha = _this.removeCaptcha.bind(_assertThisInitialized(_this)); // Event Handlers
_this.renderCaptcha = _this.renderCaptcha.bind(_this);
_this.resetCaptcha = _this.resetCaptcha.bind(_this);
_this.removeCaptcha = _this.removeCaptcha.bind(_this);
// Event Handlers
_this.handleOnLoad = _this.handleOnLoad.bind(_this);
_this.handleSubmit = _this.handleSubmit.bind(_this);
_this.handleExpire = _this.handleExpire.bind(_this);
_this.handleError = _this.handleError.bind(_this);
_this.handleOnLoad = _this.handleOnLoad.bind(_assertThisInitialized(_this));
_this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_this));
_this.handleExpire = _this.handleExpire.bind(_assertThisInitialized(_this));
_this.handleError = _this.handleError.bind(_assertThisInitialized(_this));
var isApiReady = typeof hcaptcha !== 'undefined';
if (!isApiReady) captchaScriptCreated = false;
_this.state = {
isApiReady: isApiReady,
isRemoved: false,
elementId: id || 'hcaptcha-' + nanoid(),
elementId: id || "hcaptcha-".concat(nanoid()),
captchaId: ''

@@ -102,16 +119,14 @@ };

_createClass(HCaptcha, [{
key: 'componentDidMount',
key: "componentDidMount",
value: function componentDidMount() {
//Once captcha is mounted intialize hCaptcha - hCaptcha
var _props = this.props,
languageOverride = _props.languageOverride,
reCaptchaCompat = _props.reCaptchaCompat;
var _state = this.state,
isApiReady = _state.isApiReady,
elementId = _state.elementId;
var _this$props = this.props,
languageOverride = _this$props.languageOverride,
reCaptchaCompat = _this$props.reCaptchaCompat;
var _this$state = this.state,
isApiReady = _this$state.isApiReady,
elementId = _this$state.elementId;
if (!isApiReady) {
//Check if hCaptcha has already been loaded, if not create script tag and wait to render captcha elementID - hCaptcha
if (!captchaScriptCreated) {

@@ -121,5 +136,5 @@ // Only create the script tag once, use a global variable to track

CaptchaScript(languageOverride, reCaptchaCompat);
}
} // Add onload callback to global onload listeners
// Add onload callback to global onload listeners
onLoadListeners.push(this.handleOnLoad);

@@ -131,12 +146,10 @@ } else {

}, {
key: 'componentWillUnmount',
key: "componentWillUnmount",
value: function componentWillUnmount() {
var _state2 = this.state,
isApiReady = _state2.isApiReady,
isRemoved = _state2.isRemoved,
captchaId = _state2.captchaId;
var _this$state2 = this.state,
isApiReady = _this$state2.isApiReady,
isRemoved = _this$state2.isRemoved,
captchaId = _this$state2.captchaId;
if (!isApiReady || isRemoved) return; // Reset any stored variables / timers when unmounting
if (!isApiReady || isRemoved) return;
// Reset any stored variables / timers when unmounting
hcaptcha.reset(captchaId);

@@ -146,3 +159,3 @@ hcaptcha.remove(captchaId);

}, {
key: 'shouldComponentUpdate',
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {

@@ -157,17 +170,14 @@ // Prevent component re-rendering when these internal state variables are updated

}, {
key: 'componentDidUpdate',
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var _this2 = this;
var endpoint = this.props.endpoint;
var endpoint = this.props.endpoint; // Prop Keys that could change
// Prop Keys that could change
var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint']; // See if any props changed during component update
var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint'];
// See if any props changed during component update
var match = keys.every(function (key) {
return prevProps[key] === _this2.props[key];
});
}); // If they have changed, remove current captcha and render a new one
// If they have changed, remove current captcha and render a new one
if (!match) {

@@ -179,12 +189,10 @@ this.removeCaptcha();

}, {
key: 'renderCaptcha',
key: "renderCaptcha",
value: function renderCaptcha() {
var _state3 = this.state,
isApiReady = _state3.isApiReady,
elementId = _state3.elementId;
var _this$state3 = this.state,
isApiReady = _this$state3.isApiReady,
elementId = _this$state3.elementId;
if (!isApiReady) return; //Render hCaptcha widget and provide neccessary callbacks - hCaptcha
if (!isApiReady) return;
//Render hCaptcha widget and provide neccessary callbacks - hCaptcha
var captchaId = hcaptcha.render(document.getElementById(elementId), _extends({}, this.props, {
var captchaId = hcaptcha.render(document.getElementById(elementId), _objectSpread(_objectSpread({}, this.props), {}, {
"error-callback": this.handleError,

@@ -194,30 +202,29 @@ "expired-callback": this.handleExpire,

}));
this.setState({ isRemoved: false, captchaId: captchaId });
this.setState({
isRemoved: false,
captchaId: captchaId
});
}
}, {
key: 'resetCaptcha',
key: "resetCaptcha",
value: function resetCaptcha() {
var _state4 = this.state,
isApiReady = _state4.isApiReady,
isRemoved = _state4.isRemoved,
captchaId = _state4.captchaId;
var _this$state4 = this.state,
isApiReady = _this$state4.isApiReady,
isRemoved = _this$state4.isRemoved,
captchaId = _this$state4.captchaId;
if (!isApiReady || isRemoved) return; // Reset captcha state, removes stored token and unticks checkbox
if (!isApiReady || isRemoved) return;
// Reset captcha state, removes stored token and unticks checkbox
hcaptcha.reset(captchaId);
}
}, {
key: 'removeCaptcha',
key: "removeCaptcha",
value: function removeCaptcha() {
var _state5 = this.state,
isApiReady = _state5.isApiReady,
isRemoved = _state5.isRemoved,
captchaId = _state5.captchaId;
var _this$state5 = this.state,
isApiReady = _this$state5.isApiReady,
isRemoved = _this$state5.isRemoved,
captchaId = _this$state5.captchaId;
if (!isApiReady || isRemoved) return;
this.setState({ isRemoved: true }, function () {
this.setState({
isRemoved: true
}, function () {
hcaptcha.remove(captchaId);

@@ -227,7 +234,9 @@ });

}, {
key: 'handleOnLoad',
key: "handleOnLoad",
value: function handleOnLoad() {
var _this3 = this;
this.setState({ isApiReady: true }, function () {
this.setState({
isApiReady: true
}, function () {
_this3.renderCaptcha();

@@ -237,26 +246,23 @@ });

}, {
key: 'handleSubmit',
key: "handleSubmit",
value: function handleSubmit(event) {
var onVerify = this.props.onVerify;
var _state6 = this.state,
isRemoved = _state6.isRemoved,
captchaId = _state6.captchaId;
var _this$state6 = this.state,
isRemoved = _this$state6.isRemoved,
captchaId = _this$state6.captchaId;
if (typeof hcaptcha === 'undefined' || isRemoved) return;
var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
var ekey = hcaptcha.getRespKey(captchaId); //Get current challenge session id from hCaptcha widget
onVerify(token, ekey); //Dispatch event to verify user response
}
}, {
key: 'handleExpire',
key: "handleExpire",
value: function handleExpire() {
var onExpire = this.props.onExpire;
var _state7 = this.state,
isApiReady = _state7.isApiReady,
isRemoved = _state7.isRemoved,
captchaId = _state7.captchaId;
var _this$state7 = this.state,
isApiReady = _this$state7.isApiReady,
isRemoved = _this$state7.isRemoved,
captchaId = _this$state7.captchaId;
if (!isApiReady || isRemoved) return;

@@ -268,35 +274,31 @@ hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha

}, {
key: 'handleError',
key: "handleError",
value: function handleError(event) {
var onError = this.props.onError;
var _state8 = this.state,
isApiReady = _state8.isApiReady,
isRemoved = _state8.isRemoved,
captchaId = _state8.captchaId;
var _this$state8 = this.state,
isApiReady = _this$state8.isApiReady,
isRemoved = _this$state8.isRemoved,
captchaId = _this$state8.captchaId;
if (!isApiReady || isRemoved) return;
hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
if (onError) onError(event);
}
}, {
key: 'execute',
key: "execute",
value: function execute() {
var _state9 = this.state,
isApiReady = _state9.isApiReady,
isRemoved = _state9.isRemoved,
captchaId = _state9.captchaId;
var _this$state9 = this.state,
isApiReady = _this$state9.isApiReady,
isRemoved = _this$state9.isRemoved,
captchaId = _this$state9.captchaId;
if (!isApiReady || isRemoved) return;
hcaptcha.execute(captchaId);
}
}, {
key: 'render',
key: "render",
value: function render() {
var elementId = this.state.elementId;
return React.createElement('div', { id: elementId });
return /*#__PURE__*/React.createElement("div", {
id: elementId
});
}

@@ -303,0 +305,0 @@ }]);

const React = require('react');
const {render} = require('react-dom');
const HCaptcha = require('../../src/');
const HCaptcha = require('../../dist/');

@@ -5,0 +5,0 @@ class ReactDemo extends React.Component {

{
"name": "@hcaptcha/react-hcaptcha",
"version": "0.2.2",
"version": "0.3.0-alpha",
"description": "A React library for hCaptcha",

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

"start": "webpack-dev-server --config ./webpack.config.js --mode development",
"test": "echo \"No test specified\" && exit 0",
"test": "jest",
"transpile": "babel src -d dist --copy-files",
"build": "npm run transpile",
"prepublishOnly": "npm run transpile"

@@ -23,12 +24,12 @@ },

],
"author": "Ali Neer, Brad Peters",
"license": "ISC",
"author": "hCaptcha team and contributors",
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "^3.2.0",
"jest": "^26.6.3",
"react": "^16.14.0",

@@ -35,0 +36,0 @@ "react-dom": "^16.14.0",

@@ -65,22 +65,28 @@ # React hCaptcha Component Library

- sitekey: String, **Required**
- This is your sitekey. It allows you to load hCaptcha, and to configure options like difficulty on the hCaptcha dashboard.
- size: String (normal, compact, invisible)
- This specifies the "size" of the component. hCaptcha allows you to decide how big the component will appear on render. Defaults to normal.
Want a smaller checkbox? Use compact! Invisible does not show a hCaptcha button, and instead pops up on form submit.
- theme: String (light, dark)
- hCaptcha supports both a light and dark theme. If no theme is inherently set, the captcha will always default to light.
- tabindex: Integer
- Set the tabindex of the widget and popup. When appropriate, this can make navigation of your site more intuitive. This always defaults to 0.
- languageOverride: String
- Manually set the language used to render text in the hCaptcha API. See [language codes](https://hcaptcha.com/docs/languages).
- id: String
- Manually set the ID of the hCaptcha component. Make sure each hCaptcha component generated on a single page has its own unique ID when using this prop.
- reCaptchaCompat: Boolean
- Disable drop-in replacement for reCAPTCHA with `false` to prevent hCaptcha from injecting into `window.grecaptcha`. Enabled by default.
- onVerify: Function
- On success callback that returns two parameters: A hCaptcha response token and challenge session ID called an ekey.
|Name|Values/Type|Required|Default|Description|
|---|---|---|---|---|
|`sitekey`|String|**Yes**|`-`|This is your sitekey, this allows you to load captcha. If you need a sitekey, please visit [hCaptcha](https://www.hcaptcha.com), and sign up to get your sitekey.|
|`size`|String (normal, compact, invisible)|No|`normal`|This specifies the "size" of the component. hCaptcha allows you to decide how big the component will appear on render, this always defaults to normal.|
|`theme`|String (light, dark)|No|`light`|hCaptcha supports both a light and dark theme. If no theme is inherently set, the captcha will always default to light.|
|`tabindex`|Integer|No|`0`|Set the tabindex of the widget and popup. When appropriate, this can make navigation of your site more intuitive.|
|`languageOverride`|String (ISO 639-2 code)|No|`auto`|hCaptcha auto-detects language via the user's browser. This overrides that to set a default UI language. See [language codes](https://hcaptcha.com/docs/languages).|
|`reCaptchaCompat`|Boolean|No|`true`|Disable drop-in replacement for reCAPTCHA with `false` to prevent hCaptcha from injecting into `window.grecaptcha`.|
|`id`|String|No|`random id`|Manually set the ID of the hCaptcha component. Make sure each hCaptcha component generated on a single page has its own unique ID when using this prop.|
The component emits events related to verification and expiration. Simply catch these events in the parent component: `onVerify`, `onExpire`, `onError` and handle the events as you choose. The captcha will automatically reset on error, but still emits an error.
### Events
|Event|Params|Description|
|---|---|---|
|`onError`|`err`|When an error occurs. Component will reset immediately after an error.|
|`onVerify`|`token, eKey`|When challenge is completed. The response `token` and an `eKey` (session id) are passed along.|
|`onExpire`|-|When the current token expires.|
### Methods
|Method|Description|
|---|---|
|`execute()`|Programmatically trigger a challenge request|
|`resetCaptcha()`|Reset the current challenge|
**NOTE**: Make sure to reset the hCaptcha state when you submit your form by calling the method `.resetCaptcha` on your hCaptcha React Component! Passcodes are one-time use, so if your user submits the same passcode twice then it will be rejected by the server the second time.

@@ -94,27 +100,31 @@

## Running locally for development
---
Please see: [Local Development Notes](https://docs.hcaptcha.com/#localdev).
### Note for maintainers
Summary:
#### Scripts
`sudo echo "127.0.0.1 fakelocal.com" >> /private/etc/hosts`
* `npm run start` - will start the demo app with hot reload
* `npm run test` - will test the library: unit tests
* `npm run build` - will build the production version
`npm start -- --disable-host-check`
open [http://fakelocal.com:9000](http://fakelocal.com:9000) to start the example.
#### Publishing
## Notes to Maintainers
To publish a new version, follow the next steps:
1. Bump the version in `package.json`
2. Create a [Github Release](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/managing-releases-in-a-repository#creating-a-release) with version from step 1 **without** a prefix such as `v` (e.g. `1.0.3`)
* `publish` workflow will be triggered which will: build, test and deploy the package to the [npm @hcaptcha/react-hcaptcha](https://www.npmjs.com/package/@hcaptcha/react-hcaptcha).
This repository can be found on **npm** at [@hcaptcha/react-hcaptcha](https://www.npmjs.com/package/@hcaptcha/react-hcaptcha). If any updates are committed to master the **npm** registry should be updated to reflect these changes. See steps below to update the package on **npm**:
#### Requirements
#### Running locally for development
- NPM Account
- Set as a `Maintainer` of [@hcaptcha/react-hcaptcha](https://www.npmjs.com/package/@hcaptcha/react-hcaptcha)
Please see: [Local Development Notes](https://docs.hcaptcha.com/#localdev).
#### Publishing
Summary:
- Always update package version
- Run `npm publish` from inside the current repository
`sudo echo "127.0.0.1 fakelocal.com" >> /private/etc/hosts`
`npm start -- --disable-host-check`
open [http://fakelocal.com:9000](http://fakelocal.com:9000) to start the example.

Sorry, the diff of this file is not supported yet

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