├── components ├── home │ ├── styles.css │ ├── element.js │ └── template.html ├── sensors │ ├── styles.css │ ├── template.html │ └── element.js ├── background-sync │ ├── styles.css │ ├── template.html │ └── element.js ├── dynamic-data │ ├── styles.css │ ├── template.html │ └── element.js ├── utils.js ├── base.js ├── shared-styles.css └── shell │ ├── template.html │ ├── element.js │ └── styles.css ├── robots.txt ├── .gitignore ├── images └── icons │ ├── favicon.ico │ ├── mstile-70x70.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── browserconfig.xml │ └── safari-pinned-tab.svg ├── package.json ├── site.webmanifest ├── functions ├── package.json ├── data.json ├── index.js └── package-lock.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── .travis.yml ├── cache-manifest.js ├── LICENSE ├── generate-firebase-json.js ├── index.html ├── db-helpers.js ├── README.md ├── firebase.json ├── push-manifest.json └── sw.js /components/home/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/sensors/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | # Allow crawling of all content 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .firebase* 3 | .runtimeconfig.json 4 | node_modules/ 5 | *.log 6 | -------------------------------------------------------------------------------- /images/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/favicon.ico -------------------------------------------------------------------------------- /components/background-sync/styles.css: -------------------------------------------------------------------------------- 1 | .center-content { 2 | width: 100%; 3 | text-align: center; 4 | } 5 | -------------------------------------------------------------------------------- /images/icons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/mstile-70x70.png -------------------------------------------------------------------------------- /images/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/favicon-16x16.png -------------------------------------------------------------------------------- /images/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/favicon-32x32.png -------------------------------------------------------------------------------- /images/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/mstile-150x150.png -------------------------------------------------------------------------------- /images/icons/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/mstile-310x150.png -------------------------------------------------------------------------------- /images/icons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/mstile-310x310.png -------------------------------------------------------------------------------- /images/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /images/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /images/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dabolus/vanilla-pwa/HEAD/images/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanilla-pwa", 3 | "description": "An example of a PWA completely written from scratch", 4 | "scripts": { 5 | "serve": "firebase serve", 6 | "deploy": "firebase deploy", 7 | "postinstall": "npm i --prefix functions" 8 | }, 9 | "private": true 10 | } 11 | -------------------------------------------------------------------------------- /components/home/element.js: -------------------------------------------------------------------------------- 1 | import BaseElement from '../base.js'; 2 | 3 | class MyHome extends BaseElement { 4 | constructor() { 5 | super('/components/home/template.html', '/components/home/styles.css'); 6 | } 7 | } 8 | 9 | customElements.define('my-home', MyHome); 10 | export default MyHome; 11 | -------------------------------------------------------------------------------- /images/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #2e7d32 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vanilla PWA", 3 | "short_name": "Vanilla PWA", 4 | "icons": [ 5 | { 6 | "src": "images/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "images/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#2e7d32", 17 | "background_color": "#2e7d32", 18 | "start_url": "/home", 19 | "display": "standalone" 20 | } 21 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase serve --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "express": "^4.16.4", 14 | "firebase-admin": "^7.3.0", 15 | "firebase-functions": "^2.3.1", 16 | "web-push": "^3.3.4" 17 | }, 18 | "private": true 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this PWA 4 | 5 | --- 6 | 7 | **What is the feature you would like to see in this PWA?** 8 | Write the name of the technology you would like to see added to this PWA, or a brief description of what you want to happen if you don't know its name. 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. When I open the PWA it's frustrating to see that [...] 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here. 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Status of this PR 2 | Is it still a Work In Progress (not ready yet) or is it ready to be merged? 3 | - [ ] Ready to be merged 4 | - [ ] WIP 5 | 6 | ### Impacted areas 7 | What areas of the PWA are going to be impacted by this PR? 8 | - [ ] Docs 9 | - [ ] Website 10 | - [ ] Service Worker 11 | - [ ] Backend 12 | - [ ] Other (be sure to explain behind) 13 | 14 | ### Description of this PR 15 | Explain briefly what this PR adds/fixes in the PWA. 16 | Be sure to provide enough information, so that others 17 | can easily understand what are the benefits of merging 18 | this PR into the project. 19 | 20 | ### Closing issues 21 | Link the issue this PRs is going to close (if any) 22 | -------------------------------------------------------------------------------- /components/dynamic-data/styles.css: -------------------------------------------------------------------------------- 1 | #data-container { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | } 6 | 7 | article { 8 | display: flex; 9 | flex-direction: column; 10 | width: 16rem; 11 | height: auto; 12 | margin: 2rem; 13 | border-radius: .5rem; 14 | box-shadow: 0 .06rem .065rem 0 rgba(0, 0, 0, 0.14), 0 .003rem .15rem 0 rgba(0, 0, 0, 0.12), 0 .09rem .0035rem -.065rem rgba(0, 0, 0, 0.2); 15 | background: var(--section-bg-color); 16 | color: var(--primary-text-color); 17 | } 18 | 19 | article div { 20 | padding: 1rem; 21 | } 22 | 23 | article img { 24 | width: 16rem; 25 | height: 16rem; 26 | border-radius: .5rem .5rem 0 0; 27 | } 28 | 29 | article h2 { 30 | font-size: 1.1rem; 31 | } 32 | 33 | article span { 34 | font-weight: 100; 35 | } 36 | 37 | article h2, article span { 38 | margin: .5rem 0; 39 | } 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6.11.5 4 | cache: 5 | directories: 6 | - functions/node_modules 7 | before_install: 8 | - npm i -g npm 9 | deploy: 10 | provider: firebase 11 | skip_cleanup: true 12 | token: 13 | secure: Xtx8rnrw+rR77jqFwUfOkSp/DEIw0X8qSLb9LDoz78FW8r2l3oJaeTx6LQIeFnIDFr0YYlUyGAJptAbLqcJlReSev9qSEkycjdQakG9EF5xB26lNjDFAm9T/Q1Va5dk5EZ+4QocsmE32/Q+H9hVbEFn0rQbsq6wXhr03sYkiGBYMx+qRmSLA5U5Q8tOR3usieZB2Cy+MaAkBCJql4DpfeZcWkjdfrHJ8mDkW0d4rnwc/wfYjpYWY0tYgIkvOKLq8az9Ae8T5gQ5D/u+Zu6jonvz1PqpJksFY8tyyb4vYpCcqEx0k7T+hIQOXC7lfb5UacZQsMu5rXQNb18x8CvpJKTng+a/Jdv2mE1qopHiV/E8p8QvAiINEmkdnlUbsxdP0wZOmZzZaQ3M7OHXRR+4oUz6am3XQF54j3jmgFzEfXNs5JJgNW/HuiSFG2hqrHPQTZJKW0qqNteCwKBGEJU3GMxgABOkcOawX2mgRZzRDhHG1c7W4rgR5XvGIHwXqpIIw5TIIrCKDxxKBYeQ7lTuP5iKVeQOf+OTj+iiWyavYeQTBiX5UIgkWSf9KgbgSMPWZsUAbnXBk59GSn9rWg9R5MGQIjXWSyJSPuLqF50xeqx3pxddo+v6ujIGIqnT4GffAVa50WtjHoXa/1XPVQoAn26px89Ux3CP9vBicJbRg7hM= 14 | project: vanilla-pwa 15 | -------------------------------------------------------------------------------- /components/dynamic-data/template.html: -------------------------------------------------------------------------------- 1 |
2 |

Dynamic data

3 |
4 |

This page will download some dynamic data from a mock backend.

5 |

6 | The data will be cached in your browser's IndexedDB, while the relative images 7 | will be cached in your Cache Storage. After loading the data the first time, you 8 | should be able to see it even when offline. The images are served using a "cache first" 9 | approach; this means that after they are cached the first time they will always be loaded 10 | from the local cache. The data itself is cached the first time, and then it is loaded 11 | from IDB in the subsequent requests. However, each time a request is made, the 12 | Service Worker will still try to fetch the original data and update the IDB cache if needed. 13 | This approach is commonly referred to as "stale while revalidate". 14 |

15 |
16 |
17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help improve this PWA 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /components/utils.js: -------------------------------------------------------------------------------- 1 | export const setupRouter = (cb) => { 2 | document.body.addEventListener('click', e => { 3 | if (e.defaultPrevented || e.button !== 0 || 4 | e.metaKey || e.ctrlKey || e.shiftKey) return; 5 | 6 | const anchor = e.composedPath().filter(n => n.tagName === 'A')[0]; 7 | if (!anchor || anchor.target || 8 | anchor.hasAttribute('download') || 9 | anchor.getAttribute('rel') === 'external') return; 10 | 11 | const href = anchor.href; 12 | if (!href || href.indexOf('mailto:') !== -1) return; 13 | 14 | const location = window.location; 15 | const origin = location.origin || location.protocol + '//' + location.host; 16 | if (href.indexOf(origin) !== 0) return; 17 | 18 | e.preventDefault(); 19 | if (href !== location.href) { 20 | window.history.pushState({}, '', href); 21 | cb(location, e); 22 | } 23 | }); 24 | 25 | window.addEventListener('popstate', e => cb(window.location, e)); 26 | cb(window.location, null); 27 | }; 28 | -------------------------------------------------------------------------------- /cache-manifest.js: -------------------------------------------------------------------------------- 1 | self.precacheManifest = [ 2 | 'index.html', 3 | 'site.webmanifest', 4 | 'components/base.js', 5 | 'components/utils.js', 6 | 'components/shared-styles.css', 7 | 'components/shell/element.js', 8 | 'components/shell/styles.css', 9 | 'components/shell/template.html', 10 | 'components/home/element.js', 11 | 'components/home/styles.css', 12 | 'components/home/template.html', 13 | 'components/dynamic-data/element.js', 14 | 'components/dynamic-data/styles.css', 15 | 'components/dynamic-data/template.html', 16 | 'components/background-sync/element.js', 17 | 'components/background-sync/styles.css', 18 | 'components/background-sync/template.html', 19 | 'components/sensors/element.js', 20 | 'components/sensors/styles.css', 21 | 'components/sensors/template.html', 22 | ]; 23 | 24 | self.runtimeCacheManifest = [ 25 | /placeholder\.pics/, 26 | ]; 27 | 28 | // Each of this entries should contain a capturing group that will be used as an object store on IDB 29 | self.runtimeIDBCacheManifest = [ 30 | /api\/(data)/, 31 | ]; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /components/dynamic-data/element.js: -------------------------------------------------------------------------------- 1 | import BaseElement from '../base.js'; 2 | 3 | class MyDynamicData extends BaseElement { 4 | constructor() { 5 | super('/components/dynamic-data/template.html', '/components/dynamic-data/styles.css'); 6 | } 7 | 8 | connectedCallback() { 9 | super.connectedCallback(); 10 | this.fetchData(); 11 | } 12 | 13 | async fetchData() { 14 | const [{ data }] = await Promise.all([ 15 | fetch('/api/data').then((res) => res.json()), 16 | this.readyPromise, 17 | ]); 18 | const dataContainer = this._root.querySelector('#data-container'); 19 | data.forEach(({ id, title, description, image }) => { 20 | const article = document.createElement('article'); 21 | const img = document.createElement('img'); 22 | img.src = image; 23 | img.alt = img.title = title; 24 | article.appendChild(img); 25 | const div = document.createElement('div'); 26 | const h2 = document.createElement('h2'); 27 | h2.textContent = title; 28 | div.appendChild(h2); 29 | const span = document.createElement('span'); 30 | span.textContent = description; 31 | div.appendChild(span); 32 | article.appendChild(div); 33 | dataContainer.appendChild(article); 34 | }); 35 | } 36 | } 37 | 38 | customElements.define('my-dynamic-data', MyDynamicData); 39 | export default MyDynamicData; 40 | -------------------------------------------------------------------------------- /components/base.js: -------------------------------------------------------------------------------- 1 | const supportsAdoptingStyleSheets = 'adoptedStyleSheets' in Document.prototype; 2 | 3 | class BaseElement extends HTMLElement { 4 | constructor(templateUrl, stylesUrl) { 5 | super(); 6 | this.readyPromise = Promise.all([ 7 | fetch(templateUrl).then((res) => res.text()), 8 | fetch(stylesUrl).then((res) => res.text()), 9 | fetch('/components/shared-styles.css').then((res) => res.text()), 10 | ]).then(([template, styles, sharedStyles]) => { 11 | this._styles = `${sharedStyles} ${styles}`; 12 | this._tmpl = document.createElement('template'); 13 | this._tmpl.innerHTML = `${supportsAdoptingStyleSheets ? '' : ``}${template}`; 14 | this._root = this.attachShadow({ mode: 'open' }); 15 | if (supportsAdoptingStyleSheets) { 16 | this.constructor.prototype.stylesheet = new CSSStyleSheet(); 17 | this.shadowRoot.adoptedStyleSheets = [this.constructor.prototype.stylesheet]; 18 | } 19 | this._root.appendChild(this._tmpl.content.cloneNode(true)); 20 | }); 21 | } 22 | 23 | connectedCallback() { 24 | this.readyPromise.then(() => { 25 | if ( 26 | supportsAdoptingStyleSheets && 27 | this.constructor.prototype.stylesheet && 28 | this.constructor.prototype.stylesheet.cssRules.length === 0 29 | ) { 30 | return this.constructor.prototype.stylesheet.replace(this._styles); 31 | } 32 | }); 33 | } 34 | } 35 | 36 | export default BaseElement; 37 | -------------------------------------------------------------------------------- /components/background-sync/template.html: -------------------------------------------------------------------------------- 1 |
2 |

Background sync

3 |
4 |

5 | This page allows you to send a POST request to a mock backend. The POST request won't be directly 6 | executed by the website, but it will use the Background Sync API to tell the Service Worker 7 | to execute it when possible. When the POST request is made, the server will do some mock heavy, asynchronous 8 | operations that will take random time between 0 and 5 seconds. A push notification will be sent to 9 | advise the user that everything went ok as soon as these operations have been completed. 10 | This approach allows us to build a seamless experience for the user, that is able to send 11 | the request and then close the website, even if the request hasn't been executed yet. 12 |

13 |

First of all, press the button below to enable notifications

14 |
15 | 16 |
17 |

Then, press the button below to send a POST request to the back end

18 |
19 | 20 |
21 |

22 | You should receive a notification when the POST request has been received and handled by the server.
23 | To better understand how background sync works, you might also want to go offline, 24 | press the button to send the POST request, close the website and go back online. You will receive 25 | the notification as soon as the synchronization happens in the background. 26 |

27 |
28 | -------------------------------------------------------------------------------- /components/shared-styles.css: -------------------------------------------------------------------------------- 1 | *, *::before, *::after, :host { 2 | box-sizing: border-box; 3 | } 4 | 5 | main > * { 6 | display: none; 7 | } 8 | 9 | main > [active] { 10 | display: block; 11 | } 12 | 13 | section { 14 | padding: 2rem 3rem; 15 | background: var(--section-bg-color); 16 | color: var(--primary-text-color); 17 | } 18 | 19 | p { 20 | text-align: justify; 21 | } 22 | 23 | hr { 24 | width: calc(100% - 1rem); 25 | border: 0; 26 | padding: .025rem; 27 | margin: .5rem; 28 | background-color: var(--divider-color); 29 | } 30 | 31 | a { 32 | text-decoration: none; 33 | color: var(--accent-color); 34 | } 35 | 36 | a:hover, a:focus { 37 | text-decoration: underline; 38 | } 39 | 40 | button { 41 | font-size: .875rem; 42 | font-weight: 500; 43 | padding: 0 8px 0 8px; 44 | min-width: 64px; 45 | height: 36px; 46 | color: var(--accent-color); 47 | background-color: transparent; 48 | text-decoration: none; 49 | text-transform: uppercase; 50 | border-radius: .125rem; 51 | border: none; 52 | outline: none; 53 | cursor: pointer; 54 | } 55 | 56 | button:hover:not([disabled]) { 57 | text-decoration: underline; 58 | } 59 | 60 | button[disabled] { 61 | cursor: not-allowed; 62 | color: var(--secondary-text-color); 63 | opacity: .5; 64 | } 65 | 66 | @media (min-width: 640px) { 67 | section { 68 | max-width: 37.5rem; 69 | margin: 1.5rem auto; 70 | border-radius: .5rem; 71 | box-shadow: 0 .06rem .065rem 0 rgba(0, 0, 0, 0.14), 0 .003rem .15rem 0 rgba(0, 0, 0, 0.12), 0 .09rem .0035rem -.065rem rgba(0, 0, 0, 0.2); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /functions/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "title": "Random thing 1", 5 | "description": "Just a random redish thing", 6 | "image": "https://placeholder.pics/svg/256/c62828/fff-c62828" 7 | }, 8 | { 9 | "id": 2, 10 | "title": "Random thing 2", 11 | "description": "Just a random purpleish thing", 12 | "image": "https://placeholder.pics/svg/256/6a1b9a/fff-6a1b9a" 13 | }, 14 | { 15 | "id": 3, 16 | "title": "Random thing 3", 17 | "description": "Just a random indigoish thing", 18 | "image": "https://placeholder.pics/svg/256/283593/fff-283593" 19 | }, 20 | { 21 | "id": 4, 22 | "title": "Random thing 4", 23 | "description": "Just a random light blueish thing", 24 | "image": "https://placeholder.pics/svg/256/0277bd/fff-0277bd" 25 | }, 26 | { 27 | "id": 5, 28 | "title": "Random thing 5", 29 | "description": "Just a random tealish thing", 30 | "image": "https://placeholder.pics/svg/256/00695c/fff-00695c" 31 | }, 32 | { 33 | "id": 6, 34 | "title": "Random thing 6", 35 | "description": "Just a random deep orangeish thing", 36 | "image": "https://placeholder.pics/svg/256/bf360c/fff-bf360c" 37 | }, 38 | { 39 | "id": 7, 40 | "title": "Random thing 7", 41 | "description": "Just a random brownish thing", 42 | "image": "https://placeholder.pics/svg/256/4e342e/fff-4e342e" 43 | }, 44 | { 45 | "id": 8, 46 | "title": "Random thing 8", 47 | "description": "Just a random blue greyish thing", 48 | "image": "https://placeholder.pics/svg/256/37474f/fff-37474f" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /generate-firebase-json.js: -------------------------------------------------------------------------------- 1 | const pushManifest = require('./push-manifest.json'); 2 | const { writeFileSync } = require('fs'); 3 | const { resolve } = require('path'); 4 | 5 | const firebaseJson = { 6 | hosting: { 7 | public: '.', 8 | ignore: [ 9 | 'firebase.json', 10 | 'generate-firebase-json.js', 11 | 'README.md', 12 | '**/.*', 13 | '**/node_modules/**/*', 14 | '**/functions/**/*', 15 | ], 16 | rewrites: [ 17 | { 18 | source: '/api/**', 19 | function: 'api', 20 | }, 21 | { 22 | source: '**', 23 | destination: '/index.html', 24 | }, 25 | ], 26 | headers: [ 27 | { 28 | source: '/api/**', 29 | headers: [ 30 | { 31 | key: 'Cache-Control', 32 | value: 'no-cache', 33 | }, 34 | ], 35 | }, 36 | { 37 | source: '**/sw.js', 38 | headers: [ 39 | { 40 | key: 'Service-Worker-Allowed', 41 | value: '/', 42 | }, 43 | { 44 | key: 'Cache-Control', 45 | value: 'no-cache', 46 | }, 47 | ], 48 | }, 49 | { 50 | source: '**', 51 | headers: [ 52 | { 53 | key: 'Cache-Control', 54 | value: 'private, max-age=1209600', 55 | }, 56 | ], 57 | }, 58 | ...Object.entries(pushManifest) 59 | .map(([path, deps]) => ({ 60 | source: path, 61 | headers: [{ 62 | key: 'Link', 63 | value: 64 | Object 65 | .entries(deps) 66 | .reduce((str, [depPath, { type }]) => `${str},;rel=preload;as=${type}`, '') 67 | .substring(1), 68 | }], 69 | })), 70 | ], 71 | }, 72 | }; 73 | 74 | writeFileSync( 75 | resolve(__dirname, 'firebase.json'), 76 | `${JSON.stringify(firebaseJson, null, 2)}\n`, 77 | ); 78 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vanilla PWA 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const functions = require('firebase-functions'); 3 | const bodyParser = require('body-parser'); 4 | const webPush = require('web-push'); 5 | const mockData = require('./data.json'); 6 | 7 | const app = express(); 8 | const api = express.Router(); 9 | api.use(bodyParser.json()); 10 | 11 | webPush.setVapidDetails( 12 | `mailto:${functions.config().webpush.subject}`, 13 | functions.config().webpush.publickey, 14 | functions.config().webpush.privatekey 15 | ); 16 | 17 | api.get('/data', (req, res) => { 18 | res.json({ 19 | success: true, 20 | data: mockData, 21 | }); 22 | }); 23 | 24 | api.post('/post', (req, res) => { 25 | // For this simple example, we require the user to send its subscription data on each request. 26 | // In a real world application, you would have another endpoint where the user would send 27 | // its subscription, and the back end would save it in a DB 28 | if ( 29 | !req.body || 30 | !req.body.subscription || 31 | !req.body.subscription.endpoint || 32 | !req.body.subscription.keys || 33 | !req.body.subscription.keys.p256dh || 34 | !req.body.subscription.keys.auth) { 35 | res.status(400).json({ 36 | success: false, 37 | data: 'Bad Request', 38 | }); 39 | return; 40 | } 41 | // At this point, imagine that the server has to do some kind of asynchronous 42 | // heavy work for us. Instead of blocking us, the server will respond 200 OK 43 | // to tell the client that its request has been enqueued and will be completed 44 | // at some time later on. The client will be notified via web push when the work 45 | // is done. 46 | setTimeout(() => webPush.sendNotification(req.body.subscription, JSON.stringify({ 47 | title: 'Done!', 48 | body: 'Your POST request has been received successfully', 49 | actions: [{ 50 | action: 'dismiss', 51 | title: 'Dismiss', 52 | }, { 53 | action: 'open-background-sync-page', 54 | title: 'Send another', 55 | }], 56 | // The async heavy work will finish randomly in a time span between 0 and 5 seconds 57 | })), Math.random() * 5000); 58 | res.json({ 59 | success: true, 60 | data: 'OK', 61 | }); 62 | }); 63 | 64 | app.use('/api', api); 65 | 66 | exports.api = functions.https.onRequest(app); 67 | -------------------------------------------------------------------------------- /components/home/template.html: -------------------------------------------------------------------------------- 1 |
2 |

Home

3 |
4 |

Is it possible to write a Progressive Web App without using any library, framework and build tool? Yes, it 5 | is, and that's exactly what Vanilla PWA is. Of course it isn't meant to be used for an important project, 6 | since only a few browsers support the technologies used to build it, but it is a great way to understand how the 7 | great things modern frameworks provide work.

8 |

These are the native modern technologies used in this PWA:

9 | 30 |

Take a look at its source code to check 31 | how the magic happens!

32 |
33 | -------------------------------------------------------------------------------- /components/shell/template.html: -------------------------------------------------------------------------------- 1 |
2 |

Vanilla PWA

3 | 4 | 6 | 7 | 13 |
14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 | 24 | 38 | 45 | 46 | 48 | 49 | -------------------------------------------------------------------------------- /components/sensors/template.html: -------------------------------------------------------------------------------- 1 |
2 |

Sensors

3 |
4 |

5 | This page allows you to test your device sensors, which can be accessed through 6 | the Generic Sensor API. 7 |

8 |

Linear acceleration

9 |

10 | The linear acceleration sensor measures acceleration that is applied to the 11 | device hosting the sensor, excluding the contribution of a gravity force. 12 | When a device is at rest, for instance, lying flat on the table, the sensor 13 | would measure ≈ 0 m/s2 acceleration on three axes. 14 |

15 |

16 |

Gyroscope

17 |

18 | The Gyroscope sensor measures angular velocity in rad/s around the device's 19 | local X, Y and Z axis. Most of the consumer devices have mechanical (MEMS) 20 | gyroscopes, which are inertial sensors that measure rotation rate based on 21 | inertial Coriolis force. A MEMS gyroscopes are prone to drift that is caused 22 | by sensor's gravitational sensitivity which deforms the sensor's internal 23 | mechanical system. Gyroscopes oscillate at relative high frequencies, e.g., 24 | 10's of kHz, and therefore, might consume more power compared to other sensors. 25 |

26 |

27 |

Absolute orientation

28 |

29 | The absolute orientation sensor is a fusion sensor that measures rotation of 30 | a device in relation to the Earth’s coordinate system. 31 |

32 |

33 |

Relative orientation

34 |

35 | The relative orientation sensor provides data representing rotation of a device 36 | hosting motion sensors in relation to a stationary reference coordinate system. 37 |

38 |

39 |

Ambient light

40 |

41 | The ambient light sensor provides information about ambient light levels, as 42 | detected by the device’s main light detector, in terms of lux units. 43 |

44 |

45 |

Magnetometer

46 |

47 | The magnetometer provides information about the magnetic field as detected by 48 | the device's primary magnetometer sensor. The magnetometer sensor measures the 49 | magnetic field for all three physical axes (x, y, z) in μT (micro Tesla). 50 |

51 |

52 |
53 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Vanilla PWA 2 | 3 | First off, thanks for taking the time to contribute! 4 | 5 | The following is a set of guidelines for contributing to this PWA. These are mostly guidelines, not rules. 6 | Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | ### How can I contribute? 9 | 10 | #### Reporting bugs 11 | Before reporting a bug, please make sure it hasn't already been reported by visiting the 12 | [issue section](https://github.com/Dabolus/vanilla-pwa/issues). 13 | 14 | If the bug you found hasn't been reported yet, create a new issue and assign it the proper label(s). 15 | Besides this, there isn't any specific guideline on how the bugs should be reported, Just be sure 16 | to be as clear as possible when describing it. 17 | 18 | #### Suggesting enhancements 19 | Same as the bug reporting. First of all, check if the enhancement has already been suggested. 20 | If it doesn't exist, create a new issue and give it the `enhancement` label, plus any other proper label. 21 | 22 | Keep in mind that what you may find useful might be completely useless for other users, 23 | so please, make sure that the enhancement can actually be useful for everyone before proposing it. 24 | If you find that it is actually useful only for you, consider forking the project and implementing that 25 | enhancement just for yourself. 26 | 27 | ### Styleguides 28 | 29 | #### Commit messages 30 | - Start the commit message with the type of commit, that can be one of the following: 31 | - **feat:** a new feature 32 | - **fix:** a bug fix 33 | - **docs:** changes to documentation 34 | - **style:** formatting, missing semi colons, etc; no code change 35 | - **refactor:** refactoring production code 36 | - **chore:** updating Firebase configuration or any other configuration in general; no production code change 37 | - Use the present tense ("Add feature" not "Added feature") 38 | - The type of the message has to be all lowercase; the rest of the message has to start with an uppercase letter 39 | - Limit the first line to 72 characters or less 40 | - Reference issues and pull requests liberally after the first line 41 | - When only changing documentation, include `[ci skip]` in the commit title 42 | 43 | An example of a great commit message might be: 44 | 45 | `docs: Fix typo in the readme [ci skip]` 46 | 47 | #### Pull Requests 48 | - Specify what has been changed/added/removed 49 | - Write a short and concise title. Be more specific in the description 50 | - Do not include issue numbers in the PR title; you have to add them at the bottom of the description 51 | - End all files with a newline 52 | -------------------------------------------------------------------------------- /db-helpers.js: -------------------------------------------------------------------------------- 1 | self.dbName = 'vanilla-pwa-db'; 2 | self.dbVersion = 1; 3 | 4 | // Opens the DB, adding the necessary object stores if needed. 5 | // Returns a promise that resolves with the DB if everything goes well. 6 | self.openDB = () => 7 | new Promise((resolve, reject) => { 8 | const openDbReq = indexedDB.open(self.dbName, self.dbVersion); 9 | openDbReq.onupgradeneeded = ({ oldVersion }) => { 10 | const db = openDbReq.result; 11 | switch (oldVersion) { 12 | // 0 = the DB has not been touched yet 13 | case 0: 14 | db.createObjectStore('data', { keyPath: 'id' }); 15 | db.createObjectStore('fetch-queue', { 16 | keyPath: 'id', 17 | autoIncrement: true, 18 | }); 19 | } 20 | }; 21 | openDbReq.onsuccess = () => resolve(openDbReq.result); 22 | openDbReq.onerror = reject; 23 | }); 24 | 25 | // Adds an object or an array of objects to the specified object store. 26 | // Returns a promise with the result of the transaction if everything goes well. 27 | self.putIntoDB = (objectStore, objs) => 28 | Promise.all((Array.isArray(objs) ? objs : [objs]).map(obj => 29 | self.openDB() 30 | .then((db) => new Promise((resolve, reject) => { 31 | const req = 32 | db.transaction(objectStore, 'readwrite') 33 | .objectStore(objectStore) 34 | .put(obj); 35 | req.onsuccess = () => resolve(req.result); 36 | req.onerror = reject; 37 | }), 38 | ))); 39 | 40 | // Given an ID or an array of IDs, removes all the objects matching that ID(s) from the specified object store. 41 | // Returns a promise with the result of the transaction if everything goes well. 42 | self.removeFromDB = (objectStore, ids) => 43 | Promise.all((Array.isArray(ids) ? ids : [ids]).map(id => 44 | self.openDB() 45 | .then((db) => new Promise((resolve, reject) => { 46 | const req = 47 | db.transaction(objectStore, 'readwrite') 48 | .objectStore(objectStore) 49 | .delete(id); 50 | req.onsuccess = () => resolve(req.result); 51 | req.onerror = reject; 52 | }), 53 | ))); 54 | 55 | // Gets all the objects contained in the specified object store. 56 | // Returns a promise that resolves to the array of objects if everything goes well. 57 | self.getAllFromDB = (objectStore) => 58 | self.openDB() 59 | .then((db) => new Promise((resolve, reject) => { 60 | const req = db.transaction(objectStore).objectStore(objectStore).getAll(); 61 | req.onsuccess = () => resolve(req.result); 62 | req.onerror = reject; 63 | })); 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Vanilla PWA 2 | 3 | Is it possible to write a Progressive Web App without using any library, framework and build tool? **Yes, it is**, and that's exactly what Vanilla PWA is. Of course it isn't meant to be used for an important project, since only a few browsers support the technologies used to build it, but it is a great way to understand how the great things modern frameworks provide work. 4 | 5 | These are the native modern technologies used in this PWA: 6 | 7 | - [HTML Templates](https://html.spec.whatwg.org/multipage/scripting.html#the-template-element) 8 | - [CSS Variables](https://www.w3.org/TR/css-variables/) 9 | - [Level 5 CSS Media Queries](https://drafts.csswg.org/mediaqueries-5/) 10 | - [Custom Elements](https://html.spec.whatwg.org/multipage/custom-elements.html) 11 | - [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) 12 | - [ES Modules](https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-module-system) 13 | - [Service Worker API](https://w3c.github.io/ServiceWorker) 14 | - [Fetch API](https://fetch.spec.whatwg.org) 15 | - [Notifications API](https://notifications.spec.whatwg.org) 16 | - [Storage API (Cache API, IndexedDB)](https://storage.spec.whatwg.org) 17 | - [Background Sync API](https://wicg.github.io/BackgroundSync/spec/) 18 | - [Web Share API](https://wicg.github.io/web-share/) 19 | - Many ES6/7/8 features ([async functions](https://tc39.github.io/ecmascript-asyncawait/), [object rest/spread](https://tc39.github.io/proposal-object-rest-spread/), etc.) 20 | 21 | ### Testing locally 22 | 23 | Most of the PWA can be tested by simply serving the content of this directory. 24 | However, some more complicated examples (dynamic data caching, push notifications and background sync) require a mock back end that takes the requests and does some server side work. In case you want to test that features too, you have two possibilities: 25 | 26 | - Create your own mock back end (as you can see from `functions/index.js` it is incredibly simple) 27 | - Use Firebase to test the project like I do 28 | 29 | In both cases, you will need to configure web push with a public and a private key, otherwise the notifications won't work. The simplest way to obtain your configuration is to head over [this website](https://web-push-codelab.glitch.me/). 30 | 31 | If you are using your own back end, I highly suggest you to save this data in 32 | some environment variables. If you are using Firebase, you will need to create a `.runtimeconfig.json` file in the `functions` directory following this format: 33 | ```json 34 | { 35 | "webpush": { 36 | "subject": "your email", 37 | "publickey": "your public key", 38 | "privatekey": "your private key" 39 | } 40 | } 41 | ``` 42 | Note: **this only applies to local development**. If you want to deploy the PWA, be sure to ignore this file and execute the following commands to set the variables instead: 43 | ```bash 44 | $ firebase functions:config:set webpush.subject="your email" 45 | $ firebase functions:config:set webpush.publickey="your public key" 46 | $ firebase functions:config:set webpush.privatekey="your private key" 47 | ``` 48 | 49 | Now you are ready to go! With Firebase, just use `firebase serve` to test the entire environment (hosting + functions) locally. 50 | -------------------------------------------------------------------------------- /components/sensors/element.js: -------------------------------------------------------------------------------- 1 | import BaseElement from '../base.js'; 2 | 3 | class MySensors extends BaseElement { 4 | constructor() { 5 | super('/components/sensors/template.html', '/components/sensors/styles.css'); 6 | } 7 | 8 | connectedCallback() { 9 | super.connectedCallback(); 10 | this.setupSensors(); 11 | } 12 | 13 | async setupSensors() { 14 | await this.readyPromise; 15 | if ('LinearAccelerationSensor' in window) { 16 | const acceleration = new LinearAccelerationSensor({ frequency: 60 }); 17 | const accelerationData = this._root.querySelector('#acceleration-data'); 18 | acceleration.addEventListener('reading', () => 19 | accelerationData.innerHTML = 20 | `x: ${acceleration.x}
` + 21 | `y: ${acceleration.y}
` + 22 | `z: ${acceleration.z}`); 23 | acceleration.start(); 24 | } 25 | 26 | if ('Gyroscope' in window) { 27 | const gyroscope = new Gyroscope(); 28 | const gyroscopeData = this._root.querySelector('#gyroscope-data'); 29 | gyroscope.addEventListener('reading', () => 30 | gyroscopeData.innerHTML = 31 | `x: ${gyroscope.x}
` + 32 | `y: ${gyroscope.y}
` + 33 | `z: ${gyroscope.z}`); 34 | gyroscope.start(); 35 | } 36 | 37 | if ('AbsoluteOrientationSensor' in window) { 38 | const absOrientation = new AbsoluteOrientationSensor(); 39 | const absOrientationData = this._root.querySelector('#abs-orientation-data'); 40 | absOrientation.addEventListener('reading', () => 41 | absOrientationData.innerHTML = 42 | `quaternion: ${absOrientation.quaternion}`); 43 | absOrientation.start(); 44 | } 45 | 46 | if ('RelativeOrientationSensor' in window) { 47 | const relOrientation = new RelativeOrientationSensor(); 48 | const relOrientationData = this._root.querySelector('#rel-orientation-data'); 49 | let rotationMatrix = new Float32Array(16); 50 | relOrientation.addEventListener('reading', () => { 51 | relOrientation.populateMatrix(rotationMatrix); 52 | relOrientationData.innerHTML = 53 | `rotation matrix: ${rotationMatrix}`; 54 | }); 55 | relOrientation.start(); 56 | } 57 | 58 | if ('AmbientLightSensor' in window) { 59 | const ambientLight = new AmbientLightSensor(); 60 | const ambientLightData = this._root.querySelector('#ambient-light-data'); 61 | sensor.addEventListener('reading', () => 62 | ambientLightData.innerHTML = 63 | `illuminance: ${ambientLight.illuminance}`); 64 | ambientLight.start(); 65 | } 66 | 67 | if ('Magnetometer' in window) { 68 | const magnetometer = new Magnetometer(); 69 | const magnetometerData = this._root.querySelector('#magnetometer-data'); 70 | magnetometer.addEventListener('reading', () => 71 | magnetometerData.innerHTML = 72 | `x: ${magnetometer.x}
` + 73 | `y: ${magnetometer.y}
` + 74 | `z: ${magnetometer.z}`); 75 | magnetometer.start(); 76 | } 77 | } 78 | } 79 | 80 | customElements.define('my-sensors', MySensors); 81 | export default MySensors; 82 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@dabolus.app. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /components/background-sync/element.js: -------------------------------------------------------------------------------- 1 | import BaseElement from '../base.js'; 2 | import '../../db-helpers.js'; 3 | 4 | class MyBackgroundSync extends BaseElement { 5 | constructor() { 6 | super('/components/background-sync/template.html', '/components/background-sync/styles.css'); 7 | } 8 | 9 | connectedCallback() { 10 | super.connectedCallback(); 11 | this.prepareExperiment(); 12 | } 13 | 14 | async prepareExperiment() { 15 | await this.readyPromise; 16 | this.notificationsButton = this._root.querySelector('#notifications-button'); 17 | this.postButton = this._root.querySelector('#post-button'); 18 | this.notificationsButton.addEventListener('click', () => this.askForNotifications()); 19 | this.postButton.addEventListener('click', () => this.postToServer()); 20 | this.updateButtons(); 21 | } 22 | 23 | async askForNotifications() { 24 | if (this.pushSubscription) { 25 | return; 26 | } 27 | const registration = await navigator.serviceWorker.getRegistration('/'); 28 | if (!registration) { 29 | return; 30 | } 31 | this.pushSubscription = await registration.pushManager.subscribe({ 32 | userVisibleOnly: true, 33 | // Insert your public key here 34 | applicationServerKey: this.urlBase64ToUint8Array('BBs97jHBz23RRCZEPMAmA3VJe4QG7U0tHwBx7WnrChqQtu5s1RQrv-ij7fBfaS7Js176XX2TEGjUWbI0zjxR_G4'), 35 | }).catch(() => this.updateButtons('denied')); 36 | this.updateButtons(Notification.permission); 37 | } 38 | 39 | updateButtons(status) { 40 | switch (status) { 41 | case 'denied': 42 | this.notificationsButton.textContent = this.postButton.textContent = 'Notifications denied'; 43 | this.notificationsButton.disabled = this.postButton.disabled = true; 44 | break; 45 | case 'granted': 46 | this.notificationsButton.textContent = 'Notifications allowed'; 47 | this.notificationsButton.disabled = true; 48 | this.postButton.textContent = 'Send POST request'; 49 | this.postButton.disabled = false; 50 | break; 51 | case 'sent': 52 | this.postButton.textContent = 'POST request sent'; 53 | this.postButton.disabled = true; 54 | break; 55 | default: 56 | this.notificationsButton.textContent = 'Enable notifications'; 57 | this.notificationsButton.disabled = false; 58 | this.postButton.textContent = 'You have to enable the notifications first'; 59 | this.postButton.disabled = true; 60 | } 61 | } 62 | 63 | urlBase64ToUint8Array(base64String) { 64 | const padding = '='.repeat((4 - base64String.length % 4) % 4); 65 | const base64 = (base64String + padding) 66 | .replace(/-/g, '+') 67 | .replace(/_/g, '/'); 68 | 69 | const rawData = window.atob(base64); 70 | const outputArray = new Uint8Array(rawData.length); 71 | 72 | for (let i = 0; i < rawData.length; ++i) { 73 | outputArray[i] = rawData.charCodeAt(i); 74 | } 75 | return outputArray; 76 | } 77 | 78 | async postToServer() { 79 | if (Notification.permission !== 'granted' || !this.pushSubscription || this.postRequestSent) { 80 | return; 81 | } 82 | if (!navigator.serviceWorker.controller) { 83 | return; 84 | } 85 | const registration = await navigator.serviceWorker.getRegistration('/'); 86 | if (!registration) { 87 | return; 88 | } 89 | // Add the message to the queue on IDB 90 | await self.putIntoDB('fetch-queue', { 91 | url: '/api/post', 92 | method: 'POST', 93 | headers: { 94 | 'Accept': 'application/json', 95 | 'Content-Type': 'application/json', 96 | }, 97 | body: JSON.stringify({ 98 | subscription: this.pushSubscription.toJSON(), 99 | }), 100 | }); 101 | // Tell the Service Worker to fire the background sync with the "fetch" tag 102 | await registration.sync.register('fetch'); 103 | this.postRequestSent = true; 104 | this.updateButtons('sent'); 105 | } 106 | } 107 | 108 | customElements.define('my-background-sync', MyBackgroundSync); 109 | export default MyBackgroundSync; 110 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": ".", 4 | "ignore": [ 5 | "firebase.json", 6 | "generate-firebase-json.js", 7 | "README.md", 8 | "**/.*", 9 | "**/node_modules/**/*", 10 | "**/functions/**/*" 11 | ], 12 | "rewrites": [ 13 | { 14 | "source": "/api/**", 15 | "function": "api" 16 | }, 17 | { 18 | "source": "**", 19 | "destination": "/index.html" 20 | } 21 | ], 22 | "headers": [ 23 | { 24 | "source": "/api/**", 25 | "headers": [ 26 | { 27 | "key": "Cache-Control", 28 | "value": "no-cache" 29 | } 30 | ] 31 | }, 32 | { 33 | "source": "**/sw.js", 34 | "headers": [ 35 | { 36 | "key": "Service-Worker-Allowed", 37 | "value": "/" 38 | }, 39 | { 40 | "key": "Cache-Control", 41 | "value": "no-cache" 42 | } 43 | ] 44 | }, 45 | { 46 | "source": "**", 47 | "headers": [ 48 | { 49 | "key": "Cache-Control", 50 | "value": "private, max-age=1209600" 51 | } 52 | ] 53 | }, 54 | { 55 | "source": "/", 56 | "headers": [ 57 | { 58 | "key": "Link", 59 | "value": ";rel=preload;as=script,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document" 60 | } 61 | ] 62 | }, 63 | { 64 | "source": "/home", 65 | "headers": [ 66 | { 67 | "key": "Link", 68 | "value": ";rel=preload;as=script,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document" 69 | } 70 | ] 71 | }, 72 | { 73 | "source": "/dynamic-data", 74 | "headers": [ 75 | { 76 | "key": "Link", 77 | "value": ";rel=preload;as=script,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document" 78 | } 79 | ] 80 | }, 81 | { 82 | "source": "/background-sync", 83 | "headers": [ 84 | { 85 | "key": "Link", 86 | "value": ";rel=preload;as=script,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document" 87 | } 88 | ] 89 | }, 90 | { 91 | "source": "/sensors", 92 | "headers": [ 93 | { 94 | "key": "Link", 95 | "value": ";rel=preload;as=script,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document,;rel=preload;as=script,;rel=preload;as=style,;rel=preload;as=document" 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /components/shell/element.js: -------------------------------------------------------------------------------- 1 | import BaseElement from '../base.js'; 2 | import { setupRouter } from '../utils.js'; 3 | 4 | class MyShell extends BaseElement { 5 | get pages() { 6 | return { 7 | 'home': 'Home', 8 | 'dynamic-data': 'Dynamic data', 9 | 'background-sync': 'Background sync', 10 | 'sensors': 'Sensors', 11 | }; 12 | }; 13 | 14 | constructor() { 15 | super('/components/shell/template.html', '/components/shell/styles.css'); 16 | this.setTheme(localStorage.getItem('theme') || 'light'); 17 | this.page = 'home'; 18 | } 19 | 20 | async connectedCallback() { 21 | super.connectedCallback(); 22 | setupRouter((location) => this.navigateTo(location)); 23 | await this.readyPromise; 24 | this._updateNotification = this._root.querySelector('#update-notification'); 25 | this._themesDialog = this._root.querySelector('#themes-dialog'); 26 | this._root.querySelector('#ignore').addEventListener('click', () => this.toggleUpdateNotification(false)); 27 | this._root.querySelector('#update-now').addEventListener('click', () => this.update()); 28 | this._root.querySelector('#theme-selector').addEventListener('click', () => this.toggleThemesDialog()); 29 | this._themesDialog.addEventListener('click', () => this.toggleThemesDialog()); 30 | this._root.querySelectorAll('#themes > li') 31 | .forEach((li) => li.addEventListener('click', () => this.setTheme(li.dataset.theme))); 32 | this._root.querySelector('#share-button').addEventListener('click', () => this.share()); 33 | this.checkUpdates(); 34 | } 35 | 36 | setTheme(theme) { 37 | this.setAttribute('theme', theme); 38 | localStorage.setItem('theme', theme); 39 | } 40 | 41 | share() { 42 | navigator.share({ 43 | title: document.title, 44 | text: `Check out ${this.pages[this.page]} on Vanilla PWA!`, 45 | url: window.location.href, 46 | }); 47 | } 48 | 49 | async navigateTo({ pathname }) { 50 | let path = decodeURIComponent(pathname).substring(1); 51 | if (!path || !Object.keys(this.pages).includes(path)) { 52 | history.replaceState({}, '', '/home'); 53 | path = 'home'; 54 | } 55 | const elem = await import(`../${path}/element.js`); 56 | this.page = path; 57 | // At this point the element should already be ready, 58 | // but we await its promise just to be sure. 59 | await this.readyPromise; 60 | for (const link of this._root.querySelectorAll('nav > *')) { 61 | if (link.getAttribute('href') === `/${path}`) { 62 | link.setAttribute('active', ''); 63 | } else { 64 | link.removeAttribute('active'); 65 | } 66 | } 67 | for (const page of this._root.querySelectorAll('main > *')) { 68 | if (Object.getPrototypeOf(page).constructor.name === elem.default.prototype.constructor.name) { 69 | page.setAttribute('active', ''); 70 | } else { 71 | page.removeAttribute('active'); 72 | } 73 | } 74 | document.title = `Vanilla PWA - ${this.pages[path]}`; 75 | } 76 | 77 | toggleUpdateNotification(state) { 78 | const shouldShow = typeof state === 'undefined' ? !this._updateNotification.className : state; 79 | this._updateNotification.className = shouldShow ? 'active' : ''; 80 | } 81 | 82 | toggleThemesDialog(state) { 83 | const shouldShow = typeof state === 'undefined' ? !this._themesDialog.className : state; 84 | this._themesDialog.className = shouldShow ? 'active' : ''; 85 | } 86 | 87 | async checkUpdates() { 88 | if (!navigator.serviceWorker.controller) { 89 | return; 90 | } 91 | const registration = await navigator.serviceWorker.getRegistration('/'); 92 | if (!registration) { 93 | return; 94 | } 95 | if (registration.waiting) { 96 | this.newSw = registration.waiting; 97 | this.toggleUpdateNotification(true); 98 | return; 99 | } 100 | if (registration.installing) { 101 | this.trackInstallation(registration.installing); 102 | return; 103 | } 104 | registration.addEventListener('updatefound', () => 105 | this.trackInstallation(registration.installing)); 106 | navigator.serviceWorker.addEventListener('controllerchange', () => 107 | window.location.reload()); 108 | } 109 | 110 | trackInstallation(sw) { 111 | sw.addEventListener('statechange', () => { 112 | if (sw.state === 'installed') { 113 | this.newSw = sw; 114 | this.toggleUpdateNotification(true); 115 | } 116 | }); 117 | } 118 | 119 | update() { 120 | this.toggleUpdateNotification(false); 121 | if (!this.newSw) { 122 | return; 123 | } 124 | this.newSw.postMessage({ action: 'update' }); 125 | } 126 | } 127 | 128 | customElements.define('my-shell', MyShell); 129 | export default MyShell; 130 | -------------------------------------------------------------------------------- /push-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/": { 3 | "components/base.js": { 4 | "type": "script", 5 | "weight": 1 6 | }, 7 | "components/utils.js": { 8 | "type": "script", 9 | "weight": 1 10 | }, 11 | "components/shared-styles.css": { 12 | "type": "style", 13 | "weight": 1 14 | }, 15 | "components/shell/element.js": { 16 | "type": "script", 17 | "weight": 1 18 | }, 19 | "components/shell/styles.css": { 20 | "type": "style", 21 | "weight": 1 22 | }, 23 | "components/shell/template.html": { 24 | "type": "document", 25 | "weight": 1 26 | }, 27 | "components/home/element.js": { 28 | "type": "script", 29 | "weight": 1 30 | }, 31 | "components/home/styles.css": { 32 | "type": "style", 33 | "weight": 1 34 | }, 35 | "components/home/template.html": { 36 | "type": "document", 37 | "weight": 1 38 | } 39 | }, 40 | "/home": { 41 | "components/base.js": { 42 | "type": "script", 43 | "weight": 1 44 | }, 45 | "components/utils.js": { 46 | "type": "script", 47 | "weight": 1 48 | }, 49 | "components/shared-styles.css": { 50 | "type": "style", 51 | "weight": 1 52 | }, 53 | "components/shell/element.js": { 54 | "type": "script", 55 | "weight": 1 56 | }, 57 | "components/shell/styles.css": { 58 | "type": "style", 59 | "weight": 1 60 | }, 61 | "components/shell/template.html": { 62 | "type": "document", 63 | "weight": 1 64 | }, 65 | "components/home/element.js": { 66 | "type": "script", 67 | "weight": 1 68 | }, 69 | "components/home/styles.css": { 70 | "type": "style", 71 | "weight": 1 72 | }, 73 | "components/home/template.html": { 74 | "type": "document", 75 | "weight": 1 76 | } 77 | }, 78 | "/dynamic-data": { 79 | "components/base.js": { 80 | "type": "script", 81 | "weight": 1 82 | }, 83 | "components/utils.js": { 84 | "type": "script", 85 | "weight": 1 86 | }, 87 | "components/shared-styles.css": { 88 | "type": "style", 89 | "weight": 1 90 | }, 91 | "components/shell/element.js": { 92 | "type": "script", 93 | "weight": 1 94 | }, 95 | "components/shell/styles.css": { 96 | "type": "style", 97 | "weight": 1 98 | }, 99 | "components/shell/template.html": { 100 | "type": "document", 101 | "weight": 1 102 | }, 103 | "components/dynamic-data/element.js": { 104 | "type": "script", 105 | "weight": 1 106 | }, 107 | "components/dynamic-data/styles.css": { 108 | "type": "style", 109 | "weight": 1 110 | }, 111 | "components/dynamic-data/template.html": { 112 | "type": "document", 113 | "weight": 1 114 | } 115 | }, 116 | "/background-sync": { 117 | "components/base.js": { 118 | "type": "script", 119 | "weight": 1 120 | }, 121 | "components/utils.js": { 122 | "type": "script", 123 | "weight": 1 124 | }, 125 | "components/shared-styles.css": { 126 | "type": "style", 127 | "weight": 1 128 | }, 129 | "components/shell/element.js": { 130 | "type": "script", 131 | "weight": 1 132 | }, 133 | "components/shell/styles.css": { 134 | "type": "style", 135 | "weight": 1 136 | }, 137 | "components/shell/template.html": { 138 | "type": "document", 139 | "weight": 1 140 | }, 141 | "components/background-sync/element.js": { 142 | "type": "script", 143 | "weight": 1 144 | }, 145 | "components/background-sync/styles.css": { 146 | "type": "style", 147 | "weight": 1 148 | }, 149 | "components/background-sync/template.html": { 150 | "type": "document", 151 | "weight": 1 152 | } 153 | }, 154 | "/sensors": { 155 | "components/base.js": { 156 | "type": "script", 157 | "weight": 1 158 | }, 159 | "components/utils.js": { 160 | "type": "script", 161 | "weight": 1 162 | }, 163 | "components/shared-styles.css": { 164 | "type": "style", 165 | "weight": 1 166 | }, 167 | "components/shell/element.js": { 168 | "type": "script", 169 | "weight": 1 170 | }, 171 | "components/shell/styles.css": { 172 | "type": "style", 173 | "weight": 1 174 | }, 175 | "components/shell/template.html": { 176 | "type": "document", 177 | "weight": 1 178 | }, 179 | "components/sensors/element.js": { 180 | "type": "script", 181 | "weight": 1 182 | }, 183 | "components/sensors/styles.css": { 184 | "type": "style", 185 | "weight": 1 186 | }, 187 | "components/sensors/template.html": { 188 | "type": "document", 189 | "weight": 1 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /images/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/shell/styles.css: -------------------------------------------------------------------------------- 1 | :host { 2 | --primary-color: #2e7d32; 3 | --accent-color: #d50000; 4 | --primary-text-on-primary: #fff; 5 | --secondary-text-on-primary: rgba(255, 255, 255, .75); 6 | --primary-text-color: rgba(0, 0, 0, .87); 7 | --secondary-text-color: rgba(0, 0, 0, .75); 8 | --divider-color: rgba(0, 0, 0, .12); 9 | --section-bg-color: #fefefe; 10 | --background-color: #f0f0f0; 11 | 12 | display: flex; 13 | flex-direction: column; 14 | min-height: 100%; 15 | background: var(--background-color); 16 | } 17 | 18 | :host([theme='dark']) { 19 | --primary-color: #2e7d32; 20 | --accent-color: #d50000; 21 | --primary-text-on-primary: #fff; 22 | --secondary-text-on-primary: rgba(255, 255, 255, .75); 23 | --primary-text-color: rgba(255, 255, 255, .87); 24 | --secondary-text-color: rgba(255, 255, 255, .75); 25 | --divider-color: rgba(255, 255, 255, .12); 26 | --section-bg-color: #212121; 27 | --background-color: #2f2f2f; 28 | } 29 | 30 | :host([theme='negative']) { 31 | filter: invert(100%); 32 | } 33 | 34 | :host([theme='grayscale']) { 35 | filter: grayscale(100%); 36 | } 37 | 38 | :host([theme='sepia']) { 39 | filter: sepia(100%); 40 | } 41 | 42 | :host([theme='hdr']) { 43 | filter: saturate(300%); 44 | } 45 | 46 | /* Fun fact: the first media query exists, but it hasn't been implemented by any browser yet. 47 | * This means that for now the "auto" theme won't change with the light level, but only with 48 | * the user selected system-wide theme. */ 49 | @media (light-level: dim), (prefers-color-scheme: dark) { 50 | :host([theme='auto']) { 51 | --primary-color: #2e7d32; 52 | --accent-color: #d50000; 53 | --primary-text-on-primary: #fff; 54 | --secondary-text-on-primary: rgba(255, 255, 255, .75); 55 | --primary-text-color: rgba(255, 255, 255, .87); 56 | --secondary-text-color: rgba(255, 255, 255, .75); 57 | --divider-color: rgba(255, 255, 255, .12); 58 | --section-bg-color: #212121; 59 | --background-color: #2f2f2f; 60 | } 61 | } 62 | 63 | header { 64 | position: sticky; 65 | width: 100%; 66 | height: 7rem; 67 | top: -4rem; 68 | left: 0; 69 | display: flex; 70 | flex-direction: column; 71 | align-items: center; 72 | justify-content: center; 73 | background: var(--primary-color); 74 | } 75 | 76 | header::after { 77 | position: absolute; 78 | right: 0; 79 | bottom: -.3125rem; 80 | left: 0; 81 | width: 100%; 82 | height: .3125rem; 83 | content: ''; 84 | pointer-events: none; 85 | box-shadow: inset 0 .3125rem .375rem -.1875rem rgba(0, 0, 0, .4); 86 | } 87 | 88 | header > h1 { 89 | font-size: 1.5rem; 90 | color: var(--primary-text-on-primary); 91 | } 92 | 93 | nav { 94 | width: 100%; 95 | height: 3rem; 96 | text-align: center; 97 | overflow-x: auto; 98 | white-space: nowrap; 99 | } 100 | 101 | nav > a { 102 | text-decoration: none; 103 | text-transform: uppercase; 104 | color: var(--secondary-text-on-primary); 105 | display: inline-block; 106 | padding: .5rem 1rem; 107 | } 108 | 109 | nav a[active] { 110 | color: var(--primary-text-on-primary); 111 | font-weight: bold; 112 | border-bottom: .1875rem solid var(--accent-color); 113 | } 114 | 115 | nav a:hover, nav a:focus { 116 | text-decoration: none; 117 | } 118 | 119 | #spacing { 120 | flex: 1 1 auto; 121 | } 122 | 123 | footer { 124 | position: relative; 125 | display: flex; 126 | align-items: center; 127 | justify-content: center; 128 | width: 100%; 129 | background: var(--section-bg-color); 130 | color: var(--secondary-text-color); 131 | padding: 1.5rem; 132 | } 133 | 134 | footer::before { 135 | position: absolute; 136 | right: 0; 137 | top: -.3125rem; 138 | left: 0; 139 | width: 100%; 140 | height: .3125rem; 141 | content: ''; 142 | pointer-events: none; 143 | box-shadow: inset 0 -.3125rem .375rem -.375rem rgba(0, 0, 0, .4); 144 | } 145 | 146 | footer a, footer b { 147 | color: var(--primary-text-color); 148 | font-weight: bold; 149 | } 150 | 151 | #update-notification { 152 | position: fixed; 153 | bottom: 0; 154 | left: 0; 155 | right: 0; 156 | padding: .5rem 1.25rem; 157 | background-color: var(--section-bg-color); 158 | color: var(--primary-text-color); 159 | box-shadow: 0 .24rem .26rem 0 rgba(0, 0, 0, 0.14), 0 .012rem .6rem 0 rgba(0, 0, 0, 0.12), 0 .36rem .014rem -.26rem rgba(0, 0, 0, 0.2); 160 | will-change: transform; 161 | transform: translateY(100%); 162 | transition-property: visibility, transform; 163 | transition-duration: .3s; 164 | visibility: hidden; 165 | z-index: 2; 166 | } 167 | 168 | #update-notification.active { 169 | visibility: visible; 170 | transform: translateY(0); 171 | } 172 | 173 | #update-notification .text { 174 | padding: .75rem 1.5rem .75rem .75rem; 175 | display: flex; 176 | align-items: center; 177 | } 178 | 179 | #update-notification .actions { 180 | display: flex; 181 | justify-content: flex-end; 182 | flex-wrap: wrap; 183 | } 184 | 185 | #update-notification .actions button:not(#update-now) { 186 | color: var(--secondary-text-color); 187 | } 188 | 189 | #theme-selector { 190 | cursor: pointer; 191 | fill: var(--primary-text-on-primary); 192 | width: 1.5rem; 193 | height: 1.5rem; 194 | position: absolute; 195 | right: 1rem; 196 | top: 1.3rem; 197 | } 198 | 199 | #themes-dialog { 200 | cursor: pointer; 201 | position: fixed; 202 | top: 0; 203 | left: 0; 204 | width: 100%; 205 | height: 100%; 206 | background: transparent; 207 | z-index: -1; 208 | will-change: background, z-index; 209 | transition: .3s background, .3s z-index step-end; 210 | display: flex; 211 | align-items: center; 212 | justify-content: center; 213 | } 214 | 215 | #themes-dialog .modal { 216 | width: 20rem; 217 | max-width: 100%; 218 | background: var(--section-bg-color); 219 | color: var(--primary-text-color); 220 | border-radius: .5rem; 221 | transform: scale(0); 222 | will-change: transform; 223 | transition: .3s transform; 224 | } 225 | 226 | #themes-dialog.active { 227 | background: rgba(0, 0, 0, .6); 228 | z-index: 3; 229 | transition: .3s background, .3s z-index step-start; 230 | } 231 | 232 | #themes-dialog.active .modal { 233 | transform: scale(1); 234 | } 235 | 236 | .modal .title { 237 | padding: 1rem; 238 | border-bottom: .05rem solid var(--divider-color); 239 | } 240 | 241 | #themes { 242 | list-style: none; 243 | padding: 0; 244 | margin: 0; 245 | max-height: 14rem; 246 | overflow: scroll; 247 | } 248 | 249 | #themes > li { 250 | cursor: pointer; 251 | padding: 1rem; 252 | } 253 | 254 | #themes > li:hover, #themes > li:focus { 255 | background: var(--divider-color); 256 | } 257 | 258 | #share-button { 259 | position: fixed; 260 | right: 1rem; 261 | bottom: 1rem; 262 | width: 3.5rem; 263 | height: 3.5rem; 264 | padding: 1rem; 265 | border-radius: 50%; 266 | background: var(--accent-color); 267 | fill: var(--primary-text-on-primary); 268 | cursor: pointer; 269 | box-shadow: 0 .06rem .065rem 0 rgba(0, 0, 0, 0.14), 0 .003rem .15rem 0 rgba(0, 0, 0, 0.12), 0 .09rem .0035rem -.065rem rgba(0, 0, 0, 0.2); 270 | z-index: 1; 271 | } 272 | 273 | @media (min-width: 640px) { 274 | #update-notification { 275 | width: 20rem; 276 | margin: auto; 277 | border-radius: .5rem .5rem 0 0; 278 | } 279 | 280 | #share-button { 281 | position: absolute; 282 | top: 5.25rem; 283 | right: 5.25rem; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | self.staticCacheName = 'vanilla-pwa-static'; 2 | self.staticCacheVersion = 'v1'; 3 | self.staticCacheId = `${self.staticCacheName}-${self.staticCacheVersion}`; 4 | self.runtimeCacheName = 'vanilla-pwa-runtime'; 5 | self.importScripts('./cache-manifest.js', './db-helpers.js'); 6 | 7 | self.openOrFocus = (url) => 8 | clients.matchAll({ 9 | type: 'window', 10 | }).then((clientsList) => { 11 | for (const client of clientsList) { 12 | if ((url === '*' && client.url.startsWith('/')) || url === client.url) { 13 | return client.focus(); 14 | } 15 | } 16 | return clients.openWindow(url === '*' ? '/' : url); 17 | }); 18 | 19 | // Event fired when the SW is installed 20 | self.addEventListener('install', (event) => { 21 | // When the SW is installed, add to the cache all the URLs 22 | // specified in the precache manifest. 23 | event.waitUntil( 24 | caches.open(self.staticCacheId) 25 | .then((cache) => cache.addAll(self.precacheManifest)), 26 | ); 27 | }); 28 | 29 | // Event fired when the SW is activated 30 | self.addEventListener('activate', (event) => { 31 | event.waitUntil( 32 | Promise.all([ 33 | // When the SW is activated, claim any currently available client 34 | self.clients.claim(), 35 | // Check if there is an older version 36 | // of our application cache and delete it. 37 | caches.keys().then((cacheNames) => 38 | Promise.all( 39 | cacheNames.filter((cacheName) => 40 | cacheName.startsWith(self.staticCacheName) && cacheName !== self.staticCacheId, 41 | ).map((cacheName) => 42 | caches.delete(cacheName), 43 | ), 44 | ), 45 | ), 46 | self.openDB(), 47 | ]), 48 | ); 49 | }); 50 | 51 | // Event fired when the website tries to fetch something 52 | self.addEventListener('fetch', (event) => { 53 | // For some reason, DevTools opening will trigger these o-i-c requests. 54 | // We will just ignore them to avoid showing errors in console. 55 | if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { 56 | return Promise.resolve(); 57 | } 58 | 59 | // Don't cache anything that isn't a GET request 60 | if (event.request.method !== 'GET') { 61 | return event.respondWith(fetch(event.request)); 62 | } 63 | 64 | // IDB CACHE 65 | // This part of the SW will cache data coming from our backend. 66 | // Any sort of document-type data is perfect for being added to IDB, 67 | // especially if it already features an ID. 68 | // As this data is most probably subject to change, we are going to use 69 | // a "stale while revalidate" approach. This means that we are going 70 | // to first check if the data is in IDB, but if we find it, we are 71 | // going to make a web request anyway. If it is successful, we are 72 | // going to replace the old data in IDB with the new one. 73 | // This means that the final user might incur in older data, but 74 | // the problem is easily fixable by, for example, posting a message 75 | // to the frontend, telling it that the data has been updated, 76 | // so that it can display the new ones as soon as they are available. 77 | const idbCacheRegex = self.runtimeIDBCacheManifest.find((regex) => regex.test(event.request.url)); 78 | if (idbCacheRegex) { 79 | const [, store] = idbCacheRegex.exec(event.request.url); 80 | return event.respondWith( 81 | self.getAllFromDB(store) 82 | .then((data) => { 83 | // Even if we already saved the data in idb, 84 | // we start a new request so that the data can 85 | // be updated in background. In this way, we will 86 | // see the updated data next time we open up the PWA 87 | const reqPromise = fetch(event.request) 88 | .then((res) => { 89 | // The response cannot be used anymore after being consumed, 90 | // so we have to clone it 91 | const clonedRes = res.clone(); 92 | // Put the data into IDB, then return the cloned response 93 | return res.json() 94 | .then(({ data }) => self.putIntoDB(store, data)) 95 | .then(() => clonedRes); 96 | }) 97 | .then(res => res.json()) 98 | .then((res) => { 99 | self.putIntoDB(store, res.data); 100 | return new Response(JSON.stringify(res)); 101 | }); 102 | // If we got data from IDB, respond with it 103 | if (data && Object.keys(Array.isArray(data) ? data : [data]).length > 0) { 104 | return new Response(JSON.stringify({ 105 | success: true, 106 | data, 107 | })); 108 | } 109 | // Otherwise, just return the fetch request that will cache the data into IDB 110 | return reqPromise; 111 | }), 112 | ); 113 | } 114 | 115 | // RUNTIME CACHE 116 | // This part of the SW will generate a runtime cache for 117 | // files/blobs. This is meant to be used for assets like fonts 118 | // or images that are only known at runtime and not during our SW 119 | // installation. 120 | // For example, in our case it will be used to dynamically cache 121 | // the images coming from our mock backend. Of course we cannot 122 | // know them until they come from the BE, but we know their base URL, 123 | // so we are going to add them to the static cache dynamically. 124 | // The approach used is a "cache first" approach. This means that 125 | // if we have the data in the cache, we won't be doing any fetch at all. 126 | 127 | // Try to check if the request has been cached, 128 | // otherwise fetch it. 129 | const promise = caches.match(event.request) 130 | .then((response) => response || fetch(event.request)); 131 | // If the URL is one of those who need to be cached at runtime, 132 | // clone the response and insert it into the cache. 133 | if (self.runtimeCacheManifest.some((regex) => regex.test(event.request.url))) { 134 | promise.then((fetchRes) => { 135 | const clone = fetchRes.clone(); 136 | if (!clone) { 137 | return; 138 | } 139 | caches.open(self.runtimeCacheName) 140 | .then((cache) => cache.put(event.request.url, clone)); 141 | }); 142 | } 143 | event.respondWith(promise); 144 | }); 145 | 146 | // Event fired when the website posts a message to the Service Worker 147 | self.addEventListener('message', (event) => { 148 | switch (event.data.action) { 149 | case 'update': 150 | // Skip the waiting phase and immediately replace the old Service Worker 151 | self.skipWaiting(); 152 | break; 153 | } 154 | }); 155 | 156 | // Event fired when a new background sync is registered by the website 157 | self.addEventListener('sync', (event) => { 158 | switch (event.tag) { 159 | case 'fetch': 160 | event.waitUntil( 161 | self.getAllFromDB('fetch-queue') 162 | .then((queue) => Promise.all(queue.map((reqParams) => 163 | fetch(reqParams.url, reqParams) 164 | .then((res) => res.json()) 165 | .then((res) => res.success || Promise.reject(res)) 166 | .then(() => self.removeFromDB('fetch-queue', reqParams.id))))), 167 | ); 168 | break; 169 | } 170 | }); 171 | 172 | // Event fired when a push message is sent to the Service Worker via the Web Push protocol 173 | self.addEventListener('push', (event) => { 174 | // We will parse the data received via Web Push and show it 175 | // to the user in the form of a push notification 176 | const data = event.data.json(); 177 | // We will have a default icon and badge, but the server might decide 178 | // to send different data, so we allow it to override them 179 | event.waitUntil(self.registration.showNotification(data.title, { 180 | icon: '/images/icons/android-chrome-512x512.png', 181 | badge: '/images/icons/mstile-70x70.png', 182 | ...data, 183 | })); 184 | }); 185 | 186 | // Event fired when the user clicks a notification 187 | self.addEventListener('notificationclick', (event) => { 188 | // First of all, close the notification 189 | event.notification.close(); 190 | // The notification has an action tag associated with it. 191 | // We will be doing something according to it. 192 | switch (event.action) { 193 | // The user clicked on "dismiss", so just do nothing 194 | case 'dismiss': 195 | break; 196 | // The user wants to open (or focus) the background sync page 197 | case 'open-background-sync-page': 198 | event.waitUntil(self.openOrFocus('/background-sync')); 199 | break; 200 | // In any other case (i.e. simple click on the notification), 201 | // open or focus any available page of our application 202 | default: 203 | event.waitUntil(self.openOrFocus('*')); 204 | break; 205 | } 206 | }); 207 | -------------------------------------------------------------------------------- /functions/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "@firebase/app": { 7 | "version": "0.3.17", 8 | "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.3.17.tgz", 9 | "integrity": "sha512-/8lDeeIxgdCIMffrfBPQ3bcdSkF8bx4KCp8pKMPOG/HYKoeM8I9eP4zlzxL5ABzRjvcdhK9KOYOn0jRrNrGD9g==", 10 | "requires": { 11 | "@firebase/app-types": "0.3.10", 12 | "@firebase/util": "0.2.14", 13 | "dom-storage": "2.1.0", 14 | "tslib": "1.9.3", 15 | "xmlhttprequest": "1.8.0" 16 | } 17 | }, 18 | "@firebase/app-types": { 19 | "version": "0.3.10", 20 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.3.10.tgz", 21 | "integrity": "sha512-l+5BJtSQopalBXiY/YuSaB9KF9PnDj37FLV0Sx3qJjh5B3IthCuZbPc1Vpbbbee/QZgudl0G212BBsUMGHP+fQ==" 22 | }, 23 | "@firebase/database": { 24 | "version": "0.3.20", 25 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.3.20.tgz", 26 | "integrity": "sha512-fZHRIlRQlND/UrzI1beUTRKfktjMvMEiUOar6ylFZqOj2KNVO4CrF95UGqRl0HBGhZzlBKzaDYAcJze2D6C4+Q==", 27 | "requires": { 28 | "@firebase/database-types": "0.3.11", 29 | "@firebase/logger": "0.1.13", 30 | "@firebase/util": "0.2.14", 31 | "faye-websocket": "0.11.1", 32 | "tslib": "1.9.3" 33 | } 34 | }, 35 | "@firebase/database-types": { 36 | "version": "0.3.11", 37 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.3.11.tgz", 38 | "integrity": "sha512-iRAZzs7Zlmmvh7r0XlR1MAO6I6bm1HjW9m1ytfJ6E/8+zItHnbVH4iiVVkC39r1wMGrtPMz8FiIUWoaasPF5dA==" 39 | }, 40 | "@firebase/logger": { 41 | "version": "0.1.13", 42 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.13.tgz", 43 | "integrity": "sha512-wIbLwQ2oJCkvHIE7J3FDxpScKY84fSctEEjOi0PB+Yn2dN8AwqtM7YF8rtcY8cxntv8dyR+i7GNg1Nd89cGxkA==" 44 | }, 45 | "@firebase/util": { 46 | "version": "0.2.14", 47 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.14.tgz", 48 | "integrity": "sha512-2ke1Lra0R5T+5ucCMWft/IB2rI/IzumHHYm9aqrM9lJ3XURiWmBHAYrvaPVP7///gDhJAo+NNDUCAJH/Y4PmvA==", 49 | "requires": { 50 | "tslib": "1.9.3" 51 | } 52 | }, 53 | "@google-cloud/common": { 54 | "version": "0.32.1", 55 | "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", 56 | "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", 57 | "optional": true, 58 | "requires": { 59 | "@google-cloud/projectify": "^0.3.3", 60 | "@google-cloud/promisify": "^0.4.0", 61 | "@types/request": "^2.48.1", 62 | "arrify": "^2.0.0", 63 | "duplexify": "^3.6.0", 64 | "ent": "^2.2.0", 65 | "extend": "^3.0.2", 66 | "google-auth-library": "^3.1.1", 67 | "pify": "^4.0.1", 68 | "retry-request": "^4.0.0", 69 | "teeny-request": "^3.11.3" 70 | }, 71 | "dependencies": { 72 | "arrify": { 73 | "version": "2.0.1", 74 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 75 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", 76 | "optional": true 77 | } 78 | } 79 | }, 80 | "@google-cloud/firestore": { 81 | "version": "1.3.0", 82 | "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-1.3.0.tgz", 83 | "integrity": "sha512-KUKcHUU+FwwBmJH1LqJcd+XtLPzMcS/Vni6/WCJGHBGrOBmXCey4i3Gc41ZsuSk1Qx4msH5f+4h0b3t6YLyydg==", 84 | "optional": true, 85 | "requires": { 86 | "bun": "^0.0.12", 87 | "deep-equal": "^1.0.1", 88 | "functional-red-black-tree": "^1.0.1", 89 | "google-gax": "^0.25.0", 90 | "lodash.merge": "^4.6.1", 91 | "through2": "^3.0.0" 92 | } 93 | }, 94 | "@google-cloud/paginator": { 95 | "version": "0.2.0", 96 | "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-0.2.0.tgz", 97 | "integrity": "sha512-2ZSARojHDhkLvQ+CS32K+iUhBsWg3AEw+uxtqblA7xoCABDyhpj99FPp35xy6A+XlzMhOSrHHaxFE+t6ZTQq0w==", 98 | "optional": true, 99 | "requires": { 100 | "arrify": "^1.0.1", 101 | "extend": "^3.0.1", 102 | "split-array-stream": "^2.0.0", 103 | "stream-events": "^1.0.4" 104 | } 105 | }, 106 | "@google-cloud/projectify": { 107 | "version": "0.3.3", 108 | "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", 109 | "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==", 110 | "optional": true 111 | }, 112 | "@google-cloud/promisify": { 113 | "version": "0.4.0", 114 | "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", 115 | "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==", 116 | "optional": true 117 | }, 118 | "@google-cloud/storage": { 119 | "version": "2.5.0", 120 | "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-2.5.0.tgz", 121 | "integrity": "sha512-q1mwB6RUebIahbA3eriRs8DbG2Ij81Ynb9k8hMqTPkmbd8/S6Z0d6hVvfPmnyvX9Ej13IcmEYIbymuq/RBLghA==", 122 | "optional": true, 123 | "requires": { 124 | "@google-cloud/common": "^0.32.0", 125 | "@google-cloud/paginator": "^0.2.0", 126 | "@google-cloud/promisify": "^0.4.0", 127 | "arrify": "^1.0.0", 128 | "async": "^2.0.1", 129 | "compressible": "^2.0.12", 130 | "concat-stream": "^2.0.0", 131 | "date-and-time": "^0.6.3", 132 | "duplexify": "^3.5.0", 133 | "extend": "^3.0.0", 134 | "gcs-resumable-upload": "^1.0.0", 135 | "hash-stream-validation": "^0.2.1", 136 | "mime": "^2.2.0", 137 | "mime-types": "^2.0.8", 138 | "onetime": "^5.1.0", 139 | "pumpify": "^1.5.1", 140 | "snakeize": "^0.1.0", 141 | "stream-events": "^1.0.1", 142 | "teeny-request": "^3.11.3", 143 | "through2": "^3.0.0", 144 | "xdg-basedir": "^3.0.0" 145 | }, 146 | "dependencies": { 147 | "mime": { 148 | "version": "2.4.3", 149 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", 150 | "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", 151 | "optional": true 152 | } 153 | } 154 | }, 155 | "@grpc/grpc-js": { 156 | "version": "0.3.6", 157 | "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.3.6.tgz", 158 | "integrity": "sha512-SmLNuPGlUur64bNS9aHZguqWDVQ8+Df1CGn+xsh7l6T2wiP5ArOMlywZ3TZo6z/rwKtGQgUJY9ZrPYUmHEXd/Q==", 159 | "optional": true, 160 | "requires": { 161 | "semver": "^5.5.0" 162 | }, 163 | "dependencies": { 164 | "semver": { 165 | "version": "5.7.0", 166 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 167 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", 168 | "optional": true 169 | } 170 | } 171 | }, 172 | "@grpc/proto-loader": { 173 | "version": "0.4.0", 174 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.4.0.tgz", 175 | "integrity": "sha512-Jm6o+75uWT7E6+lt8edg4J1F/9+BedOjaMgwE14pxS/AO43/0ZqK+rCLVVrXLoExwSAZvgvOD2B0ivy3Spsspw==", 176 | "optional": true, 177 | "requires": { 178 | "lodash.camelcase": "^4.3.0", 179 | "protobufjs": "^6.8.6" 180 | } 181 | }, 182 | "@protobufjs/aspromise": { 183 | "version": "1.1.2", 184 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 185 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", 186 | "optional": true 187 | }, 188 | "@protobufjs/base64": { 189 | "version": "1.1.2", 190 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 191 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", 192 | "optional": true 193 | }, 194 | "@protobufjs/codegen": { 195 | "version": "2.0.4", 196 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 197 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", 198 | "optional": true 199 | }, 200 | "@protobufjs/eventemitter": { 201 | "version": "1.1.0", 202 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 203 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", 204 | "optional": true 205 | }, 206 | "@protobufjs/fetch": { 207 | "version": "1.1.0", 208 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 209 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", 210 | "optional": true, 211 | "requires": { 212 | "@protobufjs/aspromise": "^1.1.1", 213 | "@protobufjs/inquire": "^1.1.0" 214 | } 215 | }, 216 | "@protobufjs/float": { 217 | "version": "1.0.2", 218 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 219 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", 220 | "optional": true 221 | }, 222 | "@protobufjs/inquire": { 223 | "version": "1.1.0", 224 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 225 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", 226 | "optional": true 227 | }, 228 | "@protobufjs/path": { 229 | "version": "1.1.2", 230 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 231 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", 232 | "optional": true 233 | }, 234 | "@protobufjs/pool": { 235 | "version": "1.1.0", 236 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 237 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", 238 | "optional": true 239 | }, 240 | "@protobufjs/utf8": { 241 | "version": "1.1.0", 242 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 243 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", 244 | "optional": true 245 | }, 246 | "@types/body-parser": { 247 | "version": "1.17.0", 248 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", 249 | "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", 250 | "requires": { 251 | "@types/connect": "*", 252 | "@types/node": "*" 253 | } 254 | }, 255 | "@types/caseless": { 256 | "version": "0.12.2", 257 | "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", 258 | "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", 259 | "optional": true 260 | }, 261 | "@types/connect": { 262 | "version": "3.4.32", 263 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", 264 | "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", 265 | "requires": { 266 | "@types/node": "*" 267 | } 268 | }, 269 | "@types/cors": { 270 | "version": "2.8.5", 271 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.5.tgz", 272 | "integrity": "sha512-GmK8AKu8i+s+EChK/uZ5IbrXPcPaQKWaNSGevDT/7o3gFObwSUQwqb1jMqxuo+YPvj0ckGzINI+EO7EHcmJjKg==", 273 | "requires": { 274 | "@types/express": "*" 275 | } 276 | }, 277 | "@types/express": { 278 | "version": "4.16.1", 279 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz", 280 | "integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==", 281 | "requires": { 282 | "@types/body-parser": "*", 283 | "@types/express-serve-static-core": "*", 284 | "@types/serve-static": "*" 285 | } 286 | }, 287 | "@types/express-serve-static-core": { 288 | "version": "4.16.4", 289 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.4.tgz", 290 | "integrity": "sha512-x/8h6FHm14rPWnW2HP5likD/rsqJ3t/77OWx2PLxym0hXbeBWQmcPyHmwX+CtCQpjIfgrUdEoDFcLPwPZWiqzQ==", 291 | "requires": { 292 | "@types/node": "*", 293 | "@types/range-parser": "*" 294 | } 295 | }, 296 | "@types/form-data": { 297 | "version": "2.2.1", 298 | "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", 299 | "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", 300 | "optional": true, 301 | "requires": { 302 | "@types/node": "*" 303 | } 304 | }, 305 | "@types/jsonwebtoken": { 306 | "version": "7.2.8", 307 | "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", 308 | "integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==", 309 | "requires": { 310 | "@types/node": "*" 311 | } 312 | }, 313 | "@types/lodash": { 314 | "version": "4.14.129", 315 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.129.tgz", 316 | "integrity": "sha512-oYaV0eSlnOacOr7i4X1FFdH8ttSlb57gu3I9MuStIv2CYkISEY84dNHYsC3bF6sNH7qYcu1BtVrCtQ8Q4KPTfQ==" 317 | }, 318 | "@types/long": { 319 | "version": "4.0.0", 320 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", 321 | "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==", 322 | "optional": true 323 | }, 324 | "@types/mime": { 325 | "version": "2.0.1", 326 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", 327 | "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" 328 | }, 329 | "@types/node": { 330 | "version": "8.10.48", 331 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.48.tgz", 332 | "integrity": "sha512-c35YEBTkL4rzXY2ucpSKy+UYHjUBIIkuJbWYbsGIrKLEWU5dgJMmLkkIb3qeC3O3Tpb1ZQCwecscvJTDjDjkRw==" 333 | }, 334 | "@types/range-parser": { 335 | "version": "1.2.3", 336 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 337 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" 338 | }, 339 | "@types/request": { 340 | "version": "2.48.1", 341 | "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", 342 | "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", 343 | "optional": true, 344 | "requires": { 345 | "@types/caseless": "*", 346 | "@types/form-data": "*", 347 | "@types/node": "*", 348 | "@types/tough-cookie": "*" 349 | } 350 | }, 351 | "@types/serve-static": { 352 | "version": "1.13.2", 353 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", 354 | "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", 355 | "requires": { 356 | "@types/express-serve-static-core": "*", 357 | "@types/mime": "*" 358 | } 359 | }, 360 | "@types/tough-cookie": { 361 | "version": "2.3.5", 362 | "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", 363 | "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", 364 | "optional": true 365 | }, 366 | "abort-controller": { 367 | "version": "3.0.0", 368 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 369 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 370 | "optional": true, 371 | "requires": { 372 | "event-target-shim": "^5.0.0" 373 | } 374 | }, 375 | "accepts": { 376 | "version": "1.3.7", 377 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 378 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 379 | "requires": { 380 | "mime-types": "~2.1.24", 381 | "negotiator": "0.6.2" 382 | } 383 | }, 384 | "agent-base": { 385 | "version": "4.2.1", 386 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", 387 | "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", 388 | "requires": { 389 | "es6-promisify": "^5.0.0" 390 | } 391 | }, 392 | "ansi-regex": { 393 | "version": "2.1.1", 394 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 395 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 396 | "optional": true 397 | }, 398 | "array-flatten": { 399 | "version": "1.1.1", 400 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 401 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 402 | }, 403 | "arrify": { 404 | "version": "1.0.1", 405 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 406 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 407 | "optional": true 408 | }, 409 | "ascli": { 410 | "version": "1.0.1", 411 | "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", 412 | "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", 413 | "optional": true, 414 | "requires": { 415 | "colour": "~0.7.1", 416 | "optjs": "~3.2.2" 417 | } 418 | }, 419 | "asn1.js": { 420 | "version": "5.0.1", 421 | "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.1.tgz", 422 | "integrity": "sha512-aO8EaEgbgqq77IEw+1jfx5c9zTbzvkfuRBuZsSsPnTHMkmd5AI4J6OtITLZFa381jReeaQL67J0GBTUu0+ZTVw==", 423 | "requires": { 424 | "bn.js": "^4.0.0", 425 | "inherits": "^2.0.1", 426 | "minimalistic-assert": "^1.0.0" 427 | } 428 | }, 429 | "async": { 430 | "version": "2.6.2", 431 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", 432 | "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", 433 | "optional": true, 434 | "requires": { 435 | "lodash": "^4.17.11" 436 | } 437 | }, 438 | "balanced-match": { 439 | "version": "1.0.0", 440 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 441 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 442 | "optional": true 443 | }, 444 | "base64-js": { 445 | "version": "1.3.0", 446 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", 447 | "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", 448 | "optional": true 449 | }, 450 | "bignumber.js": { 451 | "version": "7.2.1", 452 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", 453 | "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==", 454 | "optional": true 455 | }, 456 | "bn.js": { 457 | "version": "4.11.8", 458 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", 459 | "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" 460 | }, 461 | "body-parser": { 462 | "version": "1.19.0", 463 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 464 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 465 | "requires": { 466 | "bytes": "3.1.0", 467 | "content-type": "~1.0.4", 468 | "debug": "2.6.9", 469 | "depd": "~1.1.2", 470 | "http-errors": "1.7.2", 471 | "iconv-lite": "0.4.24", 472 | "on-finished": "~2.3.0", 473 | "qs": "6.7.0", 474 | "raw-body": "2.4.0", 475 | "type-is": "~1.6.17" 476 | } 477 | }, 478 | "brace-expansion": { 479 | "version": "1.1.11", 480 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 481 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 482 | "optional": true, 483 | "requires": { 484 | "balanced-match": "^1.0.0", 485 | "concat-map": "0.0.1" 486 | } 487 | }, 488 | "buffer-equal-constant-time": { 489 | "version": "1.0.1", 490 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 491 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 492 | }, 493 | "buffer-from": { 494 | "version": "1.1.1", 495 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 496 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 497 | "optional": true 498 | }, 499 | "bun": { 500 | "version": "0.0.12", 501 | "resolved": "https://registry.npmjs.org/bun/-/bun-0.0.12.tgz", 502 | "integrity": "sha512-Toms18J9DqnT+IfWkwxVTB2EaBprHvjlMWrTIsfX4xbu3ZBqVBwrERU0em1IgtRe04wT+wJxMlKHZok24hrcSQ==", 503 | "optional": true, 504 | "requires": { 505 | "readable-stream": "~1.0.32" 506 | } 507 | }, 508 | "bytebuffer": { 509 | "version": "5.0.1", 510 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", 511 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", 512 | "optional": true, 513 | "requires": { 514 | "long": "~3" 515 | }, 516 | "dependencies": { 517 | "long": { 518 | "version": "3.2.0", 519 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", 520 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", 521 | "optional": true 522 | } 523 | } 524 | }, 525 | "bytes": { 526 | "version": "3.1.0", 527 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 528 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 529 | }, 530 | "camelcase": { 531 | "version": "2.1.1", 532 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 533 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", 534 | "optional": true 535 | }, 536 | "cliui": { 537 | "version": "3.2.0", 538 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 539 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 540 | "optional": true, 541 | "requires": { 542 | "string-width": "^1.0.1", 543 | "strip-ansi": "^3.0.1", 544 | "wrap-ansi": "^2.0.0" 545 | } 546 | }, 547 | "code-point-at": { 548 | "version": "1.1.0", 549 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 550 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", 551 | "optional": true 552 | }, 553 | "colour": { 554 | "version": "0.7.1", 555 | "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", 556 | "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", 557 | "optional": true 558 | }, 559 | "compressible": { 560 | "version": "2.0.17", 561 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", 562 | "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", 563 | "optional": true, 564 | "requires": { 565 | "mime-db": ">= 1.40.0 < 2" 566 | } 567 | }, 568 | "concat-map": { 569 | "version": "0.0.1", 570 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 571 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 572 | "optional": true 573 | }, 574 | "concat-stream": { 575 | "version": "2.0.0", 576 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", 577 | "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", 578 | "optional": true, 579 | "requires": { 580 | "buffer-from": "^1.0.0", 581 | "inherits": "^2.0.3", 582 | "readable-stream": "^3.0.2", 583 | "typedarray": "^0.0.6" 584 | }, 585 | "dependencies": { 586 | "readable-stream": { 587 | "version": "3.3.0", 588 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", 589 | "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", 590 | "optional": true, 591 | "requires": { 592 | "inherits": "^2.0.3", 593 | "string_decoder": "^1.1.1", 594 | "util-deprecate": "^1.0.1" 595 | } 596 | }, 597 | "string_decoder": { 598 | "version": "1.2.0", 599 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", 600 | "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", 601 | "optional": true, 602 | "requires": { 603 | "safe-buffer": "~5.1.0" 604 | } 605 | } 606 | } 607 | }, 608 | "configstore": { 609 | "version": "4.0.0", 610 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", 611 | "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==", 612 | "optional": true, 613 | "requires": { 614 | "dot-prop": "^4.1.0", 615 | "graceful-fs": "^4.1.2", 616 | "make-dir": "^1.0.0", 617 | "unique-string": "^1.0.0", 618 | "write-file-atomic": "^2.0.0", 619 | "xdg-basedir": "^3.0.0" 620 | } 621 | }, 622 | "content-disposition": { 623 | "version": "0.5.2", 624 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 625 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 626 | }, 627 | "content-type": { 628 | "version": "1.0.4", 629 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 630 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 631 | }, 632 | "cookie": { 633 | "version": "0.3.1", 634 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 635 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 636 | }, 637 | "cookie-signature": { 638 | "version": "1.0.6", 639 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 640 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 641 | }, 642 | "core-util-is": { 643 | "version": "1.0.2", 644 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 645 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 646 | "optional": true 647 | }, 648 | "cors": { 649 | "version": "2.8.5", 650 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 651 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 652 | "requires": { 653 | "object-assign": "^4", 654 | "vary": "^1" 655 | } 656 | }, 657 | "crypto-random-string": { 658 | "version": "1.0.0", 659 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", 660 | "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", 661 | "optional": true 662 | }, 663 | "date-and-time": { 664 | "version": "0.6.3", 665 | "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.6.3.tgz", 666 | "integrity": "sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA==", 667 | "optional": true 668 | }, 669 | "debug": { 670 | "version": "2.6.9", 671 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 672 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 673 | "requires": { 674 | "ms": "2.0.0" 675 | } 676 | }, 677 | "decamelize": { 678 | "version": "1.2.0", 679 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 680 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 681 | "optional": true 682 | }, 683 | "deep-equal": { 684 | "version": "1.0.1", 685 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 686 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", 687 | "optional": true 688 | }, 689 | "depd": { 690 | "version": "1.1.2", 691 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 692 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 693 | }, 694 | "destroy": { 695 | "version": "1.0.4", 696 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 697 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 698 | }, 699 | "dicer": { 700 | "version": "0.3.0", 701 | "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", 702 | "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", 703 | "requires": { 704 | "streamsearch": "0.1.2" 705 | } 706 | }, 707 | "dom-storage": { 708 | "version": "2.1.0", 709 | "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", 710 | "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" 711 | }, 712 | "dot-prop": { 713 | "version": "4.2.0", 714 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", 715 | "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", 716 | "optional": true, 717 | "requires": { 718 | "is-obj": "^1.0.0" 719 | } 720 | }, 721 | "duplexify": { 722 | "version": "3.7.1", 723 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", 724 | "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", 725 | "optional": true, 726 | "requires": { 727 | "end-of-stream": "^1.0.0", 728 | "inherits": "^2.0.1", 729 | "readable-stream": "^2.0.0", 730 | "stream-shift": "^1.0.0" 731 | }, 732 | "dependencies": { 733 | "isarray": { 734 | "version": "1.0.0", 735 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 736 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 737 | "optional": true 738 | }, 739 | "readable-stream": { 740 | "version": "2.3.6", 741 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 742 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 743 | "optional": true, 744 | "requires": { 745 | "core-util-is": "~1.0.0", 746 | "inherits": "~2.0.3", 747 | "isarray": "~1.0.0", 748 | "process-nextick-args": "~2.0.0", 749 | "safe-buffer": "~5.1.1", 750 | "string_decoder": "~1.1.1", 751 | "util-deprecate": "~1.0.1" 752 | } 753 | }, 754 | "string_decoder": { 755 | "version": "1.1.1", 756 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 757 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 758 | "optional": true, 759 | "requires": { 760 | "safe-buffer": "~5.1.0" 761 | } 762 | } 763 | } 764 | }, 765 | "ecdsa-sig-formatter": { 766 | "version": "1.0.11", 767 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 768 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 769 | "requires": { 770 | "safe-buffer": "^5.0.1" 771 | } 772 | }, 773 | "ee-first": { 774 | "version": "1.1.1", 775 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 776 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 777 | }, 778 | "encodeurl": { 779 | "version": "1.0.2", 780 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 781 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 782 | }, 783 | "end-of-stream": { 784 | "version": "1.4.1", 785 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 786 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 787 | "optional": true, 788 | "requires": { 789 | "once": "^1.4.0" 790 | } 791 | }, 792 | "ent": { 793 | "version": "2.2.0", 794 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", 795 | "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", 796 | "optional": true 797 | }, 798 | "es6-promise": { 799 | "version": "4.2.6", 800 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", 801 | "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==" 802 | }, 803 | "es6-promisify": { 804 | "version": "5.0.0", 805 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 806 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 807 | "requires": { 808 | "es6-promise": "^4.0.3" 809 | } 810 | }, 811 | "escape-html": { 812 | "version": "1.0.3", 813 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 814 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 815 | }, 816 | "etag": { 817 | "version": "1.8.1", 818 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 819 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 820 | }, 821 | "event-target-shim": { 822 | "version": "5.0.1", 823 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 824 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 825 | "optional": true 826 | }, 827 | "express": { 828 | "version": "4.16.4", 829 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 830 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 831 | "requires": { 832 | "accepts": "~1.3.5", 833 | "array-flatten": "1.1.1", 834 | "body-parser": "1.18.3", 835 | "content-disposition": "0.5.2", 836 | "content-type": "~1.0.4", 837 | "cookie": "0.3.1", 838 | "cookie-signature": "1.0.6", 839 | "debug": "2.6.9", 840 | "depd": "~1.1.2", 841 | "encodeurl": "~1.0.2", 842 | "escape-html": "~1.0.3", 843 | "etag": "~1.8.1", 844 | "finalhandler": "1.1.1", 845 | "fresh": "0.5.2", 846 | "merge-descriptors": "1.0.1", 847 | "methods": "~1.1.2", 848 | "on-finished": "~2.3.0", 849 | "parseurl": "~1.3.2", 850 | "path-to-regexp": "0.1.7", 851 | "proxy-addr": "~2.0.4", 852 | "qs": "6.5.2", 853 | "range-parser": "~1.2.0", 854 | "safe-buffer": "5.1.2", 855 | "send": "0.16.2", 856 | "serve-static": "1.13.2", 857 | "setprototypeof": "1.1.0", 858 | "statuses": "~1.4.0", 859 | "type-is": "~1.6.16", 860 | "utils-merge": "1.0.1", 861 | "vary": "~1.1.2" 862 | }, 863 | "dependencies": { 864 | "body-parser": { 865 | "version": "1.18.3", 866 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 867 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 868 | "requires": { 869 | "bytes": "3.0.0", 870 | "content-type": "~1.0.4", 871 | "debug": "2.6.9", 872 | "depd": "~1.1.2", 873 | "http-errors": "~1.6.3", 874 | "iconv-lite": "0.4.23", 875 | "on-finished": "~2.3.0", 876 | "qs": "6.5.2", 877 | "raw-body": "2.3.3", 878 | "type-is": "~1.6.16" 879 | } 880 | }, 881 | "bytes": { 882 | "version": "3.0.0", 883 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 884 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 885 | }, 886 | "http-errors": { 887 | "version": "1.6.3", 888 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 889 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 890 | "requires": { 891 | "depd": "~1.1.2", 892 | "inherits": "2.0.3", 893 | "setprototypeof": "1.1.0", 894 | "statuses": ">= 1.4.0 < 2" 895 | } 896 | }, 897 | "iconv-lite": { 898 | "version": "0.4.23", 899 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 900 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 901 | "requires": { 902 | "safer-buffer": ">= 2.1.2 < 3" 903 | } 904 | }, 905 | "qs": { 906 | "version": "6.5.2", 907 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 908 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 909 | }, 910 | "raw-body": { 911 | "version": "2.3.3", 912 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 913 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 914 | "requires": { 915 | "bytes": "3.0.0", 916 | "http-errors": "1.6.3", 917 | "iconv-lite": "0.4.23", 918 | "unpipe": "1.0.0" 919 | } 920 | }, 921 | "setprototypeof": { 922 | "version": "1.1.0", 923 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 924 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 925 | }, 926 | "statuses": { 927 | "version": "1.4.0", 928 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 929 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 930 | } 931 | } 932 | }, 933 | "extend": { 934 | "version": "3.0.2", 935 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 936 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 937 | "optional": true 938 | }, 939 | "fast-text-encoding": { 940 | "version": "1.0.0", 941 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", 942 | "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==", 943 | "optional": true 944 | }, 945 | "faye-websocket": { 946 | "version": "0.11.1", 947 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", 948 | "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", 949 | "requires": { 950 | "websocket-driver": ">=0.5.1" 951 | } 952 | }, 953 | "finalhandler": { 954 | "version": "1.1.1", 955 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 956 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 957 | "requires": { 958 | "debug": "2.6.9", 959 | "encodeurl": "~1.0.2", 960 | "escape-html": "~1.0.3", 961 | "on-finished": "~2.3.0", 962 | "parseurl": "~1.3.2", 963 | "statuses": "~1.4.0", 964 | "unpipe": "~1.0.0" 965 | }, 966 | "dependencies": { 967 | "statuses": { 968 | "version": "1.4.0", 969 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 970 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 971 | } 972 | } 973 | }, 974 | "firebase-admin": { 975 | "version": "7.3.0", 976 | "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-7.3.0.tgz", 977 | "integrity": "sha512-G+gUU+u+8Mr/8IELeXA/44VYrGM6EEvUgYm5F6I5/oO6clUbM8b8x9QJasSeD14KQmP3iAPXcKQUALgH1wY+Vw==", 978 | "requires": { 979 | "@firebase/app": "^0.3.4", 980 | "@firebase/database": "^0.3.6", 981 | "@google-cloud/firestore": "^1.2.0", 982 | "@google-cloud/storage": "^2.3.0", 983 | "@types/node": "^8.0.53", 984 | "dicer": "^0.3.0", 985 | "jsonwebtoken": "8.1.0", 986 | "node-forge": "0.7.4" 987 | } 988 | }, 989 | "firebase-functions": { 990 | "version": "2.3.1", 991 | "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-2.3.1.tgz", 992 | "integrity": "sha512-MPeuzGFLS63VqfGErgf+kbZinXE4SoK+jxXh1NBWrjYUjXlOJBKRmVNAFJoQB8wzCvOtBuDYjGcnBNz6sKXsfw==", 993 | "requires": { 994 | "@types/cors": "^2.8.1", 995 | "@types/express": "^4.11.1", 996 | "@types/jsonwebtoken": "^7.2.6", 997 | "@types/lodash": "^4.14.34", 998 | "cors": "^2.8.4", 999 | "express": "^4.16.2", 1000 | "jsonwebtoken": "^8.2.1", 1001 | "lodash": "^4.6.1" 1002 | }, 1003 | "dependencies": { 1004 | "jsonwebtoken": { 1005 | "version": "8.5.1", 1006 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 1007 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 1008 | "requires": { 1009 | "jws": "^3.2.2", 1010 | "lodash.includes": "^4.3.0", 1011 | "lodash.isboolean": "^3.0.3", 1012 | "lodash.isinteger": "^4.0.4", 1013 | "lodash.isnumber": "^3.0.3", 1014 | "lodash.isplainobject": "^4.0.6", 1015 | "lodash.isstring": "^4.0.1", 1016 | "lodash.once": "^4.0.0", 1017 | "ms": "^2.1.1", 1018 | "semver": "^5.6.0" 1019 | } 1020 | }, 1021 | "ms": { 1022 | "version": "2.1.1", 1023 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1024 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1025 | }, 1026 | "semver": { 1027 | "version": "5.7.0", 1028 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 1029 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 1030 | } 1031 | } 1032 | }, 1033 | "forwarded": { 1034 | "version": "0.1.2", 1035 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 1036 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 1037 | }, 1038 | "fresh": { 1039 | "version": "0.5.2", 1040 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1041 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 1042 | }, 1043 | "fs.realpath": { 1044 | "version": "1.0.0", 1045 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1046 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1047 | "optional": true 1048 | }, 1049 | "functional-red-black-tree": { 1050 | "version": "1.0.1", 1051 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1052 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 1053 | "optional": true 1054 | }, 1055 | "gaxios": { 1056 | "version": "1.8.4", 1057 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", 1058 | "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", 1059 | "optional": true, 1060 | "requires": { 1061 | "abort-controller": "^3.0.0", 1062 | "extend": "^3.0.2", 1063 | "https-proxy-agent": "^2.2.1", 1064 | "node-fetch": "^2.3.0" 1065 | } 1066 | }, 1067 | "gcp-metadata": { 1068 | "version": "1.0.0", 1069 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", 1070 | "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", 1071 | "optional": true, 1072 | "requires": { 1073 | "gaxios": "^1.0.2", 1074 | "json-bigint": "^0.3.0" 1075 | } 1076 | }, 1077 | "gcs-resumable-upload": { 1078 | "version": "1.1.0", 1079 | "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-1.1.0.tgz", 1080 | "integrity": "sha512-uBz7uHqp44xjSDzG3kLbOYZDjxxR/UAGbB47A0cC907W6yd2LkcyFDTHg+bjivkHMwiJlKv4guVWcjPCk2zScg==", 1081 | "optional": true, 1082 | "requires": { 1083 | "abort-controller": "^2.0.2", 1084 | "configstore": "^4.0.0", 1085 | "gaxios": "^1.5.0", 1086 | "google-auth-library": "^3.0.0", 1087 | "pumpify": "^1.5.1", 1088 | "stream-events": "^1.0.4" 1089 | }, 1090 | "dependencies": { 1091 | "abort-controller": { 1092 | "version": "2.0.3", 1093 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-2.0.3.tgz", 1094 | "integrity": "sha512-EPSq5wr2aFyAZ1PejJB32IX9Qd4Nwus+adnp7STYFM5/23nLPBazqZ1oor6ZqbH+4otaaGXTlC8RN5hq3C8w9Q==", 1095 | "optional": true, 1096 | "requires": { 1097 | "event-target-shim": "^5.0.0" 1098 | } 1099 | } 1100 | } 1101 | }, 1102 | "glob": { 1103 | "version": "7.1.4", 1104 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 1105 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 1106 | "optional": true, 1107 | "requires": { 1108 | "fs.realpath": "^1.0.0", 1109 | "inflight": "^1.0.4", 1110 | "inherits": "2", 1111 | "minimatch": "^3.0.4", 1112 | "once": "^1.3.0", 1113 | "path-is-absolute": "^1.0.0" 1114 | } 1115 | }, 1116 | "google-auth-library": { 1117 | "version": "3.1.2", 1118 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", 1119 | "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", 1120 | "optional": true, 1121 | "requires": { 1122 | "base64-js": "^1.3.0", 1123 | "fast-text-encoding": "^1.0.0", 1124 | "gaxios": "^1.2.1", 1125 | "gcp-metadata": "^1.0.0", 1126 | "gtoken": "^2.3.2", 1127 | "https-proxy-agent": "^2.2.1", 1128 | "jws": "^3.1.5", 1129 | "lru-cache": "^5.0.0", 1130 | "semver": "^5.5.0" 1131 | }, 1132 | "dependencies": { 1133 | "semver": { 1134 | "version": "5.7.0", 1135 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 1136 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", 1137 | "optional": true 1138 | } 1139 | } 1140 | }, 1141 | "google-gax": { 1142 | "version": "0.25.6", 1143 | "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-0.25.6.tgz", 1144 | "integrity": "sha512-+CVtOSLQt42mwVvJJirhBiAvWsp8zKeb9zW5Wy3wyvb3VG9OugHzZpwvYO9D4yNPPspe7L9CpIs80I5nUJlS8w==", 1145 | "optional": true, 1146 | "requires": { 1147 | "@grpc/grpc-js": "^0.3.0", 1148 | "@grpc/proto-loader": "^0.4.0", 1149 | "duplexify": "^3.6.0", 1150 | "google-auth-library": "^3.0.0", 1151 | "google-proto-files": "^0.20.0", 1152 | "grpc": "^1.16.0", 1153 | "grpc-gcp": "^0.1.1", 1154 | "is-stream-ended": "^0.1.4", 1155 | "lodash.at": "^4.6.0", 1156 | "lodash.has": "^4.5.2", 1157 | "protobufjs": "^6.8.8", 1158 | "retry-request": "^4.0.0", 1159 | "semver": "^6.0.0", 1160 | "walkdir": "^0.3.2" 1161 | } 1162 | }, 1163 | "google-p12-pem": { 1164 | "version": "1.0.4", 1165 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", 1166 | "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", 1167 | "optional": true, 1168 | "requires": { 1169 | "node-forge": "^0.8.0", 1170 | "pify": "^4.0.0" 1171 | }, 1172 | "dependencies": { 1173 | "node-forge": { 1174 | "version": "0.8.2", 1175 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz", 1176 | "integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg==", 1177 | "optional": true 1178 | } 1179 | } 1180 | }, 1181 | "google-proto-files": { 1182 | "version": "0.20.0", 1183 | "resolved": "https://registry.npmjs.org/google-proto-files/-/google-proto-files-0.20.0.tgz", 1184 | "integrity": "sha512-ORU+XhOeDv/UPtnCYLkO1ItmfhRCRPR3ZoeVQ7GfVzEs7PVitPIhsYlY5ZzG8XXnsdmtK27ENurfQ1jhAWpZHg==", 1185 | "optional": true, 1186 | "requires": { 1187 | "@google-cloud/promisify": "^0.4.0", 1188 | "protobufjs": "^6.8.0", 1189 | "walkdir": "^0.3.0" 1190 | } 1191 | }, 1192 | "graceful-fs": { 1193 | "version": "4.1.15", 1194 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 1195 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", 1196 | "optional": true 1197 | }, 1198 | "grpc": { 1199 | "version": "1.20.3", 1200 | "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.20.3.tgz", 1201 | "integrity": "sha512-GsEsi0NVj6usS/xor8pF/xDbDiwZQR59aZl5NUZ59Sy2bdPQFZ3UePr5wevZjHboirRCIQCKRI1cCgvSWUe2ag==", 1202 | "optional": true, 1203 | "requires": { 1204 | "lodash.camelcase": "^4.3.0", 1205 | "lodash.clone": "^4.5.0", 1206 | "nan": "^2.13.2", 1207 | "node-pre-gyp": "^0.13.0", 1208 | "protobufjs": "^5.0.3" 1209 | }, 1210 | "dependencies": { 1211 | "abbrev": { 1212 | "version": "1.1.1", 1213 | "bundled": true, 1214 | "optional": true 1215 | }, 1216 | "ansi-regex": { 1217 | "version": "2.1.1", 1218 | "bundled": true, 1219 | "optional": true 1220 | }, 1221 | "aproba": { 1222 | "version": "1.2.0", 1223 | "bundled": true, 1224 | "optional": true 1225 | }, 1226 | "are-we-there-yet": { 1227 | "version": "1.1.5", 1228 | "bundled": true, 1229 | "optional": true, 1230 | "requires": { 1231 | "delegates": "^1.0.0", 1232 | "readable-stream": "^2.0.6" 1233 | } 1234 | }, 1235 | "balanced-match": { 1236 | "version": "1.0.0", 1237 | "bundled": true, 1238 | "optional": true 1239 | }, 1240 | "brace-expansion": { 1241 | "version": "1.1.11", 1242 | "bundled": true, 1243 | "optional": true, 1244 | "requires": { 1245 | "balanced-match": "^1.0.0", 1246 | "concat-map": "0.0.1" 1247 | } 1248 | }, 1249 | "chownr": { 1250 | "version": "1.1.1", 1251 | "bundled": true, 1252 | "optional": true 1253 | }, 1254 | "code-point-at": { 1255 | "version": "1.1.0", 1256 | "bundled": true, 1257 | "optional": true 1258 | }, 1259 | "concat-map": { 1260 | "version": "0.0.1", 1261 | "bundled": true, 1262 | "optional": true 1263 | }, 1264 | "console-control-strings": { 1265 | "version": "1.1.0", 1266 | "bundled": true, 1267 | "optional": true 1268 | }, 1269 | "core-util-is": { 1270 | "version": "1.0.2", 1271 | "bundled": true, 1272 | "optional": true 1273 | }, 1274 | "deep-extend": { 1275 | "version": "0.6.0", 1276 | "bundled": true, 1277 | "optional": true 1278 | }, 1279 | "delegates": { 1280 | "version": "1.0.0", 1281 | "bundled": true, 1282 | "optional": true 1283 | }, 1284 | "detect-libc": { 1285 | "version": "1.0.3", 1286 | "bundled": true, 1287 | "optional": true 1288 | }, 1289 | "fs-minipass": { 1290 | "version": "1.2.5", 1291 | "bundled": true, 1292 | "optional": true, 1293 | "requires": { 1294 | "minipass": "^2.2.1" 1295 | } 1296 | }, 1297 | "fs.realpath": { 1298 | "version": "1.0.0", 1299 | "bundled": true, 1300 | "optional": true 1301 | }, 1302 | "gauge": { 1303 | "version": "2.7.4", 1304 | "bundled": true, 1305 | "optional": true, 1306 | "requires": { 1307 | "aproba": "^1.0.3", 1308 | "console-control-strings": "^1.0.0", 1309 | "has-unicode": "^2.0.0", 1310 | "object-assign": "^4.1.0", 1311 | "signal-exit": "^3.0.0", 1312 | "string-width": "^1.0.1", 1313 | "strip-ansi": "^3.0.1", 1314 | "wide-align": "^1.1.0" 1315 | } 1316 | }, 1317 | "has-unicode": { 1318 | "version": "2.0.1", 1319 | "bundled": true, 1320 | "optional": true 1321 | }, 1322 | "iconv-lite": { 1323 | "version": "0.4.23", 1324 | "bundled": true, 1325 | "optional": true, 1326 | "requires": { 1327 | "safer-buffer": ">= 2.1.2 < 3" 1328 | } 1329 | }, 1330 | "ignore-walk": { 1331 | "version": "3.0.1", 1332 | "bundled": true, 1333 | "optional": true, 1334 | "requires": { 1335 | "minimatch": "^3.0.4" 1336 | } 1337 | }, 1338 | "inflight": { 1339 | "version": "1.0.6", 1340 | "bundled": true, 1341 | "optional": true, 1342 | "requires": { 1343 | "once": "^1.3.0", 1344 | "wrappy": "1" 1345 | } 1346 | }, 1347 | "inherits": { 1348 | "version": "2.0.3", 1349 | "bundled": true, 1350 | "optional": true 1351 | }, 1352 | "ini": { 1353 | "version": "1.3.5", 1354 | "bundled": true, 1355 | "optional": true 1356 | }, 1357 | "is-fullwidth-code-point": { 1358 | "version": "1.0.0", 1359 | "bundled": true, 1360 | "optional": true, 1361 | "requires": { 1362 | "number-is-nan": "^1.0.0" 1363 | } 1364 | }, 1365 | "isarray": { 1366 | "version": "1.0.0", 1367 | "bundled": true, 1368 | "optional": true 1369 | }, 1370 | "minimatch": { 1371 | "version": "3.0.4", 1372 | "bundled": true, 1373 | "optional": true, 1374 | "requires": { 1375 | "brace-expansion": "^1.1.7" 1376 | } 1377 | }, 1378 | "minimist": { 1379 | "version": "1.2.0", 1380 | "bundled": true, 1381 | "optional": true 1382 | }, 1383 | "minipass": { 1384 | "version": "2.3.5", 1385 | "bundled": true, 1386 | "optional": true, 1387 | "requires": { 1388 | "safe-buffer": "^5.1.2", 1389 | "yallist": "^3.0.0" 1390 | } 1391 | }, 1392 | "minizlib": { 1393 | "version": "1.2.1", 1394 | "bundled": true, 1395 | "optional": true, 1396 | "requires": { 1397 | "minipass": "^2.2.1" 1398 | } 1399 | }, 1400 | "mkdirp": { 1401 | "version": "0.5.1", 1402 | "bundled": true, 1403 | "optional": true, 1404 | "requires": { 1405 | "minimist": "0.0.8" 1406 | }, 1407 | "dependencies": { 1408 | "minimist": { 1409 | "version": "0.0.8", 1410 | "bundled": true, 1411 | "optional": true 1412 | } 1413 | } 1414 | }, 1415 | "needle": { 1416 | "version": "2.3.1", 1417 | "bundled": true, 1418 | "optional": true, 1419 | "requires": { 1420 | "debug": "^4.1.0", 1421 | "iconv-lite": "^0.4.4", 1422 | "sax": "^1.2.4" 1423 | }, 1424 | "dependencies": { 1425 | "debug": { 1426 | "version": "4.1.1", 1427 | "bundled": true, 1428 | "optional": true, 1429 | "requires": { 1430 | "ms": "^2.1.1" 1431 | } 1432 | }, 1433 | "ms": { 1434 | "version": "2.1.1", 1435 | "bundled": true, 1436 | "optional": true 1437 | } 1438 | } 1439 | }, 1440 | "node-pre-gyp": { 1441 | "version": "0.13.0", 1442 | "bundled": true, 1443 | "optional": true, 1444 | "requires": { 1445 | "detect-libc": "^1.0.2", 1446 | "mkdirp": "^0.5.1", 1447 | "needle": "^2.2.1", 1448 | "nopt": "^4.0.1", 1449 | "npm-packlist": "^1.1.6", 1450 | "npmlog": "^4.0.2", 1451 | "rc": "^1.2.7", 1452 | "rimraf": "^2.6.1", 1453 | "semver": "^5.3.0", 1454 | "tar": "^4" 1455 | } 1456 | }, 1457 | "nopt": { 1458 | "version": "4.0.1", 1459 | "bundled": true, 1460 | "optional": true, 1461 | "requires": { 1462 | "abbrev": "1", 1463 | "osenv": "^0.1.4" 1464 | } 1465 | }, 1466 | "npm-bundled": { 1467 | "version": "1.0.6", 1468 | "bundled": true, 1469 | "optional": true 1470 | }, 1471 | "npm-packlist": { 1472 | "version": "1.4.1", 1473 | "bundled": true, 1474 | "optional": true, 1475 | "requires": { 1476 | "ignore-walk": "^3.0.1", 1477 | "npm-bundled": "^1.0.1" 1478 | } 1479 | }, 1480 | "npmlog": { 1481 | "version": "4.1.2", 1482 | "bundled": true, 1483 | "optional": true, 1484 | "requires": { 1485 | "are-we-there-yet": "~1.1.2", 1486 | "console-control-strings": "~1.1.0", 1487 | "gauge": "~2.7.3", 1488 | "set-blocking": "~2.0.0" 1489 | } 1490 | }, 1491 | "number-is-nan": { 1492 | "version": "1.0.1", 1493 | "bundled": true, 1494 | "optional": true 1495 | }, 1496 | "object-assign": { 1497 | "version": "4.1.1", 1498 | "bundled": true, 1499 | "optional": true 1500 | }, 1501 | "once": { 1502 | "version": "1.4.0", 1503 | "bundled": true, 1504 | "optional": true, 1505 | "requires": { 1506 | "wrappy": "1" 1507 | } 1508 | }, 1509 | "os-homedir": { 1510 | "version": "1.0.2", 1511 | "bundled": true, 1512 | "optional": true 1513 | }, 1514 | "os-tmpdir": { 1515 | "version": "1.0.2", 1516 | "bundled": true, 1517 | "optional": true 1518 | }, 1519 | "osenv": { 1520 | "version": "0.1.5", 1521 | "bundled": true, 1522 | "optional": true, 1523 | "requires": { 1524 | "os-homedir": "^1.0.0", 1525 | "os-tmpdir": "^1.0.0" 1526 | } 1527 | }, 1528 | "path-is-absolute": { 1529 | "version": "1.0.1", 1530 | "bundled": true, 1531 | "optional": true 1532 | }, 1533 | "process-nextick-args": { 1534 | "version": "2.0.0", 1535 | "bundled": true, 1536 | "optional": true 1537 | }, 1538 | "protobufjs": { 1539 | "version": "5.0.3", 1540 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", 1541 | "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", 1542 | "optional": true, 1543 | "requires": { 1544 | "ascli": "~1", 1545 | "bytebuffer": "~5", 1546 | "glob": "^7.0.5", 1547 | "yargs": "^3.10.0" 1548 | } 1549 | }, 1550 | "rc": { 1551 | "version": "1.2.8", 1552 | "bundled": true, 1553 | "optional": true, 1554 | "requires": { 1555 | "deep-extend": "^0.6.0", 1556 | "ini": "~1.3.0", 1557 | "minimist": "^1.2.0", 1558 | "strip-json-comments": "~2.0.1" 1559 | } 1560 | }, 1561 | "readable-stream": { 1562 | "version": "2.3.6", 1563 | "bundled": true, 1564 | "optional": true, 1565 | "requires": { 1566 | "core-util-is": "~1.0.0", 1567 | "inherits": "~2.0.3", 1568 | "isarray": "~1.0.0", 1569 | "process-nextick-args": "~2.0.0", 1570 | "safe-buffer": "~5.1.1", 1571 | "string_decoder": "~1.1.1", 1572 | "util-deprecate": "~1.0.1" 1573 | } 1574 | }, 1575 | "rimraf": { 1576 | "version": "2.6.3", 1577 | "bundled": true, 1578 | "optional": true, 1579 | "requires": { 1580 | "glob": "^7.1.3" 1581 | }, 1582 | "dependencies": { 1583 | "glob": { 1584 | "version": "7.1.3", 1585 | "bundled": true, 1586 | "optional": true, 1587 | "requires": { 1588 | "fs.realpath": "^1.0.0", 1589 | "inflight": "^1.0.4", 1590 | "inherits": "2", 1591 | "minimatch": "^3.0.4", 1592 | "once": "^1.3.0", 1593 | "path-is-absolute": "^1.0.0" 1594 | } 1595 | } 1596 | } 1597 | }, 1598 | "safe-buffer": { 1599 | "version": "5.1.2", 1600 | "bundled": true, 1601 | "optional": true 1602 | }, 1603 | "safer-buffer": { 1604 | "version": "2.1.2", 1605 | "bundled": true, 1606 | "optional": true 1607 | }, 1608 | "sax": { 1609 | "version": "1.2.4", 1610 | "bundled": true, 1611 | "optional": true 1612 | }, 1613 | "semver": { 1614 | "version": "5.7.0", 1615 | "bundled": true, 1616 | "optional": true 1617 | }, 1618 | "set-blocking": { 1619 | "version": "2.0.0", 1620 | "bundled": true, 1621 | "optional": true 1622 | }, 1623 | "signal-exit": { 1624 | "version": "3.0.2", 1625 | "bundled": true, 1626 | "optional": true 1627 | }, 1628 | "string-width": { 1629 | "version": "1.0.2", 1630 | "bundled": true, 1631 | "optional": true, 1632 | "requires": { 1633 | "code-point-at": "^1.0.0", 1634 | "is-fullwidth-code-point": "^1.0.0", 1635 | "strip-ansi": "^3.0.0" 1636 | } 1637 | }, 1638 | "string_decoder": { 1639 | "version": "1.1.1", 1640 | "bundled": true, 1641 | "optional": true, 1642 | "requires": { 1643 | "safe-buffer": "~5.1.0" 1644 | } 1645 | }, 1646 | "strip-ansi": { 1647 | "version": "3.0.1", 1648 | "bundled": true, 1649 | "optional": true, 1650 | "requires": { 1651 | "ansi-regex": "^2.0.0" 1652 | } 1653 | }, 1654 | "strip-json-comments": { 1655 | "version": "2.0.1", 1656 | "bundled": true, 1657 | "optional": true 1658 | }, 1659 | "tar": { 1660 | "version": "4.4.8", 1661 | "bundled": true, 1662 | "optional": true, 1663 | "requires": { 1664 | "chownr": "^1.1.1", 1665 | "fs-minipass": "^1.2.5", 1666 | "minipass": "^2.3.4", 1667 | "minizlib": "^1.1.1", 1668 | "mkdirp": "^0.5.0", 1669 | "safe-buffer": "^5.1.2", 1670 | "yallist": "^3.0.2" 1671 | } 1672 | }, 1673 | "util-deprecate": { 1674 | "version": "1.0.2", 1675 | "bundled": true, 1676 | "optional": true 1677 | }, 1678 | "wide-align": { 1679 | "version": "1.1.3", 1680 | "bundled": true, 1681 | "optional": true, 1682 | "requires": { 1683 | "string-width": "^1.0.2 || 2" 1684 | } 1685 | }, 1686 | "wrappy": { 1687 | "version": "1.0.2", 1688 | "bundled": true, 1689 | "optional": true 1690 | }, 1691 | "yallist": { 1692 | "version": "3.0.3", 1693 | "bundled": true, 1694 | "optional": true 1695 | } 1696 | } 1697 | }, 1698 | "grpc-gcp": { 1699 | "version": "0.1.1", 1700 | "resolved": "https://registry.npmjs.org/grpc-gcp/-/grpc-gcp-0.1.1.tgz", 1701 | "integrity": "sha512-MAt0Ae9QuL2Lbbt2d+kDta5AxqRD1JVXtBcJuQKp9GeFL5TxPw/hxIyDNyivPjKEXjbG3cBGwSE3CXq6a3KHEQ==", 1702 | "optional": true, 1703 | "requires": { 1704 | "grpc": "^1.16.0", 1705 | "protobufjs": "^6.8.8" 1706 | } 1707 | }, 1708 | "gtoken": { 1709 | "version": "2.3.3", 1710 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", 1711 | "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", 1712 | "optional": true, 1713 | "requires": { 1714 | "gaxios": "^1.0.4", 1715 | "google-p12-pem": "^1.0.0", 1716 | "jws": "^3.1.5", 1717 | "mime": "^2.2.0", 1718 | "pify": "^4.0.0" 1719 | }, 1720 | "dependencies": { 1721 | "mime": { 1722 | "version": "2.4.3", 1723 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", 1724 | "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==", 1725 | "optional": true 1726 | } 1727 | } 1728 | }, 1729 | "hash-stream-validation": { 1730 | "version": "0.2.1", 1731 | "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.1.tgz", 1732 | "integrity": "sha1-7Mm5l7IYvluzEphii7gHhptz3NE=", 1733 | "optional": true, 1734 | "requires": { 1735 | "through2": "^2.0.0" 1736 | }, 1737 | "dependencies": { 1738 | "isarray": { 1739 | "version": "1.0.0", 1740 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1741 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 1742 | "optional": true 1743 | }, 1744 | "readable-stream": { 1745 | "version": "2.3.6", 1746 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1747 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1748 | "optional": true, 1749 | "requires": { 1750 | "core-util-is": "~1.0.0", 1751 | "inherits": "~2.0.3", 1752 | "isarray": "~1.0.0", 1753 | "process-nextick-args": "~2.0.0", 1754 | "safe-buffer": "~5.1.1", 1755 | "string_decoder": "~1.1.1", 1756 | "util-deprecate": "~1.0.1" 1757 | } 1758 | }, 1759 | "string_decoder": { 1760 | "version": "1.1.1", 1761 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1762 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1763 | "optional": true, 1764 | "requires": { 1765 | "safe-buffer": "~5.1.0" 1766 | } 1767 | }, 1768 | "through2": { 1769 | "version": "2.0.5", 1770 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 1771 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 1772 | "optional": true, 1773 | "requires": { 1774 | "readable-stream": "~2.3.6", 1775 | "xtend": "~4.0.1" 1776 | } 1777 | } 1778 | } 1779 | }, 1780 | "http-errors": { 1781 | "version": "1.7.2", 1782 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 1783 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 1784 | "requires": { 1785 | "depd": "~1.1.2", 1786 | "inherits": "2.0.3", 1787 | "setprototypeof": "1.1.1", 1788 | "statuses": ">= 1.5.0 < 2", 1789 | "toidentifier": "1.0.0" 1790 | } 1791 | }, 1792 | "http-parser-js": { 1793 | "version": "0.5.0", 1794 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", 1795 | "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==" 1796 | }, 1797 | "http_ece": { 1798 | "version": "1.1.0", 1799 | "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.1.0.tgz", 1800 | "integrity": "sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==", 1801 | "requires": { 1802 | "urlsafe-base64": "~1.0.0" 1803 | } 1804 | }, 1805 | "https-proxy-agent": { 1806 | "version": "2.2.1", 1807 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", 1808 | "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", 1809 | "requires": { 1810 | "agent-base": "^4.1.0", 1811 | "debug": "^3.1.0" 1812 | }, 1813 | "dependencies": { 1814 | "debug": { 1815 | "version": "3.2.6", 1816 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 1817 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 1818 | "requires": { 1819 | "ms": "^2.1.1" 1820 | } 1821 | }, 1822 | "ms": { 1823 | "version": "2.1.1", 1824 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1825 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1826 | } 1827 | } 1828 | }, 1829 | "iconv-lite": { 1830 | "version": "0.4.24", 1831 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1832 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1833 | "requires": { 1834 | "safer-buffer": ">= 2.1.2 < 3" 1835 | } 1836 | }, 1837 | "imurmurhash": { 1838 | "version": "0.1.4", 1839 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1840 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1841 | "optional": true 1842 | }, 1843 | "inflight": { 1844 | "version": "1.0.6", 1845 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1846 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1847 | "optional": true, 1848 | "requires": { 1849 | "once": "^1.3.0", 1850 | "wrappy": "1" 1851 | } 1852 | }, 1853 | "inherits": { 1854 | "version": "2.0.3", 1855 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1856 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1857 | }, 1858 | "invert-kv": { 1859 | "version": "1.0.0", 1860 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 1861 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", 1862 | "optional": true 1863 | }, 1864 | "ipaddr.js": { 1865 | "version": "1.9.0", 1866 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 1867 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 1868 | }, 1869 | "is-fullwidth-code-point": { 1870 | "version": "1.0.0", 1871 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1872 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1873 | "optional": true, 1874 | "requires": { 1875 | "number-is-nan": "^1.0.0" 1876 | } 1877 | }, 1878 | "is-obj": { 1879 | "version": "1.0.1", 1880 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 1881 | "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", 1882 | "optional": true 1883 | }, 1884 | "is-stream-ended": { 1885 | "version": "0.1.4", 1886 | "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", 1887 | "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", 1888 | "optional": true 1889 | }, 1890 | "isarray": { 1891 | "version": "0.0.1", 1892 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1893 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 1894 | "optional": true 1895 | }, 1896 | "json-bigint": { 1897 | "version": "0.3.0", 1898 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", 1899 | "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", 1900 | "optional": true, 1901 | "requires": { 1902 | "bignumber.js": "^7.0.0" 1903 | } 1904 | }, 1905 | "jsonwebtoken": { 1906 | "version": "8.1.0", 1907 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.1.0.tgz", 1908 | "integrity": "sha1-xjl80uX9WD1lwAeoPce7eOaYK4M=", 1909 | "requires": { 1910 | "jws": "^3.1.4", 1911 | "lodash.includes": "^4.3.0", 1912 | "lodash.isboolean": "^3.0.3", 1913 | "lodash.isinteger": "^4.0.4", 1914 | "lodash.isnumber": "^3.0.3", 1915 | "lodash.isplainobject": "^4.0.6", 1916 | "lodash.isstring": "^4.0.1", 1917 | "lodash.once": "^4.0.0", 1918 | "ms": "^2.0.0", 1919 | "xtend": "^4.0.1" 1920 | } 1921 | }, 1922 | "jwa": { 1923 | "version": "1.4.1", 1924 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 1925 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 1926 | "requires": { 1927 | "buffer-equal-constant-time": "1.0.1", 1928 | "ecdsa-sig-formatter": "1.0.11", 1929 | "safe-buffer": "^5.0.1" 1930 | } 1931 | }, 1932 | "jws": { 1933 | "version": "3.2.2", 1934 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 1935 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 1936 | "requires": { 1937 | "jwa": "^1.4.1", 1938 | "safe-buffer": "^5.0.1" 1939 | } 1940 | }, 1941 | "lcid": { 1942 | "version": "1.0.0", 1943 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 1944 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 1945 | "optional": true, 1946 | "requires": { 1947 | "invert-kv": "^1.0.0" 1948 | } 1949 | }, 1950 | "lodash": { 1951 | "version": "4.17.11", 1952 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 1953 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 1954 | }, 1955 | "lodash.at": { 1956 | "version": "4.6.0", 1957 | "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", 1958 | "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=", 1959 | "optional": true 1960 | }, 1961 | "lodash.camelcase": { 1962 | "version": "4.3.0", 1963 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 1964 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", 1965 | "optional": true 1966 | }, 1967 | "lodash.clone": { 1968 | "version": "4.5.0", 1969 | "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", 1970 | "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", 1971 | "optional": true 1972 | }, 1973 | "lodash.has": { 1974 | "version": "4.5.2", 1975 | "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", 1976 | "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=", 1977 | "optional": true 1978 | }, 1979 | "lodash.includes": { 1980 | "version": "4.3.0", 1981 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 1982 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 1983 | }, 1984 | "lodash.isboolean": { 1985 | "version": "3.0.3", 1986 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 1987 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 1988 | }, 1989 | "lodash.isinteger": { 1990 | "version": "4.0.4", 1991 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 1992 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 1993 | }, 1994 | "lodash.isnumber": { 1995 | "version": "3.0.3", 1996 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 1997 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 1998 | }, 1999 | "lodash.isplainobject": { 2000 | "version": "4.0.6", 2001 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 2002 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 2003 | }, 2004 | "lodash.isstring": { 2005 | "version": "4.0.1", 2006 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 2007 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 2008 | }, 2009 | "lodash.merge": { 2010 | "version": "4.6.1", 2011 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", 2012 | "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", 2013 | "optional": true 2014 | }, 2015 | "lodash.once": { 2016 | "version": "4.1.1", 2017 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 2018 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 2019 | }, 2020 | "long": { 2021 | "version": "4.0.0", 2022 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", 2023 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", 2024 | "optional": true 2025 | }, 2026 | "lru-cache": { 2027 | "version": "5.1.1", 2028 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 2029 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 2030 | "optional": true, 2031 | "requires": { 2032 | "yallist": "^3.0.2" 2033 | } 2034 | }, 2035 | "make-dir": { 2036 | "version": "1.3.0", 2037 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", 2038 | "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", 2039 | "optional": true, 2040 | "requires": { 2041 | "pify": "^3.0.0" 2042 | }, 2043 | "dependencies": { 2044 | "pify": { 2045 | "version": "3.0.0", 2046 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 2047 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 2048 | "optional": true 2049 | } 2050 | } 2051 | }, 2052 | "media-typer": { 2053 | "version": "0.3.0", 2054 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 2055 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 2056 | }, 2057 | "merge-descriptors": { 2058 | "version": "1.0.1", 2059 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 2060 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 2061 | }, 2062 | "methods": { 2063 | "version": "1.1.2", 2064 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 2065 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 2066 | }, 2067 | "mime": { 2068 | "version": "1.4.1", 2069 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 2070 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 2071 | }, 2072 | "mime-db": { 2073 | "version": "1.40.0", 2074 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 2075 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 2076 | }, 2077 | "mime-types": { 2078 | "version": "2.1.24", 2079 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 2080 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 2081 | "requires": { 2082 | "mime-db": "1.40.0" 2083 | } 2084 | }, 2085 | "mimic-fn": { 2086 | "version": "2.1.0", 2087 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 2088 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 2089 | "optional": true 2090 | }, 2091 | "minimalistic-assert": { 2092 | "version": "1.0.1", 2093 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 2094 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 2095 | }, 2096 | "minimatch": { 2097 | "version": "3.0.4", 2098 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 2099 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 2100 | "optional": true, 2101 | "requires": { 2102 | "brace-expansion": "^1.1.7" 2103 | } 2104 | }, 2105 | "minimist": { 2106 | "version": "1.2.0", 2107 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 2108 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 2109 | }, 2110 | "ms": { 2111 | "version": "2.0.0", 2112 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 2113 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 2114 | }, 2115 | "nan": { 2116 | "version": "2.13.2", 2117 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", 2118 | "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", 2119 | "optional": true 2120 | }, 2121 | "negotiator": { 2122 | "version": "0.6.2", 2123 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 2124 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 2125 | }, 2126 | "node-fetch": { 2127 | "version": "2.5.0", 2128 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.5.0.tgz", 2129 | "integrity": "sha512-YuZKluhWGJwCcUu4RlZstdAxr8bFfOVHakc1mplwHkk8J+tqM1Y5yraYvIUpeX8aY7+crCwiELJq7Vl0o0LWXw==", 2130 | "optional": true 2131 | }, 2132 | "node-forge": { 2133 | "version": "0.7.4", 2134 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.4.tgz", 2135 | "integrity": "sha512-8Df0906+tq/omxuCZD6PqhPaQDYuyJ1d+VITgxoIA8zvQd1ru+nMJcDChHH324MWitIgbVkAkQoGEEVJNpn/PA==" 2136 | }, 2137 | "number-is-nan": { 2138 | "version": "1.0.1", 2139 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 2140 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 2141 | "optional": true 2142 | }, 2143 | "object-assign": { 2144 | "version": "4.1.1", 2145 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 2146 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 2147 | }, 2148 | "on-finished": { 2149 | "version": "2.3.0", 2150 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 2151 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 2152 | "requires": { 2153 | "ee-first": "1.1.1" 2154 | } 2155 | }, 2156 | "once": { 2157 | "version": "1.4.0", 2158 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 2159 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 2160 | "optional": true, 2161 | "requires": { 2162 | "wrappy": "1" 2163 | } 2164 | }, 2165 | "onetime": { 2166 | "version": "5.1.0", 2167 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", 2168 | "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", 2169 | "optional": true, 2170 | "requires": { 2171 | "mimic-fn": "^2.1.0" 2172 | } 2173 | }, 2174 | "optjs": { 2175 | "version": "3.2.2", 2176 | "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", 2177 | "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", 2178 | "optional": true 2179 | }, 2180 | "os-locale": { 2181 | "version": "1.4.0", 2182 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 2183 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 2184 | "optional": true, 2185 | "requires": { 2186 | "lcid": "^1.0.0" 2187 | } 2188 | }, 2189 | "parseurl": { 2190 | "version": "1.3.3", 2191 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 2192 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 2193 | }, 2194 | "path-is-absolute": { 2195 | "version": "1.0.1", 2196 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2197 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 2198 | "optional": true 2199 | }, 2200 | "path-to-regexp": { 2201 | "version": "0.1.7", 2202 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 2203 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 2204 | }, 2205 | "pify": { 2206 | "version": "4.0.1", 2207 | "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", 2208 | "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", 2209 | "optional": true 2210 | }, 2211 | "process-nextick-args": { 2212 | "version": "2.0.0", 2213 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 2214 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 2215 | "optional": true 2216 | }, 2217 | "protobufjs": { 2218 | "version": "6.8.8", 2219 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", 2220 | "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", 2221 | "optional": true, 2222 | "requires": { 2223 | "@protobufjs/aspromise": "^1.1.2", 2224 | "@protobufjs/base64": "^1.1.2", 2225 | "@protobufjs/codegen": "^2.0.4", 2226 | "@protobufjs/eventemitter": "^1.1.0", 2227 | "@protobufjs/fetch": "^1.1.0", 2228 | "@protobufjs/float": "^1.0.2", 2229 | "@protobufjs/inquire": "^1.1.0", 2230 | "@protobufjs/path": "^1.1.2", 2231 | "@protobufjs/pool": "^1.1.0", 2232 | "@protobufjs/utf8": "^1.1.0", 2233 | "@types/long": "^4.0.0", 2234 | "@types/node": "^10.1.0", 2235 | "long": "^4.0.0" 2236 | }, 2237 | "dependencies": { 2238 | "@types/node": { 2239 | "version": "10.14.6", 2240 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", 2241 | "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==", 2242 | "optional": true 2243 | } 2244 | } 2245 | }, 2246 | "proxy-addr": { 2247 | "version": "2.0.5", 2248 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 2249 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 2250 | "requires": { 2251 | "forwarded": "~0.1.2", 2252 | "ipaddr.js": "1.9.0" 2253 | } 2254 | }, 2255 | "pump": { 2256 | "version": "2.0.1", 2257 | "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", 2258 | "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", 2259 | "optional": true, 2260 | "requires": { 2261 | "end-of-stream": "^1.1.0", 2262 | "once": "^1.3.1" 2263 | } 2264 | }, 2265 | "pumpify": { 2266 | "version": "1.5.1", 2267 | "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", 2268 | "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", 2269 | "optional": true, 2270 | "requires": { 2271 | "duplexify": "^3.6.0", 2272 | "inherits": "^2.0.3", 2273 | "pump": "^2.0.0" 2274 | } 2275 | }, 2276 | "qs": { 2277 | "version": "6.7.0", 2278 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 2279 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 2280 | }, 2281 | "range-parser": { 2282 | "version": "1.2.1", 2283 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 2284 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 2285 | }, 2286 | "raw-body": { 2287 | "version": "2.4.0", 2288 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 2289 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 2290 | "requires": { 2291 | "bytes": "3.1.0", 2292 | "http-errors": "1.7.2", 2293 | "iconv-lite": "0.4.24", 2294 | "unpipe": "1.0.0" 2295 | } 2296 | }, 2297 | "readable-stream": { 2298 | "version": "1.0.34", 2299 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 2300 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 2301 | "optional": true, 2302 | "requires": { 2303 | "core-util-is": "~1.0.0", 2304 | "inherits": "~2.0.1", 2305 | "isarray": "0.0.1", 2306 | "string_decoder": "~0.10.x" 2307 | } 2308 | }, 2309 | "retry-request": { 2310 | "version": "4.0.0", 2311 | "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", 2312 | "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", 2313 | "optional": true, 2314 | "requires": { 2315 | "through2": "^2.0.0" 2316 | }, 2317 | "dependencies": { 2318 | "isarray": { 2319 | "version": "1.0.0", 2320 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 2321 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 2322 | "optional": true 2323 | }, 2324 | "readable-stream": { 2325 | "version": "2.3.6", 2326 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 2327 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 2328 | "optional": true, 2329 | "requires": { 2330 | "core-util-is": "~1.0.0", 2331 | "inherits": "~2.0.3", 2332 | "isarray": "~1.0.0", 2333 | "process-nextick-args": "~2.0.0", 2334 | "safe-buffer": "~5.1.1", 2335 | "string_decoder": "~1.1.1", 2336 | "util-deprecate": "~1.0.1" 2337 | } 2338 | }, 2339 | "string_decoder": { 2340 | "version": "1.1.1", 2341 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2342 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2343 | "optional": true, 2344 | "requires": { 2345 | "safe-buffer": "~5.1.0" 2346 | } 2347 | }, 2348 | "through2": { 2349 | "version": "2.0.5", 2350 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 2351 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 2352 | "optional": true, 2353 | "requires": { 2354 | "readable-stream": "~2.3.6", 2355 | "xtend": "~4.0.1" 2356 | } 2357 | } 2358 | } 2359 | }, 2360 | "safe-buffer": { 2361 | "version": "5.1.2", 2362 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2363 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 2364 | }, 2365 | "safer-buffer": { 2366 | "version": "2.1.2", 2367 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2368 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2369 | }, 2370 | "semver": { 2371 | "version": "6.0.0", 2372 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", 2373 | "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", 2374 | "optional": true 2375 | }, 2376 | "send": { 2377 | "version": "0.16.2", 2378 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 2379 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 2380 | "requires": { 2381 | "debug": "2.6.9", 2382 | "depd": "~1.1.2", 2383 | "destroy": "~1.0.4", 2384 | "encodeurl": "~1.0.2", 2385 | "escape-html": "~1.0.3", 2386 | "etag": "~1.8.1", 2387 | "fresh": "0.5.2", 2388 | "http-errors": "~1.6.2", 2389 | "mime": "1.4.1", 2390 | "ms": "2.0.0", 2391 | "on-finished": "~2.3.0", 2392 | "range-parser": "~1.2.0", 2393 | "statuses": "~1.4.0" 2394 | }, 2395 | "dependencies": { 2396 | "http-errors": { 2397 | "version": "1.6.3", 2398 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 2399 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 2400 | "requires": { 2401 | "depd": "~1.1.2", 2402 | "inherits": "2.0.3", 2403 | "setprototypeof": "1.1.0", 2404 | "statuses": ">= 1.4.0 < 2" 2405 | } 2406 | }, 2407 | "setprototypeof": { 2408 | "version": "1.1.0", 2409 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 2410 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 2411 | }, 2412 | "statuses": { 2413 | "version": "1.4.0", 2414 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 2415 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 2416 | } 2417 | } 2418 | }, 2419 | "serve-static": { 2420 | "version": "1.13.2", 2421 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 2422 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 2423 | "requires": { 2424 | "encodeurl": "~1.0.2", 2425 | "escape-html": "~1.0.3", 2426 | "parseurl": "~1.3.2", 2427 | "send": "0.16.2" 2428 | } 2429 | }, 2430 | "setprototypeof": { 2431 | "version": "1.1.1", 2432 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 2433 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 2434 | }, 2435 | "signal-exit": { 2436 | "version": "3.0.2", 2437 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 2438 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 2439 | "optional": true 2440 | }, 2441 | "snakeize": { 2442 | "version": "0.1.0", 2443 | "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", 2444 | "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=", 2445 | "optional": true 2446 | }, 2447 | "split-array-stream": { 2448 | "version": "2.0.0", 2449 | "resolved": "https://registry.npmjs.org/split-array-stream/-/split-array-stream-2.0.0.tgz", 2450 | "integrity": "sha512-hmMswlVY91WvGMxs0k8MRgq8zb2mSen4FmDNc5AFiTWtrBpdZN6nwD6kROVe4vNL+ywrvbCKsWVCnEd4riELIg==", 2451 | "optional": true, 2452 | "requires": { 2453 | "is-stream-ended": "^0.1.4" 2454 | } 2455 | }, 2456 | "statuses": { 2457 | "version": "1.5.0", 2458 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2459 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2460 | }, 2461 | "stream-events": { 2462 | "version": "1.0.5", 2463 | "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", 2464 | "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", 2465 | "optional": true, 2466 | "requires": { 2467 | "stubs": "^3.0.0" 2468 | } 2469 | }, 2470 | "stream-shift": { 2471 | "version": "1.0.0", 2472 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", 2473 | "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", 2474 | "optional": true 2475 | }, 2476 | "streamsearch": { 2477 | "version": "0.1.2", 2478 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", 2479 | "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" 2480 | }, 2481 | "string-width": { 2482 | "version": "1.0.2", 2483 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 2484 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 2485 | "optional": true, 2486 | "requires": { 2487 | "code-point-at": "^1.0.0", 2488 | "is-fullwidth-code-point": "^1.0.0", 2489 | "strip-ansi": "^3.0.0" 2490 | } 2491 | }, 2492 | "string_decoder": { 2493 | "version": "0.10.31", 2494 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 2495 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 2496 | "optional": true 2497 | }, 2498 | "strip-ansi": { 2499 | "version": "3.0.1", 2500 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 2501 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 2502 | "optional": true, 2503 | "requires": { 2504 | "ansi-regex": "^2.0.0" 2505 | } 2506 | }, 2507 | "stubs": { 2508 | "version": "3.0.0", 2509 | "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", 2510 | "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", 2511 | "optional": true 2512 | }, 2513 | "teeny-request": { 2514 | "version": "3.11.3", 2515 | "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", 2516 | "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", 2517 | "optional": true, 2518 | "requires": { 2519 | "https-proxy-agent": "^2.2.1", 2520 | "node-fetch": "^2.2.0", 2521 | "uuid": "^3.3.2" 2522 | } 2523 | }, 2524 | "through2": { 2525 | "version": "3.0.1", 2526 | "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", 2527 | "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", 2528 | "optional": true, 2529 | "requires": { 2530 | "readable-stream": "2 || 3" 2531 | }, 2532 | "dependencies": { 2533 | "readable-stream": { 2534 | "version": "3.3.0", 2535 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", 2536 | "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", 2537 | "optional": true, 2538 | "requires": { 2539 | "inherits": "^2.0.3", 2540 | "string_decoder": "^1.1.1", 2541 | "util-deprecate": "^1.0.1" 2542 | } 2543 | }, 2544 | "string_decoder": { 2545 | "version": "1.2.0", 2546 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", 2547 | "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", 2548 | "optional": true, 2549 | "requires": { 2550 | "safe-buffer": "~5.1.0" 2551 | } 2552 | } 2553 | } 2554 | }, 2555 | "toidentifier": { 2556 | "version": "1.0.0", 2557 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 2558 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 2559 | }, 2560 | "tslib": { 2561 | "version": "1.9.3", 2562 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", 2563 | "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" 2564 | }, 2565 | "type-is": { 2566 | "version": "1.6.18", 2567 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 2568 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 2569 | "requires": { 2570 | "media-typer": "0.3.0", 2571 | "mime-types": "~2.1.24" 2572 | } 2573 | }, 2574 | "typedarray": { 2575 | "version": "0.0.6", 2576 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 2577 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 2578 | "optional": true 2579 | }, 2580 | "unique-string": { 2581 | "version": "1.0.0", 2582 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", 2583 | "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", 2584 | "optional": true, 2585 | "requires": { 2586 | "crypto-random-string": "^1.0.0" 2587 | } 2588 | }, 2589 | "unpipe": { 2590 | "version": "1.0.0", 2591 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2592 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2593 | }, 2594 | "urlsafe-base64": { 2595 | "version": "1.0.0", 2596 | "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", 2597 | "integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY=" 2598 | }, 2599 | "util-deprecate": { 2600 | "version": "1.0.2", 2601 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2602 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 2603 | "optional": true 2604 | }, 2605 | "utils-merge": { 2606 | "version": "1.0.1", 2607 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2608 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 2609 | }, 2610 | "uuid": { 2611 | "version": "3.3.2", 2612 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 2613 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 2614 | "optional": true 2615 | }, 2616 | "vary": { 2617 | "version": "1.1.2", 2618 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2619 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 2620 | }, 2621 | "walkdir": { 2622 | "version": "0.3.2", 2623 | "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.3.2.tgz", 2624 | "integrity": "sha512-0Twghia4Z5wDGDYWURlhZmI47GvERMCsXIu0QZWVVZyW9ZjpbbZvD9Zy9M6cWiQQRRbAcYajIyKNavaZZDt1Uw==", 2625 | "optional": true 2626 | }, 2627 | "web-push": { 2628 | "version": "3.3.4", 2629 | "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.3.4.tgz", 2630 | "integrity": "sha512-OLJbQPFX9g5mHhqxFBr9bdGK7YIEBkk4lXtUn+HpYp0HdoDIdgJvIa+ktJkDsRUOKksQqKIEaLAVuS7ewuC5nA==", 2631 | "requires": { 2632 | "asn1.js": "^5.0.0", 2633 | "http_ece": "1.1.0", 2634 | "https-proxy-agent": "^2.2.1", 2635 | "jws": "^3.1.3", 2636 | "minimist": "^1.2.0", 2637 | "urlsafe-base64": "^1.0.0" 2638 | } 2639 | }, 2640 | "websocket-driver": { 2641 | "version": "0.7.0", 2642 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", 2643 | "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", 2644 | "requires": { 2645 | "http-parser-js": ">=0.4.0", 2646 | "websocket-extensions": ">=0.1.1" 2647 | } 2648 | }, 2649 | "websocket-extensions": { 2650 | "version": "0.1.3", 2651 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", 2652 | "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" 2653 | }, 2654 | "window-size": { 2655 | "version": "0.1.4", 2656 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", 2657 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", 2658 | "optional": true 2659 | }, 2660 | "wrap-ansi": { 2661 | "version": "2.1.0", 2662 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 2663 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 2664 | "optional": true, 2665 | "requires": { 2666 | "string-width": "^1.0.1", 2667 | "strip-ansi": "^3.0.1" 2668 | } 2669 | }, 2670 | "wrappy": { 2671 | "version": "1.0.2", 2672 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2673 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2674 | "optional": true 2675 | }, 2676 | "write-file-atomic": { 2677 | "version": "2.4.2", 2678 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", 2679 | "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", 2680 | "optional": true, 2681 | "requires": { 2682 | "graceful-fs": "^4.1.11", 2683 | "imurmurhash": "^0.1.4", 2684 | "signal-exit": "^3.0.2" 2685 | } 2686 | }, 2687 | "xdg-basedir": { 2688 | "version": "3.0.0", 2689 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", 2690 | "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", 2691 | "optional": true 2692 | }, 2693 | "xmlhttprequest": { 2694 | "version": "1.8.0", 2695 | "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", 2696 | "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" 2697 | }, 2698 | "xtend": { 2699 | "version": "4.0.1", 2700 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 2701 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 2702 | }, 2703 | "y18n": { 2704 | "version": "3.2.1", 2705 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 2706 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", 2707 | "optional": true 2708 | }, 2709 | "yallist": { 2710 | "version": "3.0.3", 2711 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", 2712 | "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", 2713 | "optional": true 2714 | }, 2715 | "yargs": { 2716 | "version": "3.32.0", 2717 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", 2718 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", 2719 | "optional": true, 2720 | "requires": { 2721 | "camelcase": "^2.0.1", 2722 | "cliui": "^3.0.3", 2723 | "decamelize": "^1.1.1", 2724 | "os-locale": "^1.4.0", 2725 | "string-width": "^1.0.1", 2726 | "window-size": "^0.1.4", 2727 | "y18n": "^3.2.0" 2728 | } 2729 | } 2730 | } 2731 | } 2732 | --------------------------------------------------------------------------------