├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .nvmrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── firebase.json
├── functions
├── package-lock.json
└── package.json
├── package-lock.json
├── package.json
├── postcss.config.js
├── rollup.config.js
├── src
├── app.mjs
├── lib
│ ├── constants.mjs
│ ├── content-indexing.mjs
│ ├── partials.mjs
│ ├── periodic-background-sync.mjs
│ ├── route-matchers.mjs
│ ├── routes.mjs
│ ├── templates.mjs
│ └── urls.mjs
├── server.js
├── service-worker.mjs
├── static
│ ├── icon.png
│ ├── manifest.json
│ ├── offline.svg
│ ├── partials
│ │ ├── about.html
│ │ ├── foot.html
│ │ ├── head.html
│ │ └── navbar.html
│ └── robots.txt
└── styles.css
└── workbox-config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | build/
3 | functions/index.js
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = {
18 | extends: [
19 | 'eslint:recommended',
20 | 'google',
21 | ],
22 | env: {
23 | serviceworker: true,
24 | browser: true,
25 | node: true,
26 | es6: true,
27 | },
28 | globals: {
29 | workbox: false,
30 | },
31 | parserOptions: {
32 | ecmaVersion: 2017,
33 | sourceType: 'module',
34 | },
35 | rules: {
36 | 'require-jsdoc': 0,
37 | },
38 | overrides: [{
39 | files: ['**/*.mjs'],
40 | parserOptions: {
41 | sourceType: 'module',
42 | },
43 | }, {
44 | files: [
45 | '{functions,src}/*.{mjs,js}',
46 | '*.{mjs,js}',
47 | ],
48 | plugins: [
49 | 'header',
50 | ],
51 | rules: {
52 | 'header/header': [2, 'block', {
53 | pattern: 'Copyright \\d{4} Google Inc.',
54 | }],
55 | },
56 | }],
57 | };
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Directories
2 | .firebase/
3 | build/
4 | node_modules/
5 |
6 | # Files
7 | .firebaserc
8 | *.log
9 | functions/index.js
10 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v14.17.1
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2018 Google, Inc.
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## "Beyond Single Page Apps" @ Google I/O 2018
2 |
3 | The code in this project is described in more detail in
4 | [this article](https://developers.google.com/web/updates/2018/05/beyond-spa), as well as the video
5 | for the corresponding Google I/O 2018 session:
6 |
7 | [](https://www.youtube.com/watch?v=X6yof_vIQnk)
8 |
9 | ## About SO PWA
10 |
11 | Live deployment: https://so-pwa.firebaseapp.com/
12 |
13 | This is a sample
14 | [progressive web app](https://developers.google.com/web/progressive-web-apps/)
15 | which uses the [Stack Exchange API](https://api.stackexchange.com/) to fetch the
16 | top questions and answers from [Stack Overflow](https://stackoverflow.com/) for
17 | a given tag.
18 |
19 | Under the hood, it's powered by the following technologies:
20 |
21 | - Service worker generation and [Streams API](https://streams.spec.whatwg.org/)
22 | logic via [Workbox](https://developers.google.com/web/tools/workbox/).
23 | - Static and dynamic web hosting via
24 | [Firebase Cloud Functions](https://firebase.google.com/docs/functions/).
25 | - "Universal" JavaScript via ES2015 source modules, bundled for the browser and
26 | [Node](https://nodejs.org/) by [Rollup](https://rollupjs.org/), with
27 | [`babel-preset-env`](https://babeljs.io/docs/plugins/preset-env/) ensuring
28 | compatibility with various runtimes.
29 | - Shared server + service worker routing logic using
30 | [Express-style](https://expressjs.com/en/guide/routing.html) patterns and the
31 | [`regexparam`](https://github.com/lukeed/regexparam) library in the service
32 | worker.
33 |
34 | ## Contributing
35 |
36 | Please read the [guide to contributing](CONTRIBUTING.md) prior to filing any
37 | pull requests.
38 |
39 | ## License
40 |
41 | Copyright 2018 Google, Inc.
42 |
43 | Licensed under the [Apache License, Version 2.0](LICENSE) (the "License");
44 | you may not use this file except in compliance with the License. You may
45 | obtain a copy of the License at
46 |
47 | http://www.apache.org/licenses/LICENSE-2.0
48 |
49 | Unless required by applicable law or agreed to in writing, software
50 | distributed under the License is distributed on an "AS IS" BASIS,
51 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52 | See the License for the specific language governing permissions and
53 | limitations under the License.
54 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "build",
4 | "headers": [{
5 | "source":"/service-worker.js",
6 | "headers": [{
7 | "key": "Cache-Control",
8 | "value": "no-cache"
9 | }]
10 | }],
11 | "ignore": [
12 | "firebase.json",
13 | "**/.*",
14 | "**/node_modules/**"
15 | ],
16 | "rewrites": [{
17 | "source": "**",
18 | "function": "handleRequest"
19 | }]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/functions/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "requires": true,
4 | "lockfileVersion": 1,
5 | "dependencies": {
6 | "@firebase/app-types": {
7 | "version": "0.6.2",
8 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.6.2.tgz",
9 | "integrity": "sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw=="
10 | },
11 | "@firebase/auth-interop-types": {
12 | "version": "0.1.6",
13 | "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz",
14 | "integrity": "sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g=="
15 | },
16 | "@firebase/component": {
17 | "version": "0.5.3",
18 | "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.5.3.tgz",
19 | "integrity": "sha512-/TzwmlK35Mnr31zA9D4X0Obln7waAtV7nDLuNVtWhlXl0sSYRxnGES4dOhSXi0yWRneaNr+OiRBZ2gsc9PWWRg==",
20 | "requires": {
21 | "@firebase/util": "1.1.0",
22 | "tslib": "^2.1.0"
23 | }
24 | },
25 | "@firebase/database": {
26 | "version": "0.10.5",
27 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.10.5.tgz",
28 | "integrity": "sha512-/KAFZGSvvL3J4EytZsl5kgqhZwEV+ZTz6mCS3VPigkkECzT1E/JRm9h8DY5/VWmoyfqc5O2F3kqrrLf7AovoHg==",
29 | "requires": {
30 | "@firebase/auth-interop-types": "0.1.6",
31 | "@firebase/component": "0.5.3",
32 | "@firebase/database-types": "0.7.2",
33 | "@firebase/logger": "0.2.6",
34 | "@firebase/util": "1.1.0",
35 | "faye-websocket": "0.11.3",
36 | "tslib": "^2.1.0"
37 | }
38 | },
39 | "@firebase/database-types": {
40 | "version": "0.7.2",
41 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.7.2.tgz",
42 | "integrity": "sha512-cdAd/dgwvC0r3oLEDUR+ULs1vBsEvy0b27nlzKhU6LQgm9fCDzgaH9nFGv8x+S9dly4B0egAXkONkVoWcOAisg==",
43 | "requires": {
44 | "@firebase/app-types": "0.6.2"
45 | }
46 | },
47 | "@firebase/logger": {
48 | "version": "0.2.6",
49 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.2.6.tgz",
50 | "integrity": "sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw=="
51 | },
52 | "@firebase/util": {
53 | "version": "1.1.0",
54 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.1.0.tgz",
55 | "integrity": "sha512-lfuSASuPKNdfebuFR8rjFamMQUPH9iiZHcKS755Rkm/5gRT0qC7BMhCh3ZkHf7NVbplzIc/GhmX2jM+igDRCag==",
56 | "requires": {
57 | "tslib": "^2.1.0"
58 | }
59 | },
60 | "@google-cloud/common": {
61 | "version": "3.6.0",
62 | "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz",
63 | "integrity": "sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q==",
64 | "optional": true,
65 | "requires": {
66 | "@google-cloud/projectify": "^2.0.0",
67 | "@google-cloud/promisify": "^2.0.0",
68 | "arrify": "^2.0.1",
69 | "duplexify": "^4.1.1",
70 | "ent": "^2.2.0",
71 | "extend": "^3.0.2",
72 | "google-auth-library": "^7.0.2",
73 | "retry-request": "^4.1.1",
74 | "teeny-request": "^7.0.0"
75 | }
76 | },
77 | "@google-cloud/firestore": {
78 | "version": "4.12.3",
79 | "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-4.12.3.tgz",
80 | "integrity": "sha512-FTty3+paAj73KEfTJEpDxG9apLp9K3DySTeeewLLdljusRjZFgJ3jIiqi7tAKJjVsKOiXY4NRk4/0rpEQhHitQ==",
81 | "optional": true,
82 | "requires": {
83 | "fast-deep-equal": "^3.1.1",
84 | "functional-red-black-tree": "^1.0.1",
85 | "google-gax": "^2.12.0",
86 | "protobufjs": "^6.8.6"
87 | }
88 | },
89 | "@google-cloud/paginator": {
90 | "version": "3.0.5",
91 | "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-3.0.5.tgz",
92 | "integrity": "sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw==",
93 | "optional": true,
94 | "requires": {
95 | "arrify": "^2.0.0",
96 | "extend": "^3.0.2"
97 | }
98 | },
99 | "@google-cloud/projectify": {
100 | "version": "2.1.0",
101 | "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.1.0.tgz",
102 | "integrity": "sha512-qbpidP/fOvQNz3nyabaVnZqcED1NNzf7qfeOlgtAZd9knTwY+KtsGRkYpiQzcATABy4gnGP2lousM3S0nuWVzA==",
103 | "optional": true
104 | },
105 | "@google-cloud/promisify": {
106 | "version": "2.0.3",
107 | "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz",
108 | "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==",
109 | "optional": true
110 | },
111 | "@google-cloud/storage": {
112 | "version": "5.8.5",
113 | "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.5.tgz",
114 | "integrity": "sha512-i0gB9CRwQeOBYP7xuvn14M40LhHCwMjceBjxE4CTvsqL519sVY5yVKxLiAedHWGwUZHJNRa7Q2CmNfkdRwVNPg==",
115 | "optional": true,
116 | "requires": {
117 | "@google-cloud/common": "^3.6.0",
118 | "@google-cloud/paginator": "^3.0.0",
119 | "@google-cloud/promisify": "^2.0.0",
120 | "arrify": "^2.0.0",
121 | "async-retry": "^1.3.1",
122 | "compressible": "^2.0.12",
123 | "date-and-time": "^1.0.0",
124 | "duplexify": "^4.0.0",
125 | "extend": "^3.0.2",
126 | "gaxios": "^4.0.0",
127 | "gcs-resumable-upload": "^3.1.4",
128 | "get-stream": "^6.0.0",
129 | "hash-stream-validation": "^0.2.2",
130 | "mime": "^2.2.0",
131 | "mime-types": "^2.0.8",
132 | "onetime": "^5.1.0",
133 | "p-limit": "^3.0.1",
134 | "pumpify": "^2.0.0",
135 | "snakeize": "^0.1.0",
136 | "stream-events": "^1.0.1",
137 | "xdg-basedir": "^4.0.0"
138 | },
139 | "dependencies": {
140 | "mime": {
141 | "version": "2.5.2",
142 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
143 | "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
144 | "optional": true
145 | }
146 | }
147 | },
148 | "@grpc/grpc-js": {
149 | "version": "1.3.3",
150 | "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.3.3.tgz",
151 | "integrity": "sha512-KkKZrX3fVTBYCtUk8I+Y4xWaauEEOIR1mIGoPFUK8C+a9TTub5dmjowJpFGz0dqYj//wJcgVR9fqpoNhSYFfHQ==",
152 | "optional": true,
153 | "requires": {
154 | "@types/node": ">=12.12.47"
155 | }
156 | },
157 | "@grpc/proto-loader": {
158 | "version": "0.6.2",
159 | "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.2.tgz",
160 | "integrity": "sha512-q2Qle60Ht2OQBCp9S5hv1JbI4uBBq6/mqSevFNK3ZEgRDBCAkWqZPUhD/K9gXOHrHKluliHiVq2L9sw1mVyAIg==",
161 | "optional": true,
162 | "requires": {
163 | "@types/long": "^4.0.1",
164 | "lodash.camelcase": "^4.3.0",
165 | "long": "^4.0.0",
166 | "protobufjs": "^6.10.0",
167 | "yargs": "^16.1.1"
168 | }
169 | },
170 | "@panva/asn1.js": {
171 | "version": "1.0.0",
172 | "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz",
173 | "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw=="
174 | },
175 | "@popeindustries/lit-html-server": {
176 | "version": "3.1.0",
177 | "resolved": "https://registry.npmjs.org/@popeindustries/lit-html-server/-/lit-html-server-3.1.0.tgz",
178 | "integrity": "sha512-OuVxzjN17fIoCGcZFCmF6//rQ86dfevp0703SC2W8S0G257mvjuVBHdRomyVYYFqBjpr9naRz2oFdLjPvCG8bw=="
179 | },
180 | "@protobufjs/aspromise": {
181 | "version": "1.1.2",
182 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
183 | "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=",
184 | "optional": true
185 | },
186 | "@protobufjs/base64": {
187 | "version": "1.1.2",
188 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
189 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
190 | "optional": true
191 | },
192 | "@protobufjs/codegen": {
193 | "version": "2.0.4",
194 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
195 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
196 | "optional": true
197 | },
198 | "@protobufjs/eventemitter": {
199 | "version": "1.1.0",
200 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
201 | "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=",
202 | "optional": true
203 | },
204 | "@protobufjs/fetch": {
205 | "version": "1.1.0",
206 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
207 | "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
208 | "optional": true,
209 | "requires": {
210 | "@protobufjs/aspromise": "^1.1.1",
211 | "@protobufjs/inquire": "^1.1.0"
212 | }
213 | },
214 | "@protobufjs/float": {
215 | "version": "1.0.2",
216 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
217 | "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=",
218 | "optional": true
219 | },
220 | "@protobufjs/inquire": {
221 | "version": "1.1.0",
222 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
223 | "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=",
224 | "optional": true
225 | },
226 | "@protobufjs/path": {
227 | "version": "1.1.2",
228 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
229 | "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=",
230 | "optional": true
231 | },
232 | "@protobufjs/pool": {
233 | "version": "1.1.0",
234 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
235 | "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=",
236 | "optional": true
237 | },
238 | "@protobufjs/utf8": {
239 | "version": "1.1.0",
240 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
241 | "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
242 | "optional": true
243 | },
244 | "@tootallnate/once": {
245 | "version": "1.1.2",
246 | "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
247 | "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
248 | "optional": true
249 | },
250 | "@types/body-parser": {
251 | "version": "1.19.0",
252 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
253 | "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
254 | "requires": {
255 | "@types/connect": "*",
256 | "@types/node": "*"
257 | }
258 | },
259 | "@types/connect": {
260 | "version": "3.4.34",
261 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz",
262 | "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==",
263 | "requires": {
264 | "@types/node": "*"
265 | }
266 | },
267 | "@types/express": {
268 | "version": "4.17.12",
269 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz",
270 | "integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==",
271 | "requires": {
272 | "@types/body-parser": "*",
273 | "@types/express-serve-static-core": "^4.17.18",
274 | "@types/qs": "*",
275 | "@types/serve-static": "*"
276 | }
277 | },
278 | "@types/express-jwt": {
279 | "version": "0.0.42",
280 | "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
281 | "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
282 | "requires": {
283 | "@types/express": "*",
284 | "@types/express-unless": "*"
285 | }
286 | },
287 | "@types/express-serve-static-core": {
288 | "version": "4.17.21",
289 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz",
290 | "integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==",
291 | "requires": {
292 | "@types/node": "*",
293 | "@types/qs": "*",
294 | "@types/range-parser": "*"
295 | }
296 | },
297 | "@types/express-unless": {
298 | "version": "0.5.1",
299 | "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
300 | "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
301 | "requires": {
302 | "@types/express": "*"
303 | }
304 | },
305 | "@types/long": {
306 | "version": "4.0.1",
307 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
308 | "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==",
309 | "optional": true
310 | },
311 | "@types/mime": {
312 | "version": "1.3.2",
313 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
314 | "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
315 | },
316 | "@types/node": {
317 | "version": "15.12.4",
318 | "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz",
319 | "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA=="
320 | },
321 | "@types/qs": {
322 | "version": "6.9.6",
323 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz",
324 | "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA=="
325 | },
326 | "@types/range-parser": {
327 | "version": "1.2.3",
328 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
329 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
330 | },
331 | "@types/serve-static": {
332 | "version": "1.13.9",
333 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz",
334 | "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==",
335 | "requires": {
336 | "@types/mime": "^1",
337 | "@types/node": "*"
338 | }
339 | },
340 | "abort-controller": {
341 | "version": "3.0.0",
342 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
343 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
344 | "optional": true,
345 | "requires": {
346 | "event-target-shim": "^5.0.0"
347 | }
348 | },
349 | "accepts": {
350 | "version": "1.3.7",
351 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
352 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
353 | "requires": {
354 | "mime-types": "~2.1.24",
355 | "negotiator": "0.6.2"
356 | }
357 | },
358 | "agent-base": {
359 | "version": "6.0.2",
360 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
361 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
362 | "optional": true,
363 | "requires": {
364 | "debug": "4"
365 | }
366 | },
367 | "ansi-regex": {
368 | "version": "5.0.0",
369 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
370 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
371 | "optional": true
372 | },
373 | "ansi-styles": {
374 | "version": "4.3.0",
375 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
376 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
377 | "optional": true,
378 | "requires": {
379 | "color-convert": "^2.0.1"
380 | }
381 | },
382 | "array-flatten": {
383 | "version": "1.1.1",
384 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
385 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
386 | },
387 | "arrify": {
388 | "version": "2.0.1",
389 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
390 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
391 | "optional": true
392 | },
393 | "async-retry": {
394 | "version": "1.3.1",
395 | "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz",
396 | "integrity": "sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==",
397 | "optional": true,
398 | "requires": {
399 | "retry": "0.12.0"
400 | }
401 | },
402 | "axios": {
403 | "version": "0.21.1",
404 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
405 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
406 | "requires": {
407 | "follow-redirects": "^1.10.0"
408 | }
409 | },
410 | "base64-js": {
411 | "version": "1.5.1",
412 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
413 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
414 | "optional": true
415 | },
416 | "bignumber.js": {
417 | "version": "9.0.1",
418 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
419 | "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
420 | "optional": true
421 | },
422 | "body-parser": {
423 | "version": "1.19.0",
424 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
425 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
426 | "requires": {
427 | "bytes": "3.1.0",
428 | "content-type": "~1.0.4",
429 | "debug": "2.6.9",
430 | "depd": "~1.1.2",
431 | "http-errors": "1.7.2",
432 | "iconv-lite": "0.4.24",
433 | "on-finished": "~2.3.0",
434 | "qs": "6.7.0",
435 | "raw-body": "2.4.0",
436 | "type-is": "~1.6.17"
437 | },
438 | "dependencies": {
439 | "debug": {
440 | "version": "2.6.9",
441 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
442 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
443 | "requires": {
444 | "ms": "2.0.0"
445 | }
446 | }
447 | }
448 | },
449 | "buffer-equal-constant-time": {
450 | "version": "1.0.1",
451 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
452 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
453 | },
454 | "bytes": {
455 | "version": "3.1.0",
456 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
457 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
458 | },
459 | "cliui": {
460 | "version": "7.0.4",
461 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
462 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
463 | "optional": true,
464 | "requires": {
465 | "string-width": "^4.2.0",
466 | "strip-ansi": "^6.0.0",
467 | "wrap-ansi": "^7.0.0"
468 | }
469 | },
470 | "color-convert": {
471 | "version": "2.0.1",
472 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
473 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
474 | "optional": true,
475 | "requires": {
476 | "color-name": "~1.1.4"
477 | }
478 | },
479 | "color-name": {
480 | "version": "1.1.4",
481 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
482 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
483 | "optional": true
484 | },
485 | "compressible": {
486 | "version": "2.0.18",
487 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
488 | "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
489 | "optional": true,
490 | "requires": {
491 | "mime-db": ">= 1.43.0 < 2"
492 | }
493 | },
494 | "configstore": {
495 | "version": "5.0.1",
496 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz",
497 | "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==",
498 | "optional": true,
499 | "requires": {
500 | "dot-prop": "^5.2.0",
501 | "graceful-fs": "^4.1.2",
502 | "make-dir": "^3.0.0",
503 | "unique-string": "^2.0.0",
504 | "write-file-atomic": "^3.0.0",
505 | "xdg-basedir": "^4.0.0"
506 | }
507 | },
508 | "content-disposition": {
509 | "version": "0.5.3",
510 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
511 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
512 | "requires": {
513 | "safe-buffer": "5.1.2"
514 | }
515 | },
516 | "content-type": {
517 | "version": "1.0.4",
518 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
519 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
520 | },
521 | "cookie": {
522 | "version": "0.4.0",
523 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
524 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
525 | },
526 | "cookie-signature": {
527 | "version": "1.0.6",
528 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
529 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
530 | },
531 | "cors": {
532 | "version": "2.8.5",
533 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
534 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
535 | "requires": {
536 | "object-assign": "^4",
537 | "vary": "^1"
538 | }
539 | },
540 | "crypto-random-string": {
541 | "version": "2.0.0",
542 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
543 | "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
544 | "optional": true
545 | },
546 | "date-and-time": {
547 | "version": "1.0.1",
548 | "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-1.0.1.tgz",
549 | "integrity": "sha512-7u+uNfnjWkX+YFQfivvW24TjaJG6ahvTrfw1auq7KlC7osuGcZBIWGBvB9UcENjH6JnLVhMqlRripk1dSHjAUA==",
550 | "optional": true
551 | },
552 | "debug": {
553 | "version": "4.3.1",
554 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
555 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
556 | "requires": {
557 | "ms": "2.1.2"
558 | },
559 | "dependencies": {
560 | "ms": {
561 | "version": "2.1.2",
562 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
563 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
564 | }
565 | }
566 | },
567 | "depd": {
568 | "version": "1.1.2",
569 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
570 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
571 | },
572 | "destroy": {
573 | "version": "1.0.4",
574 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
575 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
576 | },
577 | "dicer": {
578 | "version": "0.3.0",
579 | "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz",
580 | "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==",
581 | "requires": {
582 | "streamsearch": "0.1.2"
583 | }
584 | },
585 | "dot-prop": {
586 | "version": "5.3.0",
587 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
588 | "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
589 | "optional": true,
590 | "requires": {
591 | "is-obj": "^2.0.0"
592 | }
593 | },
594 | "duplexify": {
595 | "version": "4.1.1",
596 | "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
597 | "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
598 | "optional": true,
599 | "requires": {
600 | "end-of-stream": "^1.4.1",
601 | "inherits": "^2.0.3",
602 | "readable-stream": "^3.1.1",
603 | "stream-shift": "^1.0.0"
604 | }
605 | },
606 | "ecdsa-sig-formatter": {
607 | "version": "1.0.11",
608 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
609 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
610 | "requires": {
611 | "safe-buffer": "^5.0.1"
612 | }
613 | },
614 | "ee-first": {
615 | "version": "1.1.1",
616 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
617 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
618 | },
619 | "emoji-regex": {
620 | "version": "8.0.0",
621 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
622 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
623 | "optional": true
624 | },
625 | "encodeurl": {
626 | "version": "1.0.2",
627 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
628 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
629 | },
630 | "end-of-stream": {
631 | "version": "1.4.4",
632 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
633 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
634 | "optional": true,
635 | "requires": {
636 | "once": "^1.4.0"
637 | }
638 | },
639 | "ent": {
640 | "version": "2.2.0",
641 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
642 | "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
643 | "optional": true
644 | },
645 | "escalade": {
646 | "version": "3.1.1",
647 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
648 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
649 | "optional": true
650 | },
651 | "escape-html": {
652 | "version": "1.0.3",
653 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
654 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
655 | },
656 | "etag": {
657 | "version": "1.8.1",
658 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
659 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
660 | },
661 | "event-target-shim": {
662 | "version": "5.0.1",
663 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
664 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
665 | "optional": true
666 | },
667 | "express": {
668 | "version": "4.17.1",
669 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
670 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
671 | "requires": {
672 | "accepts": "~1.3.7",
673 | "array-flatten": "1.1.1",
674 | "body-parser": "1.19.0",
675 | "content-disposition": "0.5.3",
676 | "content-type": "~1.0.4",
677 | "cookie": "0.4.0",
678 | "cookie-signature": "1.0.6",
679 | "debug": "2.6.9",
680 | "depd": "~1.1.2",
681 | "encodeurl": "~1.0.2",
682 | "escape-html": "~1.0.3",
683 | "etag": "~1.8.1",
684 | "finalhandler": "~1.1.2",
685 | "fresh": "0.5.2",
686 | "merge-descriptors": "1.0.1",
687 | "methods": "~1.1.2",
688 | "on-finished": "~2.3.0",
689 | "parseurl": "~1.3.3",
690 | "path-to-regexp": "0.1.7",
691 | "proxy-addr": "~2.0.5",
692 | "qs": "6.7.0",
693 | "range-parser": "~1.2.1",
694 | "safe-buffer": "5.1.2",
695 | "send": "0.17.1",
696 | "serve-static": "1.14.1",
697 | "setprototypeof": "1.1.1",
698 | "statuses": "~1.5.0",
699 | "type-is": "~1.6.18",
700 | "utils-merge": "1.0.1",
701 | "vary": "~1.1.2"
702 | },
703 | "dependencies": {
704 | "debug": {
705 | "version": "2.6.9",
706 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
707 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
708 | "requires": {
709 | "ms": "2.0.0"
710 | }
711 | }
712 | }
713 | },
714 | "extend": {
715 | "version": "3.0.2",
716 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
717 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
718 | "optional": true
719 | },
720 | "fast-deep-equal": {
721 | "version": "3.1.3",
722 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
723 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
724 | "optional": true
725 | },
726 | "fast-text-encoding": {
727 | "version": "1.0.3",
728 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
729 | "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==",
730 | "optional": true
731 | },
732 | "faye-websocket": {
733 | "version": "0.11.3",
734 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
735 | "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
736 | "requires": {
737 | "websocket-driver": ">=0.5.1"
738 | }
739 | },
740 | "finalhandler": {
741 | "version": "1.1.2",
742 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
743 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
744 | "requires": {
745 | "debug": "2.6.9",
746 | "encodeurl": "~1.0.2",
747 | "escape-html": "~1.0.3",
748 | "on-finished": "~2.3.0",
749 | "parseurl": "~1.3.3",
750 | "statuses": "~1.5.0",
751 | "unpipe": "~1.0.0"
752 | },
753 | "dependencies": {
754 | "debug": {
755 | "version": "2.6.9",
756 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
757 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
758 | "requires": {
759 | "ms": "2.0.0"
760 | }
761 | }
762 | }
763 | },
764 | "firebase-admin": {
765 | "version": "9.9.0",
766 | "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-9.9.0.tgz",
767 | "integrity": "sha512-04HT7JAAqcJYty95qf15IBD9CXf+vr7S8zNU6Zt1ayC1J05DLaCsUd19/sCNAjZ614KHexAYUtyLgZoJwu2wOQ==",
768 | "requires": {
769 | "@firebase/database": "^0.10.0",
770 | "@firebase/database-types": "^0.7.2",
771 | "@google-cloud/firestore": "^4.5.0",
772 | "@google-cloud/storage": "^5.3.0",
773 | "@types/node": ">=12.12.47",
774 | "dicer": "^0.3.0",
775 | "jsonwebtoken": "^8.5.1",
776 | "jwks-rsa": "^2.0.2",
777 | "node-forge": "^0.10.0"
778 | }
779 | },
780 | "firebase-functions": {
781 | "version": "3.14.1",
782 | "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.14.1.tgz",
783 | "integrity": "sha512-hL/qm+i5i1qKYmAFMlQ4mwRngDkP+3YT3F4E4Nd5Hj2QKeawBdZiMGgEt6zqTx08Zq04vHiSnSM0z75UJRSg6Q==",
784 | "requires": {
785 | "@types/express": "4.17.3",
786 | "cors": "^2.8.5",
787 | "express": "^4.17.1",
788 | "lodash": "^4.17.14"
789 | },
790 | "dependencies": {
791 | "@types/express": {
792 | "version": "4.17.3",
793 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.3.tgz",
794 | "integrity": "sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==",
795 | "requires": {
796 | "@types/body-parser": "*",
797 | "@types/express-serve-static-core": "*",
798 | "@types/serve-static": "*"
799 | }
800 | }
801 | }
802 | },
803 | "follow-redirects": {
804 | "version": "1.13.2",
805 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz",
806 | "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA=="
807 | },
808 | "forwarded": {
809 | "version": "0.1.2",
810 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
811 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
812 | },
813 | "fresh": {
814 | "version": "0.5.2",
815 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
816 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
817 | },
818 | "functional-red-black-tree": {
819 | "version": "1.0.1",
820 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
821 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
822 | "optional": true
823 | },
824 | "gaxios": {
825 | "version": "4.3.0",
826 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.0.tgz",
827 | "integrity": "sha512-pHplNbslpwCLMyII/lHPWFQbJWOX0B3R1hwBEOvzYi1GmdKZruuEHK4N9V6f7tf1EaPYyF80mui1+344p6SmLg==",
828 | "optional": true,
829 | "requires": {
830 | "abort-controller": "^3.0.0",
831 | "extend": "^3.0.2",
832 | "https-proxy-agent": "^5.0.0",
833 | "is-stream": "^2.0.0",
834 | "node-fetch": "^2.3.0"
835 | }
836 | },
837 | "gcp-metadata": {
838 | "version": "4.3.0",
839 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.0.tgz",
840 | "integrity": "sha512-L9XQUpvKJCM76YRSmcxrR4mFPzPGsgZUH+GgHMxAET8qc6+BhRJq63RLhWakgEO2KKVgeSDVfyiNjkGSADwNTA==",
841 | "optional": true,
842 | "requires": {
843 | "gaxios": "^4.0.0",
844 | "json-bigint": "^1.0.0"
845 | }
846 | },
847 | "gcs-resumable-upload": {
848 | "version": "3.2.0",
849 | "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.2.0.tgz",
850 | "integrity": "sha512-k6OAkrO0N1zgvwTRxgCC43K9BMvNUBhZkkFELsMSlgAVs7Sd9C1TA9pmDLBZmFBN8sdvrObsSbCfOOFnHULRvA==",
851 | "optional": true,
852 | "requires": {
853 | "abort-controller": "^3.0.0",
854 | "configstore": "^5.0.0",
855 | "extend": "^3.0.2",
856 | "gaxios": "^4.0.0",
857 | "google-auth-library": "^7.0.0",
858 | "pumpify": "^2.0.0",
859 | "stream-events": "^1.0.4"
860 | }
861 | },
862 | "get-caller-file": {
863 | "version": "2.0.5",
864 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
865 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
866 | "optional": true
867 | },
868 | "get-stream": {
869 | "version": "6.0.1",
870 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
871 | "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
872 | "optional": true
873 | },
874 | "google-auth-library": {
875 | "version": "7.1.2",
876 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.1.2.tgz",
877 | "integrity": "sha512-FMipHgfe2u1LzWsf2n9zEB9KsJ8M3n8OYTHbHtlkzPCyo7IknXQR5X99nfvwUHGuX+iEpihUZxDuPm7+qBYeXg==",
878 | "optional": true,
879 | "requires": {
880 | "arrify": "^2.0.0",
881 | "base64-js": "^1.3.0",
882 | "ecdsa-sig-formatter": "^1.0.11",
883 | "fast-text-encoding": "^1.0.0",
884 | "gaxios": "^4.0.0",
885 | "gcp-metadata": "^4.2.0",
886 | "gtoken": "^5.0.4",
887 | "jws": "^4.0.0",
888 | "lru-cache": "^6.0.0"
889 | }
890 | },
891 | "google-gax": {
892 | "version": "2.15.1",
893 | "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-2.15.1.tgz",
894 | "integrity": "sha512-I5j4JRxx1HfZZBXnQs7gUvRHaTqT8XZ6U/QQWI/mDbf05dXP/vLni+ZkDzUh/XHDtIo/MPVkuEUhkwWwi+vwTg==",
895 | "optional": true,
896 | "requires": {
897 | "@grpc/grpc-js": "~1.3.0",
898 | "@grpc/proto-loader": "^0.6.1",
899 | "@types/long": "^4.0.0",
900 | "abort-controller": "^3.0.0",
901 | "duplexify": "^4.0.0",
902 | "fast-text-encoding": "^1.0.3",
903 | "google-auth-library": "^7.0.2",
904 | "is-stream-ended": "^0.1.4",
905 | "node-fetch": "^2.6.1",
906 | "object-hash": "^2.1.1",
907 | "protobufjs": "^6.10.2",
908 | "retry-request": "^4.0.0"
909 | }
910 | },
911 | "google-p12-pem": {
912 | "version": "3.1.0",
913 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.0.tgz",
914 | "integrity": "sha512-JUtEHXL4DY/N+xhlm7TC3qL797RPAtk0ZGXNs3/gWyiDHYoA/8Rjes0pztkda+sZv4ej1EoO2KhWgW5V9KTrSQ==",
915 | "optional": true,
916 | "requires": {
917 | "node-forge": "^0.10.0"
918 | }
919 | },
920 | "graceful-fs": {
921 | "version": "4.2.6",
922 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
923 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
924 | "optional": true
925 | },
926 | "gtoken": {
927 | "version": "5.3.0",
928 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.0.tgz",
929 | "integrity": "sha512-mCcISYiaRZrJpfqOs0QWa6lfEM/C1V9ASkzFmuz43XBb5s1Vynh+CZy1ECeeJXVGx2PRByjYzb4Y4/zr1byr0w==",
930 | "optional": true,
931 | "requires": {
932 | "gaxios": "^4.0.0",
933 | "google-p12-pem": "^3.0.3",
934 | "jws": "^4.0.0"
935 | }
936 | },
937 | "hash-stream-validation": {
938 | "version": "0.2.4",
939 | "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz",
940 | "integrity": "sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==",
941 | "optional": true
942 | },
943 | "html-escaper": {
944 | "version": "3.0.3",
945 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
946 | "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
947 | },
948 | "http-errors": {
949 | "version": "1.7.2",
950 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
951 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
952 | "requires": {
953 | "depd": "~1.1.2",
954 | "inherits": "2.0.3",
955 | "setprototypeof": "1.1.1",
956 | "statuses": ">= 1.5.0 < 2",
957 | "toidentifier": "1.0.0"
958 | }
959 | },
960 | "http-parser-js": {
961 | "version": "0.5.3",
962 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz",
963 | "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg=="
964 | },
965 | "http-proxy-agent": {
966 | "version": "4.0.1",
967 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
968 | "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
969 | "optional": true,
970 | "requires": {
971 | "@tootallnate/once": "1",
972 | "agent-base": "6",
973 | "debug": "4"
974 | }
975 | },
976 | "https-proxy-agent": {
977 | "version": "5.0.0",
978 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
979 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
980 | "optional": true,
981 | "requires": {
982 | "agent-base": "6",
983 | "debug": "4"
984 | }
985 | },
986 | "iconv-lite": {
987 | "version": "0.4.24",
988 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
989 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
990 | "requires": {
991 | "safer-buffer": ">= 2.1.2 < 3"
992 | }
993 | },
994 | "imurmurhash": {
995 | "version": "0.1.4",
996 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
997 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
998 | "optional": true
999 | },
1000 | "inherits": {
1001 | "version": "2.0.3",
1002 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1003 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
1004 | },
1005 | "ipaddr.js": {
1006 | "version": "1.9.0",
1007 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
1008 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
1009 | },
1010 | "is-fullwidth-code-point": {
1011 | "version": "3.0.0",
1012 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
1013 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
1014 | "optional": true
1015 | },
1016 | "is-obj": {
1017 | "version": "2.0.0",
1018 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
1019 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
1020 | "optional": true
1021 | },
1022 | "is-stream": {
1023 | "version": "2.0.0",
1024 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
1025 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
1026 | "optional": true
1027 | },
1028 | "is-stream-ended": {
1029 | "version": "0.1.4",
1030 | "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
1031 | "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==",
1032 | "optional": true
1033 | },
1034 | "is-typedarray": {
1035 | "version": "1.0.0",
1036 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1037 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
1038 | "optional": true
1039 | },
1040 | "jose": {
1041 | "version": "2.0.5",
1042 | "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.5.tgz",
1043 | "integrity": "sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA==",
1044 | "requires": {
1045 | "@panva/asn1.js": "^1.0.0"
1046 | }
1047 | },
1048 | "json-bigint": {
1049 | "version": "1.0.0",
1050 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
1051 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
1052 | "optional": true,
1053 | "requires": {
1054 | "bignumber.js": "^9.0.0"
1055 | }
1056 | },
1057 | "jsonwebtoken": {
1058 | "version": "8.5.1",
1059 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
1060 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
1061 | "requires": {
1062 | "jws": "^3.2.2",
1063 | "lodash.includes": "^4.3.0",
1064 | "lodash.isboolean": "^3.0.3",
1065 | "lodash.isinteger": "^4.0.4",
1066 | "lodash.isnumber": "^3.0.3",
1067 | "lodash.isplainobject": "^4.0.6",
1068 | "lodash.isstring": "^4.0.1",
1069 | "lodash.once": "^4.0.0",
1070 | "ms": "^2.1.1",
1071 | "semver": "^5.6.0"
1072 | },
1073 | "dependencies": {
1074 | "jwa": {
1075 | "version": "1.4.1",
1076 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
1077 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
1078 | "requires": {
1079 | "buffer-equal-constant-time": "1.0.1",
1080 | "ecdsa-sig-formatter": "1.0.11",
1081 | "safe-buffer": "^5.0.1"
1082 | }
1083 | },
1084 | "jws": {
1085 | "version": "3.2.2",
1086 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
1087 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
1088 | "requires": {
1089 | "jwa": "^1.4.1",
1090 | "safe-buffer": "^5.0.1"
1091 | }
1092 | },
1093 | "ms": {
1094 | "version": "2.1.3",
1095 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1096 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1097 | },
1098 | "semver": {
1099 | "version": "5.7.1",
1100 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1101 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
1102 | }
1103 | }
1104 | },
1105 | "jwa": {
1106 | "version": "2.0.0",
1107 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
1108 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
1109 | "optional": true,
1110 | "requires": {
1111 | "buffer-equal-constant-time": "1.0.1",
1112 | "ecdsa-sig-formatter": "1.0.11",
1113 | "safe-buffer": "^5.0.1"
1114 | }
1115 | },
1116 | "jwks-rsa": {
1117 | "version": "2.0.3",
1118 | "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.0.3.tgz",
1119 | "integrity": "sha512-/rkjXRWAp0cS00tunsHResw68P5iTQru8+jHufLNv3JHc4nObFEndfEUSuPugh09N+V9XYxKUqi7QrkmCHSSSg==",
1120 | "requires": {
1121 | "@types/express-jwt": "0.0.42",
1122 | "debug": "^4.1.0",
1123 | "jose": "^2.0.5",
1124 | "limiter": "^1.1.5",
1125 | "lru-memoizer": "^2.1.2"
1126 | }
1127 | },
1128 | "jws": {
1129 | "version": "4.0.0",
1130 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
1131 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
1132 | "optional": true,
1133 | "requires": {
1134 | "jwa": "^2.0.0",
1135 | "safe-buffer": "^5.0.1"
1136 | }
1137 | },
1138 | "limiter": {
1139 | "version": "1.1.5",
1140 | "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
1141 | "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
1142 | },
1143 | "lodash": {
1144 | "version": "4.17.21",
1145 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
1146 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
1147 | },
1148 | "lodash.camelcase": {
1149 | "version": "4.3.0",
1150 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
1151 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
1152 | "optional": true
1153 | },
1154 | "lodash.clonedeep": {
1155 | "version": "4.5.0",
1156 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
1157 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
1158 | },
1159 | "lodash.includes": {
1160 | "version": "4.3.0",
1161 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
1162 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
1163 | },
1164 | "lodash.isboolean": {
1165 | "version": "3.0.3",
1166 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
1167 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
1168 | },
1169 | "lodash.isinteger": {
1170 | "version": "4.0.4",
1171 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
1172 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
1173 | },
1174 | "lodash.isnumber": {
1175 | "version": "3.0.3",
1176 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
1177 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
1178 | },
1179 | "lodash.isplainobject": {
1180 | "version": "4.0.6",
1181 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
1182 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
1183 | },
1184 | "lodash.isstring": {
1185 | "version": "4.0.1",
1186 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
1187 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
1188 | },
1189 | "lodash.once": {
1190 | "version": "4.1.1",
1191 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
1192 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
1193 | },
1194 | "long": {
1195 | "version": "4.0.0",
1196 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
1197 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
1198 | "optional": true
1199 | },
1200 | "lru-cache": {
1201 | "version": "6.0.0",
1202 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
1203 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
1204 | "requires": {
1205 | "yallist": "^4.0.0"
1206 | }
1207 | },
1208 | "lru-memoizer": {
1209 | "version": "2.1.4",
1210 | "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz",
1211 | "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==",
1212 | "requires": {
1213 | "lodash.clonedeep": "^4.5.0",
1214 | "lru-cache": "~4.0.0"
1215 | },
1216 | "dependencies": {
1217 | "lru-cache": {
1218 | "version": "4.0.2",
1219 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
1220 | "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
1221 | "requires": {
1222 | "pseudomap": "^1.0.1",
1223 | "yallist": "^2.0.0"
1224 | }
1225 | },
1226 | "yallist": {
1227 | "version": "2.1.2",
1228 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
1229 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
1230 | }
1231 | }
1232 | },
1233 | "make-dir": {
1234 | "version": "3.1.0",
1235 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
1236 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
1237 | "optional": true,
1238 | "requires": {
1239 | "semver": "^6.0.0"
1240 | }
1241 | },
1242 | "media-typer": {
1243 | "version": "0.3.0",
1244 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1245 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
1246 | },
1247 | "merge-descriptors": {
1248 | "version": "1.0.1",
1249 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1250 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
1251 | },
1252 | "methods": {
1253 | "version": "1.1.2",
1254 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1255 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
1256 | },
1257 | "mime": {
1258 | "version": "1.6.0",
1259 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1260 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1261 | },
1262 | "mime-db": {
1263 | "version": "1.43.0",
1264 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
1265 | "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
1266 | },
1267 | "mime-types": {
1268 | "version": "2.1.26",
1269 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
1270 | "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
1271 | "requires": {
1272 | "mime-db": "1.43.0"
1273 | }
1274 | },
1275 | "mimic-fn": {
1276 | "version": "2.1.0",
1277 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
1278 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
1279 | "optional": true
1280 | },
1281 | "ms": {
1282 | "version": "2.0.0",
1283 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1284 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1285 | },
1286 | "negotiator": {
1287 | "version": "0.6.2",
1288 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
1289 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
1290 | },
1291 | "node-fetch": {
1292 | "version": "2.6.1",
1293 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
1294 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
1295 | "optional": true
1296 | },
1297 | "node-forge": {
1298 | "version": "0.10.0",
1299 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
1300 | "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
1301 | },
1302 | "object-assign": {
1303 | "version": "4.1.1",
1304 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1305 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
1306 | },
1307 | "object-hash": {
1308 | "version": "2.2.0",
1309 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
1310 | "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
1311 | "optional": true
1312 | },
1313 | "on-finished": {
1314 | "version": "2.3.0",
1315 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1316 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1317 | "requires": {
1318 | "ee-first": "1.1.1"
1319 | }
1320 | },
1321 | "once": {
1322 | "version": "1.4.0",
1323 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1324 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1325 | "optional": true,
1326 | "requires": {
1327 | "wrappy": "1"
1328 | }
1329 | },
1330 | "onetime": {
1331 | "version": "5.1.2",
1332 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
1333 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
1334 | "optional": true,
1335 | "requires": {
1336 | "mimic-fn": "^2.1.0"
1337 | }
1338 | },
1339 | "p-limit": {
1340 | "version": "3.1.0",
1341 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
1342 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
1343 | "optional": true,
1344 | "requires": {
1345 | "yocto-queue": "^0.1.0"
1346 | }
1347 | },
1348 | "parseurl": {
1349 | "version": "1.3.3",
1350 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1351 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1352 | },
1353 | "path-to-regexp": {
1354 | "version": "0.1.7",
1355 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1356 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
1357 | },
1358 | "protobufjs": {
1359 | "version": "6.11.2",
1360 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
1361 | "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
1362 | "optional": true,
1363 | "requires": {
1364 | "@protobufjs/aspromise": "^1.1.2",
1365 | "@protobufjs/base64": "^1.1.2",
1366 | "@protobufjs/codegen": "^2.0.4",
1367 | "@protobufjs/eventemitter": "^1.1.0",
1368 | "@protobufjs/fetch": "^1.1.0",
1369 | "@protobufjs/float": "^1.0.2",
1370 | "@protobufjs/inquire": "^1.1.0",
1371 | "@protobufjs/path": "^1.1.2",
1372 | "@protobufjs/pool": "^1.1.0",
1373 | "@protobufjs/utf8": "^1.1.0",
1374 | "@types/long": "^4.0.1",
1375 | "@types/node": ">=13.7.0",
1376 | "long": "^4.0.0"
1377 | }
1378 | },
1379 | "proxy-addr": {
1380 | "version": "2.0.5",
1381 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
1382 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
1383 | "requires": {
1384 | "forwarded": "~0.1.2",
1385 | "ipaddr.js": "1.9.0"
1386 | }
1387 | },
1388 | "pseudomap": {
1389 | "version": "1.0.2",
1390 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
1391 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
1392 | },
1393 | "pump": {
1394 | "version": "3.0.0",
1395 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
1396 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
1397 | "optional": true,
1398 | "requires": {
1399 | "end-of-stream": "^1.1.0",
1400 | "once": "^1.3.1"
1401 | }
1402 | },
1403 | "pumpify": {
1404 | "version": "2.0.1",
1405 | "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
1406 | "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
1407 | "optional": true,
1408 | "requires": {
1409 | "duplexify": "^4.1.1",
1410 | "inherits": "^2.0.3",
1411 | "pump": "^3.0.0"
1412 | }
1413 | },
1414 | "qs": {
1415 | "version": "6.7.0",
1416 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
1417 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
1418 | },
1419 | "range-parser": {
1420 | "version": "1.2.1",
1421 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1422 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1423 | },
1424 | "raw-body": {
1425 | "version": "2.4.0",
1426 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
1427 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
1428 | "requires": {
1429 | "bytes": "3.1.0",
1430 | "http-errors": "1.7.2",
1431 | "iconv-lite": "0.4.24",
1432 | "unpipe": "1.0.0"
1433 | }
1434 | },
1435 | "readable-stream": {
1436 | "version": "3.6.0",
1437 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
1438 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
1439 | "optional": true,
1440 | "requires": {
1441 | "inherits": "^2.0.3",
1442 | "string_decoder": "^1.1.1",
1443 | "util-deprecate": "^1.0.1"
1444 | }
1445 | },
1446 | "require-directory": {
1447 | "version": "2.1.1",
1448 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1449 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
1450 | "optional": true
1451 | },
1452 | "retry": {
1453 | "version": "0.12.0",
1454 | "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
1455 | "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
1456 | "optional": true
1457 | },
1458 | "retry-request": {
1459 | "version": "4.1.3",
1460 | "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.3.tgz",
1461 | "integrity": "sha512-QnRZUpuPNgX0+D1xVxul6DbJ9slvo4Rm6iV/dn63e048MvGbUZiKySVt6Tenp04JqmchxjiLltGerOJys7kJYQ==",
1462 | "optional": true,
1463 | "requires": {
1464 | "debug": "^4.1.1"
1465 | }
1466 | },
1467 | "safe-buffer": {
1468 | "version": "5.1.2",
1469 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1470 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
1471 | },
1472 | "safer-buffer": {
1473 | "version": "2.1.2",
1474 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1475 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1476 | },
1477 | "semver": {
1478 | "version": "6.3.0",
1479 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
1480 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
1481 | "optional": true
1482 | },
1483 | "send": {
1484 | "version": "0.17.1",
1485 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
1486 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
1487 | "requires": {
1488 | "debug": "2.6.9",
1489 | "depd": "~1.1.2",
1490 | "destroy": "~1.0.4",
1491 | "encodeurl": "~1.0.2",
1492 | "escape-html": "~1.0.3",
1493 | "etag": "~1.8.1",
1494 | "fresh": "0.5.2",
1495 | "http-errors": "~1.7.2",
1496 | "mime": "1.6.0",
1497 | "ms": "2.1.1",
1498 | "on-finished": "~2.3.0",
1499 | "range-parser": "~1.2.1",
1500 | "statuses": "~1.5.0"
1501 | },
1502 | "dependencies": {
1503 | "debug": {
1504 | "version": "2.6.9",
1505 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1506 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1507 | "requires": {
1508 | "ms": "2.0.0"
1509 | },
1510 | "dependencies": {
1511 | "ms": {
1512 | "version": "2.0.0",
1513 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1514 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1515 | }
1516 | }
1517 | },
1518 | "ms": {
1519 | "version": "2.1.1",
1520 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
1521 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
1522 | }
1523 | }
1524 | },
1525 | "serve-static": {
1526 | "version": "1.14.1",
1527 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
1528 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
1529 | "requires": {
1530 | "encodeurl": "~1.0.2",
1531 | "escape-html": "~1.0.3",
1532 | "parseurl": "~1.3.3",
1533 | "send": "0.17.1"
1534 | }
1535 | },
1536 | "setprototypeof": {
1537 | "version": "1.1.1",
1538 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1539 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1540 | },
1541 | "signal-exit": {
1542 | "version": "3.0.3",
1543 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
1544 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
1545 | "optional": true
1546 | },
1547 | "snakeize": {
1548 | "version": "0.1.0",
1549 | "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz",
1550 | "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=",
1551 | "optional": true
1552 | },
1553 | "statuses": {
1554 | "version": "1.5.0",
1555 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1556 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1557 | },
1558 | "stream-events": {
1559 | "version": "1.0.5",
1560 | "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
1561 | "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
1562 | "optional": true,
1563 | "requires": {
1564 | "stubs": "^3.0.0"
1565 | }
1566 | },
1567 | "stream-shift": {
1568 | "version": "1.0.1",
1569 | "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
1570 | "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
1571 | "optional": true
1572 | },
1573 | "streamsearch": {
1574 | "version": "0.1.2",
1575 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
1576 | "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
1577 | },
1578 | "string-width": {
1579 | "version": "4.2.2",
1580 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
1581 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
1582 | "optional": true,
1583 | "requires": {
1584 | "emoji-regex": "^8.0.0",
1585 | "is-fullwidth-code-point": "^3.0.0",
1586 | "strip-ansi": "^6.0.0"
1587 | }
1588 | },
1589 | "string_decoder": {
1590 | "version": "1.3.0",
1591 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
1592 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
1593 | "optional": true,
1594 | "requires": {
1595 | "safe-buffer": "~5.2.0"
1596 | },
1597 | "dependencies": {
1598 | "safe-buffer": {
1599 | "version": "5.2.1",
1600 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1601 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1602 | "optional": true
1603 | }
1604 | }
1605 | },
1606 | "strip-ansi": {
1607 | "version": "6.0.0",
1608 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
1609 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
1610 | "optional": true,
1611 | "requires": {
1612 | "ansi-regex": "^5.0.0"
1613 | }
1614 | },
1615 | "stubs": {
1616 | "version": "3.0.0",
1617 | "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
1618 | "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=",
1619 | "optional": true
1620 | },
1621 | "teeny-request": {
1622 | "version": "7.1.0",
1623 | "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.0.tgz",
1624 | "integrity": "sha512-hPfSc05a7Mf3syqVhSkrVMb844sMiP60MrfGMts3ft6V6UlSkEIGQzgwf0dy1KjdE3FV2lJ5s7QCBFcaoQLA6g==",
1625 | "optional": true,
1626 | "requires": {
1627 | "http-proxy-agent": "^4.0.0",
1628 | "https-proxy-agent": "^5.0.0",
1629 | "node-fetch": "^2.6.1",
1630 | "stream-events": "^1.0.5",
1631 | "uuid": "^8.0.0"
1632 | }
1633 | },
1634 | "toidentifier": {
1635 | "version": "1.0.0",
1636 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
1637 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
1638 | },
1639 | "tslib": {
1640 | "version": "2.3.0",
1641 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
1642 | "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
1643 | },
1644 | "type-is": {
1645 | "version": "1.6.18",
1646 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1647 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1648 | "requires": {
1649 | "media-typer": "0.3.0",
1650 | "mime-types": "~2.1.24"
1651 | }
1652 | },
1653 | "typedarray-to-buffer": {
1654 | "version": "3.1.5",
1655 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
1656 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
1657 | "optional": true,
1658 | "requires": {
1659 | "is-typedarray": "^1.0.0"
1660 | }
1661 | },
1662 | "unique-string": {
1663 | "version": "2.0.0",
1664 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
1665 | "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
1666 | "optional": true,
1667 | "requires": {
1668 | "crypto-random-string": "^2.0.0"
1669 | }
1670 | },
1671 | "unpipe": {
1672 | "version": "1.0.0",
1673 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1674 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
1675 | },
1676 | "util-deprecate": {
1677 | "version": "1.0.2",
1678 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1679 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
1680 | "optional": true
1681 | },
1682 | "utils-merge": {
1683 | "version": "1.0.1",
1684 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1685 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
1686 | },
1687 | "uuid": {
1688 | "version": "8.3.2",
1689 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
1690 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
1691 | "optional": true
1692 | },
1693 | "vary": {
1694 | "version": "1.1.2",
1695 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1696 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
1697 | },
1698 | "websocket-driver": {
1699 | "version": "0.7.4",
1700 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
1701 | "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
1702 | "requires": {
1703 | "http-parser-js": ">=0.5.1",
1704 | "safe-buffer": ">=5.1.0",
1705 | "websocket-extensions": ">=0.1.1"
1706 | }
1707 | },
1708 | "websocket-extensions": {
1709 | "version": "0.1.4",
1710 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
1711 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="
1712 | },
1713 | "wrap-ansi": {
1714 | "version": "7.0.0",
1715 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
1716 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
1717 | "optional": true,
1718 | "requires": {
1719 | "ansi-styles": "^4.0.0",
1720 | "string-width": "^4.1.0",
1721 | "strip-ansi": "^6.0.0"
1722 | }
1723 | },
1724 | "wrappy": {
1725 | "version": "1.0.2",
1726 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1727 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1728 | "optional": true
1729 | },
1730 | "write-file-atomic": {
1731 | "version": "3.0.3",
1732 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
1733 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
1734 | "optional": true,
1735 | "requires": {
1736 | "imurmurhash": "^0.1.4",
1737 | "is-typedarray": "^1.0.0",
1738 | "signal-exit": "^3.0.2",
1739 | "typedarray-to-buffer": "^3.1.5"
1740 | }
1741 | },
1742 | "xdg-basedir": {
1743 | "version": "4.0.0",
1744 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
1745 | "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==",
1746 | "optional": true
1747 | },
1748 | "y18n": {
1749 | "version": "5.0.8",
1750 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
1751 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
1752 | "optional": true
1753 | },
1754 | "yallist": {
1755 | "version": "4.0.0",
1756 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1757 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1758 | },
1759 | "yargs": {
1760 | "version": "16.2.0",
1761 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
1762 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
1763 | "optional": true,
1764 | "requires": {
1765 | "cliui": "^7.0.2",
1766 | "escalade": "^3.1.1",
1767 | "get-caller-file": "^2.0.5",
1768 | "require-directory": "^2.1.1",
1769 | "string-width": "^4.2.0",
1770 | "y18n": "^5.0.5",
1771 | "yargs-parser": "^20.2.2"
1772 | }
1773 | },
1774 | "yargs-parser": {
1775 | "version": "20.2.9",
1776 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
1777 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
1778 | "optional": true
1779 | },
1780 | "yocto-queue": {
1781 | "version": "0.1.0",
1782 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
1783 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
1784 | "optional": true
1785 | }
1786 | }
1787 | }
1788 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "serve": "firebase serve --only functions",
6 | "shell": "firebase experimental:functions:shell",
7 | "start": "npm run shell",
8 | "deploy": "firebase deploy --only functions",
9 | "logs": "firebase functions:log"
10 | },
11 | "dependencies": {
12 | "@popeindustries/lit-html-server": "^3.1.0",
13 | "axios": "^0.21.1",
14 | "express": "^4.17.1",
15 | "firebase-admin": "^9.9.0",
16 | "firebase-functions": "^3.14.1",
17 | "html-escaper": "^3.0.3",
18 | "lru-cache": "^6.0.0"
19 | },
20 | "private": true,
21 | "engines": {
22 | "node": "14"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "so-pwa",
3 | "version": "1.0.0",
4 | "description": "A PWA for Stack Overflow content.",
5 | "scripts": {
6 | "build": "run-s clean static css inline rollup workbox",
7 | "clean": "shx rm -rf build",
8 | "css": "postcss --output /tmp/styles.css src/styles.css",
9 | "deploy": "run-s build firebase-deploy",
10 | "firebase-deploy": "firebase deploy --only hosting,functions",
11 | "firebase-serve": "firebase serve --only hosting,functions",
12 | "inline": "regex-replace '\\/\\*styles\\*\\/' \"$(cat /tmp/styles.css)\" build/partials/",
13 | "lint": "eslint --ext='.js,.mjs' .",
14 | "rollup": "rollup --config rollup.config.js",
15 | "serve": "run-s build firebase-serve",
16 | "static": "shx cp -r 'src/static' build",
17 | "workbox": "workbox injectManifest"
18 | },
19 | "private": true,
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/GoogleChromeLabs/so-pwa.git"
23 | },
24 | "author": "Jeff Posnick",
25 | "license": "Apache-2.0",
26 | "bugs": {
27 | "url": "https://github.com/GoogleChromeLabs/so-pwa/issues"
28 | },
29 | "homepage": "https://github.com/GoogleChromeLabs/so-pwa#readme",
30 | "devDependencies": {
31 | "@ampproject/rollup-plugin-closure-compiler": "^0.26.0",
32 | "@babel/core": "^7.14.6",
33 | "@babel/preset-env": "^7.14.7",
34 | "@popeindustries/lit-html-server": "^3.1.0",
35 | "@rollup/plugin-commonjs": "^19.0.0",
36 | "@rollup/plugin-node-resolve": "^13.0.0",
37 | "@rollup/plugin-replace": "^2.4.2",
38 | "@surma/rollup-plugin-off-main-thread": "^2.2.2",
39 | "cssnano": "^5.0.6",
40 | "eslint": "^7.29.0",
41 | "eslint-config-google": "^0.14.0",
42 | "eslint-plugin-header": "^3.1.1",
43 | "firebase-tools": "^9.14.0",
44 | "html-escaper": "^3.0.3",
45 | "npm-run-all": "^4.1.5",
46 | "postcss": "^8.3.5",
47 | "postcss-cli": "^8.3.1",
48 | "postcss-css-variables": "^0.18.0",
49 | "regex-replace": "^2.3.1",
50 | "regexparam": "^2.0.0",
51 | "rollup": "^2.52.2",
52 | "rollup-plugin-babel": "^4.4.0",
53 | "rollup-plugin-string": "^3.0.0",
54 | "shx": "^0.3.3",
55 | "workbox-cacheable-response": "^6.1.5",
56 | "workbox-cli": "^6.1.5",
57 | "workbox-core": "^6.1.5",
58 | "workbox-expiration": "^6.1.5",
59 | "workbox-precaching": "^6.1.5",
60 | "workbox-routing": "^6.1.5",
61 | "workbox-strategies": "^6.1.5",
62 | "workbox-streams": "^6.1.5"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = {
18 | plugins: {
19 | 'postcss-css-variables': {},
20 | 'cssnano': {},
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {string} from 'rollup-plugin-string';
18 | import babel from 'rollup-plugin-babel';
19 | import compiler from '@ampproject/rollup-plugin-closure-compiler';
20 | import OMT from '@surma/rollup-plugin-off-main-thread';
21 | import replace from '@rollup/plugin-replace';
22 | import resolve from '@rollup/plugin-node-resolve';
23 |
24 | // The version of Chromium used by Samsung Internet 11.x.
25 | const BROWSER_TARGET = {
26 | browsers: ['chrome >= 75'],
27 | };
28 |
29 | // The version of node used in Firebase Cloud Functions.
30 | const NODE_TARGET = {
31 | node: '14',
32 | };
33 |
34 | export default [{
35 | input: 'src/server.js',
36 | external: [
37 | '@popeindustries/lit-html-server',
38 | '@popeindustries/lit-html-server/directives/unsafe-html',
39 | 'axios',
40 | 'express',
41 | 'firebase-functions',
42 | 'https',
43 | 'html-escaper',
44 | 'lru-cache',
45 | ],
46 | plugins: [
47 | string({
48 | include: 'build/partials/**/*.html',
49 | }),
50 | babel({
51 | presets: [['@babel/preset-env', {
52 | targets: NODE_TARGET,
53 | modules: false,
54 | }]],
55 | }),
56 | ],
57 | output: {
58 | file: 'functions/index.js',
59 | format: 'cjs',
60 | },
61 | }, {
62 | input: 'src/service-worker.mjs',
63 | manualChunks: (id) => {
64 | if (!id.includes('/node_modules/')) {
65 | return undefined;
66 | }
67 |
68 | const chunkNames = [
69 | 'lit-html',
70 | 'html-escaper',
71 | 'regexparam',
72 | 'workbox',
73 | ];
74 |
75 | return chunkNames.find((chunkName) => id.includes(chunkName)) || 'misc';
76 | },
77 | plugins: [
78 | replace({
79 | 'preventAssignment': true,
80 | 'process.env.NODE_ENV': JSON.stringify(
81 | process.env.NODE_ENV || 'development'),
82 | }),
83 | resolve({
84 | browser: true,
85 | }),
86 | babel({
87 | presets: [['@babel/preset-env', {
88 | targets: BROWSER_TARGET,
89 | modules: false,
90 | }]],
91 | }),
92 | OMT(), // eslint-disable-line new-cap
93 | compiler(),
94 | ],
95 | output: {
96 | dir: 'build',
97 | format: 'amd',
98 | },
99 | }, {
100 | input: 'src/app.mjs',
101 | plugins: [
102 | resolve({
103 | browser: true,
104 | }),
105 | babel({
106 | presets: [['@babel/preset-env', {
107 | targets: BROWSER_TARGET,
108 | modules: false,
109 | }]],
110 | }),
111 | compiler(),
112 | ],
113 | output: {
114 | file: 'build/app.js',
115 | format: 'iife',
116 | sourcemap: true,
117 | },
118 | }];
119 |
--------------------------------------------------------------------------------
/src/app.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {unescape} from 'html-escaper';
18 |
19 | import {API_CACHE_NAME} from './lib/constants.mjs';
20 | import {syncContentIndex} from './lib/content-indexing.mjs';
21 |
22 | window.addEventListener('load', async () => {
23 | if (self._title) {
24 | document.title = unescape(self._title);
25 | }
26 |
27 | if ('serviceWorker' in navigator) {
28 | const registration = await navigator.serviceWorker.register(
29 | '/service-worker.js');
30 |
31 | syncContentIndex(registration);
32 | }
33 |
34 | const apiCache = await caches.open(API_CACHE_NAME);
35 | const cachedRequests = await apiCache.keys();
36 | const cachedUrls = cachedRequests.map((request) => request.url);
37 |
38 | const offlineIndicator = document.querySelector('#offline');
39 | const cards = document.querySelectorAll('.card');
40 | const uncachedCards = [...cards].filter((card) => {
41 | return !cachedUrls.includes(card.dataset.cacheUrl);
42 | });
43 |
44 | const onlineHandler = () => {
45 | for (const uncachedCard of uncachedCards) {
46 | uncachedCard.style.opacity = '1.0';
47 | }
48 | offlineIndicator.style.display = 'none';
49 | };
50 |
51 | const offlineHandler = () => {
52 | for (const uncachedCard of uncachedCards) {
53 | uncachedCard.style.opacity = '0.3';
54 | }
55 | offlineIndicator.style.display = 'block';
56 | };
57 |
58 | if (navigator.onLine) {
59 | onlineHandler();
60 | } else {
61 | offlineHandler();
62 | }
63 |
64 | window.addEventListener('online', onlineHandler);
65 | window.addEventListener('offline', offlineHandler);
66 | });
67 |
--------------------------------------------------------------------------------
/src/lib/constants.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | export const API_CACHE_NAME = 'api-cache';
18 | export const DEFAULT_TAG = 'service-worker';
19 | export const SORT_ORDERS = {
20 | ACTIVITY: 'activity',
21 | VOTES: 'votes',
22 | };
23 | export const DEFAULT_SORT = SORT_ORDERS.VOTES;
24 | export const PBS_TAG = 'content-sync';
25 |
--------------------------------------------------------------------------------
/src/lib/content-indexing.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {API_CACHE_NAME} from './constants.mjs';
18 |
19 | // This method syncs the currently cached media with the Content Indexing API
20 | // (on browsers that support it). The Cache Storage is the source of truth.
21 | export async function syncContentIndex(registration) {
22 | // Bail early if the Content Indexing API isn't supported.
23 | if (!('index' in registration)) {
24 | return;
25 | }
26 |
27 | // Get a list of everything currently in the content index.
28 | const ids = new Set();
29 | for (const contentDescription of await registration.index.getAll()) {
30 | // Add each currently indexed id to the set.
31 | ids.add(contentDescription.id);
32 | }
33 |
34 | // Get a list of all cached media.
35 | const cache = await caches.open(API_CACHE_NAME);
36 | const cachedRequests = await cache.keys();
37 |
38 | for (const request of cachedRequests) {
39 | const url = new URL(request.url);
40 | const pathParts = url.pathname.split('/');
41 | // If this is a cached API result for a page, then the 3rd split item
42 | // will be a number.
43 | if (!isNaN(pathParts[3])) {
44 | const response = await cache.match(request);
45 | const json = await response.json();
46 | const [data] = json.items;
47 |
48 | // Use the question_id as the authoritative id value.
49 | const id = data.question_id;
50 |
51 | if (!id || ids.has(id)) {
52 | ids.delete(id);
53 | } else {
54 | const url = `/questions/${id}`;
55 | await registration.index.add({
56 | id,
57 | url,
58 | launchUrl: url,
59 | category: 'article',
60 | description: 'A question from Stack Overflow.',
61 | icons: [{
62 | src: '/icon.png',
63 | sizes: '192x192',
64 | type: 'image/png',
65 | }],
66 | title: data.title,
67 | });
68 | }
69 | }
70 | }
71 |
72 | // Finally, for all of the ids that are currently in the index but aren't
73 | // cached (i.e. all values that are still in the ids set), remove
74 | // them from the index.
75 | for (const id of ids) {
76 | await registration.index.delete(id);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/lib/partials.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | export default {
18 | about: 'partials/about.html',
19 | foot: 'partials/foot.html',
20 | head: 'partials/head.html',
21 | navbar: 'partials/navbar.html',
22 | };
23 |
--------------------------------------------------------------------------------
/src/lib/periodic-background-sync.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {API_CACHE_NAME, DEFAULT_SORT, DEFAULT_TAG, PBS_TAG}
18 | from './constants.mjs';
19 | import {listQuestionsForTag} from './urls.mjs';
20 |
21 | export async function initialize() {
22 | if ('periodicSync' in self.registration) {
23 | self.addEventListener('periodicsync', (event) => {
24 | if (event.tag === PBS_TAG) {
25 | event.waitUntil((async () => {
26 | const cache = await caches.open(API_CACHE_NAME);
27 | const url = listQuestionsForTag(DEFAULT_TAG, DEFAULT_SORT);
28 | await cache.add(url);
29 | console.log(`In periodicsync handler, updated`, url);
30 | })());
31 | }
32 | });
33 |
34 | const status = await self.navigator.permissions.query({
35 | name: 'periodic-background-sync',
36 | });
37 |
38 | if (status.state === 'granted') {
39 | const tags = await self.registration.periodicSync.getTags();
40 | if (tags.includes(PBS_TAG)) {
41 | console.log(`Already registered for periodic background sync with tag`,
42 | PBS_TAG);
43 | } else {
44 | try {
45 | await registration.periodicSync.register(PBS_TAG, {
46 | // An interval of one day.
47 | minInterval: 24 * 60 * 60 * 1000,
48 | });
49 | console.log(`Registered for periodic background sync with tag`,
50 | PBS_TAG);
51 | } catch (error) {
52 | console.error(`Periodic background sync permission is 'granted', ` +
53 | `but something went wrong:`, error);
54 | }
55 | }
56 | } else {
57 | console.info(`Periodic background sync permission is not 'granted', so ` +
58 | `skipping registration.`);
59 | }
60 | } else {
61 | console.log(`Periodic background sync is not available in this browser.`);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/lib/route-matchers.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {parse} from 'regexparam';
18 |
19 | import routes from './routes.mjs';
20 |
21 | const routeMatchers = new Map();
22 | for (const [routeName, expressRoute] of routes) {
23 | // regexparam creates a RegExp that works when matched against just the
24 | // pathname, but Workbox matches against the full URL (including origin and
25 | // search params) when doing RegExp matching. To work around this,
26 | // we'll create our own functions that implement the matchCallback interface:
27 | // https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing.Route#~matchCallback
28 | const regExp = parse(expressRoute).pattern;
29 | const matcher = ({url}) => regExp.exec(url.pathname);
30 | routeMatchers.set(routeName, matcher);
31 | }
32 |
33 | export default routeMatchers;
34 |
--------------------------------------------------------------------------------
/src/lib/routes.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | const routes = new Map([
18 | ['about', '/about'],
19 | ['questions', '/questions/:questionId'],
20 | ['index', '/'],
21 | ]);
22 |
23 | export default routes;
24 |
--------------------------------------------------------------------------------
/src/lib/templates.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {escape, unescape} from 'html-escaper';
18 | import {html} from '@popeindustries/lit-html-server';
19 | import {unsafeHTML} from
20 | '@popeindustries/lit-html-server/directives/unsafe-html';
21 |
22 |
23 | import {DEFAULT_TAG, SORT_ORDERS} from './constants.mjs';
24 | import {getQuestion} from './urls.mjs';
25 |
26 | function formatDate(timestamp) {
27 | return new Date(timestamp * 1000).toLocaleString();
28 | }
29 |
30 | function profile({imageUrl, date, profileLink, displayName, anchorLink}) {
31 | return html`
32 |
33 |
${displayName}
37 | at
38 |
${date}
39 |
40 | `;
41 | }
42 |
43 | function questionCard({id, title}) {
44 | return html`
45 |
48 | ${title}
49 |
50 | `;
51 | }
52 |
53 | export function index(tag, items, sort) {
54 | if (!items) {
55 | return html`Unable to list questions for the tag.
`;
56 | }
57 |
58 | const titleString = (sort === SORT_ORDERS.VOTES ? 'Top' : 'Active') +
59 | ` "${tag}" Questions`;
60 | const title = html`
61 | ${titleString}
62 | `;
63 |
64 | const form = html`
65 |
71 | `;
72 |
73 | const questionCards = items.map((item) => questionCard({
74 | id: item.question_id,
75 | title: unescape(item.title),
76 | }));
77 |
78 | const questions = html`${questionCards}
`;
79 |
80 | const metadataScript = html`
81 |
84 | `;
85 |
86 | return html`
87 | ${title}
88 | ${form}
89 | ${questions}
90 | ${metadataScript}
91 | `;
92 | }
93 |
94 | export function question(item) {
95 | if (!item) {
96 | return html`Unable to load question.
`;
97 | }
98 |
99 | const ownerProfile = profile({
100 | anchorLink: item.link,
101 | date: formatDate(item.creation_date),
102 | displayName: item.owner.display_name,
103 | imageUrl: item.owner.profile_image,
104 | profileLink: item.owner.link,
105 | });
106 |
107 | const title = unescape(item.title);
108 |
109 | const question = html`
110 | ${title}
111 | ${ownerProfile}
112 | ${unsafeHTML(item.body)}
113 | `;
114 |
115 | const answers = item.answers ? item.answers
116 | .sort((a, b) => a.score < b.score)
117 | .map((answer) => {
118 | const answererProfile = profile({
119 | anchorLink: answer.link,
120 | date: formatDate(answer.creation_date),
121 | displayName: answer.owner.display_name,
122 | imageUrl: answer.owner.profile_image,
123 | profileLink: answer.owner.link,
124 | });
125 |
126 | return html`
127 | ${answererProfile}
128 | ${unsafeHTML(answer.body)}
129 | `;
130 | }) : [];
131 |
132 | const metadataScript = html`
133 |
136 | `;
137 |
138 | return html`
139 | ${question}
140 |
141 | ${answers}
142 | ${metadataScript}
143 | `;
144 | }
145 |
146 | export function error(message) {
147 | return html`
148 | Sorry, this page couldn't be loaded.
149 | Try a cached page instead.
150 | ${message}
151 | `;
152 | }
153 |
--------------------------------------------------------------------------------
/src/lib/urls.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | const PATH = `https://api.stackexchange.com/2.2/questions`;
18 |
19 | // As per https://api.stackexchange.com/docs/throttle
20 | // While this is a read-only, non-secret key, please register your own
21 | // and replace this value if you fork this project!
22 | const KEY = `LJ54sdY)tUYvfsHg2kwLvQ((`;
23 | const SITE = 'stackoverflow';
24 |
25 | export function listQuestionsForTag(tag, sort) {
26 | const url = new URL(PATH);
27 |
28 | url.searchParams.set('filter', '!C(o*VY))7BGSrm5xK');
29 | url.searchParams.set('key', KEY);
30 | url.searchParams.set('order', 'desc');
31 | url.searchParams.set('pagesize', 100);
32 | url.searchParams.set('site', SITE);
33 | url.searchParams.set('sort', sort);
34 | url.searchParams.set('tagged', tag);
35 |
36 | return url.href;
37 | }
38 |
39 | export function getQuestion(questionId) {
40 | const url = new URL(PATH);
41 | url.pathname += `/${questionId}`;
42 |
43 | url.searchParams.set('filter',
44 | '!oDhDpbIIc)pcGHpmWvn_fa0Hu6PKHizd-W.RnKEVsIq');
45 | url.searchParams.set('key', KEY);
46 | url.searchParams.set('site', SITE);
47 |
48 | return url.href;
49 | }
50 |
--------------------------------------------------------------------------------
/src/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | // CommonJS imports from node_modules or core.
18 | import axios from 'axios';
19 | import express from 'express';
20 | import functions from 'firebase-functions';
21 | import https from 'https';
22 | import LRU from 'lru-cache';
23 | import {renderToString} from '@popeindustries/lit-html-server';
24 |
25 | // Local ES2105 imports.
26 | import {DEFAULT_SORT, DEFAULT_TAG} from './lib/constants.mjs';
27 | import * as templates from './lib/templates.mjs';
28 | import * as urls from './lib/urls.mjs';
29 | import routes from './lib/routes.mjs';
30 |
31 | // HTML imports.
32 | import aboutPartial from '../build/partials/about.html';
33 | import footPartial from '../build/partials/foot.html';
34 | import headPartial from '../build/partials/head.html';
35 | import navbarPartial from '../build/partials/navbar.html';
36 |
37 | // See https://cloud.google.com/functions/docs/bestpractices/networking#http_requests_with_an_axios_package
38 | const apiClient = axios.create({
39 | baseURL: '',
40 | timeout: 10000,
41 | });
42 |
43 | const httpsAgent = new https.Agent({
44 | keepAlive: true,
45 | });
46 |
47 | // Once a browser client has an active service worker, it will no longer need
48 | // to obtain API responses from this server process. But, to cut down on the
49 | // number of API requests that fresh browser clients might trigger, let's put
50 | // in some light-weight caching that's local to this process.
51 | const apiCache = new LRU({
52 | max: 100,
53 | maxAge: 1000 * 60 * 5, // 5 minutes.
54 | });
55 |
56 | async function requestData(url) {
57 | const cachedResponse = apiCache.get(url);
58 | if (cachedResponse) {
59 | return cachedResponse;
60 | }
61 |
62 | const networkResponse = await apiClient.request({
63 | httpsAgent,
64 | url,
65 | });
66 |
67 | const data = networkResponse.data;
68 | apiCache.set(url, data);
69 | return data;
70 | }
71 |
72 | const app = express();
73 |
74 | app.get(routes.get('about'), async (req, res) => {
75 | res.send(headPartial + navbarPartial + aboutPartial + footPartial);
76 | });
77 |
78 | app.get(routes.get('questions'), async (req, res) => {
79 | res.write(headPartial + navbarPartial);
80 |
81 | const questionId = req.params.questionId;
82 | try {
83 | const data = await requestData(urls.getQuestion(questionId));
84 | const questionHTML = await renderToString(
85 | templates.question(data.items[0]));
86 | res.write(questionHTML);
87 | } catch (error) {
88 | const errorHTML = await renderToString(templates.error(error.message));
89 | res.write(errorHTML);
90 | }
91 |
92 | res.write(footPartial);
93 | res.end();
94 | });
95 |
96 | app.get(routes.get('index'), async (req, res) => {
97 | res.write(headPartial + navbarPartial);
98 |
99 | try {
100 | const tag = req.query.tag || DEFAULT_TAG;
101 | const sort = req.params.sort || DEFAULT_SORT;
102 | const data = await requestData(urls.listQuestionsForTag(tag, sort));
103 | const indexHTML = await renderToString(
104 | templates.index(tag, data.items, sort));
105 | res.write(indexHTML);
106 | } catch (error) {
107 | const errorHTML = await renderToString(templates.error(error.message));
108 | res.write(errorHTML);
109 | }
110 |
111 | res.write(footPartial);
112 | res.end();
113 | });
114 |
115 | export const handleRequest = functions.https.onRequest(app);
116 |
--------------------------------------------------------------------------------
/src/service-worker.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | import {CacheableResponsePlugin} from 'workbox-cacheable-response';
18 | import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';
19 | import {cleanupOutdatedCaches, matchPrecache, precacheAndRoute}
20 | from 'workbox-precaching';
21 | import {clientsClaim} from 'workbox-core';
22 | import {ExpirationPlugin} from 'workbox-expiration';
23 | import {registerRoute} from 'workbox-routing';
24 | import {strategy as streamsStrategy} from 'workbox-streams';
25 | import {renderToStream} from '@popeindustries/lit-html-server';
26 |
27 | import {API_CACHE_NAME, DEFAULT_TAG, DEFAULT_SORT} from './lib/constants.mjs';
28 | import * as templates from './lib/templates.mjs';
29 | import * as urls from './lib/urls.mjs';
30 | import partials from './lib/partials.mjs';
31 | import {initialize as pbsInitialize} from './lib/periodic-background-sync.mjs';
32 | import routeMatchers from './lib/route-matchers.mjs';
33 |
34 | precacheAndRoute(self.__WB_MANIFEST);
35 | cleanupOutdatedCaches();
36 |
37 | const apiStrategy = new StaleWhileRevalidate({
38 | cacheName: API_CACHE_NAME,
39 | plugins: [
40 | new ExpirationPlugin({maxEntries: 50}),
41 | ],
42 | });
43 |
44 | registerRoute(
45 | routeMatchers.get('about'),
46 | streamsStrategy([
47 | () => matchPrecache(partials.head),
48 | () => matchPrecache(partials.navbar),
49 | () => matchPrecache(partials.about),
50 | () => matchPrecache(partials.foot),
51 | ]),
52 | );
53 |
54 | registerRoute(
55 | routeMatchers.get('questions'),
56 | streamsStrategy([
57 | () => matchPrecache(partials.head),
58 | () => matchPrecache(partials.navbar),
59 | async ({event, params}) => {
60 | try {
61 | const questionId = params[1];
62 | const questionResponse = await apiStrategy.handle({
63 | event,
64 | request: urls.getQuestion(questionId),
65 | });
66 | const data = await questionResponse.json();
67 | return renderToStream(templates.question(data.items[0]));
68 | } catch (error) {
69 | return renderToStream(templates.error(error.message));
70 | }
71 | },
72 | () => matchPrecache(partials.foot),
73 | ]),
74 | );
75 |
76 | registerRoute(
77 | routeMatchers.get('index'),
78 | streamsStrategy([
79 | () => matchPrecache(partials.head),
80 | () => matchPrecache(partials.navbar),
81 | async ({event, url}) => {
82 | try {
83 | const sort = url.searchParams.get('sort') || DEFAULT_SORT;
84 | const tag = url.searchParams.get('tag') || DEFAULT_TAG;
85 | const listResponse = await apiStrategy.handle({
86 | event,
87 | request: urls.listQuestionsForTag(tag, sort),
88 | });
89 | const data = await listResponse.json();
90 | return renderToStream(templates.index(tag, data.items, sort));
91 | } catch (error) {
92 | return renderToStream(templates.error(error.message));
93 | }
94 | },
95 | () => matchPrecache(partials.foot),
96 | ]),
97 | );
98 |
99 | // Gravatar images support CORS, so we won't be storing opaque responses.
100 | registerRoute(
101 | ({url}) => url.origin === 'https://www.gravatar.com',
102 | new CacheFirst({
103 | cacheName: 'profile-images',
104 | plugins: [
105 | new ExpirationPlugin({
106 | maxEntries: 50,
107 | purgeOnQuotaError: true,
108 | }),
109 | ],
110 | }),
111 | );
112 |
113 | registerRoute(
114 | ({request}) => request.destination === 'image',
115 | new CacheFirst({
116 | cacheName: 'other-images',
117 | plugins: [
118 | new CacheableResponsePlugin({statuses: [0, 200]}),
119 | new ExpirationPlugin({
120 | maxEntries: 10,
121 | purgeOnQuotaError: true,
122 | }),
123 | ],
124 | }),
125 | );
126 |
127 | self.skipWaiting();
128 | clientsClaim();
129 |
130 | pbsInitialize();
131 |
--------------------------------------------------------------------------------
/src/static/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/so-pwa/f1fa063a498a26a1a449fbfbca6b8e9bf6a337fc/src/static/icon.png
--------------------------------------------------------------------------------
/src/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "SO PWA",
3 | "name": "SO Progressive Web App",
4 | "icons": [{
5 | "src": "/icon.png",
6 | "sizes": "512x512",
7 | "type": "image/png"
8 | }],
9 | "start_url": "/?utm_source=homescreen",
10 | "display": "minimal-ui",
11 | "background_color": "#ededed",
12 | "theme_color": "#0093c4"
13 | }
14 |
--------------------------------------------------------------------------------
/src/static/offline.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/static/partials/about.html:
--------------------------------------------------------------------------------
1 | About SO PWA
2 |
3 | This is a sample
4 | progressive web app
5 | which uses the
6 | Stack Exchange API to fetch
7 | the top questions and answers from
8 | Stack Overflow for a given tag.
9 |
10 |
11 | Under the hood, it's powered by the following technologies:
12 |
13 |
14 |
15 | Service worker generation and
16 | Streams API logic via
17 | Workbox .
18 |
19 |
20 | Static and dynamic web hosting via
21 | Firebase Cloud Functions .
22 |
23 |
24 | "Universal" JavaScript via ES2015 source modules, bundled for the browser
25 | and Node by
26 | Rollup , with
27 | babel-preset-env
28 | ensuring compatibility with various runtimes.
29 |
30 |
31 | Shared server + service worker routing logic using
32 | Express-style
33 | patterns and the
34 | regexparam
35 | library in the service worker.
36 |
37 |
38 | Shared server + service worker HTML generation logic, using
39 | lit-html-server
40 | to render the
41 | lit-html
templates.
42 |
43 |
44 |
45 | This project is open source, and
46 | available on GitHub .
47 |
48 |
51 |
--------------------------------------------------------------------------------
/src/static/partials/foot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | All content comes from the Stack Exchange Network, using the
5 | public API ,
6 | and is licensed under
7 | CC BY-SA 3.0
8 |
9 |
10 |
11 |