summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@codeispoetry.ru>2017-06-21 14:11:49 +0300
committerAndrej Mihajlov <and@codeispoetry.ru>2017-06-21 14:11:49 +0300
commit5ae8eda0075ec72424c478fdf1ff731db43b0ff5 (patch)
treec642e59864520408cbe8c1f7967cf814fdee43e8
parent7950d83c2d70c72bb08415d8ecf823d85ce7bd35 (diff)
parent00c6ccf614bc92b72262e4ac57e8d602573ffc65 (diff)
downloadmullvadvpn-5ae8eda0075ec72424c478fdf1ff731db43b0ff5.tar.xz
mullvadvpn-5ae8eda0075ec72424c478fdf1ff731db43b0ff5.zip
Merge branch 'feature/flow-typed/enums-migration'
-rw-r--r--.eslintrc3
-rw-r--r--.flowconfig2
-rw-r--r--.vscode/settings.json7
-rw-r--r--app/app.js17
-rw-r--r--app/components/Connect.js27
-rw-r--r--app/components/Login.js29
-rw-r--r--app/components/Settings.js3
-rw-r--r--app/containers/LoginPage.js3
-rw-r--r--app/enums.js23
-rw-r--r--app/lib/backend-redux-actions.js13
-rw-r--r--app/reducers/connect.js3
-rw-r--r--app/reducers/user.js3
-rw-r--r--app/routes.js5
-rw-r--r--package.json2
-rw-r--r--test/actions.spec.js9
-rw-r--r--test/mocks/redux.js5
-rw-r--r--test/reducers.spec.js5
-rw-r--r--test/routing.spec.js3
-rw-r--r--yarn.lock18
19 files changed, 77 insertions, 103 deletions
diff --git a/.eslintrc b/.eslintrc
index 70248418cd..eb889110f2 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -26,7 +26,8 @@
"no-unused-expressions" : [ 0 ], // until fixed https://github.com/babel/babel-eslint/issues/158
"no-unused-vars": ["error", {"args": all, "argsIgnorePattern": "_.*"}],
"block-scoped-var": [ 0 ], // until fixed https://github.com/eslint/eslint/issues/2253
- "react/prop-types": [ 0 ]
+ "react/prop-types": [ 0 ],
+ "flowtype/define-flow-type": 1
},
"env": {
"es6": true,
diff --git a/.flowconfig b/.flowconfig
index 6d8e2c3c6e..e08fa5ecae 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -10,5 +10,7 @@
flow-libs/
[options]
+emoji=true
+suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
munge_underscores=true
unsafe.enable_getters_and_setters=true
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 1a1f9a53e7..0000000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "search.exclude": {
- "build/": true,
- "dist/": true,
- "docs/": true
- }
-} \ No newline at end of file
diff --git a/app/app.js b/app/app.js
index 7eef8f3685..86084202af 100644
--- a/app/app.js
+++ b/app/app.js
@@ -13,7 +13,8 @@ import connectActions from './actions/connect';
import Backend from './lib/backend';
import mapBackendEventsToReduxActions from './lib/backend-redux-actions';
import mapBackendEventsToRouter from './lib/backend-routing';
-import { LoginState, ConnectionState } from './enums';
+
+import type { LoginState, ConnectionState } from './enums';
import type { TrayIconType } from './lib/tray-icon-manager';
const initialState = {};
@@ -22,16 +23,16 @@ const store = configureStore(initialState, memoryHistory);
const backend = new Backend();
// reset login state if user quit the app during login
-if([LoginState.connecting, LoginState.failed].includes(store.getState().user.status)) {
+if((['connecting', 'failed']: Array<LoginState>).includes(store.getState().user.status)) {
store.dispatch(userActions.loginChange({
- status: LoginState.none
+ status: 'none'
}));
}
// reset connection state if user quit the app when connecting
-if(store.getState().connect.status === ConnectionState.connecting) {
+if(store.getState().connect.status === 'connecting') {
store.dispatch(connectActions.connectionChange({
- status: ConnectionState.disconnected
+ status: 'disconnected'
}));
}
@@ -40,10 +41,10 @@ if(store.getState().connect.status === ConnectionState.connecting) {
/**
* Get tray icon type based on connection state
*/
-const getIconType = (s: string): TrayIconType => {
+const getIconType = (s: ConnectionState): TrayIconType => {
switch(s) {
- case ConnectionState.connected: return 'secured';
- case ConnectionState.connecting: return 'securing';
+ case 'connected': return 'secured';
+ case 'connecting': return 'securing';
default: return 'unsecured';
}
};
diff --git a/app/components/Connect.js b/app/components/Connect.js
index c060986af3..0a3bd761a9 100644
--- a/app/components/Connect.js
+++ b/app/components/Connect.js
@@ -7,7 +7,6 @@ import cheapRuler from 'cheap-ruler';
import { Layout, Container, Header } from './Layout';
import { mapbox as mapboxConfig } from '../config';
import Backend from '../lib/backend';
-import { ConnectionState } from '../enums';
import ExternalLinkSVG from '../assets/images/icon-extLink.svg';
import type HeaderBarStyle from './HeaderBar';
@@ -109,9 +108,9 @@ export default class Connect extends Component {
const preferredServer = this.props.settings.preferredServer;
const serverInfo = this.props.getServerInfo(preferredServer);
- const isConnecting = this.props.connect.status === ConnectionState.connecting;
- const isConnected = this.props.connect.status === ConnectionState.connected;
- const isDisconnected = this.props.connect.status === ConnectionState.disconnected;
+ const isConnecting = this.props.connect.status === 'connecting';
+ const isConnected = this.props.connect.status === 'connected';
+ const isDisconnected = this.props.connect.status === 'disconnected';
const altitude = (isConnecting ? 300 : 100) * 1000;
@@ -338,19 +337,19 @@ export default class Connect extends Component {
headerStyle(): HeaderBarStyle {
switch(this.props.connect.status) {
- case ConnectionState.connecting:
- case ConnectionState.disconnected:
+ case 'connecting':
+ case 'disconnected':
return 'error';
- case ConnectionState.connected:
+ case 'connected':
return 'success';
}
}
networkSecurityClass() {
let classes = ['connect__status-security'];
- if(this.props.connect.status === ConnectionState.connected) {
+ if(this.props.connect.status === 'connected') {
classes.push('connect__status-security--secure');
- } else if(this.props.connect.status === ConnectionState.disconnected) {
+ } else if(this.props.connect.status === 'disconnected') {
classes.push('connect__status-security--unsecured');
}
@@ -359,15 +358,15 @@ export default class Connect extends Component {
networkSecurityMessage() {
switch(this.props.connect.status) {
- case ConnectionState.connected: return 'Secure connection';
- case ConnectionState.connecting: return 'Creating secure connection';
+ case 'connected': return 'Secure connection';
+ case 'connecting': return 'Creating secure connection';
default: return 'Unsecured connection';
}
}
spinnerClass() {
var classes = ['connect__status-icon'];
- if(this.props.connect.status !== ConnectionState.connecting) {
+ if(this.props.connect.status !== 'connecting') {
classes.push('connect__status-icon--hidden');
}
return classes.join(' ');
@@ -375,14 +374,14 @@ export default class Connect extends Component {
ipAddressClass() {
var classes = ['connect__status-ipaddress'];
- if(this.props.connect.status === ConnectionState.connecting) {
+ if(this.props.connect.status === 'connecting') {
classes.push('connect__status-ipaddress--invisible');
}
return classes.join(' ');
}
displayLocation() {
- if(this.props.connect.status === ConnectionState.disconnected) {
+ if(this.props.connect.status === 'disconnected') {
const { location, country, city } = this.props.user;
return { location, country, city };
}
diff --git a/app/components/Login.js b/app/components/Login.js
index 3e841e096f..2bcf40950e 100644
--- a/app/components/Login.js
+++ b/app/components/Login.js
@@ -1,7 +1,6 @@
import React, { Component, PropTypes } from 'react';
import { If, Then } from 'react-if';
import { Layout, Container, Header } from './Layout';
-import { LoginState } from '../enums';
import AccountInput from './AccountInput';
import ExternalLinkSVG from '../assets/images/icon-extLink.svg';
import LoginArrowSVG from '../assets/images/icon-arrow.svg';
@@ -28,7 +27,7 @@ export default class Login extends Component {
const prev = this.props.user || {};
const next = nextProps.user || {};
- if(prev.status !== next.status && next.status === LoginState.failed) {
+ if(prev.status !== next.status && next.status === 'failed') {
this.setState({ notifyOnFirstChangeAfterFailure: true });
}
}
@@ -63,17 +62,17 @@ export default class Login extends Component {
formTitle(s) {
switch(s) {
- case LoginState.connecting: return 'Logging in...';
- case LoginState.failed: return 'Login failed';
- case LoginState.ok: return 'Login successful';
+ case 'connecting': return 'Logging in...';
+ case 'failed': return 'Login failed';
+ case 'ok': return 'Login successful';
default: return 'Login';
}
}
formSubtitle(s, e) {
switch(s) {
- case LoginState.failed: return e.message;
- case LoginState.connecting: return 'Checking account number';
+ case 'failed': return e.message;
+ case 'connecting': return 'Checking account number';
default: return 'Enter your account number';
}
}
@@ -86,10 +85,10 @@ export default class Login extends Component {
}
switch(user.status) {
- case LoginState.connecting:
+ case 'connecting':
classes.push('login-form__input-wrap--inactive');
break;
- case LoginState.failed:
+ case 'failed':
classes.push('login-form__input-wrap--error');
break;
}
@@ -100,8 +99,8 @@ export default class Login extends Component {
footerClass(user) {
const classes = ['login-footer'];
switch(user.status) {
- case LoginState.ok:
- case LoginState.connecting:
+ case 'ok':
+ case 'connecting':
classes.push('login-footer--invisible');
break;
}
@@ -115,7 +114,7 @@ export default class Login extends Component {
classes.push('login-form__submit--active');
}
- if(user.status === LoginState.connecting) {
+ if(user.status === 'connecting') {
classes.push('login-form__submit--invisible');
}
@@ -126,9 +125,9 @@ export default class Login extends Component {
const { account, status, error } = this.props.user;
const title = this.formTitle(status);
const subtitle = this.formSubtitle(status, error);
- const isConnecting = status === LoginState.connecting;
- const isFailed = status === LoginState.failed;
- const isLoggedIn = status === LoginState.ok;
+ const isConnecting = status === 'connecting';
+ const isFailed = status === 'failed';
+ const isLoggedIn = status === 'ok';
const inputWrapClass = this.inputWrapClass(this.props.user);
const footerClass = this.footerClass(this.props.user);
diff --git a/app/components/Settings.js b/app/components/Settings.js
index 08a9af441b..cc8dc24f25 100644
--- a/app/components/Settings.js
+++ b/app/components/Settings.js
@@ -4,7 +4,6 @@ import { If, Then, Else } from 'react-if';
import { Layout, Container, Header } from './Layout';
import Switch from './Switch';
import CustomScrollbars from './CustomScrollbars';
-import { LoginState } from '../enums';
export default class Settings extends Component {
@@ -34,7 +33,7 @@ export default class Settings extends Component {
}
render() {
- const isLoggedIn = this.props.user.status === LoginState.ok;
+ const isLoggedIn = this.props.user.status === 'ok';
let isOutOfTime = false, formattedPaidUntil = '';
let paidUntilIso = this.props.user.paidUntil;
diff --git a/app/containers/LoginPage.js b/app/containers/LoginPage.js
index f5a75ca989..163e55f145 100644
--- a/app/containers/LoginPage.js
+++ b/app/containers/LoginPage.js
@@ -4,7 +4,6 @@ import { bindActionCreators } from 'redux';
import { push } from 'react-router-redux';
import Login from '../components/Login';
import userActions from '../actions/user';
-import { LoginState } from '../enums';
import { links } from '../config';
const mapStateToProps = (state) => state;
@@ -15,7 +14,7 @@ const mapDispatchToProps = (dispatch, props) => {
onSettings: () => dispatch(push('/settings')),
onLogin: (account) => login(backend, account),
onChange: (account) => loginChange({ account }),
- onFirstChangeAfterFailure: () => loginChange({ status: LoginState.none, error: null }),
+ onFirstChangeAfterFailure: () => loginChange({ status: 'none', error: null }),
onExternalLink: (type) => shell.openExternal(links[type])
};
};
diff --git a/app/enums.js b/app/enums.js
index 8613b52bf8..6f4647efa6 100644
--- a/app/enums.js
+++ b/app/enums.js
@@ -1,20 +1,3 @@
-import Enum from './lib/enum';
-
-/**
- * Login state enum used for React components
- * @type {LoginState}
- * @property {string} none Initial state (not logged in)
- * @property {string} connecting Attempting to log in
- * @property {string} failed Failed to log in
- * @property {string} ok Logged in
- */
-export const LoginState = new Enum('none', 'connecting', 'failed', 'ok');
-
-/**
- * Connection state enum used for React components
- * @type {ConnectionState}
- * @property {string} disconnected Initial state (disconnected)
- * @property {string} connecting Connecting
- * @property {string} connected Connected
- */
-export const ConnectionState = new Enum('disconnected', 'connecting', 'connected');
+// @flow
+export type LoginState = 'none' | 'connecting' | 'failed' | 'ok';
+export type ConnectionState = 'disconnected' | 'connecting' | 'connected';
diff --git a/app/lib/backend-redux-actions.js b/app/lib/backend-redux-actions.js
index 4e3dd63323..50bfd22943 100644
--- a/app/lib/backend-redux-actions.js
+++ b/app/lib/backend-redux-actions.js
@@ -1,7 +1,6 @@
import userActions from '../actions/user';
import connectActions from '../actions/connect';
import Backend from './backend';
-import { LoginState, ConnectionState } from '../enums';
import log from 'electron-log';
/**
@@ -22,7 +21,7 @@ export default function mapBackendEventsToReduxActions(backend, store) {
const onConnecting = (serverAddress) => {
store.dispatch(connectActions.connectionChange({
- status: ConnectionState.connecting,
+ status: 'connecting',
serverAddress
}));
};
@@ -31,33 +30,33 @@ export default function mapBackendEventsToReduxActions(backend, store) {
if (error) {
log.error('Unable to connect to', serverAddress, error);
} else {
- store.dispatch(connectActions.connectionChange({ status: ConnectionState.connected }));
+ store.dispatch(connectActions.connectionChange({ status: 'connected' }));
}
};
const onDisconnect = () => {
store.dispatch(connectActions.connectionChange({
- status: ConnectionState.disconnected,
+ status: 'disconnected',
serverAddress: null
}));
};
const onLoggingIn = (info) => {
store.dispatch(userActions.loginChange(Object.assign({
- status: LoginState.connecting,
+ status: 'connecting',
error: null
}, info)));
};
const onLogin = (info, error) => {
- const status = error ? LoginState.failed : LoginState.ok;
+ const status = error ? 'failed' : 'ok';
const paidUntil = info.paidUntil ? info.paidUntil : null;
store.dispatch(userActions.loginChange({ paidUntil, status, error }));
};
const onLogout = () => {
store.dispatch(userActions.loginChange({
- status: LoginState.none,
+ status: 'none',
account: '',
paidUntil: null,
error: null
diff --git a/app/reducers/connect.js b/app/reducers/connect.js
index 35af5e888b..cf3dc35906 100644
--- a/app/reducers/connect.js
+++ b/app/reducers/connect.js
@@ -1,9 +1,8 @@
import { handleActions } from 'redux-actions';
-import { ConnectionState } from '../enums';
import actions from '../actions/connect';
const initialState = {
- status: ConnectionState.disconnected,
+ status: 'disconnected',
isOnline: true,
serverAddress: null,
clientIp: null
diff --git a/app/reducers/user.js b/app/reducers/user.js
index 15685034b8..1409c77a4e 100644
--- a/app/reducers/user.js
+++ b/app/reducers/user.js
@@ -1,6 +1,5 @@
import { handleActions } from 'redux-actions';
import actions from '../actions/user';
-import { LoginState } from '../enums';
const initialState = {
account: null,
@@ -8,7 +7,7 @@ const initialState = {
location: [0, 0],
country: null,
city: null,
- status: LoginState.none,
+ status: 'none',
error: null
};
diff --git a/app/routes.js b/app/routes.js
index e61782f5f1..d815d12100 100644
--- a/app/routes.js
+++ b/app/routes.js
@@ -7,7 +7,6 @@ import ConnectPage from './containers/ConnectPage';
import SettingsPage from './containers/SettingsPage';
import AccountPage from './containers/AccountPage';
import SelectLocationPage from './containers/SelectLocationPage';
-import { LoginState } from './enums';
import { getTransitionProps } from './transitions';
/**
@@ -55,7 +54,7 @@ export default function makeRoutes(getState, componentProps) {
return (
<Route {...rest} render={ (routeProps) => {
const { user } = getState();
- const isLoggedIn = user.status === LoginState.ok;
+ const isLoggedIn = user.status === 'ok';
if(isLoggedIn) {
return renderMergedProps(component, routeProps, ...rest);
@@ -76,7 +75,7 @@ export default function makeRoutes(getState, componentProps) {
return (
<Route {...rest} render={ (routeProps) => {
const { user } = getState();
- const isLoggedIn = user.status === LoginState.ok;
+ const isLoggedIn = user.status === 'ok';
if(isLoggedIn) {
return (<Redirect to={ '/connect' } />);
diff --git a/package.json b/package.json
index 615dfde17c..9b16195226 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
"eslint": "^3.14.1",
"eslint-plugin-flowtype": "^2.34.0",
"eslint-plugin-react": "^6.9.0",
- "flow-bin": "^0.46.0",
+ "flow-bin": "^0.48.0",
"flow-typed": "^2.1.2",
"isomorphic-fetch": "^2.2.1",
"jsdom": "^9.11.0",
diff --git a/test/actions.spec.js b/test/actions.spec.js
index e1420e92d2..914483161e 100644
--- a/test/actions.spec.js
+++ b/test/actions.spec.js
@@ -7,7 +7,6 @@ import { newMockIpc } from './mocks/ipc';
import userActions from '../app/actions/user';
import connectActions from '../app/actions/connect';
import mapBackendEventsToReduxActions from '../app/lib/backend-redux-actions';
-import { LoginState, ConnectionState } from '../app/enums';
describe('actions', function() {
this.timeout(10000);
@@ -100,11 +99,11 @@ describe('actions', function() {
user: {
account: '3333234567890',
paidUntil: '2038-01-01T00:00:00.000Z',
- status: LoginState.ok
+ status: 'ok'
},
connect: {
serverAddress: '1.2.3.4',
- status: ConnectionState.connected
+ status: 'connected'
}
});
@@ -132,11 +131,11 @@ describe('actions', function() {
user: {
account: '3333234567890',
paidUntil: '2038-01-01T00:00:00.000Z',
- status: LoginState.ok
+ status: 'ok'
},
connect: {
serverAddress: '1.2.3.4',
- status: ConnectionState.connected
+ status: 'connected'
}
});
diff --git a/test/mocks/redux.js b/test/mocks/redux.js
index 6855274296..0d4d73518f 100644
--- a/test/mocks/redux.js
+++ b/test/mocks/redux.js
@@ -1,7 +1,6 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { defaultServer } from '../../app/config';
-import { LoginState, ConnectionState } from '../../app/enums';
// fetch is absent in node environment
// this will automatically import it into global scope
@@ -13,11 +12,11 @@ export const mockState = () => {
return {
user: {
account: null,
- status: LoginState.none,
+ status: 'none',
error: null
},
connect: {
- status: ConnectionState.disconnected,
+ status: 'disconnected',
serverAddress: null,
clientIp: null
},
diff --git a/test/reducers.spec.js b/test/reducers.spec.js
index 8a5402c3e8..7a5470e930 100644
--- a/test/reducers.spec.js
+++ b/test/reducers.spec.js
@@ -3,7 +3,6 @@ import userReducer from '../app/reducers/user';
import connectReducer from '../app/reducers/connect';
import settingsReducer from '../app/reducers/settings';
import { defaultServer } from '../app/config';
-import { LoginState, ConnectionState } from '../app/enums';
describe('reducers', () => {
@@ -12,7 +11,7 @@ describe('reducers', () => {
type: 'USER_LOGIN_CHANGE',
payload: {
account: '1111',
- status: LoginState.failed,
+ status: 'failed',
error: new Error('Something went wrong')
}
};
@@ -24,7 +23,7 @@ describe('reducers', () => {
const action = {
type: 'CONNECTION_CHANGE',
payload: {
- status: ConnectionState.connected,
+ status: 'connected',
serverAddress: '2.1.1.2',
clientIp: '2.1.1.1'
}
diff --git a/test/routing.spec.js b/test/routing.spec.js
index 2ce33e73c2..e728ca59a6 100644
--- a/test/routing.spec.js
+++ b/test/routing.spec.js
@@ -3,7 +3,6 @@ import { expect } from 'chai';
import { filterMinorActions, mockState, mockStore } from './mocks/redux';
import userActions from '../app/actions/user';
import mapBackendEventsToRouter from '../app/lib/backend-routing';
-import { LoginState } from '../app/enums';
import Backend from '../app/lib/backend';
import { newMockIpc } from './mocks/ipc';
@@ -18,7 +17,7 @@ describe('routing', function() {
let state = Object.assign(mockState(), {
user: {
account: '1111234567890',
- status: LoginState.ok
+ status: 'ok'
}
});
diff --git a/yarn.lock b/yarn.lock
index 6924abe385..7a3a61d6c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2292,6 +2292,12 @@ esdoc@^0.5.2:
minimist "1.2.0"
taffydb "2.7.2"
+eslint-plugin-flowtype@^2.34.0:
+ version "2.34.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.34.0.tgz#b9875f314652e5081623c9d2b18a346bbb759c09"
+ dependencies:
+ lodash "^4.15.0"
+
eslint-plugin-react@^6.9.0:
version "6.10.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78"
@@ -2583,9 +2589,9 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
-flow-bin@^0.46.0:
- version "0.46.0"
- resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.46.0.tgz#06ad7fe19dddb1042264438064a2a32fee12b872"
+flow-bin@^0.48.0:
+ version "0.48.0"
+ resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.48.0.tgz#72d075143524358db8901525e3c784dc13a7c7ee"
flow-remove-types@^1.1.2:
version "1.2.1"
@@ -3667,7 +3673,7 @@ lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
+lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4016,8 +4022,8 @@ npmlog@^4.0.2:
set-blocking "~2.0.0"
"nseventmonitor@git+https://github.com/pronebird/NSEventMonitor.git":
- version "0.0.2"
- resolved "git+https://github.com/pronebird/NSEventMonitor.git#f6af316e7a92c1d50872ef3552271fdd4d8d4459"
+ version "0.0.3"
+ resolved "git+https://github.com/pronebird/NSEventMonitor.git#2707370d46748c2e4de99f0f762daf6d3b28b431"
dependencies:
bindings "^1.2.1"