├── 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},${depPath}>;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 | Please enable JavaScript to view this website.
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 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
Choose a theme
27 |
28 | Light
29 | Dark
30 | Auto
31 | Negative
32 | Grayscale
33 | Sepia
34 | HDR
35 |
36 |
37 |
38 |
39 |
Update available!
40 |
41 | Ignore
42 | Update now
43 |
44 |
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 |
--------------------------------------------------------------------------------