summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/gulpfile.js7
-rw-r--r--gui/package-lock.json1210
-rw-r--r--gui/package.json6
-rwxr-xr-xgui/scripts/build-proto.sh33
-rw-r--r--gui/src/main/daemon-rpc.ts1543
-rw-r--r--gui/src/main/index.ts49
-rw-r--r--gui/src/renderer/components/RedeemVoucher.tsx2
-rw-r--r--gui/src/shared/daemon-rpc-types.ts18
-rw-r--r--gui/tasks/scripts.js9
9 files changed, 2282 insertions, 595 deletions
diff --git a/gui/gulpfile.js b/gui/gulpfile.js
index f203fc16d0..6a7226c1aa 100644
--- a/gui/gulpfile.js
+++ b/gui/gulpfile.js
@@ -1,4 +1,4 @@
-const { task, series } = require('gulp');
+const { task, series, parallel } = require('gulp');
const rimraf = require('rimraf');
const scripts = require('./tasks/scripts');
@@ -9,8 +9,9 @@ const dist = require('./tasks/distribution');
task('clean', function (done) {
rimraf('./build', done);
});
-task('build', series('clean', assets.copyAll, scripts.build));
-task('develop', series('clean', watch.start));
+task('build-proto', scripts.buildProto);
+task('build', series('clean', parallel(assets.copyAll, scripts.buildProto), scripts.build));
+task('develop', series('clean', scripts.buildProto, watch.start));
task('pack-win', series('build', dist.packWin));
task('pack-linux', series('build', dist.packLinux));
task('pack-mac', series('build', dist.packMac));
diff --git a/gui/package-lock.json b/gui/package-lock.json
index c3b781bf69..57840f46f4 100644
--- a/gui/package-lock.json
+++ b/gui/package-lock.json
@@ -397,6 +397,21 @@
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
},
+ "@grpc/grpc-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.1.2.tgz",
+ "integrity": "sha512-k2u86Bkm/3xrjUaSWeIyzXScBt/cC8uE7BznR0cpueQi11R33W6qfJdMrkrsmSHirp5likR55JSXUrcWG6ybHA==",
+ "requires": {
+ "semver": "^6.2.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ }
+ }
+ },
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@@ -559,6 +574,12 @@
"@types/node": "*"
}
},
+ "@types/google-protobuf": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.2.tgz",
+ "integrity": "sha512-ifFemzjNchFBCtHS6bZNhSZCBu7tbtOe0e8qY0z2J4HtFXmPJjm6fXSaQsTG7yhShBEZtt2oP/bkwu5k+emlkQ==",
+ "dev": true
+ },
"@types/history": {
"version": "4.7.5",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz",
@@ -872,8 +893,7 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
- "optional": true
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"accepts": {
"version": "1.3.7",
@@ -958,6 +978,105 @@
}
}
},
+ "ansi-bgblack": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgblack/-/ansi-bgblack-0.1.1.tgz",
+ "integrity": "sha1-poulAHiHcBtqr74/oNrf36juPKI=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgblue": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgblue/-/ansi-bgblue-0.1.1.tgz",
+ "integrity": "sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgcyan": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgcyan/-/ansi-bgcyan-0.1.1.tgz",
+ "integrity": "sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bggreen": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bggreen/-/ansi-bggreen-0.1.1.tgz",
+ "integrity": "sha1-TjGRJIUplD9DIelr8THRwTgWr0k=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgmagenta": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgmagenta/-/ansi-bgmagenta-0.1.1.tgz",
+ "integrity": "sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgred": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgred/-/ansi-bgred-0.1.1.tgz",
+ "integrity": "sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgwhite": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgwhite/-/ansi-bgwhite-0.1.1.tgz",
+ "integrity": "sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bgyellow": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bgyellow/-/ansi-bgyellow-0.1.1.tgz",
+ "integrity": "sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-black": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-black/-/ansi-black-0.1.1.tgz",
+ "integrity": "sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-blue": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-blue/-/ansi-blue-0.1.1.tgz",
+ "integrity": "sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-bold": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-bold/-/ansi-bold-0.1.1.tgz",
+ "integrity": "sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-colors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
@@ -967,6 +1086,24 @@
"ansi-wrap": "^0.1.0"
}
},
+ "ansi-cyan": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
+ "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-dim": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-dim/-/ansi-dim-0.1.1.tgz",
+ "integrity": "sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-escapes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
@@ -993,23 +1130,131 @@
"ansi-wrap": "0.1.0"
}
},
+ "ansi-green": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz",
+ "integrity": "sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-grey": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-grey/-/ansi-grey-0.1.1.tgz",
+ "integrity": "sha1-WdmLasK6GfilF5jphT+6eDOaM8E=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-hidden": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-hidden/-/ansi-hidden-0.1.1.tgz",
+ "integrity": "sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-inverse": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-inverse/-/ansi-inverse-0.1.1.tgz",
+ "integrity": "sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-italic": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-italic/-/ansi-italic-0.1.1.tgz",
+ "integrity": "sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-magenta": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-magenta/-/ansi-magenta-0.1.1.tgz",
+ "integrity": "sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-red": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
+ "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
+ "ansi-reset": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-reset/-/ansi-reset-0.1.1.tgz",
+ "integrity": "sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-strikethrough": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-strikethrough/-/ansi-strikethrough-0.1.1.tgz",
+ "integrity": "sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
+ "ansi-underline": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-underline/-/ansi-underline-0.1.1.tgz",
+ "integrity": "sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-white": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-white/-/ansi-white-0.1.1.tgz",
+ "integrity": "sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"ansi-wrap": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
"integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
"dev": true
},
+ "ansi-yellow": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-yellow/-/ansi-yellow-0.1.1.tgz",
+ "integrity": "sha1-y5NW8vRscy8OMZnmEClVp32oPB0=",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
@@ -1179,8 +1424,7 @@
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
- "optional": true
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"archy": {
"version": "1.0.0",
@@ -1192,7 +1436,6 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
- "optional": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -1585,6 +1828,15 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
+ "autolinker": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz",
+ "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=",
+ "dev": true,
+ "requires": {
+ "gulp-header": "^1.7.1"
+ }
+ },
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
@@ -2503,8 +2755,7 @@
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "optional": true
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"chromium-pickle-js": {
"version": "0.2.0",
@@ -2696,6 +2947,23 @@
"typedarray": "^0.0.6"
}
},
+ "concat-with-sourcemaps": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
+ "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"config-chain": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
@@ -2762,8 +3030,7 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
- "optional": true
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"content-type": {
"version": "1.0.4",
@@ -2813,6 +3080,38 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
+ "create-frame": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-1.0.0.tgz",
+ "integrity": "sha1-i5XyaR4ySbYIBEPjPQutn49pdao=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "isobject": "^3.0.0",
+ "lazy-cache": "^2.0.2"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
"cross-env": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz",
@@ -2932,6 +3231,26 @@
"resolve": "^1.1.10"
}
},
+ "date.js": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz",
+ "integrity": "sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==",
+ "dev": true,
+ "requires": {
+ "debug": "~3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3072,8 +3391,7 @@
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
- "optional": true
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"depd": {
"version": "1.1.2",
@@ -3096,8 +3414,7 @@
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
- "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
- "optional": true
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"detect-node": {
"version": "2.0.4",
@@ -4000,6 +4317,12 @@
"has-binary2": "~1.0.2"
}
},
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "dev": true
+ },
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
@@ -4089,6 +4412,12 @@
"is-arrayish": "^0.2.1"
}
},
+ "error-symbol": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/error-symbol/-/error-symbol-0.1.0.tgz",
+ "integrity": "sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y=",
+ "dev": true
+ },
"es-abstract": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
@@ -4853,6 +5182,23 @@
}
}
},
+ "falsey": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/falsey/-/falsey-0.3.2.tgz",
+ "integrity": "sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^5.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
"fancy-log": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
@@ -5108,6 +5454,12 @@
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
"dev": true
},
+ "fs-exists-sync": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
+ "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
+ "dev": true
+ },
"fs-extra": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
@@ -5123,7 +5475,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
- "optional": true,
"requires": {
"minipass": "^2.6.0"
}
@@ -5739,7 +6090,6 @@
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
- "optional": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -5763,6 +6113,42 @@
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
"dev": true
},
+ "get-object": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz",
+ "integrity": "sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=",
+ "dev": true,
+ "requires": {
+ "is-number": "^2.0.2",
+ "isobject": "^0.2.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
+ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "isobject": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz",
+ "integrity": "sha1-o0MhkvObkQtfAsyYlIeDbscKqF4=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
"get-value": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -5993,6 +6379,11 @@
"sparkles": "^1.0.0"
}
},
+ "google-protobuf": {
+ "version": "4.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-4.0.0-rc.2.tgz",
+ "integrity": "sha512-KANe0ePi8Uk0m17xvjI1bXrE7LRflSuNczkAlHlOEeqXnSBy96tDS5beKwKOnNr0xYmlbC9XiQsj8fOLLyDp2Q=="
+ },
"got": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -6035,6 +6426,89 @@
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
+ "grpc-tools": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.9.1.tgz",
+ "integrity": "sha512-t2JFMPLjxcgwVSJwFEauFaoEiO56kijxSwehQDgZNR/hrStJCH0pHGsjqJNuCOvmI9Z31pYOfgj4zeInTQWh5A==",
+ "dev": true,
+ "requires": {
+ "node-pre-gyp": "^0.15.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "needle": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz",
+ "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz",
+ "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==",
+ "dev": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.3",
+ "needle": "^2.5.0",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4.4.2"
+ }
+ }
+ }
+ },
+ "grpc_tools_node_protoc_ts": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-4.1.0.tgz",
+ "integrity": "sha512-+10Q2LhTalc41e+hCfP7lCLYfyDlAdntEKTn43QHE3CPafNHRu5KgqAHkDg4cUiDEJFUcKhddaYUPI5uR7c2AA==",
+ "dev": true,
+ "requires": {
+ "google-protobuf": "3.5.0",
+ "handlebars": "4.7.4",
+ "handlebars-helpers": "0.10.0"
+ },
+ "dependencies": {
+ "google-protobuf": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.5.0.tgz",
+ "integrity": "sha1-uMxjx02DRXvYqakEUDyO+ya8ozk=",
+ "dev": true
+ }
+ }
+ },
"gud": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
@@ -6110,6 +6584,17 @@
}
}
},
+ "gulp-header": {
+ "version": "1.8.12",
+ "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz",
+ "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==",
+ "dev": true,
+ "requires": {
+ "concat-with-sourcemaps": "*",
+ "lodash.template": "^4.4.0",
+ "through2": "^2.0.0"
+ }
+ },
"gulp-inject-string": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.2.tgz",
@@ -6166,6 +6651,325 @@
"glogg": "^1.0.0"
}
},
+ "handlebars": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.4.tgz",
+ "integrity": "sha512-Is8+SzHv8K9STNadlBVpVhxXrSXxVgTyIvhdg2Qjak1SfSZ7iEozLHdwiX1jJ9lLFkcFJxqGK5s/cI7ZX+qGkQ==",
+ "dev": true,
+ "requires": {
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "yargs": "^15.3.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "handlebars-helper-create-frame": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/handlebars-helper-create-frame/-/handlebars-helper-create-frame-0.1.0.tgz",
+ "integrity": "sha1-iqUdEK62QI/MZgXUDXc1YohIegM=",
+ "dev": true,
+ "requires": {
+ "create-frame": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "handlebars-helpers": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.10.0.tgz",
+ "integrity": "sha512-QiyhQz58u/DbuV41VnfpE0nhy6YCH4vB514ajysV8SoKmP+DxU+pR+fahVyNECHj+jiwEN2VrvxD/34/yHaLUg==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-sort": "^0.1.4",
+ "create-frame": "^1.0.0",
+ "define-property": "^1.0.0",
+ "falsey": "^0.3.2",
+ "for-in": "^1.0.2",
+ "for-own": "^1.0.0",
+ "get-object": "^0.2.0",
+ "get-value": "^2.0.6",
+ "handlebars": "^4.0.11",
+ "handlebars-helper-create-frame": "^0.1.0",
+ "handlebars-utils": "^1.0.6",
+ "has-value": "^1.0.0",
+ "helper-date": "^1.0.1",
+ "helper-markdown": "^1.0.0",
+ "helper-md": "^0.2.2",
+ "html-tag": "^2.0.0",
+ "is-even": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "is-number": "^4.0.0",
+ "kind-of": "^6.0.0",
+ "lazy-cache": "^2.0.2",
+ "logging-helpers": "^1.0.0",
+ "micromatch": "^3.1.4",
+ "relative": "^3.0.2",
+ "striptags": "^3.1.0",
+ "to-gfm-code-block": "^0.1.1",
+ "year": "^0.2.1"
+ },
+ "dependencies": {
+ "array-sort": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz",
+ "integrity": "sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==",
+ "dev": true,
+ "requires": {
+ "default-compare": "^1.0.0",
+ "get-value": "^2.0.6",
+ "kind-of": "^5.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-number": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+ "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+ "dev": true
+ }
+ }
+ },
+ "handlebars-utils": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/handlebars-utils/-/handlebars-utils-1.0.6.tgz",
+ "integrity": "sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0",
+ "typeof-article": "^0.1.1"
+ }
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -6221,8 +7025,7 @@
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
- "optional": true
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"has-value": {
"version": "1.0.0",
@@ -6268,6 +7071,57 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
+ "helper-date": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-1.0.1.tgz",
+ "integrity": "sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==",
+ "dev": true,
+ "requires": {
+ "date.js": "^0.3.1",
+ "handlebars-utils": "^1.0.4",
+ "moment": "^2.18.1"
+ }
+ },
+ "helper-markdown": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-1.0.0.tgz",
+ "integrity": "sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA==",
+ "dev": true,
+ "requires": {
+ "handlebars-utils": "^1.0.2",
+ "highlight.js": "^9.12.0",
+ "remarkable": "^1.7.1"
+ }
+ },
+ "helper-md": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz",
+ "integrity": "sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=",
+ "dev": true,
+ "requires": {
+ "ent": "^2.2.0",
+ "extend-shallow": "^2.0.1",
+ "fs-exists-sync": "^0.1.0",
+ "remarkable": "^1.6.2"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "highlight.js": {
+ "version": "9.18.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz",
+ "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==",
+ "dev": true
+ },
"history": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
@@ -6303,6 +7157,16 @@
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
"dev": true
},
+ "html-tag": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-2.0.0.tgz",
+ "integrity": "sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g==",
+ "dev": true,
+ "requires": {
+ "is-self-closing": "^1.0.1",
+ "kind-of": "^6.0.0"
+ }
+ },
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -6392,7 +7256,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
- "optional": true,
"requires": {
"minimatch": "^3.0.4"
}
@@ -6445,6 +7308,12 @@
"wrappy": "1"
}
},
+ "info-symbol": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/info-symbol/-/info-symbol-0.1.0.tgz",
+ "integrity": "sha1-J4QdcoZ920JCzWEtecEGM4gcang=",
+ "dev": true
+ },
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@@ -6792,6 +7661,15 @@
"integrity": "sha1-pGnQVvl1aXxYyYxgI+sKp5r4laI=",
"dev": true
},
+ "is-even": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-even/-/is-even-1.0.0.tgz",
+ "integrity": "sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=",
+ "dev": true,
+ "requires": {
+ "is-odd": "^0.1.2"
+ }
+ },
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -6888,6 +7766,15 @@
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"dev": true
},
+ "is-odd": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz",
+ "integrity": "sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0"
+ }
+ },
"is-path-inside": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz",
@@ -6933,6 +7820,15 @@
"is-unc-path": "^1.0.0"
}
},
+ "is-self-closing": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-self-closing/-/is-self-closing-1.0.1.tgz",
+ "integrity": "sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg==",
+ "dev": true,
+ "requires": {
+ "self-closing-tags": "^1.0.1"
+ }
+ },
"is-set": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
@@ -7217,6 +8113,15 @@
"package-json": "^6.3.0"
}
},
+ "lazy-cache": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
+ "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=",
+ "dev": true,
+ "requires": {
+ "set-getter": "^0.1.0"
+ }
+ },
"lazy-val": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.4.tgz",
@@ -7465,6 +8370,12 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
+ "lodash._reinterpolate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
+ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
+ "dev": true
+ },
"lodash.escape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
@@ -7494,6 +8405,35 @@
"integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=",
"dev": true
},
+ "lodash.template": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
+ "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "^3.0.0",
+ "lodash.templatesettings": "^4.0.0"
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
+ "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
+ "dev": true,
+ "requires": {
+ "lodash._reinterpolate": "^3.0.0"
+ }
+ },
+ "log-ok": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/log-ok/-/log-ok-0.1.1.tgz",
+ "integrity": "sha1-vqPdNqzQuKckDXhza1uXxlREozQ=",
+ "dev": true,
+ "requires": {
+ "ansi-green": "^0.1.1",
+ "success-symbol": "^0.1.0"
+ }
+ },
"log-symbols": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
@@ -7555,6 +8495,68 @@
}
}
},
+ "log-utils": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/log-utils/-/log-utils-0.2.1.tgz",
+ "integrity": "sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^0.2.0",
+ "error-symbol": "^0.1.0",
+ "info-symbol": "^0.1.0",
+ "log-ok": "^0.1.1",
+ "success-symbol": "^0.1.0",
+ "time-stamp": "^1.0.1",
+ "warning-symbol": "^0.1.0"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-0.2.0.tgz",
+ "integrity": "sha1-csMd4qDZoszQysMMyYI+6y9kNLU=",
+ "dev": true,
+ "requires": {
+ "ansi-bgblack": "^0.1.1",
+ "ansi-bgblue": "^0.1.1",
+ "ansi-bgcyan": "^0.1.1",
+ "ansi-bggreen": "^0.1.1",
+ "ansi-bgmagenta": "^0.1.1",
+ "ansi-bgred": "^0.1.1",
+ "ansi-bgwhite": "^0.1.1",
+ "ansi-bgyellow": "^0.1.1",
+ "ansi-black": "^0.1.1",
+ "ansi-blue": "^0.1.1",
+ "ansi-bold": "^0.1.1",
+ "ansi-cyan": "^0.1.1",
+ "ansi-dim": "^0.1.1",
+ "ansi-gray": "^0.1.1",
+ "ansi-green": "^0.1.1",
+ "ansi-grey": "^0.1.1",
+ "ansi-hidden": "^0.1.1",
+ "ansi-inverse": "^0.1.1",
+ "ansi-italic": "^0.1.1",
+ "ansi-magenta": "^0.1.1",
+ "ansi-red": "^0.1.1",
+ "ansi-reset": "^0.1.1",
+ "ansi-strikethrough": "^0.1.1",
+ "ansi-underline": "^0.1.1",
+ "ansi-white": "^0.1.1",
+ "ansi-yellow": "^0.1.1",
+ "lazy-cache": "^2.0.1"
+ }
+ }
+ }
+ },
+ "logging-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-1.0.0.tgz",
+ "integrity": "sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0",
+ "log-utils": "^0.2.1"
+ }
+ },
"lolex": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lolex/-/lolex-3.1.0.tgz",
@@ -7575,24 +8577,6 @@
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true
},
- "lru-cache": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
- "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
- "dev": true,
- "requires": {
- "pseudomap": "^1.0.2",
- "yallist": "^2.1.2"
- },
- "dependencies": {
- "yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
- "dev": true
- }
- }
- },
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -7781,7 +8765,6 @@
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -7791,7 +8774,6 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
- "optional": true,
"requires": {
"minipass": "^2.9.0"
}
@@ -8351,6 +9333,12 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"dev": true
},
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
"next-tick": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
@@ -8431,7 +9419,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
- "optional": true,
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
@@ -8474,7 +9461,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
- "optional": true,
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
@@ -8502,14 +9488,12 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
- "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
- "optional": true
+ "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
},
"npm-packlist": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
- "optional": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1",
@@ -8520,7 +9504,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
- "optional": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -8790,8 +9773,7 @@
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
- "optional": true
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-locale": {
"version": "1.4.0",
@@ -8811,7 +9793,6 @@
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
- "optional": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
@@ -9246,12 +10227,6 @@
"event-stream": "=3.3.4"
}
},
- "pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
- "dev": true
- },
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@@ -9753,6 +10728,42 @@
"rc": "^1.2.8"
}
},
+ "relative": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz",
+ "integrity": "sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=",
+ "dev": true,
+ "requires": {
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "remarkable": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
+ "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.10",
+ "autolinker": "~0.28.0"
+ }
+ },
"remove-bom-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
@@ -10031,6 +11042,12 @@
"object-assign": "^4.1.1"
}
},
+ "self-closing-tags": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/self-closing-tags/-/self-closing-tags-1.0.1.tgz",
+ "integrity": "sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA==",
+ "dev": true
+ },
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
@@ -10208,6 +11225,15 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
+ "set-getter": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz",
+ "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=",
+ "dev": true,
+ "requires": {
+ "to-object-path": "^0.3.0"
+ }
+ },
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -11150,6 +12176,12 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
+ "striptags": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz",
+ "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=",
+ "dev": true
+ },
"styled-components": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.0.tgz",
@@ -11182,6 +12214,12 @@
"resolved": "https://registry.npmjs.org/subscribableevent/-/subscribableevent-1.0.1.tgz",
"integrity": "sha512-rFXit43+QoyWRUV/aTiYMUUJl71xmDcxED2cza3PEED75pgHTofMyPcPQrnHzyPqZh+kazEd36eydUKbOZBCpQ=="
},
+ "success-symbol": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz",
+ "integrity": "sha1-JAIuSG878c3KCUKDt2nEctO3KJc=",
+ "dev": true
+ },
"sumchecker": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
@@ -11291,7 +12329,6 @@
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
- "optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
@@ -11306,7 +12343,6 @@
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
- "optional": true,
"requires": {
"minimist": "^1.2.5"
}
@@ -11379,6 +12415,16 @@
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
"through2-filter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
@@ -11453,6 +12499,12 @@
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
},
+ "to-gfm-code-block": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz",
+ "integrity": "sha1-JdBFpfrlUxielje1kJANpzLYqoI=",
+ "dev": true
+ },
"to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -11707,6 +12759,26 @@
"is-typedarray": "^1.0.0"
}
},
+ "typeof-article": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/typeof-article/-/typeof-article-0.1.1.tgz",
+ "integrity": "sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.1.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
"typescript": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
@@ -11719,6 +12791,13 @@
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
"dev": true
},
+ "uglify-js": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
+ "integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
+ "dev": true,
+ "optional": true
+ },
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
@@ -12120,6 +13199,12 @@
"loose-envify": "^1.0.0"
}
},
+ "warning-symbol": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/warning-symbol/-/warning-symbol-0.1.0.tgz",
+ "integrity": "sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE=",
+ "dev": true
+ },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -12272,6 +13357,12 @@
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
"dev": true
},
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true
+ },
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
@@ -12281,8 +13372,7 @@
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "optional": true
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"yargs": {
"version": "15.4.1",
@@ -12632,6 +13722,12 @@
"fd-slicer": "~1.1.0"
}
},
+ "year": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/year/-/year-0.2.1.tgz",
+ "integrity": "sha1-QIOuUgoxiyPshgN/MADLiSvfm7A=",
+ "dev": true
+ },
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
diff --git a/gui/package.json b/gui/package.json
index ff29dcbef0..927db17ba9 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -13,10 +13,12 @@
"license": "GPL-3.0",
"dependencies": {
"argv-split": "^2.0.1",
+ "@grpc/grpc-js": "^1.1.2",
"connected-react-router": "^6.8.0",
"d3-geo-projection": "^2.7.0",
"electron-log": "^4.1.1",
"gettext-parser": "^4.0.3",
+ "google-protobuf": "^4.0.0-rc.2",
"history": "^4.6.1",
"jsonrpc-lite": "^2.0.7",
"linux-app-list": "^1.0.1",
@@ -47,6 +49,7 @@
"@types/d3-geo": "^1.11.0",
"@types/enzyme": "^3.1.15",
"@types/enzyme-adapter-react-16": "^1.0.3",
+ "@types/google-protobuf": "^3.7.2",
"@types/mkdirp": "^1.0.0",
"@types/mocha": "^5.2.6",
"@types/node": "^10.12.3",
@@ -81,6 +84,8 @@
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.3",
"gettext-extractor": "^3.5.2",
+ "grpc-tools": "^1.9.1",
+ "grpc_tools_node_protoc_ts": "^4.1.0",
"gulp": "^4.0.2",
"gulp-inject-string": "^1.1.2",
"gulp-typescript": "^5.0.1",
@@ -94,6 +99,7 @@
"scripts": {
"postinstall": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=true electron-builder install-app-deps",
"build": "gulp build",
+ "build-proto": "gulp build-proto",
"lint": "eslint --ext tsx,ts .",
"format": "prettier \"**/*.{js,css,ts,tsx}\" --write",
"develop": "gulp develop",
diff --git a/gui/scripts/build-proto.sh b/gui/scripts/build-proto.sh
new file mode 100755
index 0000000000..a6cf6a5d94
--- /dev/null
+++ b/gui/scripts/build-proto.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -eu
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd "$SCRIPT_DIR"
+
+NODE_MODULES_DIR="$(cd ../node_modules/.bin && pwd)"
+PROTO_DIR="../../mullvad-management-interface/proto"
+PROTO_FILENAME="management_interface.proto"
+DESTINATION_DIR="../build/src/main/management_interface"
+TYPES_DESTINATION_DIR="../src/main/management_interface"
+
+TS_PROTOC_PLUGIN="$NODE_MODULES_DIR/protoc-gen-ts"
+if [[ "$(uname -s)" == "MINGW"* ]]; then
+ TS_PROTOC_PLUGIN="$TS_PROTOC_PLUGIN.cmd"
+fi
+
+mkdir -p $DESTINATION_DIR
+mkdir -p $TYPES_DESTINATION_DIR
+
+"$NODE_MODULES_DIR/grpc_tools_node_protoc" \
+ --js_out=import_style=commonjs,binary:$DESTINATION_DIR \
+ --grpc_out=generate_package_definition:$DESTINATION_DIR \
+ --proto_path=$PROTO_DIR \
+ $PROTO_DIR/$PROTO_FILENAME
+
+"$NODE_MODULES_DIR/grpc_tools_node_protoc" \
+ --plugin=protoc-gen-ts=$TS_PROTOC_PLUGIN \
+ --ts_out=generate_package_definition:$TYPES_DESTINATION_DIR \
+ --proto_path=$PROTO_DIR \
+ $PROTO_DIR/$PROTO_FILENAME
+
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index c3d3fe33a6..ff755e2a36 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -1,324 +1,75 @@
+import * as grpc from '@grpc/grpc-js';
+import {
+ BoolValue,
+ StringValue,
+ UInt32Value,
+} from 'google-protobuf/google/protobuf/wrappers_pb.js';
+import log from 'electron-log';
+import { Empty } from 'google-protobuf/google/protobuf/empty_pb.js';
+import { promisify } from 'util';
import {
AccountToken,
+ Constraint,
+ IRelayListCountry,
+ IRelayListCity,
+ IRelayListHostname,
+ IWireguardTunnelData,
+ IBridgeConstraints,
+ IWireguardConstraints,
+ ITunnelOptions,
+ IOpenVpnConstraints,
+ IRelayList,
+ IShadowsocksEndpointData,
+ RelayProtocol,
BridgeSettings,
+ FirewallPolicyError,
BridgeState,
- DaemonEvent,
- IAccountData,
- IAppVersionInfo,
ILocation,
- IRelayList,
- ISettings,
- IWireguardPublicKey,
+ IAppVersionInfo,
+ IAccountData,
+ IOpenVpnTunnelData,
+ TunnelState,
+ AfterDisconnect,
+ IErrorState,
+ ErrorStateCause,
+ TunnelParameterError,
+ ITunnelStateRelayInfo,
+ TunnelType,
+ IProxyEndpoint,
+ ProxyType,
KeygenEvent,
+ IWireguardPublicKey,
+ ISettings,
+ ConnectionConfig,
+ DaemonEvent,
+ RelaySettings,
RelaySettingsUpdate,
- TunnelState,
- VoucherErrorCode,
+ RelayLocation,
+ ProxySettings,
VoucherResponse,
+ TunnelProtocol,
} from '../shared/daemon-rpc-types';
-import { CommunicationError, InvalidAccountError, NoDaemonError } from './errors';
-import JsonRpcClient, {
- RemoteError as JsonRpcRemoteError,
- SocketTransport,
- TimeOutError as JsonRpcTimeOutError,
-} from './jsonrpc-client';
-import { camelCaseToSnakeCase, snakeCaseToCamelCase } from './transform-object-keys';
-
-import { validate } from 'validated/object';
-import {
- arrayOf,
- boolean,
- enumeration,
- maybe,
- Node as SchemaNode,
- number,
- object,
- oneOf,
- partialObject,
- string,
-} from 'validated/schema';
-
-const locationSchema = maybe(
- partialObject({
- ipv4: maybe(string),
- ipv6: maybe(string),
- country: string,
- city: maybe(string),
- latitude: number,
- longitude: number,
- mullvad_exit_ip: boolean,
- hostname: maybe(string),
- bridge_hostname: maybe(string),
- }),
-);
-
-const constraint = <T>(constraintValue: SchemaNode<T>) => {
- return oneOf(
- string, // any
- object({
- only: constraintValue,
- }),
- );
-};
-const locationConstraintSchema = constraint(
- oneOf(
- object({
- hostname: arrayOf(string),
- }),
- object({
- city: arrayOf(string),
- }),
- object({
- country: string,
- }),
- ),
-);
-
-const customTunnelEndpointSchema = oneOf(
- object({
- openvpn: object({
- endpoint: object({
- address: string,
- protocol: enumeration('udp', 'tcp'),
- }),
- username: string,
- password: string,
- }),
- }),
- object({
- wireguard: object({
- tunnel: object({
- private_key: string,
- addresses: arrayOf(string),
- }),
- peer: object({
- public_key: string,
- allowed_ips: arrayOf(string),
- endpoint: string,
- }),
- ipv4_gateway: string,
- ipv6_gateway: maybe(string),
- }),
- }),
-);
-
-const relaySettingsSchema = oneOf(
- object({
- normal: partialObject({
- location: locationConstraintSchema,
- tunnel_protocol: constraint(enumeration('wireguard', 'openvpn')),
- wireguard_constraints: partialObject({
- port: constraint(number),
- }),
- openvpn_constraints: partialObject({
- port: constraint(number),
- protocol: constraint(enumeration('udp', 'tcp')),
- }),
- }),
- }),
- object({
- custom_tunnel_endpoint: partialObject({
- host: string,
- config: customTunnelEndpointSchema,
- }),
- }),
-);
+import * as managementInterface from './management_interface/management_interface_grpc_pb';
+import * as grpcTypes from './management_interface/management_interface_pb';
+import { CommunicationError, InvalidAccountError } from './errors';
+import consumePromise from '../shared/promise';
-const relayListSchema = partialObject({
- countries: arrayOf(
- partialObject({
- name: string,
- code: string,
- cities: arrayOf(
- partialObject({
- name: string,
- code: string,
- latitude: number,
- longitude: number,
- relays: arrayOf(
- partialObject({
- hostname: string,
- ipv4_addr_in: string,
- include_in_country: boolean,
- active: boolean,
- weight: number,
- bridges: maybe(
- partialObject({
- shadowsocks: arrayOf(
- object({
- port: number,
- cipher: string,
- password: string,
- protocol: enumeration('tcp', 'udp'),
- }),
- ),
- }),
- ),
- tunnels: maybe(
- partialObject({
- openvpn: arrayOf(
- partialObject({
- port: number,
- protocol: string,
- }),
- ),
- wireguard: arrayOf(
- partialObject({
- port_ranges: arrayOf(arrayOf(number)),
- public_key: string,
- }),
- ),
- }),
- ),
- }),
- ),
- }),
- ),
- }),
- ),
-});
-
-const openVpnProxySchema = maybe(
- oneOf(
- object({
- local: partialObject({
- port: number,
- peer: string,
- }),
- }),
- object({
- remote: partialObject({
- address: string,
- auth: maybe(
- partialObject({
- username: string,
- password: string,
- }),
- ),
- }),
- }),
- object({
- shadowsocks: partialObject({
- peer: string,
- password: string,
- cipher: string,
- }),
- }),
- ),
-);
+const NETWORK_CALL_TIMEOUT = 10000;
+const CHANNEL_STATE_TIMEOUT = 1000 * 60 * 60;
-const bridgeSettingsSchema = oneOf(
- partialObject({ normal: partialObject({ location: locationConstraintSchema }) }),
- partialObject({ custom: openVpnProxySchema }),
+const ManagementServiceClient = grpc.makeClientConstructor(
+ // @ts-ignore
+ managementInterface['mullvad_daemon.management_interface.ManagementService'],
+ 'ManagementService',
);
-const tunnelOptionsSchema = partialObject({
- openvpn: partialObject({
- mssfix: maybe(number),
- }),
- wireguard: partialObject({
- mtu: maybe(number),
- // only relevant on linux
- fmwark: maybe(number),
- }),
- generic: partialObject({
- enable_ipv6: boolean,
- }),
-});
-
-const accountDataSchema = partialObject({
- expiry: string,
-});
-
-const voucherResponseSchema = partialObject({
- new_expiry: string,
-});
-
-const tunnelStateSchema = oneOf(
- object({
- state: enumeration('disconnecting'),
- details: enumeration('nothing', 'block', 'reconnect'),
- }),
- object({
- state: enumeration('connecting', 'connected'),
- details: object({
- endpoint: partialObject({
- address: string,
- protocol: enumeration('tcp', 'udp'),
- tunnel_type: enumeration('wireguard', 'openvpn'),
- proxy: maybe(
- partialObject({
- address: string,
- protocol: enumeration('tcp', 'udp'),
- proxy_type: enumeration('shadowsocks', 'custom'),
- }),
- ),
- }),
- location: maybe(locationSchema),
- }),
- }),
- object({
- state: enumeration('error'),
- details: object({
- block_failure: maybe(
- object({
- reason: enumeration('generic', 'locked'),
- details: maybe(
- object({
- name: string,
- pid: number,
- }),
- ),
- }),
- ),
- cause: oneOf(
- object({
- reason: enumeration(
- 'ipv6_unavailable',
- 'set_dns_error',
- 'start_tunnel_error',
- 'is_offline',
- 'tap_adapter_problem',
- ),
- }),
- object({
- reason: enumeration('set_firewall_policy_error'),
- details: object({
- reason: enumeration('generic', 'locked'),
- details: maybe(
- object({
- name: string,
- pid: number,
- }),
- ),
- }),
- }),
- object({
- reason: enumeration('auth_failed'),
- details: maybe(string),
- }),
- object({
- reason: enumeration('tunnel_parameter_error'),
- details: enumeration(
- 'no_matching_relay',
- 'no_matching_bridge_relay',
- 'no_wireguard_key',
- 'custom_tunnel_host_resultion_error',
- ),
- }),
- ),
- }),
- }),
- object({
- state: enumeration('connected', 'connecting', 'disconnected'),
- }),
+const noConnectionError = new Error('No connection established to daemon');
+const configNotSupported = new Error('Setting custom settings is not supported');
+const invalidErrorStateCause = new Error(
+ 'VPN_PERMISSION_DENIED is not a valid error state cause on desktop',
);
-const appVersionInfoSchema = partialObject({
- supported: boolean,
- suggested_upgrade: maybe(string),
-});
-
export class ConnectionObserver {
constructor(private openHandler: () => void, private closeHandler: (error?: Error) => void) {}
@@ -338,7 +89,7 @@ export class ConnectionObserver {
export class SubscriptionListener<T> {
// Only meant to be used by DaemonRpc
// @internal
- public subscriptionId?: string | number;
+ public subscriptionId?: number;
constructor(
private eventHandler: (payload: T) => void,
@@ -358,328 +109,1130 @@ export class SubscriptionListener<T> {
}
}
-const settingsSchema = partialObject({
- account_token: maybe(string),
- allow_lan: boolean,
- auto_connect: boolean,
- block_when_disconnected: boolean,
- show_beta_releases: boolean,
- bridge_settings: bridgeSettingsSchema,
- bridge_state: enumeration('on', 'auto', 'off'),
- relay_settings: relaySettingsSchema,
- tunnel_options: tunnelOptionsSchema,
-});
+export class ResponseParseError extends Error {
+ constructor(message: string) {
+ super(message);
+ }
+}
-const wireguardPublicKey = object({
- key: string,
- created: string,
-});
+type CallFunctionArgument<T, R> =
+ | ((arg: T, callback: (error: Error | null, result: R) => void) => void)
+ | undefined;
-const keygenEventSchema = oneOf(
- enumeration('too_many_keys', 'generation_failure'),
- object({
- new_key: object({
- key: string,
- created: string,
- }),
- }),
-);
+export class DaemonRpc {
+ constructor(connectionParams: string) {
+ this.client = (new ManagementServiceClient(
+ connectionParams,
+ grpc.credentials.createInsecure(),
+ this.channelOptions(),
+ ) as unknown) as managementInterface.ManagementServiceClient;
+ }
-const daemonEventSchema = oneOf(
- object({
- tunnel_state: tunnelStateSchema,
- }),
- object({
- settings: settingsSchema,
- }),
- object({
- relay_list: relayListSchema,
- }),
- object({
- wireguard_key: keygenEventSchema,
- }),
- object({
- app_version_info: appVersionInfoSchema,
- }),
-);
+ private client: managementInterface.ManagementServiceClient;
+ private isConnected = false;
+ private connectionObservers: ConnectionObserver[] = [];
+ private nextSubscriptionId = 0;
+ private subscriptions: Map<number, grpc.ClientReadableStream<grpcTypes.DaemonEvent>> = new Map();
+ private reconnectionTimeout?: number;
-export class ResponseParseError extends Error {
- constructor(message: string, private validationErrorValue?: Error) {
- super(message);
+ private subscriptionId(): number {
+ const current = this.nextSubscriptionId;
+ this.nextSubscriptionId += 1;
+ return current;
}
- get validationError(): Error | undefined {
- return this.validationErrorValue;
+ private deadlineFromNow() {
+ return Date.now() + NETWORK_CALL_TIMEOUT;
}
-}
-// Timeout used for RPC calls that do networking
-const NETWORK_CALL_TIMEOUT = 10000;
+ private channelStateTimeout(): number {
+ return Date.now() + CHANNEL_STATE_TIMEOUT;
+ }
-export class DaemonRpc {
- private transport = new JsonRpcClient(new SocketTransport());
+ private callEmpty<R>(fn: CallFunctionArgument<Empty, R>): Promise<R> {
+ return this.call<Empty, R>(fn, new Empty());
+ }
+
+ private callString<R>(fn: CallFunctionArgument<StringValue, R>, value?: string): Promise<R> {
+ const googleString = new StringValue();
+
+ if (value !== undefined) {
+ googleString.setValue(value);
+ }
+
+ return this.call<StringValue, R>(fn, googleString);
+ }
+
+ private callBool<R>(fn: CallFunctionArgument<BoolValue, R>, value?: boolean): Promise<R> {
+ const googleBool = new BoolValue();
+
+ if (value !== undefined) {
+ googleBool.setValue(value);
+ }
+
+ return this.call<BoolValue, R>(fn, googleBool);
+ }
+
+ private callNumber<R>(fn: CallFunctionArgument<UInt32Value, R>, value?: number): Promise<R> {
+ const googleNumber = new UInt32Value();
+
+ if (value !== undefined) {
+ googleNumber.setValue(value);
+ }
+
+ return this.call<UInt32Value, R>(fn, googleNumber);
+ }
+
+ private call<T, R>(fn: CallFunctionArgument<T, R>, arg: T): Promise<R> {
+ if (fn && this.isConnected) {
+ return promisify<T, R>(fn.bind(this.client))(arg);
+ } else {
+ throw noConnectionError;
+ }
+ }
+
+ public connect(): Promise<void> {
+ return new Promise((resolve, reject) => {
+ this.client.waitForReady(this.deadlineFromNow(), (error) => {
+ if (error) {
+ this.connectionObservers.forEach((observer) => observer.onClose(error));
+ this.ensureConnectivity();
+ reject(error);
+ } else {
+ this.reconnectionTimeout = undefined;
+ this.isConnected = true;
+ this.connectionObservers.forEach((observer) => observer.onOpen());
+ this.setChannelCallback();
+ resolve();
+ }
+ });
+ });
+ }
- public connect(connectionParams: { path: string }): Promise<void> {
- return this.transport.connect(connectionParams);
+ private channelOptions(): grpc.ClientOptions {
+ return {
+ 'grpc.max_reconnect_backoff_ms': 3000,
+ 'grpc.initial_reconnect_backoff_ms': 3000,
+ 'grpc.keepalive_time_ms': Math.pow(2, 30),
+ 'grpc.keepalive_timeout_ms': Math.pow(2, 30),
+ };
+ }
+
+ private connectivityChangeCallback(timeoutErr?: Error) {
+ const channel = this.client.getChannel();
+ const currentState = channel?.getConnectivityState(true);
+ log.debug(`GRPC Channel connectivity state changed to ${currentState}`);
+ if (channel) {
+ if (timeoutErr) {
+ this.setChannelCallback(currentState);
+ return;
+ }
+ const wasConnected = this.isConnected;
+ if (this.channelDisconnected(currentState)) {
+ this.connectionObservers.forEach((observer) => observer.onClose());
+ this.isConnected = false;
+ // Try and reconnect in case
+ consumePromise(
+ this.connect().catch((error) => {
+ log.error(`Failed to reconnect - ${error}`);
+ }),
+ );
+ this.setChannelCallback(currentState);
+ } else if (!wasConnected && currentState === grpc.connectivityState.READY) {
+ this.isConnected = true;
+ this.connectionObservers.forEach((observer) => observer.onOpen());
+ this.setChannelCallback(currentState);
+ }
+ }
+ }
+
+ private channelDisconnected(state: grpc.connectivityState): boolean {
+ return (
+ (state === grpc.connectivityState.SHUTDOWN ||
+ state === grpc.connectivityState.TRANSIENT_FAILURE ||
+ state === grpc.connectivityState.IDLE) &&
+ this.isConnected
+ );
+ }
+
+ private setChannelCallback(currentState?: grpc.connectivityState) {
+ const channel = this.client.getChannel();
+ if (currentState === undefined && channel) {
+ currentState = channel?.getConnectivityState(false);
+ }
+ if (currentState) {
+ channel.watchConnectivityState(currentState, this.channelStateTimeout(), (error) =>
+ this.connectivityChangeCallback(error),
+ );
+ }
+ }
+
+ // Since grpc.Channel.watchConnectivityState() isn't always running as intended, whenever the
+ // client fails to connect at first, `ensureConnectivity()` should be called so that it tries to
+ // check the connectivity state and nudge the client into connecting.
+ // `grpc.Channel.getConnectivityState(true)` should make it attempt to connect.
+ private ensureConnectivity() {
+ this.reconnectionTimeout = setTimeout(() => {
+ const lastState = this.client.getChannel().getConnectivityState(true);
+ if (this.channelDisconnected(lastState)) {
+ this.connectionObservers.forEach((observer) => observer.onClose());
+ this.isConnected = false;
+ }
+ if (!this.isConnected) {
+ consumePromise(
+ this.connect().catch((error) => {
+ log.error(`Failed to reconnect - ${error}`);
+ }),
+ );
+ }
+ }, 3000);
}
public disconnect() {
- this.transport.disconnect();
+ this.isConnected = false;
+ this.subscriptions.clear();
+ this.client.close();
+ if (this.reconnectionTimeout) {
+ clearTimeout(this.reconnectionTimeout);
+ }
}
public addConnectionObserver(observer: ConnectionObserver) {
- this.transport.on('open', observer.onOpen).on('close', observer.onClose);
+ this.connectionObservers.push(observer);
+ const currentState = this.client.getChannel()?.getConnectivityState(true);
+ if (
+ currentState === grpc.connectivityState.SHUTDOWN ||
+ currentState === grpc.connectivityState.TRANSIENT_FAILURE ||
+ currentState === grpc.connectivityState.IDLE
+ ) {
+ observer.onClose();
+ } else {
+ observer.onOpen();
+ }
}
public removeConnectionObserver(observer: ConnectionObserver) {
- this.transport.off('open', observer.onOpen).off('close', observer.onClose);
+ const index = this.connectionObservers.indexOf(observer);
+ if (index !== -1) {
+ this.connectionObservers.splice(index, 1);
+ }
}
public async getAccountData(accountToken: AccountToken): Promise<IAccountData> {
- let response;
try {
- response = await this.transport.send('get_account_data', accountToken, NETWORK_CALL_TIMEOUT);
+ const response = await this.callString<grpcTypes.AccountData>(
+ this.client.getAccountData,
+ accountToken,
+ );
+ const expiry = response.getExpiry()!.toDate().toISOString();
+ return { expiry };
} catch (error) {
- if (error instanceof JsonRpcRemoteError) {
+ if (error.code) {
switch (error.code) {
- case -200: // Account doesn't exist
+ case grpc.status.UNAUTHENTICATED:
throw new InvalidAccountError();
- case -32603: // Internal error
+ default:
throw new CommunicationError();
}
- } else if (error instanceof JsonRpcTimeOutError) {
- throw new NoDaemonError();
- } else {
- throw error;
}
- }
-
- try {
- return validate(accountDataSchema, response);
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_account_data', error);
+ throw error;
}
}
public async getWwwAuthToken(): Promise<string> {
- const response = await this.transport.send('get_www_auth_token');
- try {
- return validate(string, response);
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_www_auth_token', error);
- }
+ const response = await this.callEmpty<StringValue>(this.client.getWwwAuthToken);
+ return response.getValue();
}
public async submitVoucher(voucherCode: string): Promise<VoucherResponse> {
try {
- const response = await this.transport.send('submit_voucher', voucherCode);
- const new_expiry = validate(voucherResponseSchema, response).new_expiry;
- return { type: 'success', new_expiry };
+ const response = await this.callString<grpcTypes.VoucherSubmission>(
+ this.client.submitVoucher,
+ voucherCode,
+ );
+
+ const secondsAdded = ensureExists(
+ response.getSecondsAdded(),
+ "no 'secondsAdded' field in voucher response",
+ );
+ const newExpiry = ensureExists(
+ response.getNewExpiry(),
+ "no 'newExpiry' field in voucher response",
+ )
+ .toDate()
+ .toISOString();
+ return {
+ type: 'success',
+ secondsAdded,
+ newExpiry,
+ };
} catch (error) {
- if (error instanceof JsonRpcRemoteError) {
+ if (error.code) {
switch (error.code) {
- case VoucherErrorCode.Invalid:
+ case grpc.status.NOT_FOUND:
return { type: 'invalid' };
- case VoucherErrorCode.AlreadyUsed:
+ case grpc.status.RESOURCE_EXHAUSTED:
return { type: 'already_used' };
}
}
+ return { type: 'error' };
}
-
- return { type: 'error' };
}
- public async getRelayLocations(): Promise<IRelayList> {
- const response = await this.transport.send('get_relay_locations');
- try {
- return snakeCaseToCamelCase(validate(relayListSchema, response));
- } catch (error) {
- throw new ResponseParseError(`Invalid response from get_relay_locations: ${error}`, error);
+ public getRelayLocations(): Promise<IRelayList> {
+ if (this.isConnected) {
+ return new Promise((resolve, reject) => {
+ const relayLocations: IRelayListCountry[] = [];
+ const stream = this.client.getRelayLocations(new Empty());
+ stream.on('data', (country: grpcTypes.RelayListCountry) =>
+ relayLocations.push(convertFromRelayListCountry(country.toObject())),
+ );
+ stream.on('end', () => resolve({ countries: relayLocations }));
+ stream.on('close', reject);
+ });
+ } else {
+ throw noConnectionError;
}
}
public async createNewAccount(): Promise<string> {
- const response = await this.transport.send('create_new_account');
- return validate(string, response);
+ const response = await this.callEmpty<StringValue>(this.client.createNewAccount);
+ return response.getValue();
}
public async setAccount(accountToken?: AccountToken): Promise<void> {
- await this.transport.send('set_account', [accountToken]);
+ await this.callString(this.client.setAccount, accountToken);
}
+ // TODO: Custom tunnel configurations are not supported by the GUI.
public async updateRelaySettings(relaySettings: RelaySettingsUpdate): Promise<void> {
- await this.transport.send('update_relay_settings', [camelCaseToSnakeCase(relaySettings)]);
+ if ('normal' in relaySettings) {
+ const settingsUpdate = relaySettings.normal;
+ const grpcRelaySettings = new grpcTypes.RelaySettingsUpdate();
+
+ const normalUpdate = new grpcTypes.NormalRelaySettingsUpdate();
+
+ if (settingsUpdate.tunnelProtocol) {
+ const tunnelTypeUpdate = new grpcTypes.TunnelTypeUpdate();
+ tunnelTypeUpdate.setTunnelType(
+ convertToTunnelTypeConstraint(settingsUpdate.tunnelProtocol),
+ );
+ normalUpdate.setTunnelType(tunnelTypeUpdate);
+ }
+
+ if (settingsUpdate.location) {
+ normalUpdate.setLocation(convertToLocation(liftConstraint(settingsUpdate.location)));
+ }
+
+ if (settingsUpdate.wireguardConstraints) {
+ normalUpdate.setWireguardConstraints(
+ convertToWireguardConstraints(settingsUpdate.wireguardConstraints),
+ );
+ }
+
+ if (settingsUpdate.openvpnConstraints) {
+ normalUpdate.setOpenvpnConstraints(
+ convertToOpenVpnConstraints(settingsUpdate.openvpnConstraints),
+ );
+ }
+
+ grpcRelaySettings.setNormal(normalUpdate);
+ await this.call<grpcTypes.RelaySettingsUpdate, Empty>(
+ this.client.updateRelaySettings,
+ grpcRelaySettings,
+ );
+ }
}
public async setAllowLan(allowLan: boolean): Promise<void> {
- await this.transport.send('set_allow_lan', [allowLan]);
+ await this.callBool(this.client.setAllowLan, allowLan);
}
public async setShowBetaReleases(showBetaReleases: boolean): Promise<void> {
- await this.transport.send('set_show_beta_releases', [showBetaReleases]);
+ await this.callBool(this.client.setShowBetaReleases, showBetaReleases);
}
public async setEnableIpv6(enableIpv6: boolean): Promise<void> {
- await this.transport.send('set_enable_ipv6', [enableIpv6]);
+ await this.callBool(this.client.setEnableIpv6, enableIpv6);
}
public async setBlockWhenDisconnected(blockWhenDisconnected: boolean): Promise<void> {
- await this.transport.send('set_block_when_disconnected', [blockWhenDisconnected]);
+ await this.callBool(this.client.setBlockWhenDisconnected, blockWhenDisconnected);
}
public async setBridgeState(bridgeState: BridgeState): Promise<void> {
- await this.transport.send('set_bridge_state', [bridgeState]);
+ const bridgeStateMap = {
+ auto: grpcTypes.BridgeState.State.AUTO,
+ on: grpcTypes.BridgeState.State.ON,
+ off: grpcTypes.BridgeState.State.OFF,
+ };
+
+ const grpcBridgeState = new grpcTypes.BridgeState();
+ grpcBridgeState.setState(bridgeStateMap[bridgeState]);
+ await this.call<grpcTypes.BridgeState, Empty>(this.client.setBridgeState, grpcBridgeState);
}
public async setBridgeSettings(bridgeSettings: BridgeSettings): Promise<void> {
- await this.transport.send('set_bridge_settings', [bridgeSettings]);
+ const grpcBridgeSettings = new grpcTypes.BridgeSettings();
+
+ if ('normal' in bridgeSettings) {
+ const normalSettings = convertToNormalBridgeSettings(bridgeSettings.normal);
+ grpcBridgeSettings.setNormal(normalSettings);
+ }
+
+ if ('custom' in bridgeSettings) {
+ throw configNotSupported;
+ }
+
+ await this.call<grpcTypes.BridgeSettings, Empty>(
+ this.client.setBridgeSettings,
+ grpcBridgeSettings,
+ );
}
public async setOpenVpnMssfix(mssfix?: number): Promise<void> {
- await this.transport.send('set_openvpn_mssfix', [mssfix]);
+ await this.callNumber(this.client.setOpenvpnMssfix, mssfix);
}
public async setWireguardMtu(mtu?: number): Promise<void> {
- await this.transport.send('set_wireguard_mtu', [mtu]);
+ await this.callNumber(this.client.setWireguardMtu, mtu);
}
public async setAutoConnect(autoConnect: boolean): Promise<void> {
- await this.transport.send('set_auto_connect', [autoConnect]);
+ await this.callBool(this.client.setAutoConnect, autoConnect);
}
public async connectTunnel(): Promise<void> {
- await this.transport.send('connect');
+ await this.callEmpty(this.client.connectTunnel);
}
public async disconnectTunnel(): Promise<void> {
- await this.transport.send('disconnect');
+ await this.callEmpty(this.client.disconnectTunnel);
}
public async reconnectTunnel(): Promise<void> {
- await this.transport.send('reconnect');
+ await this.callEmpty(this.client.reconnectTunnel);
}
- public async getLocation(): Promise<ILocation | undefined> {
- const response = await this.transport.send('get_current_location', [], NETWORK_CALL_TIMEOUT);
- try {
- const validatedObject = validate(locationSchema, response);
- if (validatedObject) {
- return snakeCaseToCamelCase(validatedObject);
- } else {
- return undefined;
- }
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_current_location', error);
- }
+ public async getLocation(): Promise<ILocation> {
+ const response = await this.callEmpty<grpcTypes.GeoIpLocation>(this.client.getCurrentLocation);
+ return response.toObject();
}
public async getState(): Promise<TunnelState> {
- const response = await this.transport.send('get_state');
- try {
- return snakeCaseToCamelCase(validate(tunnelStateSchema, response));
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_state', error);
- }
+ const response = await this.callEmpty<grpcTypes.TunnelState>(this.client.getTunnelState);
+ return convertFromTunnelState(response)!;
}
public async getSettings(): Promise<ISettings> {
- const response = await this.transport.send('get_settings');
- try {
- return snakeCaseToCamelCase(validate(settingsSchema, response));
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_settings', error);
- }
+ const response = await this.callEmpty<grpcTypes.Settings>(this.client.getSettings);
+ return convertFromSettings(response)!;
}
- public async subscribeDaemonEventListener(
- listener: SubscriptionListener<DaemonEvent>,
- ): Promise<void> {
- const subscriptionId = await this.transport.subscribe('daemon_event', (payload) => {
- let daemonEvent: DaemonEvent;
+ public subscribeDaemonEventListener(listener: SubscriptionListener<DaemonEvent>) {
+ const call = this.isConnected && this.client.eventsListen(new Empty());
+ if (!call) {
+ throw noConnectionError;
+ }
+ const subscriptionId = this.subscriptionId();
+ listener.subscriptionId = subscriptionId;
+ this.subscriptions.set(subscriptionId, call);
+ call.on('data', (data: grpcTypes.DaemonEvent) => {
try {
- daemonEvent = snakeCaseToCamelCase(validate(daemonEventSchema, payload));
- } catch (error) {
- listener.onError(new ResponseParseError('Invalid payload from daemon_event', error));
- return;
+ const daemonEvent = convertFromDaemonEvent(data);
+ listener.onEvent(daemonEvent);
+ } catch (err) {
+ listener.onError(err);
}
+ });
- listener.onEvent(daemonEvent);
+ call.on('error', (error) => {
+ // if the subscription was cancelled by the client, there's no reason to
+ // invoke the onError handler
+ if (
+ 'code' in error &&
+ error['code'] === grpc.status.CANCELLED &&
+ !this.subscriptions.has(subscriptionId)
+ ) {
+ return;
+ }
+ listener.onError(error);
+ this.removeSubscription(subscriptionId);
});
+ }
- listener.subscriptionId = subscriptionId;
+ public unsubscribeDaemonEventListener(listener: SubscriptionListener<DaemonEvent>) {
+ const id = listener.subscriptionId;
+ if (id !== undefined) {
+ this.removeSubscription(id);
+ }
}
- public async unsubscribeDaemonEventListener(
- listener: SubscriptionListener<DaemonEvent>,
- ): Promise<void> {
- if (listener.subscriptionId) {
- return this.transport.unsubscribe('daemon_event', listener.subscriptionId);
+ private removeSubscription(id: number) {
+ const subscription = this.subscriptions.get(id);
+ if (subscription !== undefined) {
+ this.subscriptions.delete(id);
+ subscription.cancel();
}
}
public async getAccountHistory(): Promise<AccountToken[]> {
- const response = await this.transport.send('get_account_history');
- try {
- return validate(arrayOf(string), response);
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_account_history');
- }
+ const response = await this.callEmpty<grpcTypes.AccountHistory>(this.client.getAccountHistory);
+ return response.toObject().tokenList;
}
public async removeAccountFromHistory(accountToken: AccountToken): Promise<void> {
- await this.transport.send('remove_account_from_history', accountToken);
+ await this.callString(this.client.removeAccountFromHistory, accountToken);
}
public async getCurrentVersion(): Promise<string> {
- const response = await this.transport.send('get_current_version');
- try {
- return validate(string, response);
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_current_version');
- }
+ const response = await this.callEmpty<StringValue>(this.client.getCurrentVersion);
+ return response.getValue();
}
public async generateWireguardKey(): Promise<KeygenEvent> {
- const response = await this.transport.send('generate_wireguard_key');
- try {
- const validatedResponse = validate(keygenEventSchema, response);
- switch (validatedResponse) {
- case 'too_many_keys':
- case 'generation_failure':
- return validatedResponse;
- default:
- return snakeCaseToCamelCase(validatedResponse as object);
+ const response = await this.callEmpty<grpcTypes.KeygenEvent>(this.client.generateWireguardKey);
+ return convertFromKeygenEvent(response);
+ }
+
+ public async getWireguardKey(): Promise<IWireguardPublicKey> {
+ const response = await this.callEmpty<grpcTypes.PublicKey>(this.client.getWireguardKey);
+ return {
+ created: response.getCreated()!.toDate().toISOString(),
+ key: convertFromWireguardKey(response.getKey()),
+ };
+ }
+
+ public async verifyWireguardKey(): Promise<boolean> {
+ const response = await this.callEmpty<BoolValue>(this.client.verifyWireguardKey);
+ return response.getValue();
+ }
+
+ public async getVersionInfo(): Promise<IAppVersionInfo> {
+ const response = await this.callEmpty<grpcTypes.AppVersionInfo>(this.client.getVersionInfo);
+ return response.toObject();
+ }
+}
+
+function liftConstraint<T>(constraint: Constraint<T> | undefined): T | undefined {
+ if (constraint !== undefined && constraint !== 'any') {
+ return constraint.only;
+ }
+ return undefined;
+}
+
+function convertFromRelayListCountry(
+ country: grpcTypes.RelayListCountry.AsObject,
+): IRelayListCountry {
+ return {
+ ...country,
+ cities: country.citiesList.map(convertFromRelayListCity),
+ };
+}
+
+function convertFromRelayListCity(city: grpcTypes.RelayListCity.AsObject): IRelayListCity {
+ return {
+ ...city,
+ relays: city.relaysList.map(convertFromRelayListRelay),
+ };
+}
+
+function convertFromRelayListRelay(relay: grpcTypes.Relay.AsObject): IRelayListHostname {
+ return {
+ ...relay,
+ tunnels: relay.tunnels && {
+ ...relay.tunnels,
+ openvpn: relay.tunnels.openvpnList.map(convertFromOpenvpnList),
+ wireguard: relay.tunnels.wireguardList.map(convertFromWireguardList),
+ },
+ bridges: relay.bridges && {
+ shadowsocks: relay.bridges.shadowsocksList.map(convertFromShadowsocksList),
+ },
+ };
+}
+
+function convertFromOpenvpnList(
+ openvpn: grpcTypes.OpenVpnEndpointData.AsObject,
+): IOpenVpnTunnelData {
+ return {
+ ...openvpn,
+ protocol: convertFromTransportProtocol(openvpn.protocol),
+ };
+}
+
+function convertFromWireguardList(
+ wireguard: grpcTypes.WireguardEndpointData.AsObject,
+): IWireguardTunnelData {
+ return {
+ ...wireguard,
+ portRanges: wireguard.portRangesList,
+ publicKey: convertFromWireguardKey(wireguard.publicKey),
+ };
+}
+
+function convertFromWireguardKey(publicKey: Uint8Array | string): string {
+ if (typeof publicKey === 'string') {
+ return publicKey;
+ }
+ return Buffer.from(publicKey).toString('base64');
+}
+
+function convertFromShadowsocksList(
+ shadowsocks: grpcTypes.ShadowsocksEndpointData.AsObject,
+): IShadowsocksEndpointData {
+ return {
+ ...shadowsocks,
+ protocol: convertFromTransportProtocol(shadowsocks.protocol),
+ };
+}
+
+function convertFromTransportProtocol(protocol: grpcTypes.TransportProtocol): RelayProtocol {
+ const protocolMap: Record<grpcTypes.TransportProtocol, RelayProtocol> = {
+ [grpcTypes.TransportProtocol.TCP]: 'tcp',
+ [grpcTypes.TransportProtocol.UDP]: 'udp',
+ };
+ return protocolMap[protocol];
+}
+
+function convertFromTunnelState(tunnelState: grpcTypes.TunnelState): TunnelState | undefined {
+ const tunnelStateObject = tunnelState.toObject();
+ switch (tunnelState.getStateCase()) {
+ case grpcTypes.TunnelState.StateCase.STATE_NOT_SET:
+ return undefined;
+ case grpcTypes.TunnelState.StateCase.DISCONNECTED:
+ return { state: 'disconnected' };
+ case grpcTypes.TunnelState.StateCase.DISCONNECTING: {
+ const detailsMap: Record<grpcTypes.AfterDisconnect, AfterDisconnect> = {
+ [grpcTypes.AfterDisconnect.NOTHING]: 'nothing',
+ [grpcTypes.AfterDisconnect.BLOCK]: 'block',
+ [grpcTypes.AfterDisconnect.RECONNECT]: 'reconnect',
+ };
+ return (
+ tunnelStateObject.disconnecting && {
+ state: 'disconnecting',
+ details: detailsMap[tunnelStateObject.disconnecting.afterDisconnect],
+ }
+ );
+ }
+ case grpcTypes.TunnelState.StateCase.ERROR:
+ return (
+ tunnelStateObject.error?.errorState && {
+ state: 'error',
+ details: convertFromTunnelStateError(tunnelStateObject.error.errorState),
+ }
+ );
+ case grpcTypes.TunnelState.StateCase.CONNECTING:
+ return {
+ state: 'connecting',
+ details:
+ tunnelStateObject.connecting?.relayInfo &&
+ convertFromTunnelStateRelayInfo(tunnelStateObject.connecting.relayInfo),
+ };
+ case grpcTypes.TunnelState.StateCase.CONNECTED: {
+ const relayInfo =
+ tunnelStateObject.connected?.relayInfo &&
+ convertFromTunnelStateRelayInfo(tunnelStateObject.connected.relayInfo);
+ return (
+ relayInfo && {
+ state: 'connected',
+ details: relayInfo,
+ }
+ );
+ }
+ }
+}
+
+function convertFromTunnelStateError(state: grpcTypes.ErrorState.AsObject): IErrorState {
+ return {
+ ...state,
+ cause: convertFromTunnelStateErrorCause(state.cause, state),
+ blockFailure: state.blockingError
+ ? convertFromFirewallPolicyError(state.blockingError)
+ : undefined,
+ };
+}
+
+function convertFromTunnelStateErrorCause(
+ cause: grpcTypes.ErrorState.Cause,
+ state: grpcTypes.ErrorState.AsObject,
+): ErrorStateCause {
+ switch (cause) {
+ case grpcTypes.ErrorState.Cause.IS_OFFLINE:
+ return { reason: 'is_offline' };
+ case grpcTypes.ErrorState.Cause.SET_DNS_ERROR:
+ return { reason: 'set_dns_error' };
+ case grpcTypes.ErrorState.Cause.IPV6_UNAVAILABLE:
+ return { reason: 'ipv6_unavailable' };
+ case grpcTypes.ErrorState.Cause.START_TUNNEL_ERROR:
+ return { reason: 'start_tunnel_error' };
+ case grpcTypes.ErrorState.Cause.TAP_ADAPTER_PROBLEM:
+ return { reason: 'tap_adapter_problem' };
+ case grpcTypes.ErrorState.Cause.SET_FIREWALL_POLICY_ERROR:
+ return {
+ reason: 'set_firewall_policy_error',
+ details: convertFromFirewallPolicyError(state.policyError!),
+ };
+ case grpcTypes.ErrorState.Cause.AUTH_FAILED:
+ return { reason: 'auth_failed', details: state.authFailReason };
+ case grpcTypes.ErrorState.Cause.TUNNEL_PARAMETER_ERROR: {
+ const parameterErrorMap: Record<
+ grpcTypes.ErrorState.GenerationError,
+ TunnelParameterError
+ > = {
+ [grpcTypes.ErrorState.GenerationError.NO_MATCHING_RELAY]: 'no_matching_relay',
+ [grpcTypes.ErrorState.GenerationError.NO_MATCHING_BRIDGE_RELAY]: 'no_matching_bridge_relay',
+ [grpcTypes.ErrorState.GenerationError.NO_WIREGUARD_KEY]: 'no_wireguard_key',
+ [grpcTypes.ErrorState.GenerationError.CUSTOM_TUNNEL_HOST_RESOLUTION_ERROR]:
+ 'custom_tunnel_host_resultion_error',
+ };
+ return { reason: 'tunnel_parameter_error', details: parameterErrorMap[state.parameterError] };
+ }
+ case grpcTypes.ErrorState.Cause.VPN_PERMISSION_DENIED:
+ // VPN_PERMISSION_DENIED is only ever created on Android
+ throw invalidErrorStateCause;
+ }
+}
+
+function convertFromFirewallPolicyError(
+ error: grpcTypes.ErrorState.FirewallPolicyError.AsObject,
+): FirewallPolicyError {
+ switch (error.type) {
+ case grpcTypes.ErrorState.FirewallPolicyError.ErrorType.GENERIC:
+ return { reason: 'generic' };
+ case grpcTypes.ErrorState.FirewallPolicyError.ErrorType.LOCKED: {
+ const pid = error.lockPid;
+ const name = error.lockName;
+ return { reason: 'locked', details: pid && name ? { pid, name } : undefined };
+ }
+ }
+}
+
+function convertFromTunnelStateRelayInfo(
+ state: grpcTypes.TunnelStateRelayInfo.AsObject,
+): ITunnelStateRelayInfo | undefined {
+ if (state.tunnelEndpoint) {
+ return {
+ ...state,
+ endpoint: {
+ ...state.tunnelEndpoint,
+ tunnelType: convertFromTunnelType(state.tunnelEndpoint.tunnelType),
+ protocol: convertFromTransportProtocol(state.tunnelEndpoint.protocol),
+ proxy: state.tunnelEndpoint.proxy && convertFromProxyEndpoint(state.tunnelEndpoint.proxy),
+ },
+ };
+ }
+ return undefined;
+}
+
+function convertFromTunnelType(tunnelType: grpcTypes.TunnelType): TunnelType {
+ const tunnelTypeMap: Record<grpcTypes.TunnelType, TunnelType> = {
+ [grpcTypes.TunnelType.WIREGUARD]: 'wireguard',
+ [grpcTypes.TunnelType.OPENVPN]: 'openvpn',
+ };
+
+ return tunnelTypeMap[tunnelType];
+}
+
+function convertFromProxyEndpoint(proxyEndpoint: grpcTypes.ProxyEndpoint.AsObject): IProxyEndpoint {
+ const proxyTypeMap: Record<grpcTypes.ProxyType, ProxyType> = {
+ [grpcTypes.ProxyType.CUSTOM]: 'custom',
+ [grpcTypes.ProxyType.SHADOWSOCKS]: 'shadowsocks',
+ };
+
+ return {
+ ...proxyEndpoint,
+ protocol: convertFromTransportProtocol(proxyEndpoint.protocol),
+ proxyType: proxyTypeMap[proxyEndpoint.proxyType],
+ };
+}
+
+function convertFromSettings(settings: grpcTypes.Settings): ISettings | undefined {
+ const settingsObject = settings.toObject();
+ const bridgeState = convertFromBridgeState(settingsObject.bridgeState!.state!);
+ const relaySettings = convertFromRelaySettings(settings.getRelaySettings())!;
+ const bridgeSettings = convertFromBridgeSettings(settingsObject.bridgeSettings!);
+ const tunnelOptions = convertFromTunnelOptions(settingsObject.tunnelOptions!);
+ return {
+ ...settings.toObject(),
+ bridgeState,
+ relaySettings,
+ bridgeSettings,
+ tunnelOptions,
+ };
+}
+
+function convertFromBridgeState(bridgeState: grpcTypes.BridgeState.State): BridgeState {
+ const bridgeStateMap: Record<grpcTypes.BridgeState.State, BridgeState> = {
+ [grpcTypes.BridgeState.State.AUTO]: 'auto',
+ [grpcTypes.BridgeState.State.ON]: 'on',
+ [grpcTypes.BridgeState.State.OFF]: 'off',
+ };
+
+ return bridgeStateMap[bridgeState];
+}
+
+function convertFromRelaySettings(
+ relaySettings?: grpcTypes.RelaySettings,
+): RelaySettings | undefined {
+ if (relaySettings) {
+ switch (relaySettings.getEndpointCase()) {
+ case grpcTypes.RelaySettings.EndpointCase.ENDPOINT_NOT_SET:
+ return undefined;
+ case grpcTypes.RelaySettings.EndpointCase.CUSTOM: {
+ const custom = relaySettings.getCustom()?.toObject();
+ const config = relaySettings.getCustom()?.getConfig();
+ const connectionConfig = config && convertFromConnectionConfig(config);
+ return (
+ custom &&
+ connectionConfig && {
+ customTunnelEndpoint: {
+ ...custom,
+ config: connectionConfig,
+ },
+ }
+ );
+ }
+ case grpcTypes.RelaySettings.EndpointCase.NORMAL: {
+ const normal = relaySettings.getNormal()!;
+ const grpcLocation = normal.getLocation();
+ const location = grpcLocation
+ ? { only: convertFromLocation(grpcLocation.toObject()) }
+ : 'any';
+ const tunnelProtocol = convertFromTunnelTypeConstraint(normal.getTunnelType()!);
+ const openvpnConstraints = convertFromOpenVpnConstraints(normal.getOpenvpnConstraints()!);
+ const wireguardConstraints = convertFromWireguardConstraints(
+ normal.getWireguardConstraints()!,
+ );
+
+ return {
+ normal: {
+ location,
+ tunnelProtocol,
+ wireguardConstraints,
+ openvpnConstraints,
+ },
+ };
}
- } catch (error) {
- throw new ResponseParseError(`Invalid response from generate_wireguard_key ${error}`);
}
+ } else {
+ return undefined;
}
+}
- public async getWireguardKey(): Promise<IWireguardPublicKey | undefined> {
- const response = await this.transport.send('get_wireguard_key');
- try {
- return validate(maybe(wireguardPublicKey), response) || undefined;
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_wireguard_key');
+function convertFromBridgeSettings(
+ bridgeSettings: grpcTypes.BridgeSettings.AsObject,
+): BridgeSettings {
+ const normalSettings = bridgeSettings.normal;
+ if (normalSettings) {
+ const grpcLocation = normalSettings.location;
+ const location = grpcLocation ? { only: convertFromLocation(grpcLocation) } : 'any';
+ return {
+ normal: {
+ location,
+ },
+ };
+ }
+
+ const customSettings = (settings: ProxySettings): BridgeSettings => {
+ return { custom: settings };
+ };
+
+ const localSettings = bridgeSettings.local;
+ if (localSettings) {
+ return customSettings({
+ port: localSettings?.port!,
+ peer: localSettings?.peer!,
+ });
+ }
+
+ const remoteSettings = bridgeSettings.remote;
+ if (remoteSettings) {
+ return customSettings({
+ address: remoteSettings?.address!,
+ auth: {
+ ...remoteSettings?.auth!,
+ },
+ });
+ }
+
+ const shadowsocksSettings = bridgeSettings.shadowsocks!;
+ return customSettings({
+ peer: shadowsocksSettings.peer!,
+ password: shadowsocksSettings.password!,
+ cipher: shadowsocksSettings.cipher!,
+ });
+}
+
+function convertFromConnectionConfig(
+ connectionConfig: grpcTypes.ConnectionConfig,
+): ConnectionConfig | undefined {
+ const connectionConfigObject = connectionConfig.toObject();
+ switch (connectionConfig.getConfigCase()) {
+ case grpcTypes.ConnectionConfig.ConfigCase.CONFIG_NOT_SET:
+ return undefined;
+ case grpcTypes.ConnectionConfig.ConfigCase.WIREGUARD:
+ return (
+ connectionConfigObject.wireguard &&
+ connectionConfigObject.wireguard.tunnel &&
+ connectionConfigObject.wireguard.peer && {
+ wireguard: {
+ ...connectionConfigObject.wireguard,
+ tunnel: {
+ privateKey: convertFromWireguardKey(
+ connectionConfigObject.wireguard.tunnel.privateKey,
+ ),
+ addresses: connectionConfigObject.wireguard.tunnel.addressesList,
+ },
+ peer: {
+ ...connectionConfigObject.wireguard.peer,
+ addresses: connectionConfigObject.wireguard.peer.allowedIpsList,
+ publicKey: convertFromWireguardKey(connectionConfigObject.wireguard.peer.publicKey),
+ },
+ },
+ }
+ );
+ case grpcTypes.ConnectionConfig.ConfigCase.OPENVPN: {
+ const [ip, port] = connectionConfigObject.openvpn!.address.split(':');
+ return {
+ openvpn: {
+ ...connectionConfigObject.openvpn!,
+ endpoint: {
+ ip,
+ port: parseInt(port, 10),
+ protocol: convertFromTransportProtocol(connectionConfigObject.openvpn!.protocol),
+ },
+ },
+ };
}
}
+}
- public async verifyWireguardKey(): Promise<boolean> {
- const response = await this.transport.send('verify_wireguard_key');
- try {
- return validate(boolean, response);
- } catch (error) {
- throw new ResponseParseError('Invalid response from verify_wireguard_key');
+function convertFromLocation(location: grpcTypes.RelayLocation.AsObject): RelayLocation {
+ if (location.hostname) {
+ return { hostname: [location.country, location.city, location.hostname] };
+ }
+ if (location.city) {
+ return { city: [location.country, location.city] };
+ }
+
+ return { country: location.country };
+}
+
+function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObject): ITunnelOptions {
+ return {
+ openvpn: {
+ mssfix: tunnelOptions.openvpn!.mssfix,
+ },
+ wireguard: {
+ mtu: tunnelOptions.wireguard!.mtu,
+ },
+ generic: {
+ enableIpv6: tunnelOptions.generic!.enableIpv6,
+ },
+ };
+}
+
+function convertFromDaemonEvent(data: grpcTypes.DaemonEvent): DaemonEvent {
+ const tunnelState = data.getTunnelState();
+ if (tunnelState !== undefined) {
+ return { tunnelState: convertFromTunnelState(tunnelState)! };
+ }
+
+ const settings = data.getSettings();
+ if (settings !== undefined) {
+ return { settings: convertFromSettings(settings)! };
+ }
+
+ const relayList = data.getRelayList();
+ if (relayList !== undefined) {
+ return {
+ relayList: {
+ countries: relayList
+ .getCountriesList()
+ ?.map((country: grpcTypes.RelayListCountry) =>
+ convertFromRelayListCountry(country.toObject()),
+ ),
+ },
+ };
+ }
+
+ const keygenEvent = data.getKeyEvent();
+ if (keygenEvent !== undefined) {
+ return {
+ wireguardKey: convertFromKeygenEvent(keygenEvent),
+ };
+ }
+
+ return {
+ appVersionInfo: data.getVersionInfo()!.toObject(),
+ };
+}
+
+function convertFromKeygenEvent(data: grpcTypes.KeygenEvent): KeygenEvent {
+ switch (data.getEvent()) {
+ case grpcTypes.KeygenEvent.KeygenEvent.TOO_MANY_KEYS:
+ return 'too_many_keys';
+ case grpcTypes.KeygenEvent.KeygenEvent.NEW_KEY: {
+ const newKey = data.getNewKey();
+ return newKey
+ ? {
+ newKey: {
+ created: newKey.getCreated()!.toDate().toISOString(),
+ key: convertFromWireguardKey(newKey.getKey()),
+ },
+ }
+ : 'generation_failure';
}
+ case grpcTypes.KeygenEvent.KeygenEvent.GENERATION_FAILURE:
+ return 'generation_failure';
}
+}
- public async getVersionInfo(): Promise<IAppVersionInfo> {
- const response = await this.transport.send('get_version_info', [], NETWORK_CALL_TIMEOUT);
- try {
- return snakeCaseToCamelCase(validate(appVersionInfoSchema, response));
- } catch (error) {
- throw new ResponseParseError('Invalid response from get_version_info');
+function convertFromOpenVpnConstraints(
+ constraints: grpcTypes.OpenvpnConstraints,
+): IOpenVpnConstraints {
+ const port = convertFromConstraint(constraints.getPort());
+ let protocol: Constraint<RelayProtocol> = 'any';
+ switch (constraints.getProtocol()?.getProtocol()) {
+ case grpcTypes.TransportProtocol.TCP:
+ protocol = { only: 'tcp' };
+ break;
+ case grpcTypes.TransportProtocol.UDP:
+ protocol = { only: 'udp' };
+ break;
+ }
+
+ return { port, protocol };
+}
+
+function convertFromWireguardConstraints(
+ constraints: grpcTypes.WireguardConstraints,
+): IWireguardConstraints {
+ const port = convertFromConstraint(constraints.getPort());
+ return { port };
+}
+
+function convertFromTunnelTypeConstraint(
+ constraint: grpcTypes.TunnelTypeConstraint | undefined,
+): Constraint<TunnelProtocol> {
+ switch (constraint?.getTunnelType()) {
+ case grpcTypes.TunnelType.WIREGUARD: {
+ return { only: 'wireguard' };
+ }
+ case grpcTypes.TunnelType.OPENVPN: {
+ return { only: 'openvpn' };
}
+ default: {
+ return 'any';
+ }
+ }
+}
+
+function convertFromConstraint<T>(value: T | undefined): Constraint<T> {
+ if (value) {
+ return { only: value };
+ } else {
+ return 'any';
+ }
+}
+
+function convertToNormalBridgeSettings(
+ constraints: IBridgeConstraints,
+): grpcTypes.BridgeSettings.BridgeConstraints {
+ const normalBridgeSettings = new grpcTypes.BridgeSettings.BridgeConstraints();
+ normalBridgeSettings.setLocation(convertToLocation(liftConstraint(constraints.location)));
+
+ return normalBridgeSettings;
+}
+
+function convertToLocation(
+ constraint: RelayLocation | undefined,
+): grpcTypes.RelayLocation | undefined {
+ const location = new grpcTypes.RelayLocation();
+ if (constraint && 'hostname' in constraint) {
+ const [countryCode, cityCode, hostname] = constraint.hostname;
+ location.setCountry(countryCode);
+ location.setCity(cityCode);
+ location.setHostname(hostname);
+ return location;
+ } else if (constraint && 'city' in constraint) {
+ location.setCountry(constraint.city[0]);
+ location.setCity(constraint.city[1]);
+ return location;
+ } else if (constraint && 'country' in constraint) {
+ location.setCountry(constraint.country);
+ return location;
+ } else {
+ return undefined;
+ }
+}
+
+function convertToTunnelTypeConstraint(
+ constraint: Constraint<TunnelType>,
+): grpcTypes.TunnelTypeConstraint | undefined {
+ const grpcConstraint = new grpcTypes.TunnelTypeConstraint();
+
+ if (constraint !== undefined && constraint !== 'any' && 'only' in constraint) {
+ switch (constraint.only) {
+ case 'wireguard':
+ grpcConstraint.setTunnelType(grpcTypes.TunnelType.WIREGUARD);
+ return grpcConstraint;
+ case 'openvpn':
+ grpcConstraint.setTunnelType(grpcTypes.TunnelType.OPENVPN);
+ return grpcConstraint;
+ }
+ }
+ return undefined;
+}
+
+function convertToOpenVpnConstraints(
+ constraints: Partial<IOpenVpnConstraints> | undefined,
+): grpcTypes.OpenvpnConstraints | undefined {
+ const openvpnConstraints = new grpcTypes.OpenvpnConstraints();
+ if (constraints) {
+ const port = liftConstraint(constraints.port);
+ if (port) {
+ openvpnConstraints.setPort(port);
+ }
+ const protocol = liftConstraint(constraints.protocol);
+ if (protocol) {
+ const transportConstraint = new grpcTypes.TransportProtocolConstraint();
+ transportConstraint.setProtocol(convertToTransportProtocol(protocol));
+ openvpnConstraints.setProtocol(transportConstraint);
+ }
+ return openvpnConstraints;
+ }
+
+ return undefined;
+}
+
+function convertToWireguardConstraints(
+ constraint: Partial<IWireguardConstraints> | undefined,
+): grpcTypes.WireguardConstraints | undefined {
+ if (constraint) {
+ const wireguardConstraints = new grpcTypes.WireguardConstraints();
+ const port = liftConstraint(constraint.port);
+ if (port) {
+ wireguardConstraints.setPort(port);
+ }
+ return wireguardConstraints;
+ }
+ return undefined;
+}
+
+function convertToTransportProtocol(protocol: RelayProtocol): grpcTypes.TransportProtocol {
+ switch (protocol) {
+ case 'udp':
+ return grpcTypes.TransportProtocol.UDP;
+ case 'tcp':
+ return grpcTypes.TransportProtocol.TCP;
+ }
+}
+
+function ensureExists<T>(value: T | undefined, errorMessage: string): T {
+ if (value) {
+ return value;
}
+ throw new ResponseParseError(errorMessage);
}
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 53ee113410..3ad81f453d 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -44,12 +44,7 @@ import consumePromise from '../shared/promise';
import { Scheduler } from '../shared/scheduler';
import AccountDataCache from './account-data-cache';
import { getOpenAtLogin, setOpenAtLogin } from './autostart';
-import {
- ConnectionObserver,
- DaemonRpc,
- ResponseParseError,
- SubscriptionListener,
-} from './daemon-rpc';
+import { ConnectionObserver, DaemonRpc, SubscriptionListener } from './daemon-rpc';
import { InvalidAccountError } from './errors';
import Expectation from './expectation';
import GuiSettings from './gui-settings';
@@ -63,7 +58,7 @@ import WindowController from './window-controller';
const linuxSplitTunneling = process.platform === 'linux' && require('./linux-split-tunneling');
const DAEMON_RPC_PATH =
- process.platform === 'win32' ? '//./pipe/Mullvad VPN' : '/var/run/mullvad-vpn';
+ process.platform === 'win32' ? 'unix:////./pipe/Mullvad VPN' : 'unix:///var/run/mullvad-vpn';
const AUTO_CONNECT_FALLBACK_DELAY = 6000;
@@ -95,7 +90,7 @@ class ApplicationMain {
private windowController?: WindowController;
private trayIconController?: TrayIconController;
- private daemonRpc = new DaemonRpc();
+ private daemonRpc = new DaemonRpc(DAEMON_RPC_PATH);
private daemonEventListener?: SubscriptionListener<DaemonEvent>;
private reconnectBackoff = new ReconnectionBackoff();
private connectedToDaemon = false;
@@ -138,7 +133,6 @@ class ApplicationMain {
},
wireguard: {
mtu: undefined,
- fwmark: undefined,
},
},
};
@@ -310,7 +304,7 @@ class ApplicationMain {
// Unsubscribe the event handler
try {
if (this.daemonEventListener) {
- await this.daemonRpc.unsubscribeDaemonEventListener(this.daemonEventListener);
+ this.daemonRpc.unsubscribeDaemonEventListener(this.daemonEventListener);
log.info('Unsubscribed from the daemon events');
}
@@ -326,6 +320,8 @@ class ApplicationMain {
if (process.platform === 'darwin' && this.windowController) {
this.windowController.window.closable = true;
}
+
+ this.daemonRpc.disconnect();
}
private detectLocale(): string {
@@ -419,7 +415,7 @@ class ApplicationMain {
// subscribe to events
try {
- this.daemonEventListener = await this.subscribeEvents();
+ this.daemonEventListener = this.subscribeEvents();
} catch (error) {
log.error(`Failed to subscribe: ${error.message}`);
@@ -500,6 +496,9 @@ class ApplicationMain {
};
private onDaemonDisconnected = (error?: Error) => {
+ if (this.daemonEventListener) {
+ this.daemonRpc.unsubscribeDaemonEventListener(this.daemonEventListener);
+ }
// make sure we were connected before to distinguish between a failed attempt to reconnect and
// connection loss.
const wasConnected = this.connectedToDaemon;
@@ -507,6 +506,9 @@ class ApplicationMain {
// Reset the daemon event listener since it's going to be invalidated on disconnect
this.daemonEventListener = undefined;
+ // TODO: GRPC doesn't set an error, but without an error, the UI won't be updated
+ error = error === undefined && wasConnected ? new Error('Connection to daemon lost') : error;
+
if (wasConnected) {
this.connectedToDaemon = false;
@@ -529,32 +531,24 @@ class ApplicationMain {
} else {
log.error(`Failed to connect to daemon: ${error.message}`);
}
-
- this.reconnectToDaemon();
} else {
log.info('Disconnected from the daemon');
}
};
private connectToDaemon() {
- consumePromise(this.daemonRpc.connect({ path: DAEMON_RPC_PATH }));
- }
-
- private reconnectToDaemon() {
- this.reconnectBackoff.attempt(() => {
- this.connectToDaemon();
- });
+ consumePromise(this.daemonRpc.connect());
}
private recoverFromBootstrapError(_error?: Error) {
// Attempt to reconnect to daemon if the program fails to fetch settings, tunnel state or
// subscribe for RPC events.
- this.daemonRpc.disconnect();
-
- this.reconnectToDaemon();
+ if (this.daemonEventListener) {
+ this.daemonRpc.unsubscribeDaemonEventListener(this.daemonEventListener);
+ }
}
- private async subscribeEvents(): Promise<SubscriptionListener<DaemonEvent>> {
+ private subscribeEvents(): SubscriptionListener<DaemonEvent> {
const daemonEventListener = new SubscriptionListener(
(daemonEvent: DaemonEvent) => {
if ('tunnelState' in daemonEvent) {
@@ -575,14 +569,11 @@ class ApplicationMain {
},
(error: Error) => {
log.error(`Cannot deserialize the daemon event: ${error.message}`);
-
- if (error instanceof ResponseParseError && error.validationError) {
- log.error(error.validationError.message);
- }
+ log.error(error.stack);
},
);
- await this.daemonRpc.subscribeDaemonEventListener(daemonEventListener);
+ this.daemonRpc.subscribeDaemonEventListener(daemonEventListener);
return daemonEventListener;
}
diff --git a/gui/src/renderer/components/RedeemVoucher.tsx b/gui/src/renderer/components/RedeemVoucher.tsx
index daf5074603..7ca83c43d1 100644
--- a/gui/src/renderer/components/RedeemVoucher.tsx
+++ b/gui/src/renderer/components/RedeemVoucher.tsx
@@ -86,7 +86,7 @@ export function RedeemVoucherContainer(props: IRedeemVoucherProps) {
if (response.type === 'success') {
setValue('');
closeScheduler.schedule(() => {
- updateAccountExpiry(response.new_expiry);
+ updateAccountExpiry(response.newExpiry);
onSuccess?.();
}, 1000);
} else {
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index 1f1779a4b0..78b6f60c17 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -46,14 +46,14 @@ export type ErrorStateCause =
export type AfterDisconnect = 'nothing' | 'block' | 'reconnect';
-export type TunnelType = 'wireguard' | 'openvpn';
+export type TunnelType = 'any' | 'wireguard' | 'openvpn';
export function tunnelTypeToString(tunnel: TunnelType): string {
switch (tunnel) {
case 'wireguard':
return 'WireGuard';
case 'openvpn':
return 'OpenVPN';
- default:
+ case 'any':
return '';
}
}
@@ -235,14 +235,16 @@ export interface IOpenVpnTunnelData {
}
export interface IWireguardTunnelData {
- // Port ranges are an array of pairs, such as [[53,53], [10_000, 60_000]],
- // which in this case translates that the specific tunnel can be connected on
- // port 53 and ports 10'000 through 60'000.
- portRanges: Array<[number, number]>;
+ portRanges: Array<IPortRange>;
// Public key of the tunnel.
publicKey: string;
}
+export interface IPortRange {
+ first: number;
+ last: number;
+}
+
export interface IShadowsocksEndpointData {
port: number;
cipher: string;
@@ -256,8 +258,6 @@ export interface ITunnelOptions {
};
wireguard: {
mtu?: number;
- // Only relevant on Linux
- fwmark?: number;
};
generic: {
enableIpv6: boolean;
@@ -330,7 +330,7 @@ export interface ISocketAddress {
}
export type VoucherResponse =
- | { type: 'success'; new_expiry: string }
+ | { type: 'success'; newExpiry: string; secondsAdded: number }
| { type: 'invalid' | 'already_used' | 'error' };
export enum VoucherErrorCode {
diff --git a/gui/tasks/scripts.js b/gui/tasks/scripts.js
index 82896dac21..5ee33a9071 100644
--- a/gui/tasks/scripts.js
+++ b/gui/tasks/scripts.js
@@ -1,4 +1,5 @@
-const { src, dest } = require('gulp');
+const { exec } = require('child_process');
+const { src, dest, series } = require('gulp');
const ts = require('gulp-typescript');
const inject = require('gulp-inject-string');
const TscWatchClient = require('tsc-watch/client');
@@ -25,7 +26,13 @@ function compileScripts() {
.pipe(dest('build'));
}
+function buildProto(callback) {
+ exec('bash ./scripts/build-proto.sh', (err) => callback(err));
+}
+
compileScripts.displayName = 'compile-scripts';
+buildProto.displayName = 'build-proto';
exports.build = compileScripts;
+exports.buildProto = buildProto;
exports.makeWatchCompiler = makeWatchCompiler;