Socket
Socket
Sign inDemoInstall

react-router-redux

Package Overview
Dependencies
Maintainers
2
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-router-redux - npm Package Compare versions

Comparing version 3.0.0 to 4.0.0-beta.1

lib/actions.js

2

CHANGELOG.md

@@ -7,3 +7,3 @@ ## [HEAD](https://github.com/rackt/react-router-redux/compare/3.0.0...master)

Techincally, 2.1.0 broke semver. The appropriate @timdorr's have been flogged. So, we're bumping the major version to catch up.
Technically, 2.1.0 broke semver. The appropriate @timdorr's have been flogged. So, we're bumping the major version to catch up.

@@ -10,0 +10,0 @@ - Fixed Resets in Redux Dev Tools. [3ae8110f](https://github.com/rackt/react-router-redux/commit/3ae8110f)

'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; };
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.routeReducer = routeReducer;
exports.syncHistory = syncHistory;
exports.routerMiddleware = exports.routerActions = exports.goForward = exports.goBack = exports.go = exports.replace = exports.push = exports.CALL_HISTORY_METHOD = exports.routerReducer = exports.LOCATION_CHANGE = exports.syncHistoryWithStore = undefined;
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var _reducer = require('./reducer');
// Constants
var TRANSITION = exports.TRANSITION = '@@router/TRANSITION';
var UPDATE_LOCATION = exports.UPDATE_LOCATION = '@@router/UPDATE_LOCATION';
var SELECT_LOCATION = function SELECT_LOCATION(state) {
return state.routing.location;
};
function transition(method) {
return function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return {
type: TRANSITION,
payload: { method: method, args: args }
};
};
}
var push = exports.push = transition('push');
var replace = exports.replace = transition('replace');
var go = exports.go = transition('go');
var goBack = exports.goBack = transition('goBack');
var goForward = exports.goForward = transition('goForward');
var routeActions = exports.routeActions = { push: push, replace: replace, go: go, goBack: goBack, goForward: goForward };
function updateLocation(location) {
return {
type: UPDATE_LOCATION,
payload: location
};
}
// Reducer
var initialState = {
location: undefined
};
function routeReducer() {
var state = arguments.length <= 0 || arguments[0] === undefined ? initialState : arguments[0];
var _ref = arguments[1];
var type = _ref.type;
var location = _ref.payload;
if (type !== UPDATE_LOCATION) {
return state;
Object.defineProperty(exports, 'LOCATION_CHANGE', {
enumerable: true,
get: function get() {
return _reducer.LOCATION_CHANGE;
}
});
Object.defineProperty(exports, 'routerReducer', {
enumerable: true,
get: function get() {
return _reducer.routerReducer;
}
});
return _extends({}, state, { location: location });
}
var _actions = require('./actions');
// Syncing
function syncHistory(history) {
var unsubscribeHistory = undefined,
currentKey = undefined,
unsubscribeStore = undefined;
var connected = false,
syncing = false;
history.listen(function (location) {
initialState.location = location;
})();
function middleware(store) {
unsubscribeHistory = history.listen(function (location) {
currentKey = location.key;
if (syncing) {
// Don't dispatch a new action if we're replaying location.
return;
}
store.dispatch(updateLocation(location));
});
connected = true;
return function (next) {
return function (action) {
if (action.type !== TRANSITION || !connected) {
return next(action);
}
var _action$payload = action.payload;
var method = _action$payload.method;
var args = _action$payload.args;
history[method].apply(history, _toConsumableArray(args));
};
};
Object.defineProperty(exports, 'CALL_HISTORY_METHOD', {
enumerable: true,
get: function get() {
return _actions.CALL_HISTORY_METHOD;
}
});
Object.defineProperty(exports, 'push', {
enumerable: true,
get: function get() {
return _actions.push;
}
});
Object.defineProperty(exports, 'replace', {
enumerable: true,
get: function get() {
return _actions.replace;
}
});
Object.defineProperty(exports, 'go', {
enumerable: true,
get: function get() {
return _actions.go;
}
});
Object.defineProperty(exports, 'goBack', {
enumerable: true,
get: function get() {
return _actions.goBack;
}
});
Object.defineProperty(exports, 'goForward', {
enumerable: true,
get: function get() {
return _actions.goForward;
}
});
Object.defineProperty(exports, 'routerActions', {
enumerable: true,
get: function get() {
return _actions.routerActions;
}
});
middleware.listenForReplays = function (store) {
var selectLocationState = arguments.length <= 1 || arguments[1] === undefined ? SELECT_LOCATION : arguments[1];
var _sync = require('./sync');
var getLocationState = function getLocationState() {
return selectLocationState(store.getState());
};
var initialLocation = getLocationState();
var _sync2 = _interopRequireDefault(_sync);
unsubscribeStore = store.subscribe(function () {
var location = getLocationState();
var _middleware = require('./middleware');
// If we're resetting to the beginning, use the saved initial value. We
// need to dispatch a new action at this point to populate the store
// appropriately.
if (location.key === initialLocation.key) {
history.replace(initialLocation);
return;
}
var _middleware2 = _interopRequireDefault(_middleware);
// Otherwise, if we need to update the history location, do so without
// dispatching a new action, as we're just bringing history in sync
// with the store.
if (location.key !== currentKey) {
syncing = true;
history.transitionTo(location);
syncing = false;
}
});
};
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
middleware.unsubscribe = function () {
unsubscribeHistory();
if (unsubscribeStore) {
unsubscribeStore();
}
connected = false;
};
return middleware;
}
exports.syncHistoryWithStore = _sync2.default;
exports.routerMiddleware = _middleware2.default;
{
"name": "react-router-redux",
"version": "3.0.0",
"version": "4.0.0-beta.1",
"description": "Ruthlessly simple bindings to keep react-router and redux in sync",

@@ -19,6 +19,6 @@ "main": "lib/index",

"scripts": {
"build": "mkdir -p lib && babel ./src/index.js --out-file ./lib/index.js",
"build": "mkdir -p lib && babel ./src -d lib",
"lint": "eslint examples src test",
"test": "npm run lint && npm run test:node && npm run test:browser",
"test:node": "mocha --compilers js:babel-core/register --recursive ./test/node",
"test:node": "mocha --compilers js:babel-register --recursive ./test/*.spec.js",
"test:browser": "karma start",

@@ -45,4 +45,6 @@ "test:cov": "npm run test:cov:browser && npm run test:cov:node && npm run test:cov:report",

"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.1.2",
"babel-preset-stage-2": "^6.3.13",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-1": "^6.3.13",
"babel-register": "^6.4.3",
"eslint": "^1.10.3",

@@ -52,3 +54,3 @@ "eslint-config-rackt": "^1.1.1",

"expect": "^1.13.0",
"history": "^1.14.0",
"history": "^2.0.0",
"isparta": "^4.0.0",

@@ -55,0 +57,0 @@ "isparta-loader": "^2.0.0",

@@ -5,14 +5,16 @@ # react-router-redux

**Let react-router do all the work** :sparkles:
> **Keep your router in sync with application state** :sparkles:
_Formerly known as redux-simple-router_
[Redux](https://github.com/rackt/redux) is awesome. [React Router](https://github.com/rackt/react-router) is cool. The problem is that react-router manages an important piece of your application state: the URL. If you are using redux, you want your app state to fully represent your UI; if you snapshotted the app state, you should be able to load it up later and see the same thing.
You're a smart person. You use [Redux](https://github.com/rackt/redux) to manage your application state. You use [React Router](https://github.com/rackt/react-router) to do routing. All is good.
react-router does a great job of mapping the current URL to a component tree, and continually does so with any URL changes. This is very useful, but we really want to store this state in redux as well.
But the two libraries don't coordinate. You want to do time travel with your application state, but React Router doesn't navigate between pages when you replay actions. It controls an important part of application state: the URL.
The entire state that we are interested in boils down to one thing: the URL. This is an extremely simple library that just puts the URL in redux state and keeps it in sync with any react-router changes. Additionally, you can change the URL via redux and react-router will change accordingly.
This library helps you keep that bit of state in sync with your Redux store. We keep a copy of the current location hidden in state. When you rewind your application state with a tool like [Redux DevTools](https://github.com/gaearon/redux-devtools), that state change is propagated to React Router so it can adjust the component tree accordingly. You can jump around in state, rewinding, replaying, and resetting as much as you'd like, and this library will ensure the two stay in sync at all times.
## Installation
```
npm install react-router-redux
npm install --save react-router-redux
```

@@ -22,32 +24,12 @@

View the [CHANGELOG](https://github.com/rackt/react-router-redux/blob/master/CHANGELOG.md) for recent changes.
## How It Works
Read the [API docs](#api) farther down this page.
This library allows you to use React Router's APIs as they are documented. And, you can use redux like you normally would, with a single app state. The library simply enhances a history instance to allow it to synchronize any changes it receives into application state.
##### _What about redux-router?_
[history](https://github.com/rackt/history) + `store` ([redux](https://github.com/rackt/redux)) &rarr; [**react-router-redux**](https://github.com/rackt/react-router-redux) &rarr; enhanced [history](https://github.com/rackt/history) &rarr; [react-router](https://github.com/rackt/react-router)
[redux-router](https://github.com/rackt/redux-router) is another project which solves the same problem. However, it's far more complex. Take a quick look at [the code for this library](https://github.com/rackt/react-router-redux/blob/master/src/index.js)—it's extremely minimal. redux-router is much bigger and more complex.
## Tutorial
That said, redux-router is a fine project and has features this doesn't provide. Use it if you like it better.
**Compared with redux-router:**
* Much smaller and simpler. You don't need to learn another library on top of everything else.
* We encourage direct access of react-router APIs. Need server-side rendering, or something else advanced? Just read react-router's docs.
* We only put the current location in state, whereas redux-router stores the entire router state, which includes router-specific things like params.
### Usage
The idea of this library is to use react-router's functionality exactly like its documentation tells you to. You can access all of its APIs in routing components. Additionally, you can use redux like you normally would, with a single app state.
[redux](https://github.com/rackt/redux) (`store.routing`) &nbsp;&harr;&nbsp; [**react-router-redux**](https://github.com/rackt/react-router-redux) &nbsp;&harr;&nbsp; [history](https://github.com/rackt/history) (`history.location`) &nbsp;&harr;&nbsp; [react-router](https://github.com/rackt/react-router)
We only store current URL and state, whereas redux-router stores the entire location object from react-router. You can read it, and also change it with an action.
### Tutorial
Let's take a look at a simple example.
**Note:** This example uses `react-router`'s 2.0 API, which is currently released under version 2.0.0-rc5.
```js

@@ -59,21 +41,21 @@ import React from 'react'

import { Router, Route, browserHistory } from 'react-router'
import { syncHistory, routeReducer } from 'react-router-redux'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
import reducers from '<project-path>/reducers'
const reducer = combineReducers(Object.assign({}, reducers, {
routing: routeReducer
}))
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
...reducers,
routing: routerReducer
})
)
// Sync dispatched route actions to the history
const reduxRouterMiddleware = syncHistory(browserHistory)
const createStoreWithMiddleware = applyMiddleware(reduxRouterMiddleware)(createStore)
// Create an enhanced history that syncs navigation events with the store
const history = syncHistoryWithStore(browserHistory, store)
const store = createStoreWithMiddleware(reducer)
// Required for replaying actions from devtools to work
reduxRouterMiddleware.listenForReplays(store)
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
/* Tell the Router to use our enhanced history */
<Router history={history}>
<Route path="/" component={App}>

@@ -89,28 +71,29 @@ <Route path="foo" component={Foo}/>

Now you can read from `state.routing.location.pathname` to get the URL. It's far more likely that you want to change the URL more often, however. You can use the `push` action creator that we provide:
Now any time you navigate, which can come from pressing browser buttons or navigating in your application code, the enhanced history will first pass the new location through the Redux store and then on to React Router to update the component tree. If you time travel, it will also pass the new state to React Router to update the component tree again.
#### How do I watch for navigation events, such as for analytics?
Simply listen to the enhanced history via `history.listen`. This takes in a function that will receive a `location` any time the store updates. This includes any time travel activity performed on the store.
```js
import { routeActions } from 'react-router-redux'
const history = syncHistoryWithStore(browserHistory, store)
function MyComponent({ dispatch }) {
return <Button onClick={() => dispatch(routeActions.push('/foo'))}/>;
}
history.listen(location => analyticsService.track(location.pathname))
```
This will change the state, which will trigger a change in react-router. Additionally, if you want to respond to the path update action, just handle the `UPDATE_LOCATION` constant that we provide:
For other kinds of events in your system, you can use middleware on your Redux store like normal to watch any action that is dispatched to the store.
```js
import { UPDATE_LOCATION } from 'react-router-redux'
#### What if I use Immutable.js with my Redux store?
function update(state, action) {
switch(action.type) {
case UPDATE_LOCATION:
// do something here
}
}
```
**But how do I access router props in a Container component?**
When using a wrapper for your store's state, such as Immutable.js, you will need to change two things from the standard setup:
react-router [injects route information via a child component's props](https://github.com/rackt/react-router/blob/latest/docs/Introduction.md#getting-url-parameters). This makes accessing them from a simple component easy. When using a react-redux Container to connect simple components to the store state and dispatch you can access these injected route information from the [2nd argument of `mapStateToProps`](https://github.com/rackt/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) as follows:
1. Provide your own reducer function that will receive actions of type `LOCATION_CHANGE` and return the payload merged into state.
2. Pass a selector to access the payload state via the `selectLocationState` option on `syncHistoryWithStore`.
These two hooks will allow you to store the state that this library uses in whatever format or wrapper you would like.
#### How do I access router state in a container component?
React Router [provides route information via a route component's props](https://github.com/rackt/react-router/blob/latest/docs/Introduction.md#getting-url-parameters). This makes it easy to access them from a container component. When using [react-redux](https://github.com/rackt/react-redux) to `connect()` your components to state, you can access the router's props from the [2nd argument of `mapStateToProps`](https://github.com/rackt/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options):
```js

@@ -125,60 +108,69 @@ function mapStateToProps(state, ownProps) {

You should not read the location state directly from the Redux store. This is because React Router operates asynchronously (to handle things such as dynamically-loaded components) and your component tree may not yet be updated in sync with your Redux state. You should rely on the props passed by React Router, as they are only updated after it has processed all asynchronous code.
### Examples
#### What if I want to issue navigation events via Redux actions?
* [examples/basic](https://github.com/rackt/react-router-redux/blob/master/examples/basic) - basic reference implementation
React Router provides singleton versions of history (`browserHistory` and `hashHistory`) that you can import and use from anywhere in your application. However, if you prefer Redux style actions, the library also provides a set of action creators and a middleware to capture them and redirect them to your history instance.
```js
import { routerMiddleware, push } from 'react-router-redux'
// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
reducers,
applyMiddleware(middleware)
)
// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))
```
## Examples
- [examples/basic](/examples/basic) - basic reference implementation
Examples from the community:
* [davezuko/react-redux-starter-kit](https://github.com/davezuko/react-redux-starter-kit) - popular redux starter kit
* **tip**: migrating from redux-router? use [this commit](https://github.com/davezuko/react-redux-starter-kit/commit/db66626ca8a02ecf030a3f7f5a669ac338fd5897) as a reference
* [freeqaz/redux-simple-router-example](https://github.com/freeqaz/redux-simple-router-example) - example implementation
* [choonkending/react-webpack-node](https://github.com/choonkending/react-webpack-node) - boilerplate for universal redux and react-router
* [tj/frontend-boilerplate](https://github.com/tj/frontend-boilerplate)
* [bdefore/universal-redux](https://github.com/bdefore/universal-redux) - npm package for universally rendered redux applications
* [tomatau/breko-hub](https://github.com/tomatau/breko-hub) - Babel, React & Koa, Hot Universal Boilerplate - focused on developer experience.
* [yangli1990/react-redux-isomorphic](https://github.com/yangli1990/Isomorphic-Universal-React-Template) - boilerplate for universal redux and redux-simple-router
* [StevenIseki/redux-simple-router-example](https://github.com/StevenIseki/redux-simple-router-example)
* [mattkrick/meatier](https://github.com/mattkrick/meatier) - SSR, dual dev/prod client builds
* [mxstbr/react-boilerplate](https://github.com/mxstbr/react-boilerplate/tree/v3.0.0) - :fire: Quick setup for performance orientated, offline first React.js applications
* **Tip**: Upgrading from react-router w/o react-router-redux? Use [this PR](https://github.com/mxstbr/react-boilerplate/pull/98/files) as a reference!
- None yet, since we just released a new version :smile:
_Have an example to add? Send us a PR!_
&rarr; _Have an example to add? Send us a PR!_ &larr;
### API
## API
#### `syncHistory(history: History) => ReduxMiddleware`
#### `routerReducer()`
Call this to create a middleware that can be applied with Redux's `applyMiddleware` to allow actions to call history methods. The middleware will look for route actions created by `push`, `replace`, etc. and applies them to the history.
**You must add this reducer to your store for syncing to work.**
#### `ReduxMiddleware.listenForReplays(store: ReduxStore, selectLocationState?: function)`
A reducer function that stores location updates from `history`. If you use `combineReducers`, it should be nested under the `routing` key.
By default, the syncing logic will not respond to replaying of actions, which means it won't work with projects like redux-devtools. Call this function on the middleware object returned from `syncHistory` and give it the store to listen to, and it will properly work with action replays. Obviously, you would do that after you have created the store and everything else has been set up.
#### `history = syncHistoryWithStore(history, store, [options])`
Supply an optional function `selectLocationState` to customize where to find the location state on your app state. It defaults to `state => state.routing.location`, so you would install the reducer under the name "routing". Feel free to change this to whatever you like.
Creates an enhanced history from the provided history. This history changes `history.listen` to pass all location updates through the provided store first. This ensures if the store is updated either from a navigation event or from a time travel action, such as a replay, the listeners of the enhanced history will stay in sync.
#### `ReduxMiddleware.unsubscribe()`
**You must provide the enhanced history to your `<Router>` component.** This ensures your routes stay in sync with your location and your store at the same time.
Call this on the middleware returned from `syncHistory` to stop the syncing process set up by `listenForReplays`.
The `options` object takes in the following optional keys:
#### `routeReducer`
- `selectLocationState` - (default `state => state.routing`) A selector function to obtain the history state from your store. Useful when not using the provided `routerReducer` to store history state. Allows you to use wrappers, such as Immutable.js.
- `adjustUrlOnReplay` - (default `true`) When `false`, the URL will not be kept in sync during time travel. This is useful when using `persistState` from Redux DevTools and not wanting to maintain the URL state when restoring state.
A reducer function that keeps track of the router state. You must add this reducer to your app reducers when creating the store. It will return a `location` property in state. If you use `combineReducers`, it will be nested under wherever property you add it to (`state.routing` in the example above).
#### `push(location)`, `replace(location)`, `go(number)`, `goBack()`, `goForward()`
**Warning:** It is a bad pattern to use `react-redux`'s `connect` decorator to map the state from this reducer to props on your `Route` components. This can lead to infinite loops and performance problems. `react-router` already provides this for you via `this.props.location`.
**You must install `routerMiddleware` for these action creators to work.**
#### `UPDATE_LOCATION`
Action creators that correspond with the [history methods of the same name](https://github.com/rackt/history/blob/master/docs/GettingStarted.md#navigation). For reference they are defined as follows:
An action type that you can listen for in your reducers to be notified of route updates.
- `push` - Pushes a new location to history, becoming the current location.
- `replace` - Replaces the current location in history.
- `go` - Moves backwards or forwards a relative number of locations in history.
- `goForward` - Moves forward one location. Equivalent to `go(1)`
- `goBack` - Moves backwards one location. Equivalent to `go(-1)`
#### `routeActions`
Both `push` and `replace` take in a [location descriptor](https://github.com/rackt/history/blob/master/docs/Glossary.md#locationdescriptor), which can be an object describing the URL or a plain string URL.
An object that contains all the actions creators you can use to manipulate history:
These action creators are also available in one single object as `routerActions`, which can be used as a convenience when using Redux's `bindActionCreators()`.
* `push(nextLocation: LocationDescriptor)`
* `replace(nextLocation: LocationDescriptor)`
* `go(n: number)`
* `goForward()`
* `goBack()`
#### `routerMiddleware(history)`
A [location descriptor](https://github.com/rackt/history/blob/master/docs/Glossary.md#locationdescriptor) can be a descriptive object (see the link) or a normal URL string. The most common action is to push a new URL via `routeActions.push(...)`. These all directly call the analogous [history methods](https://github.com/rackt/history/blob/master/docs/GettingStarted.md#navigation).
A middleware you can apply to your Redux `store` to capture dispatched actions created by the action creators. It will redirect those actions to the provided `history` instance.

@@ -1,112 +0,9 @@

// Constants
export syncHistoryWithStore from './sync'
export { LOCATION_CHANGE, routerReducer } from './reducer'
export const TRANSITION = '@@router/TRANSITION'
export const UPDATE_LOCATION = '@@router/UPDATE_LOCATION'
const SELECT_LOCATION = state => state.routing.location
function transition(method) {
return (...args) => ({
type: TRANSITION,
payload: { method, args }
})
}
export const push = transition('push')
export const replace = transition('replace')
export const go = transition('go')
export const goBack = transition('goBack')
export const goForward = transition('goForward')
export const routeActions = { push, replace, go, goBack, goForward }
function updateLocation(location) {
return {
type: UPDATE_LOCATION,
payload: location
}
}
// Reducer
const initialState = {
location: undefined
}
export function routeReducer(state = initialState, { type, payload: location }) {
if (type !== UPDATE_LOCATION) {
return state
}
return { ...state, location }
}
// Syncing
export function syncHistory(history) {
let unsubscribeHistory, currentKey, unsubscribeStore
let connected = false, syncing = false
history.listen(location => { initialState.location = location })()
function middleware(store) {
unsubscribeHistory = history.listen(location => {
currentKey = location.key
if (syncing) {
// Don't dispatch a new action if we're replaying location.
return
}
store.dispatch(updateLocation(location))
})
connected = true
return next => action => {
if (action.type !== TRANSITION || !connected) {
return next(action)
}
const { payload: { method, args } } = action
history[method](...args)
}
}
middleware.listenForReplays =
(store, selectLocationState = SELECT_LOCATION) => {
const getLocationState = () => selectLocationState(store.getState())
const initialLocation = getLocationState()
unsubscribeStore = store.subscribe(() => {
const location = getLocationState()
// If we're resetting to the beginning, use the saved initial value. We
// need to dispatch a new action at this point to populate the store
// appropriately.
if (location.key === initialLocation.key) {
history.replace(initialLocation)
return
}
// Otherwise, if we need to update the history location, do so without
// dispatching a new action, as we're just bringing history in sync
// with the store.
if (location.key !== currentKey) {
syncing = true
history.transitionTo(location)
syncing = false
}
})
}
middleware.unsubscribe = () => {
unsubscribeHistory()
if (unsubscribeStore) {
unsubscribeStore()
}
connected = false
}
return middleware
}
export {
CALL_HISTORY_METHOD,
push, replace, go, goBack, goForward,
routerActions
} from './actions'
export routerMiddleware from './middleware'
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