├── .env.example ├── .gitignore ├── README.md ├── index.js ├── package.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | # Push notifications keys. You can generate them with command "./node_modules/.bin/web-push generate-vapid-keys" 2 | PUBLIC_VAPID_KEY= 3 | PRIVATE_VAPID_KEY= 4 | # This must be either a URL or a 'mailto:' address. 5 | # For example: 'https://my-site.com/contact' or 'mailto: contact@my-site.com' 6 | WEB_PUSH_CONTACT="mailto: contact@my-site.com" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | .env 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | It is a backend part of the web push demo. 2 | 3 | Install: 4 | 1. Run `yarn install` 5 | 2. Run `./node_modules/.bin/web-push generate-vapid-keys` to get your VAPID keys. 6 | 3. Copy `.env.example` to `.env` and fill in public and private key params with generated values. 7 | 4. Start the server with `node index.js` 8 | 9 | [Detailed explanation based on this repo](https://medium.com/seladir/how-to-implement-web-push-notifications-in-your-node-react-app-9bed79b53f34) 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const dotenv = require('dotenv') 3 | const bodyParser = require('body-parser') 4 | const cors = require('cors') 5 | const webpush = require('web-push') 6 | 7 | const app = express() 8 | 9 | dotenv.config() 10 | 11 | app.use(cors()) 12 | app.use(bodyParser.json()) 13 | 14 | webpush.setVapidDetails(process.env.WEB_PUSH_CONTACT, process.env.PUBLIC_VAPID_KEY, process.env.PRIVATE_VAPID_KEY) 15 | 16 | app.get('/', (req, res) => { 17 | res.send('Hello world!') 18 | }) 19 | 20 | app.post('/notifications/subscribe', (req, res) => { 21 | const subscription = req.body 22 | 23 | console.log(subscription) 24 | 25 | const payload = JSON.stringify({ 26 | title: 'Hello!', 27 | body: 'It works.', 28 | }) 29 | 30 | webpush.sendNotification(subscription, payload) 31 | .then(result => console.log(result)) 32 | .catch(e => console.log(e.stack)) 33 | 34 | res.status(200).json({'success': true}) 35 | }); 36 | 37 | app.listen(9000, () => console.log('The server has been started on the port 9000')) 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-webpush-node", 3 | "version": "0.1.0", 4 | "dependencies": { 5 | "body-parser": "^1.18.3", 6 | "cors": "^2.8.5", 7 | "dotenv": "^7.0.0", 8 | "express": "^4.16.4", 9 | "web-push": "^3.3.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | accepts@~1.3.5: 6 | version "1.3.5" 7 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" 8 | dependencies: 9 | mime-types "~2.1.18" 10 | negotiator "0.6.1" 11 | 12 | agent-base@^4.1.0: 13 | version "4.2.1" 14 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" 15 | dependencies: 16 | es6-promisify "^5.0.0" 17 | 18 | array-flatten@1.1.1: 19 | version "1.1.1" 20 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 21 | 22 | asn1.js@^5.0.0: 23 | version "5.0.1" 24 | resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.0.1.tgz#7668b56416953f0ce3421adbb3893ace59c96f59" 25 | dependencies: 26 | bn.js "^4.0.0" 27 | inherits "^2.0.1" 28 | minimalistic-assert "^1.0.0" 29 | 30 | bn.js@^4.0.0: 31 | version "4.11.8" 32 | resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" 33 | 34 | body-parser@1.18.3, body-parser@^1.18.3: 35 | version "1.18.3" 36 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" 37 | dependencies: 38 | bytes "3.0.0" 39 | content-type "~1.0.4" 40 | debug "2.6.9" 41 | depd "~1.1.2" 42 | http-errors "~1.6.3" 43 | iconv-lite "0.4.23" 44 | on-finished "~2.3.0" 45 | qs "6.5.2" 46 | raw-body "2.3.3" 47 | type-is "~1.6.16" 48 | 49 | buffer-equal-constant-time@1.0.1: 50 | version "1.0.1" 51 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" 52 | 53 | bytes@3.0.0: 54 | version "3.0.0" 55 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" 56 | 57 | content-disposition@0.5.2: 58 | version "0.5.2" 59 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" 60 | 61 | content-type@~1.0.4: 62 | version "1.0.4" 63 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 64 | 65 | cookie-signature@1.0.6: 66 | version "1.0.6" 67 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 68 | 69 | cookie@0.3.1: 70 | version "0.3.1" 71 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 72 | 73 | cors@^2.8.5: 74 | version "2.8.5" 75 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" 76 | dependencies: 77 | object-assign "^4" 78 | vary "^1" 79 | 80 | debug@2.6.9: 81 | version "2.6.9" 82 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 83 | dependencies: 84 | ms "2.0.0" 85 | 86 | debug@^3.1.0: 87 | version "3.2.6" 88 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 89 | dependencies: 90 | ms "^2.1.1" 91 | 92 | depd@~1.1.2: 93 | version "1.1.2" 94 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 95 | 96 | destroy@~1.0.4: 97 | version "1.0.4" 98 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 99 | 100 | dotenv@^7.0.0: 101 | version "7.0.0" 102 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" 103 | 104 | ecdsa-sig-formatter@1.0.11: 105 | version "1.0.11" 106 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" 107 | dependencies: 108 | safe-buffer "^5.0.1" 109 | 110 | ee-first@1.1.1: 111 | version "1.1.1" 112 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 113 | 114 | encodeurl@~1.0.2: 115 | version "1.0.2" 116 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 117 | 118 | es6-promise@^4.0.3: 119 | version "4.2.6" 120 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" 121 | 122 | es6-promisify@^5.0.0: 123 | version "5.0.0" 124 | resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" 125 | dependencies: 126 | es6-promise "^4.0.3" 127 | 128 | escape-html@~1.0.3: 129 | version "1.0.3" 130 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 131 | 132 | etag@~1.8.1: 133 | version "1.8.1" 134 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 135 | 136 | express@^4.16.4: 137 | version "4.16.4" 138 | resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" 139 | dependencies: 140 | accepts "~1.3.5" 141 | array-flatten "1.1.1" 142 | body-parser "1.18.3" 143 | content-disposition "0.5.2" 144 | content-type "~1.0.4" 145 | cookie "0.3.1" 146 | cookie-signature "1.0.6" 147 | debug "2.6.9" 148 | depd "~1.1.2" 149 | encodeurl "~1.0.2" 150 | escape-html "~1.0.3" 151 | etag "~1.8.1" 152 | finalhandler "1.1.1" 153 | fresh "0.5.2" 154 | merge-descriptors "1.0.1" 155 | methods "~1.1.2" 156 | on-finished "~2.3.0" 157 | parseurl "~1.3.2" 158 | path-to-regexp "0.1.7" 159 | proxy-addr "~2.0.4" 160 | qs "6.5.2" 161 | range-parser "~1.2.0" 162 | safe-buffer "5.1.2" 163 | send "0.16.2" 164 | serve-static "1.13.2" 165 | setprototypeof "1.1.0" 166 | statuses "~1.4.0" 167 | type-is "~1.6.16" 168 | utils-merge "1.0.1" 169 | vary "~1.1.2" 170 | 171 | finalhandler@1.1.1: 172 | version "1.1.1" 173 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" 174 | dependencies: 175 | debug "2.6.9" 176 | encodeurl "~1.0.2" 177 | escape-html "~1.0.3" 178 | on-finished "~2.3.0" 179 | parseurl "~1.3.2" 180 | statuses "~1.4.0" 181 | unpipe "~1.0.0" 182 | 183 | forwarded@~0.1.2: 184 | version "0.1.2" 185 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 186 | 187 | fresh@0.5.2: 188 | version "0.5.2" 189 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 190 | 191 | http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: 192 | version "1.6.3" 193 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" 194 | dependencies: 195 | depd "~1.1.2" 196 | inherits "2.0.3" 197 | setprototypeof "1.1.0" 198 | statuses ">= 1.4.0 < 2" 199 | 200 | http_ece@1.0.5: 201 | version "1.0.5" 202 | resolved "https://registry.yarnpkg.com/http_ece/-/http_ece-1.0.5.tgz#b60660faaf14215102d1493ea720dcd92b53372f" 203 | dependencies: 204 | urlsafe-base64 "~1.0.0" 205 | 206 | https-proxy-agent@^2.2.1: 207 | version "2.2.1" 208 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" 209 | dependencies: 210 | agent-base "^4.1.0" 211 | debug "^3.1.0" 212 | 213 | iconv-lite@0.4.23: 214 | version "0.4.23" 215 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" 216 | dependencies: 217 | safer-buffer ">= 2.1.2 < 3" 218 | 219 | inherits@2.0.3, inherits@^2.0.1: 220 | version "2.0.3" 221 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 222 | 223 | ipaddr.js@1.8.0: 224 | version "1.8.0" 225 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" 226 | 227 | jwa@^1.4.1: 228 | version "1.4.1" 229 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" 230 | dependencies: 231 | buffer-equal-constant-time "1.0.1" 232 | ecdsa-sig-formatter "1.0.11" 233 | safe-buffer "^5.0.1" 234 | 235 | jws@^3.1.3: 236 | version "3.2.2" 237 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" 238 | dependencies: 239 | jwa "^1.4.1" 240 | safe-buffer "^5.0.1" 241 | 242 | media-typer@0.3.0: 243 | version "0.3.0" 244 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 245 | 246 | merge-descriptors@1.0.1: 247 | version "1.0.1" 248 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 249 | 250 | methods@~1.1.2: 251 | version "1.1.2" 252 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 253 | 254 | mime-db@~1.38.0: 255 | version "1.38.0" 256 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" 257 | 258 | mime-types@~2.1.18: 259 | version "2.1.22" 260 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" 261 | dependencies: 262 | mime-db "~1.38.0" 263 | 264 | mime@1.4.1: 265 | version "1.4.1" 266 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" 267 | 268 | minimalistic-assert@^1.0.0: 269 | version "1.0.1" 270 | resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" 271 | 272 | minimist@^1.2.0: 273 | version "1.2.0" 274 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 275 | 276 | ms@2.0.0: 277 | version "2.0.0" 278 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 279 | 280 | ms@^2.1.1: 281 | version "2.1.1" 282 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 283 | 284 | negotiator@0.6.1: 285 | version "0.6.1" 286 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 287 | 288 | object-assign@^4: 289 | version "4.1.1" 290 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 291 | 292 | on-finished@~2.3.0: 293 | version "2.3.0" 294 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 295 | dependencies: 296 | ee-first "1.1.1" 297 | 298 | parseurl@~1.3.2: 299 | version "1.3.2" 300 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 301 | 302 | path-to-regexp@0.1.7: 303 | version "0.1.7" 304 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 305 | 306 | proxy-addr@~2.0.4: 307 | version "2.0.4" 308 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" 309 | dependencies: 310 | forwarded "~0.1.2" 311 | ipaddr.js "1.8.0" 312 | 313 | qs@6.5.2: 314 | version "6.5.2" 315 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" 316 | 317 | range-parser@~1.2.0: 318 | version "1.2.0" 319 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 320 | 321 | raw-body@2.3.3: 322 | version "2.3.3" 323 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" 324 | dependencies: 325 | bytes "3.0.0" 326 | http-errors "1.6.3" 327 | iconv-lite "0.4.23" 328 | unpipe "1.0.0" 329 | 330 | safe-buffer@5.1.2, safe-buffer@^5.0.1: 331 | version "5.1.2" 332 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 333 | 334 | "safer-buffer@>= 2.1.2 < 3": 335 | version "2.1.2" 336 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 337 | 338 | send@0.16.2: 339 | version "0.16.2" 340 | resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" 341 | dependencies: 342 | debug "2.6.9" 343 | depd "~1.1.2" 344 | destroy "~1.0.4" 345 | encodeurl "~1.0.2" 346 | escape-html "~1.0.3" 347 | etag "~1.8.1" 348 | fresh "0.5.2" 349 | http-errors "~1.6.2" 350 | mime "1.4.1" 351 | ms "2.0.0" 352 | on-finished "~2.3.0" 353 | range-parser "~1.2.0" 354 | statuses "~1.4.0" 355 | 356 | serve-static@1.13.2: 357 | version "1.13.2" 358 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" 359 | dependencies: 360 | encodeurl "~1.0.2" 361 | escape-html "~1.0.3" 362 | parseurl "~1.3.2" 363 | send "0.16.2" 364 | 365 | setprototypeof@1.1.0: 366 | version "1.1.0" 367 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" 368 | 369 | "statuses@>= 1.4.0 < 2": 370 | version "1.5.0" 371 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 372 | 373 | statuses@~1.4.0: 374 | version "1.4.0" 375 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" 376 | 377 | type-is@~1.6.16: 378 | version "1.6.16" 379 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" 380 | dependencies: 381 | media-typer "0.3.0" 382 | mime-types "~2.1.18" 383 | 384 | unpipe@1.0.0, unpipe@~1.0.0: 385 | version "1.0.0" 386 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 387 | 388 | urlsafe-base64@^1.0.0, urlsafe-base64@~1.0.0: 389 | version "1.0.0" 390 | resolved "https://registry.yarnpkg.com/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz#23f89069a6c62f46cf3a1d3b00169cefb90be0c6" 391 | 392 | utils-merge@1.0.1: 393 | version "1.0.1" 394 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 395 | 396 | vary@^1, vary@~1.1.2: 397 | version "1.1.2" 398 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 399 | 400 | web-push@^3.3.3: 401 | version "3.3.3" 402 | resolved "https://registry.yarnpkg.com/web-push/-/web-push-3.3.3.tgz#8dc7c578dd1243ceb5a8377389424e87ea9b15cc" 403 | dependencies: 404 | asn1.js "^5.0.0" 405 | http_ece "1.0.5" 406 | https-proxy-agent "^2.2.1" 407 | jws "^3.1.3" 408 | minimist "^1.2.0" 409 | urlsafe-base64 "^1.0.0" 410 | --------------------------------------------------------------------------------