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

react-redux-toastr

Package Overview
Dependencies
Maintainers
1
Versions
134
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-redux-toastr - npm Package Compare versions

Comparing version 2.0.4 to 2.1.0

lib/css/react-redux-toastr.css

3

CHANGELOG.md

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

#2.1.0
change ReduxToastr props name from `confirm` to `confirmOptions`
the `message` method can close automaticly you you provide a `timeout` in the `message` options
#1.1.0

@@ -2,0 +5,0 @@ Improve the code end fix [issue](https://github.com/diegoddox/redux-toastr/issues/3)

9

lib/actions.js

@@ -13,3 +13,3 @@ 'use strict';

exports.error = error;
exports.confirm = confirm;
exports.showConfirm = showConfirm;
exports.hideConfirm = hideConfirm;

@@ -89,9 +89,6 @@

function confirm(message, options) {
function showConfirm(obj) {
return {
type: _constants.SHOW_CONFIRM,
payload: {
message: message,
options: options
}
payload: obj
};

@@ -98,0 +95,0 @@ }

@@ -8,2 +8,4 @@ '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; };
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; }; }();

@@ -41,4 +43,2 @@

var _utils = require('./utils.js');
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

@@ -66,10 +66,2 @@

_this.handleRemoveToastr = function (id) {
_this.props.remove(id);
};
_this.handleHideConfirm = function () {
_this.props.hideConfirm();
};
_config2.default.set('timeOut', _this.props.timeOut);

@@ -83,23 +75,10 @@ _config2.default.set('newestOnTop', _this.props.newestOnTop);

value: function componentDidMount() {
var _this2 = this;
var _props = this.props;
var addToastrAction = _props.addToastrAction;
var showConfirm = _props.showConfirm;
var clean = _props.clean;
var onAddToastr = function onAddToastr(toastr) {
_this2.props.addToastrAction(toastr);
};
var onCleanToastr = function onCleanToastr() {
if (_this2.props.toastr.toastrs.length) {
_this2.props.clean();
}
};
var confirm = function confirm(obj) {
// Fire if we don't have any active confirm
if (!_this2.props.toastr.confirm.show) {
_this2.props.confirm(obj.message, obj.options);
}
};
_toastrEmitter.EE.on('toastr/confirm', confirm);
_toastrEmitter.EE.on('add/toastr', onAddToastr);
_toastrEmitter.EE.on('clean/toastr', onCleanToastr);
_toastrEmitter.EE.on('toastr/confirm', showConfirm);
_toastrEmitter.EE.on('add/toastr', addToastrAction);
_toastrEmitter.EE.on('clean/toastr', clean);
}

@@ -116,33 +95,16 @@ }, {

value: function render() {
var _this3 = this;
var _this2 = this;
var toastrPosition = (0, _utils.checkPositionName)(this.props.position);
var classes = (0, _classnames2.default)('redux-toastr', toastrPosition);
var _props = this.props;
var toastr = _props.toastr;
var confirm = _props.confirm;
var classes = (0, _classnames2.default)('redux-toastr', this.props.position);
var _props$toastr = this.props.toastr;
var confirm = _props$toastr.confirm;
var toastrs = _props$toastr.toastrs;
var confirmOkText = (0, _utils.hasProperty)(confirm, 'okText') ? confirm.okText : 'ok';
var confirmCancelText = (0, _utils.hasProperty)(confirm, 'cancelText') ? confirm.cancelText : 'cancel';
var confirmProps = {
hideConfirm: this.handleHideConfirm,
confirm: toastr.confirm,
okText: confirmOkText,
cancelText: confirmCancelText
};
return _react2.default.createElement(
'div',
{ className: classes },
_react2.default.createElement(_ToastrConfirm2.default, confirmProps),
toastr.toastrs.map(function (item) {
var props = {
key: item.id,
toastr: item,
timeOut: _this3.props.timeOut,
remove: _this3.handleRemoveToastr
};
return _react2.default.createElement(_ToastrBox2.default, props);
_react2.default.createElement(_ToastrConfirm2.default, _extends({ confirm: confirm }, this.props)),
toastrs.map(function (item) {
return _react2.default.createElement(_ToastrBox2.default, _extends({ key: item.id, item: item }, _this2.props));
})

@@ -162,3 +124,3 @@ );

timeOut: _react.PropTypes.number,
confirm: _react.PropTypes.object
confirmOptions: _react.PropTypes.object
};

@@ -169,7 +131,7 @@ ReduxToastr.defaultProps = {

timeOut: 5000,
confirm: {
confirmOptions: {
okText: 'ok',
onCancelText: 'cancel'
cancelText: 'cancel'
}
};
exports.default = ReduxToastr;

@@ -45,4 +45,3 @@ 'use strict';

_this.handleClick = function (e) {
e.preventDefault();
_this.handleClick = function () {
_this._removeToastr();

@@ -52,19 +51,11 @@ };

_this.mouseEnter = function () {
if (_this.intervalId) {
clearTimeout(_this.intervalId);
_this._setIntervalId(null);
if (_this.isHiding) {
_this._setIsHiding(false);
}
}
clearTimeout(_this.intervalId);
_this._setIntervalId(null);
_this._setIsHiding(false);
};
_this.mouseLeave = function () {
var toastr = _this.props.toastr;
if (_this.isHiding || toastr.type == 'message') {
return;
if (_this.isHiding || _this.props.item.type !== 'message') {
_this._setIntervalId(setTimeout(_this._removeToastr, 1000));
}
_this._setIntervalId(setTimeout(_this._removeToastr, 1000));
};

@@ -75,4 +66,5 @@

var remove = _this$props.remove;
var toastr = _this$props.toastr;
var options = toastr.options;
var item = _this$props.item;
var options = item.options;
var id = item.id;

@@ -82,10 +74,8 @@

_this._setIsHiding(false);
remove(toastr.id);
remove(id);
if (options.onHideComplete) {
options.onHideComplete();
}
} else if (!_this.isHiding) {
if (options.onShowComplete) {
options.onShowComplete();
}
} else if (!_this.isHiding && options.onShowComplete) {
options.onShowComplete();
}

@@ -95,8 +85,7 @@ };

_this._removeToastr = function () {
if (_this.isHiding) {
return;
if (!_this.isHiding) {
_this._setIsHiding(true);
_this._setTransition(true);
(0, _utils.onCSSTransitionEnd)(_this.toastrBox, _this._onAnimationComplite);
}
_this._setIsHiding(true);
_this._setTransition(true);
(0, _utils.onCSSTransitionEnd)(_this.toastrBox, _this._onAnimationComplite);
};

@@ -106,10 +95,8 @@

var node = _this.toastrBox;
var animationType = hide ? _this.props.transition.out : _this.props.transition.in;
var animationType = hide ? _this.transitionOut : _this.transitionIn;
var onEndListener = function onEndListener(e) {
if (e && e.target !== node) {
return;
if (e && e.target == node) {
_CSSCore2.default.removeClass(node, animationType);
}
_CSSCore2.default.removeClass(node, animationType);
};

@@ -123,6 +110,4 @@

var node = _this.toastrBox;
var transition = _this.props.transition;
_CSSCore2.default.removeClass(node, transition.in);
_CSSCore2.default.removeClass(node, transition.out);
_CSSCore2.default.removeClass(node, _this.transitionIn);
_CSSCore2.default.removeClass(node, _this.transitionOut);
};

@@ -139,18 +124,13 @@

_this.renderMessage = function () {
var toastr = _this.props.toastr;
var _this$props$item = _this.props.item;
var message = _this$props$item.message;
var type = _this$props$item.type;
if (toastr.type == 'message') {
return _react2.default.createElement(
'div',
{ className: 'message' },
_react2.default.createElement('p', { dangerouslySetInnerHTML: { __html: toastr.message } })
);
}
return _react2.default.createElement(
'div',
{ className: 'message' },
toastr.message
);
return type == 'message' ? _react2.default.createElement('p', { dangerouslySetInnerHTML: { __html: message } }) : message;
};
_this.isHiding = false;
_this.intervalId = null;
_this.transitionIn = 'bounceIn';
_this.transitionOut = 'bounceOut';
return _this;

@@ -160,17 +140,13 @@ }

_createClass(ToastrBox, [{
key: 'componentWillMount',
value: function componentWillMount() {
this.isHiding = false;
this.intervalId = null;
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var toastr = this.props.toastr;
var item = this.props.item;
var timeOut = item.options.timeOut;
var timeOut = _config2.default.get('timeOut');
var time = toastr.options.timeOut || timeOut;
if (typeof timeOut === 'undefined' && item.type !== 'message') {
timeOut = _config2.default.get('timeOut');
}
if (toastr.type !== 'message') {
this._setIntervalId(setTimeout(this._removeToastr, time));
if (timeOut) {
this._setIntervalId(setTimeout(this._removeToastr, timeOut));
}

@@ -193,6 +169,8 @@

var toastr = this.props.toastr;
var _props$item = this.props.item;
var title = _props$item.title;
var type = _props$item.type;
var options = _props$item.options;
var classes = (0, _classnames2.default)('toastr', 'animated', toastr.type);
var icons = (0, _classnames2.default)('icon-holder', toastr.options.icon);
var classes = (0, _classnames2.default)('toastr', 'animated', type, options.icon);
return _react2.default.createElement(

@@ -202,19 +180,28 @@ 'div',

className: classes,
onMouseEnter: this.mouseEnter,
onMouseLeave: this.mouseLeave,
onMouseEnter: function onMouseEnter() {
return _this2.mouseEnter();
},
onMouseLeave: function onMouseLeave() {
return _this2.mouseLeave();
},
onClick: function onClick() {
return _this2.handleClick();
},
ref: function ref(_ref) {
return _this2.toastrBox = _ref;
} },
_react2.default.createElement('div', { className: icons }),
_react2.default.createElement(
'div',
{ className: 'message-holder', onClick: this.handleClick },
toastr.title && _react2.default.createElement(
{ className: 'message-holder' },
title && _react2.default.createElement(
'div',
{ className: 'title' },
toastr.title
title
),
this.renderMessage()
),
_react2.default.createElement('button', { onClick: this.handleClick, className: 'close icon-close-round' })
_react2.default.createElement(
'div',
{ className: 'message' },
this.renderMessage()
)
)
);

@@ -229,11 +216,4 @@ }

ToastrBox.propTypes = {
toastr: _react.PropTypes.object.isRequired,
transition: _react.PropTypes.object
toastr: _react.PropTypes.object.isRequired
};
ToastrBox.defaultProps = {
transition: {
in: 'bounceIn',
out: 'bounceOut'
}
};
exports.default = ToastrBox;

@@ -47,3 +47,5 @@ 'use strict';

(0, _utils.returnFuncFromObj)(options, 'onOk');
if (options.onOk) {
options.onOk();
}
_this._setTransition();

@@ -55,3 +57,5 @@ };

(0, _utils.returnFuncFromObj)(options, 'onCancel');
if (options.onCancel) {
options.onCancel();
}
_this._setTransition();

@@ -107,5 +111,5 @@ };

var _props = this.props;
var okText = _props.okText;
var cancelText = _props.cancelText;
var _props$confirmOptions = this.props.confirmOptions;
var okText = _props$confirmOptions.okText;
var cancelText = _props$confirmOptions.cancelText;

@@ -112,0 +116,0 @@ var classes = (0, _classnames2.default)('confirm-holder', { active: this.props.confirm.show });

@@ -13,3 +13,2 @@ 'use strict';

exports.onCSSTransitionEnd = onCSSTransitionEnd;
exports.returnFuncFromObj = returnFuncFromObj;

@@ -109,8 +108,2 @@ var _ReactTransitionEvents = require('react/lib/ReactTransitionEvents');

function returnFuncFromObj(obj, name) {
if (obj && hasProperty(obj, name)) {
return obj[name] && obj[name]();
}
}
function isString(obj) {

@@ -117,0 +110,0 @@ if (typeof obj == 'string') {

{
"name": "react-redux-toastr",
"version": "2.0.4",
"version": "2.1.0",
"description": "react-redux-toastr is a React toastr message implemented with Redux",

@@ -16,2 +16,5 @@ "main": "lib/index.js",

},
"eslintConfig": {
"root": true
},
"betterScripts": {

@@ -37,3 +40,3 @@ "build:umd:min": {

"lint": {
"command": "eslint . --ext .js --ext .jsx || true"
"command": "node_modules/.bin/eslint . --ext .js --ext .jsx || true"
},

@@ -44,8 +47,3 @@ "clean": {

},
"author": [
{
"name": "Diego Oliveira",
"url": "https://github.com/diegoddox"
}
],
"author": "Diego Oliveira",
"license": "MIT",

@@ -52,0 +50,0 @@ "repository": {

@@ -16,3 +16,3 @@ ##`react-redux-toastr` [demo](http://diegoddox.github.io/react-redux-toastr/)

```
<link href="http://diegoddox.github.io/react-redux-toastr/2.0.1/react-redux-toastr.min.css" rel="stylesheet" type="text/css">
<link href="http://diegoddox.github.io/react-redux-toastr/2.1.0/react-redux-toastr.min.css" rel="stylesheet" type="text/css">
```

@@ -110,10 +110,9 @@ ##### 3. The third thing you need to do is to add the `react-redux-toastr` `reducer` to Redux.

##### Toastr: `message`
This one are in case you wanna show a large amount of information, unlike the other methods above this will not close automatically to close the user has to click on the close button.
This one is in case you wanna show a large amount of information, unlike the other methods above this will not close automatically unless you provide a `timeout` in the `message` options.
In case you don't pass the `title` a default one will be provided.
```
const toastrMessageOptions = {
onShowComplete: () => console.log('SHOW: animation is done'),
onHideComplete: () => console.log('HIDE: animation is done')
onHideComplete: () => console.log('HIDE: animation is done'),
timeout: 10000
};

@@ -140,7 +139,7 @@ toastr.message('Title', 'text <img src="myimage.jpg" />', toastrMessageOptions)

```
const confirmOptions = {
const options = {
okText: 'confirm text',
cancelText: 'cancel text'
};
<ReduxToastr confirm={confirmOptions}/>
<ReduxToastr confirmOptions={options}/>
```

@@ -147,0 +146,0 @@ # Run a local demo

@@ -56,9 +56,6 @@ import {ADD_TOASTR, REMOVE_TOASTR, CLEAN_TOASTR, SHOW_CONFIRM, HIDE_CONFIRM} from './constants';

export function confirm(message, options) {
export function showConfirm(obj) {
return {
type: SHOW_CONFIRM,
payload: {
message,
options
}
payload: obj
};

@@ -65,0 +62,0 @@ }

@@ -11,4 +11,2 @@ import React, {Component, PropTypes} from 'react';

import {checkPositionName, hasProperty} from './utils.js';
@connect(state => ({

@@ -26,3 +24,3 @@ toastr: state.toastr

timeOut: PropTypes.number,
confirm: PropTypes.object
confirmOptions: PropTypes.object
};

@@ -34,5 +32,5 @@

timeOut: 5000,
confirm: {
confirmOptions: {
okText: 'ok',
onCancelText: 'cancel'
cancelText: 'cancel'
}

@@ -48,21 +46,6 @@ };

componentDidMount() {
const onAddToastr = (toastr) => {
this.props.addToastrAction(toastr);
};
const onCleanToastr = () => {
if (this.props.toastr.toastrs.length) {
this.props.clean();
}
};
const confirm = (obj) => {
// Fire if we don't have any active confirm
if (!this.props.toastr.confirm.show) {
this.props.confirm(obj.message, obj.options);
}
};
EE.on('toastr/confirm', confirm);
EE.on('add/toastr', onAddToastr);
EE.on('clean/toastr', onCleanToastr);
const {addToastrAction, showConfirm, clean} = this.props;
EE.on('toastr/confirm', showConfirm);
EE.on('add/toastr', addToastrAction);
EE.on('clean/toastr', clean);
}

@@ -76,40 +59,10 @@

handleRemoveToastr = (id) => {
this.props.remove(id);
};
handleHideConfirm = () => {
this.props.hideConfirm();
};
render() {
const toastrPosition = checkPositionName(this.props.position);
const classes = classnames('redux-toastr', toastrPosition);
const {toastr, confirm} = this.props;
const classes = classnames('redux-toastr', this.props.position);
const {confirm, toastrs} = this.props.toastr;
const confirmOkText = hasProperty(confirm, 'okText') ? confirm.okText : 'ok';
const confirmCancelText = hasProperty(confirm, 'cancelText') ? confirm.cancelText : 'cancel';
const confirmProps = {
hideConfirm: this.handleHideConfirm,
confirm: toastr.confirm,
okText: confirmOkText,
cancelText: confirmCancelText
};
return (
<div className={classes}>
<ToastrConfirm {...confirmProps}/>
{toastr.toastrs.map((item) => {
const props = {
key: item.id,
toastr: item,
timeOut: this.props.timeOut,
remove: this.handleRemoveToastr
};
return (
<ToastrBox {...props}/>
);
})}
<ToastrConfirm confirm={confirm} {...this.props}/>
{toastrs.map(item => <ToastrBox key={item.id} item={item} {...this.props}/>)}
</div>

@@ -116,0 +69,0 @@ );

@@ -12,29 +12,22 @@ import CSSCore from 'fbjs/lib/CSSCore';

static propTypes = {
toastr: PropTypes.object.isRequired,
transition: PropTypes.object
toastr: PropTypes.object.isRequired
};
static defaultProps = {
transition: {
in: 'bounceIn',
out: 'bounceOut'
}
};
constructor(props) {
super(props);
}
componentWillMount() {
this.isHiding = false;
this.intervalId = null;
this.transitionIn = 'bounceIn';
this.transitionOut = 'bounceOut'
}
componentDidMount() {
const {toastr} = this.props;
const timeOut = config.get('timeOut');
const time = toastr.options.timeOut || timeOut;
const {item} = this.props;
let {timeOut} = item.options;
if (typeof timeOut === 'undefined' && item.type !== 'message') {
timeOut = config.get('timeOut');
}
if (toastr.type !== 'message') {
this._setIntervalId(setTimeout(this._removeToastr, time));
if (timeOut) {
this._setIntervalId(setTimeout(this._removeToastr, timeOut));
}

@@ -52,4 +45,3 @@

handleClick = (e) => {
e.preventDefault();
handleClick = () => {
this._removeToastr();

@@ -59,34 +51,25 @@ };

mouseEnter = () => {
if (this.intervalId) {
clearTimeout(this.intervalId);
this._setIntervalId(null);
if (this.isHiding) {
this._setIsHiding(false);
}
}
clearTimeout(this.intervalId);
this._setIntervalId(null);
this._setIsHiding(false);
};
mouseLeave = () => {
const {toastr} = this.props;
if (this.isHiding || toastr.type == 'message') {
return;
if (this.isHiding || this.props.item.type !== 'message') {
this._setIntervalId(setTimeout(this._removeToastr, 1000));
}
this._setIntervalId(setTimeout(this._removeToastr, 1000));
};
_onAnimationComplite = () => {
const {remove, toastr} = this.props;
const {options} = toastr;
const {remove, item} = this.props;
const {options, id} = item;
if (this.isHiding) {
this._setIsHiding(false);
remove(toastr.id);
remove(id);
if (options.onHideComplete) {
options.onHideComplete();
}
} else if (!this.isHiding) {
if (options.onShowComplete) {
options.onShowComplete();
}
} else if (!this.isHiding && options.onShowComplete) {
options.onShowComplete();
}

@@ -96,8 +79,7 @@ };

_removeToastr = () => {
if (this.isHiding) {
return;
if (!this.isHiding) {
this._setIsHiding(true);
this._setTransition(true);
onCSSTransitionEnd(this.toastrBox, this._onAnimationComplite);
}
this._setIsHiding(true);
this._setTransition(true);
onCSSTransitionEnd(this.toastrBox, this._onAnimationComplite);
};

@@ -107,10 +89,8 @@

const node = this.toastrBox;
const animationType = hide ? this.props.transition.out : this.props.transition.in;
const animationType = hide ? this.transitionOut : this.transitionIn;
const onEndListener = (e) => {
if (e && e.target !== node) {
return;
if (e && e.target == node) {
CSSCore.removeClass(node, animationType);
}
CSSCore.removeClass(node, animationType);
};

@@ -124,5 +104,4 @@

const node = this.toastrBox;
const {transition} = this.props;
CSSCore.removeClass(node, transition.in);
CSSCore.removeClass(node, transition.out);
CSSCore.removeClass(node, this.transitionIn);
CSSCore.removeClass(node, this.transitionOut);
};

@@ -139,27 +118,20 @@

renderMessage = () => {
const {toastr} = this.props;
if (toastr.type == 'message') {
return <div className="message"><p dangerouslySetInnerHTML={{__html: toastr.message}}></p></div>;
}
return <div className="message">{toastr.message}</div>;
const {message, type} = this.props.item;
return type == 'message' ? <p dangerouslySetInnerHTML={{__html: message}}></p> : message;
};
render() {
const {toastr} = this.props;
const classes = classnames('toastr', 'animated', toastr.type);
const icons = classnames('icon-holder', toastr.options.icon);
const {title, type, options} = this.props.item;
const classes = classnames('toastr', 'animated', type, options.icon);
return (
<div
className={classes}
onMouseEnter={this.mouseEnter}
onMouseLeave={this.mouseLeave}
onMouseEnter={() => this.mouseEnter()}
onMouseLeave={() => this.mouseLeave()}
onClick={() => this.handleClick()}
ref={(ref) => this.toastrBox = ref}>
<div className={icons}></div>
<div className="message-holder" onClick={this.handleClick}>
{toastr.title &&
<div className="title">{toastr.title}</div>}
{this.renderMessage()}
<div className="message-holder">
{title && <div className="title">{title}</div>}
<div className="message">{this.renderMessage()}</div>
</div>
<button onClick={this.handleClick} className="close icon-close-round"></button>
</div>

@@ -166,0 +138,0 @@ );

import React, {Component, PropTypes} from 'react';
import cn from 'classnames';
import CSSCore from 'fbjs/lib/CSSCore';
import {onCSSTransitionEnd, returnFuncFromObj} from './utils';
import {onCSSTransitionEnd} from './utils';
import Button from './Button';

@@ -30,3 +30,5 @@

const {options} = this.props.confirm;
returnFuncFromObj(options, 'onOk');
if (options.onOk) {
options.onOk();
}
this._setTransition();

@@ -37,3 +39,5 @@ };

const {options} = this.props.confirm;
returnFuncFromObj(options, 'onCancel');
if (options.onCancel) {
options.onCancel();
}
this._setTransition();

@@ -73,3 +77,3 @@ };

render() {
const {okText, cancelText} = this.props;
const {okText, cancelText} = this.props.confirmOptions;
const classes = cn('confirm-holder', {active: this.props.confirm.show});

@@ -76,0 +80,0 @@ return (

@@ -32,3 +32,3 @@ import ReactTransitionEvents from 'react/lib/ReactTransitionEvents';

}
if (!obj.options.icon) {

@@ -86,8 +86,2 @@ obj.options.icon = mapToIcon(type);

export function returnFuncFromObj(obj, name) {
if (obj && hasProperty(obj, name)) {
return obj[name] && obj[name]();
}
}
function isString(obj) {

@@ -94,0 +88,0 @@ if (typeof obj == 'string') {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc