├── .clang-format
├── .github
└── CODEOWNERS
├── .gitignore
├── .mocharc.json
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── bin
└── prpl-server
├── package-lock.json
├── package.json
├── src
├── cli.ts
├── prpl.ts
├── push.ts
└── test
│ ├── prpl_test.ts
│ ├── push_test.ts
│ └── static
│ ├── es2015
│ ├── fragment.html
│ ├── index.html
│ ├── push-manifest.json
│ └── service-worker.js
│ ├── fallback
│ ├── fragment.html
│ └── index.html
│ └── standalone
│ ├── fragment.html
│ ├── index.html
│ └── push-manifest.json
└── tsconfig.json
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 | AlignAfterOpenBracket: AlwaysBreak
3 | AllowAllParametersOfDeclarationOnNextLine: false
4 | AllowShortBlocksOnASingleLine: false
5 | AllowShortCaseLabelsOnASingleLine: false
6 | AllowShortFunctionsOnASingleLine: None
7 | AllowShortIfStatementsOnASingleLine: false
8 | AllowShortLoopsOnASingleLine: false
9 | BinPackArguments: false
10 | BinPackParameters: false
11 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @aomarks
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "ui": "tdd"
3 | }
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | test/
3 |
4 | .clang-format
5 | .travis.yml
6 | tsconfig.json
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | - "node"
5 | script:
6 | - npm test
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/)
6 | and this project adheres to [Semantic Versioning](http://semver.org/).
7 |
8 |
9 |
10 |
11 | ## [1.4.2] 2020-08-12
12 | - Update dependencies.
13 | - Clean up unnecessary NPM package files.
14 |
15 | ## [1.4.1] 2020-08-07
16 | - Clean up unnecessary dependencies.
17 |
18 | ## [1.4.0] 2019-01-07
19 | - Don't cache response for SW.
20 |
21 | ## [1.3.0] 2018-12-06
22 | - Support crossorigin setting in push-manifest.
23 |
24 | ## [1.2.0] 2018-05-22
25 | - Add `as=fetch` as a valid value for preload headers.
26 |
27 | ## [1.1.0] 2018-04-23
28 | - Update browser-capabilities to pick up the latest user agent information for modules and service workers. Support for dynamic `import()` and `import.meta` are now requirements for the `modules` capability.
29 | - If a build with a push manifest is served to a browser that does not support push (according to browser-capabilities), then we will still set preload headers, but with the `nopush` attribute set.
30 |
31 | ## [1.0.0] 2017-10-31
32 | - Add `forwardErrors` option to pass 404s and other HTTP errors down to the next Express error-handling middleware.
33 | - Recommend `npm` instead of `yarn` and switch to `npm` lock file.
34 | - Check file existence asynchronously so the event loop is not blocked.
35 |
36 | ## [0.11.0] 2017-10-23
37 | - Add `unregisterMissingServiceWorkers` option (default true) which serves a tiny self-unregistering service worker for would-be 404 service worker requests, to prevent clients from getting stuck with invalid service workers indefinitely.
38 |
39 | ## [0.10.2] 2017-10-18
40 | - Require latest browser capabilities, which removes Firefox from push capable browsers due to https://bugzilla.mozilla.org/show_bug.cgi?id=1409570.
41 | - Bump Yarn lock dependencies.
42 |
43 | ## [0.10.1] 2017-09-12
44 | - Check the original URL path against the push manifest in addition to the resolved filename. This allows mapping application route patterns to push resources.
45 |
46 | ## [0.10.0] 2017-09-11
47 | - Push manifest keys are now regular expression patterns instead of exact paths.
48 | - The `Cache-Control` header is now set to 1 minute by default (except for the entrypoint). Added the `cacheControl` config property and `--cache-control` flag to override.
49 |
50 | ## [0.9.0] 2017-08-23
51 | - Add `--bot-proxy` flag to proxy requests from bots through [Rendertron](https://github.com/GoogleChrome/rendertron).
52 |
53 | ## [0.8.0] 2017-08-09
54 | - Switch to https://github.com/Polymer/browser-capabilities library.
55 | - Add `modules` capability.
56 | - Declare TypeScript typings in package.
57 |
58 | ## [0.7.0] 2017-05-30
59 | - Add `--version` flag.
60 |
61 | ## [0.6.0] 2017-05-23
62 | - Relative push manifest paths are now interpreted as relative to the location of the push manifest file itself. Previously they were always interpreted as relative to the server root.
63 | - Extra safeguard against directory traversal attacks.
64 |
65 | ## [0.5.0] 2017-05-22
66 | - Add `serviceworker` to browser capability detection.
67 |
68 | ## [0.4.0] 2017-05-19
69 | - Add HTTP to HTTPS redirection.
70 |
71 | ## [0.3.0] 2017-05-18
72 | - The commandline server now compresses responses.
73 | - Fixed Windows bugs; now tested on AppVeyor.
74 |
75 | ## [0.2.0] 2017-05-16
76 | - Initial release.
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
2 | This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
3 | The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
4 | Code distributed by Google as part of the polymer project is also
5 | subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/Polymer/prpl-server-node)
2 | [](https://ci.appveyor.com/project/aomarks/prpl-server-node/branch/master)
3 | [](https://www.npmjs.com/package/prpl-server)
4 |
5 | # prpl-server
6 |
7 | An HTTP server for Node designed to serve [PRPL](https://developers.google.com/web/fundamentals/performance/prpl-pattern/) apps in production.
8 |
9 | > ⚠️IMPORTANT⚠️ `prpl-server` is in maintenance mode, and is no longer recommended. Reports and PRs for critical bugs and security issues will be accepted, but we will no longer accept new feature requests or PRs.
10 | >
11 | > For differential serving, we now recommend a simple two-build configuration using `nomodule` for client-side capability sniffing. See https://jasonformat.com/modern-script-loading/ for more details on this pattern.
12 | >
13 | > If you are looking for a modern *development* sever, we recommend [@web/dev-server](https://modern-web.dev/docs/dev-server/overview/).
14 | >
15 | > In addition, note that Chromium is [considering](https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ?pli=1) removing support for Server Push in a future version. Consider using [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload) tags as a simpler alternative with similar performance benefits.
16 |
17 | ## Contents
18 | - [Usage](#usage)
19 | - [As a binary](#as-a-binary)
20 | - [As a library](#as-a-library)
21 | - [Differential Serving](#differential-serving)
22 | - [Builds](#builds)
23 | - [Capabilities](#capabilities)
24 | - [Entrypoint](#entrypoint)
25 | - [Base paths](#base-paths)
26 | - [HTTP/2 Server Push](#http-2-server-push)
27 | - [Push manifest](#push-manifest)
28 | - [Link preload headers](#link-preload-headers)
29 | - [Testing push locally](#testing-push-locally)
30 | - [Service Workers](#service-workers)
31 | - [Scope header](#scope-header)
32 | - [404 handling](#404-handling)
33 | - [HTTPS](#https)
34 | - [Caching](#caching)
35 | - [HTTP Errors](#http-errors)
36 | - [Rendering for Bots](#rendering-for-bots)
37 | - [Google App Engine Quickstart](#google-app-engine-quickstart)
38 |
39 | ## Usage
40 |
41 | ### As a binary
42 | ```sh
43 | $ npm install -g prpl-server
44 | $ prpl-server --root . --config polymer.json
45 | ```
46 |
47 | ### As a library
48 |
49 | ```sh
50 | $ npm install --save prpl-server
51 | ```
52 |
53 | ```js
54 | prpl = require('prpl-server');
55 | express = require('express');
56 |
57 | const app = express();
58 |
59 | app.get('/api/launch', (req, res, next) => res.send('boom'));
60 |
61 | app.get('/*', prpl.makeHandler('.', {
62 | builds: [
63 | {name: 'modern', browserCapabilities: ['es2015', 'push']},
64 | {name: 'fallback'},
65 | ],
66 | }));
67 |
68 | app.listen(8080);
69 | ```
70 |
71 | ## Differential Serving
72 |
73 | Modern browsers offer great features that improve performance, but most applications need to support older browsers too. prpl-server can serve different versions of your application to different browsers by detecting browser capabilities using the user-agent header.
74 |
75 | ### Builds
76 |
77 | prpl-server understands the notion of a *build*, a variant of your application optimized for a particular set of browser capabilities.
78 |
79 | Builds are specified in a JSON configuration file. This format is compatible with [`polymer.json`](https://www.polymer-project.org/2.0/docs/tools/polymer-json), so if you are already using polymer-cli for your build pipeline, you can annotate your existing builds with browser capabilities, and copy the configuration to your server root. prpl-server will look for a file called `polymer.json` in the server root, or you can specify it directly with the `--config` flag.
80 |
81 |
82 | In this example we define two builds, one for modern browsers that support ES2015 and HTTP/2 Push, and a fallback build for other browsers:
83 |
84 | ```
85 | {
86 | "entrypoint": "index.html",
87 | "builds": [
88 | {"name": "modern", "browserCapabilities": ["es2015", "push"]},
89 | {"name": "fallback"}
90 | ]
91 | }
92 | ```
93 |
94 | ### Capabilities
95 |
96 | The `browserCapabilities` field defines the browser features required for that build. prpl-server analyzes the request user-agent header and picks the best build for which all capabilities are met. If multiple builds are compatible, the one with more capabilities is preferred. If there is a tie, the build that comes earlier in the configuration file wins.
97 |
98 | You should always include a fallback build with no capability requirements. If you don't, prpl-server will warn at startup, and will return a 500 error on entrypoint requests to browsers for which no build can be served.
99 |
100 | The following keywords are supported. See also the [browser-capabilities](https://github.com/Polymer/tools/tree/master/packages/browser-capabilities) library which prpl-server uses.
101 |
102 | | Keyword | Description
103 | | :---- | :----
104 | | push | [HTTP/2 Server Push](https://developers.google.com/web/fundamentals/performance/http2/#server-push)
105 | | serviceworker | [Service Worker API](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers)
106 | | modules | [JavaScript Modules](https://www.chromestatus.com/feature/5365692190687232) (including dynamic `import()` and `import.meta`)
107 | | es2015 | [ECMAScript 2015 (aka ES6)](https://developers.google.com/web/shows/ttt/series-2/es2015)
108 | | es2016 | ECMAScript 2016
109 | | es2017 | ECMAScript 2017
110 | | es2018 | ECMAScript 2018
111 |
112 | ## Entrypoint
113 |
114 | In the [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/), the *entrypoint* is a small HTML file that acts as the application bootstrap.
115 |
116 | prpl-server will serve the entrypoint from the best compatible build from `/`, and from any path that does not have a file extension and is not an existing file.
117 |
118 | prpl-server expects that each build subdirectory contains its own entrypoint file. By default it is `index.html`, or you can specify another name with the `entrypoint` configuration file setting.
119 |
120 | Note that because the entrypoint is served from many URLs, and varies by user-agent, cache hits for the entrypoint will be minimal, so it should be kept as small as possible.
121 |
122 | ## Base paths
123 |
124 | Since prpl-server serves resources from build subdirectories, your application source can't know the absolute URLs of build-specific resources upfront.
125 |
126 | For most documents in your application, the solution is to use relative URLs to refer to other resources in the build, and absolute URLs to refer to resources outside of the build (e.g. static assets, APIs). However, since the *entrypoint* is served from URLs that do not match its location in the build tree, relative URLs will not resolve correctly.
127 |
128 | The solution we recommend is to place a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag in your entrypoint to anchor its relative URLs to the correct build subdirectory, regardless of the URL the entrypoint was served from. You may then use relative URLs to refer to build-specific resources from your entrypoint, as though you were in your build subdirectory. Put `` in your source entrypoint, so that URLs resolve when serving your source directly during development. In your build pipeline, update each entrypoint's base tag to match its build subdirectory (e.g. ``).
129 |
130 | If you are using polymer-cli, set `{"autoBasePath": true}` in your `polymer.json` to perform this base tag update automatically.
131 |
132 | Note that `` tags only affect relative URLs, so to refer to resources outside of the build from your entrypoint, use absolute URLs as you normally would.
133 |
134 | ## HTTP/2 Server Push
135 |
136 | Server Push allows an HTTP/2 server to preemptively send additional resources alongside a response. This can improve latency by eliminating subsequent round-trips for dependencies such as scripts, CSS, and HTML imports.
137 |
138 |
139 | ### Push manifest
140 |
141 | prpl-server looks for a file called `push-manifest.json` in each build subdirectory, and uses it to map incoming request paths to the additional resources that should be pushed with it. The original push manifest file format is described [here](https://github.com/GoogleChrome/http2-push-manifest). Tools for generating a push manifest include [http2-push-manifest](https://github.com/GoogleChrome/http2-push-manifest) and [polymer-cli](https://github.com/Polymer/polymer-cli).
142 |
143 | Each key in the push manifest is a regular expression pattern that will be matched against the incoming request path. Patterns are forced to match exactly (e.g. `foo.html` is equivalent to `^foo.html$`). You can use wildcard patterns to push resources for client-side application routes (e.g. `/articles/.*`). In the case of the entrypoint, the resolved filename (e.g. `index.html`) is used as a key to the push manifest, in addition to the application route.
144 |
145 | Resources in the push manifest can be specified as absolute or relative paths. Absolute paths are interpreted relative to the server root directory. Relative paths are interpreted relative to the location of the push manifest file itself (i.e. the build subdirectory), so that they do not need to know which build subdirectory they are being served from. Push manifests generated by `polymer-cli` always use relative paths.
146 |
147 | ### Link preload headers
148 |
149 | prpl-server is designed to be used behind an HTTP/2 reverse proxy, and currently does not generate push responses itself. Instead it sets [preload link](https://w3c.github.io/preload/#server-push-http-2) headers, which are intercepted by cooperating reverse proxy servers and upgraded into push responses. Servers that implement this upgrading behavior include [Apache](https://httpd.apache.org/docs/trunk/mod/mod_http2.html#h2push), [nghttpx](https://github.com/nghttp2/nghttp2#nghttpx---proxy), and [Google App Engine](https://cloud.google.com/appengine/).
150 |
151 | If a build with a push manifest is served to a browser that does not support push according to the [browser-capabilities](https://github.com/Polymer/tools/tree/master/packages/browser-capabilities) support matrix, then a `nopush` attribute is added to the generated preload link headers.
152 |
153 | ### Testing push locally
154 |
155 | To confirm your push manifest is working during local development, you can look for `Link: ; rel=preload` response headers in your browser dev tools.
156 |
157 | To see genuine push locally, you will need to run a local HTTP/2 reverse proxy such as [nghttpx](https://github.com/nghttp2/nghttp2#nghttpx---proxy):
158 |
159 | - Install nghttpx ([Homebrew](http://brewformulas.org/Nghttp2), [Ubuntu](http://packages.ubuntu.com/zesty/nghttp2), [source](https://github.com/nghttp2/nghttp2#building-from-git)).
160 | - Generate a self-signed TLS certificate, e.g. `openssl req -newkey rsa:2048 -x509 -nodes -keyout server.key -out server.crt`
161 | - Start prpl-server (assuming default `127.0.0.1:8080`).
162 | - Start nghttpx: `nghttpx -f127.0.0.1,8443 -b127.0.0.1,8080 server.key server.crt --no-ocsp`
163 | - Visit `https://localhost:8443`. In Chrome, Push responses will show up in the Network tab as Initiator: Push / Other.
164 |
165 | Note that Chrome will not allow a service worker to be registered over HTTPS with a self-signed certificate. You can enable [chrome://flags/#allow-insecure-localhost](chrome://flags/#allow-insecure-localhost) to bypass this check. See [this page](https://www.chromium.org/blink/serviceworker/service-worker-faq) for more tips on developing service workers in Chrome.
166 |
167 | ## Service Workers
168 |
169 | ### Scope header
170 | prpl-server sets the [`Service-Worker-Allowed`](https://www.w3.org/TR/service-workers-1/#service-worker-allowed) header to `/` for any request path ending with `service-worker.js`. This allows a service worker served from a build subdirectory to be registered with a scope outside of that directory, e.g. `register('service-worker.js', {scope: '/'})`.
171 |
172 | ### 404 handling
173 |
174 | prpl-server automatically serves a tiny self-unregistering service worker for any request path ending with `service-worker.js` that would otherwise have had a `404 Not Found` response. To disable this behavior, set `unregisterMissingServiceWorkers: false` in your configuration file.
175 |
176 | This can be useful when the location of a service worker has changed, as it will prevent clients from getting stuck with an old service worker indefinitely.
177 |
178 | This problem arises because when a service worker updates, a `404` is treated as a failed update. It does not cause the service worker to be unregistered. See [w3c/ServiceWorker#204](https://github.com/w3c/ServiceWorker/issues/204) for more discussion of this problem.
179 |
180 | ## HTTPS
181 |
182 | Your apps should always be served over HTTPS. It protects your user's data, and is *required* for features like service workers and HTTP/2.
183 |
184 | If the `--https-redirect` flag is set, prpl-server will redirect all HTTP requests to HTTPS. It sends a `301 Moved Permanently` redirect to an `https://` address with the same hostname on the default HTTPS port (443).
185 |
186 | prpl-server trusts [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) and [`X-Forwarded-Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) headers from your reverse proxy to determine the client's true protocol and hostname. Most reverse proxies automatically set these headers, but if you encounter issues with redirect loops, missing or incorrect `X-Forwarded-*` headers may be the cause.
187 |
188 | You should always use `--https-redirect` in production, unless your reverse proxy already performs HTTPS redirection.
189 |
190 | ## Caching
191 |
192 | By default, prpl-server sets the [`Cache-Control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) header to `max-age=60` (1 minute), except for the entrypoint and service worker which gets `max-age=0`. [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) headers are also sent, so resources that have not changed on the server can be re-validated efficiently.
193 |
194 | To change this default for non-entrypoint resources, set the `cacheControl` property in your configuration file, or the `--cache-control` command-line flag, to the desired `Cache-Control` header value. You may want to set `--cache-control=no-cache` during development.
195 |
196 | For more advanced caching behavior, [use prpl-server as a library](#as-a-library) with Express and register a middleware that sets the `Cache-Control` header before registering the prpl-server middleware. If prpl-server sees that the `Cache-Control` header has already been set, it will not modify it. For example, to set year-long caching for images:
197 |
198 | ```js
199 | app.get('/images/*', (req, res, next) => {
200 | res.setHeader('Cache-Control', 'public, max-age=31536000');
201 | next();
202 | });
203 |
204 | app.get('/*', prpl.makeHandler('.', config))
205 | ```
206 |
207 | Choosing the right cache headers for your application can be complex. See [*Caching best practices & max-age gotchas*](https://jakearchibald.com/2016/caching-best-practices/) for one starting point.
208 |
209 | ## HTTP Errors
210 |
211 | By default, if a `404 Not Found` or other HTTP server error occurs, prpl-server will serve a minimal `text/plain` response. To serve custom errors, [use prpl-server as a library](#as-a-library) with Express, set `forwardErrors: true` in your configuration object, and register an [error-handling middleware](http://expressjs.com/en/guide/error-handling.html) after registering the prpl-server handler:
212 |
213 | ```js
214 | app.get('/*', prpl.makeHandler('.', {
215 | builds: [ ... ],
216 | forwardErrors: true
217 | }));
218 |
219 | app.use((err, req, res, next) => {
220 | if (err.status === 404) {
221 | res.status(404).sendFile('my-custom-404.html', {root: rootDir});
222 | } else {
223 | next();
224 | }
225 | });
226 | ```
227 |
228 | ## Rendering for Bots
229 |
230 | Many bots don't execute JavaScript when processing your application. This can cause your application to not render correctly when crawled by some search engines, social networks, and link rendering bots.
231 |
232 | One solution to this problem is [Rendertron](https://github.com/GoogleChrome/rendertron). Rendertron is a server which runs headless Chrome to render and serialize web pages for these bots, so all the content is contained in one network request. Use the `--bot-proxy` flag to instruct prpl-server to proxy requests from a known list of bots through a Rendertron server.
233 |
234 | Note that you can also use the [Rendertron middleware](https://github.com/GoogleChrome/rendertron/tree/master/middleware) directly if you have a custom Express server.
235 |
236 | ## Google App Engine Quickstart
237 |
238 | [Google App Engine](https://cloud.google.com/appengine/) is a managed server platform that [supports Node.js](https://cloud.google.com/nodejs/). You can deploy prpl-server to App Engine [standard environment](https://cloud.google.com/appengine/docs/standard/nodejs/) with a few steps:
239 |
240 | 1. Follow [these instructions](https://cloud.google.com/appengine/docs/standard/nodejs/quickstart) to set up a Google Cloud project and install the Google Cloud SDK. As instructed, run the `gcloud init` command to authenticate and choose your project ID.
241 |
242 | 2. `cd` to the directory you want to serve (e.g. your app's `build/` directory if you are using polymer-cli).
243 |
244 | 3. Run `npm init` and follow the prompts to create your `package.json`.
245 |
246 | 4. Run `npm install --save prpl-server` to add prpl-server as a dependency.
247 |
248 | 5. Edit your `package.json` to add a `start` script. This is the command App Engine runs when your app starts. Configure `prpl-server` to listen on all hosts, and to redirect HTTP connections to HTTPS. You should also specify the version of Node your app requires via the `engines` section.
249 |
250 | ```json
251 | {
252 | "scripts": {
253 | "start": "prpl-server --host 0.0.0.0 --https-redirect"
254 | },
255 | "engines": {
256 | "node": "8.x.x"
257 | }
258 | }
259 | ```
260 |
261 | 6. Create an `app.yaml` file. This tells App Engine that you want to use Node.js in the App Engine standard environment:
262 |
263 | ```yaml
264 | runtime: nodejs8
265 | ```
266 |
267 | 7. Run `gcloud app deploy` to deploy to your App Engine project. `gcloud` will tell you the URL your app is being served from. For next steps, check out the Node.js in the App Engine standard environment [documentation](https://cloud.google.com/appengine/docs/standard/nodejs/).
268 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: "6"
4 | - nodejs_version: ""
5 |
6 | install:
7 | - ps: Install-Product node $env:nodejs_version
8 | - npm install
9 |
10 | test_script:
11 | - node --version
12 | - npm --version
13 | - npm test
14 |
15 | build: off
16 |
--------------------------------------------------------------------------------
/bin/prpl-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * @license
5 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
6 | * This code may only be used under the BSD style license found at
7 | * http://polymer.github.io/LICENSE.txt
8 | * The complete set of authors may be found at
9 | * http://polymer.github.io/AUTHORS.txt
10 | * The complete set of contributors may be found at
11 | * http://polymer.github.io/CONTRIBUTORS.txt
12 | * Code distributed by Google as part of the polymer project is also
13 | * subject to an additional IP rights grant found at
14 | * http://polymer.github.io/PATENTS.txt
15 | */
16 |
17 | 'use strict';
18 |
19 | const cli = require('../lib/cli.js');
20 |
21 | try {
22 | cli.run(process.argv);
23 | } catch (e) {
24 | console.error(e);
25 | process.exit(64);
26 | }
27 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prpl-server",
3 | "version": "1.4.2",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/body-parser": {
8 | "version": "1.19.0",
9 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
10 | "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
11 | "requires": {
12 | "@types/connect": "*",
13 | "@types/node": "*"
14 | }
15 | },
16 | "@types/chai": {
17 | "version": "4.2.12",
18 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz",
19 | "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==",
20 | "dev": true
21 | },
22 | "@types/compression": {
23 | "version": "1.7.0",
24 | "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz",
25 | "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==",
26 | "dev": true,
27 | "requires": {
28 | "@types/express": "*"
29 | }
30 | },
31 | "@types/connect": {
32 | "version": "3.4.33",
33 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
34 | "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
35 | "requires": {
36 | "@types/node": "*"
37 | }
38 | },
39 | "@types/express": {
40 | "version": "4.17.7",
41 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
42 | "integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
43 | "requires": {
44 | "@types/body-parser": "*",
45 | "@types/express-serve-static-core": "*",
46 | "@types/qs": "*",
47 | "@types/serve-static": "*"
48 | }
49 | },
50 | "@types/express-serve-static-core": {
51 | "version": "4.17.9",
52 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz",
53 | "integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==",
54 | "requires": {
55 | "@types/node": "*",
56 | "@types/qs": "*",
57 | "@types/range-parser": "*"
58 | }
59 | },
60 | "@types/http-errors": {
61 | "version": "1.8.0",
62 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz",
63 | "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==",
64 | "dev": true
65 | },
66 | "@types/mime": {
67 | "version": "2.0.3",
68 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz",
69 | "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q=="
70 | },
71 | "@types/mocha": {
72 | "version": "8.0.2",
73 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.2.tgz",
74 | "integrity": "sha512-5cv8rmqT3KX9XtWDvSgGYfS4OwrKM2eei90GWLnTYz+AXRiBv5uYcKBjnkQ4katNvfYk3+o2bHGZUsDhdcoUyg==",
75 | "dev": true
76 | },
77 | "@types/node": {
78 | "version": "14.0.27",
79 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
80 | "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g=="
81 | },
82 | "@types/qs": {
83 | "version": "6.9.4",
84 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz",
85 | "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ=="
86 | },
87 | "@types/range-parser": {
88 | "version": "1.2.3",
89 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
90 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
91 | },
92 | "@types/send": {
93 | "version": "0.14.5",
94 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.14.5.tgz",
95 | "integrity": "sha512-0mwoiK3DXXBu0GIfo+jBv4Wo5s1AcsxdpdwNUtflKm99VEMvmBPJ+/NBNRZy2R5JEYfWL/u4nAHuTUTA3wFecQ==",
96 | "dev": true,
97 | "requires": {
98 | "@types/mime": "*",
99 | "@types/node": "*"
100 | }
101 | },
102 | "@types/serve-static": {
103 | "version": "1.13.5",
104 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
105 | "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==",
106 | "requires": {
107 | "@types/express-serve-static-core": "*",
108 | "@types/mime": "*"
109 | }
110 | },
111 | "@types/statuses": {
112 | "version": "2.0.0",
113 | "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.0.tgz",
114 | "integrity": "sha512-V4dX/J9hyQ8hq2hztP+8xB1girGot5ltjtmxaJusf6AGyxi2RvWJrpWj4/DxF0/GFNOkswUVnbkGXKaaY9juQQ==",
115 | "dev": true
116 | },
117 | "@types/ua-parser-js": {
118 | "version": "0.7.33",
119 | "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
120 | "integrity": "sha512-ngUKcHnytUodUCL7C6EZ+lVXUjTMQb+9p/e1JjV5tN9TVzS98lHozWEFRPY1QcCdwFeMsmVWfZ3DPPT/udCyIw=="
121 | },
122 | "@types/valid-url": {
123 | "version": "1.0.3",
124 | "resolved": "https://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.3.tgz",
125 | "integrity": "sha512-+33x29mg+ecU88ODdWpqaie2upIuRkhujVLA7TuJjM823cNMbeggfI6NhxewaRaRF8dy+g33e4uIg/m5Mb3xDQ==",
126 | "dev": true
127 | },
128 | "accepts": {
129 | "version": "1.3.7",
130 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
131 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
132 | "requires": {
133 | "mime-types": "~2.1.24",
134 | "negotiator": "0.6.2"
135 | }
136 | },
137 | "ajv": {
138 | "version": "6.12.3",
139 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
140 | "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
141 | "requires": {
142 | "fast-deep-equal": "^3.1.1",
143 | "fast-json-stable-stringify": "^2.0.0",
144 | "json-schema-traverse": "^0.4.1",
145 | "uri-js": "^4.2.2"
146 | }
147 | },
148 | "ansi-colors": {
149 | "version": "4.1.1",
150 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
151 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
152 | "dev": true
153 | },
154 | "ansi-escape-sequences": {
155 | "version": "5.1.2",
156 | "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-5.1.2.tgz",
157 | "integrity": "sha512-JcpoVp1W1bl1Qn4cVuiXEhD6+dyXKSOgCn2zlzE8inYgCJCBy1aPnUhlz6I4DFum8D4ovb9Qi/iAjUcGvG2lqw==",
158 | "requires": {
159 | "array-back": "^4.0.0"
160 | }
161 | },
162 | "ansi-regex": {
163 | "version": "3.0.0",
164 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
165 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
166 | "dev": true
167 | },
168 | "ansi-styles": {
169 | "version": "3.2.1",
170 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
171 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
172 | "requires": {
173 | "color-convert": "^1.9.0"
174 | }
175 | },
176 | "anymatch": {
177 | "version": "3.1.1",
178 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
179 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
180 | "dev": true,
181 | "requires": {
182 | "normalize-path": "^3.0.0",
183 | "picomatch": "^2.0.4"
184 | }
185 | },
186 | "argparse": {
187 | "version": "1.0.10",
188 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
189 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
190 | "dev": true,
191 | "requires": {
192 | "sprintf-js": "~1.0.2"
193 | }
194 | },
195 | "array-back": {
196 | "version": "4.0.1",
197 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz",
198 | "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg=="
199 | },
200 | "array-flatten": {
201 | "version": "1.1.1",
202 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
203 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
204 | },
205 | "array.prototype.map": {
206 | "version": "1.0.2",
207 | "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
208 | "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
209 | "dev": true,
210 | "requires": {
211 | "define-properties": "^1.1.3",
212 | "es-abstract": "^1.17.0-next.1",
213 | "es-array-method-boxes-properly": "^1.0.0",
214 | "is-string": "^1.0.4"
215 | }
216 | },
217 | "asn1": {
218 | "version": "0.2.4",
219 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
220 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
221 | "requires": {
222 | "safer-buffer": "~2.1.0"
223 | }
224 | },
225 | "assert-plus": {
226 | "version": "1.0.0",
227 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
228 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
229 | },
230 | "assertion-error": {
231 | "version": "1.1.0",
232 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
233 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
234 | "dev": true
235 | },
236 | "async": {
237 | "version": "1.5.2",
238 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
239 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
240 | "dev": true
241 | },
242 | "asynckit": {
243 | "version": "0.4.0",
244 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
245 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
246 | },
247 | "aws-sign2": {
248 | "version": "0.7.0",
249 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
250 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
251 | },
252 | "aws4": {
253 | "version": "1.10.0",
254 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
255 | "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
256 | },
257 | "balanced-match": {
258 | "version": "1.0.0",
259 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
260 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
261 | "dev": true
262 | },
263 | "bcrypt-pbkdf": {
264 | "version": "1.0.2",
265 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
266 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
267 | "requires": {
268 | "tweetnacl": "^0.14.3"
269 | }
270 | },
271 | "binary-extensions": {
272 | "version": "2.1.0",
273 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
274 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
275 | "dev": true
276 | },
277 | "body-parser": {
278 | "version": "1.19.0",
279 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
280 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
281 | "requires": {
282 | "bytes": "3.1.0",
283 | "content-type": "~1.0.4",
284 | "debug": "2.6.9",
285 | "depd": "~1.1.2",
286 | "http-errors": "1.7.2",
287 | "iconv-lite": "0.4.24",
288 | "on-finished": "~2.3.0",
289 | "qs": "6.7.0",
290 | "raw-body": "2.4.0",
291 | "type-is": "~1.6.17"
292 | },
293 | "dependencies": {
294 | "bytes": {
295 | "version": "3.1.0",
296 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
297 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
298 | },
299 | "http-errors": {
300 | "version": "1.7.2",
301 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
302 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
303 | "requires": {
304 | "depd": "~1.1.2",
305 | "inherits": "2.0.3",
306 | "setprototypeof": "1.1.1",
307 | "statuses": ">= 1.5.0 < 2",
308 | "toidentifier": "1.0.0"
309 | }
310 | },
311 | "statuses": {
312 | "version": "1.5.0",
313 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
314 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
315 | }
316 | }
317 | },
318 | "brace-expansion": {
319 | "version": "1.1.11",
320 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
321 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
322 | "dev": true,
323 | "requires": {
324 | "balanced-match": "^1.0.0",
325 | "concat-map": "0.0.1"
326 | }
327 | },
328 | "braces": {
329 | "version": "3.0.2",
330 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
331 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
332 | "dev": true,
333 | "requires": {
334 | "fill-range": "^7.0.1"
335 | }
336 | },
337 | "browser-capabilities": {
338 | "version": "1.1.4",
339 | "resolved": "https://registry.npmjs.org/browser-capabilities/-/browser-capabilities-1.1.4.tgz",
340 | "integrity": "sha512-BezMQhbQklxjRQpZZQ8tnbzEo6AldUwMh8/PeWt5/CTBSwByQRXZEAK2fbnEahQ4poeeaI0suAYRq25A1YGOmw==",
341 | "requires": {
342 | "@types/ua-parser-js": "^0.7.31",
343 | "ua-parser-js": "^0.7.15"
344 | }
345 | },
346 | "browser-stdout": {
347 | "version": "1.3.1",
348 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
349 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
350 | "dev": true
351 | },
352 | "buffer-from": {
353 | "version": "1.1.1",
354 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
355 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
356 | "dev": true
357 | },
358 | "bytes": {
359 | "version": "3.0.0",
360 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
361 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
362 | },
363 | "camelcase": {
364 | "version": "5.3.1",
365 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
366 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
367 | "dev": true
368 | },
369 | "caseless": {
370 | "version": "0.12.0",
371 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
372 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
373 | },
374 | "chai": {
375 | "version": "4.2.0",
376 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
377 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
378 | "dev": true,
379 | "requires": {
380 | "assertion-error": "^1.1.0",
381 | "check-error": "^1.0.2",
382 | "deep-eql": "^3.0.1",
383 | "get-func-name": "^2.0.0",
384 | "pathval": "^1.1.0",
385 | "type-detect": "^4.0.5"
386 | }
387 | },
388 | "chalk": {
389 | "version": "2.4.2",
390 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
391 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
392 | "requires": {
393 | "ansi-styles": "^3.2.1",
394 | "escape-string-regexp": "^1.0.5",
395 | "supports-color": "^5.3.0"
396 | }
397 | },
398 | "check-error": {
399 | "version": "1.0.2",
400 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
401 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
402 | "dev": true
403 | },
404 | "chokidar": {
405 | "version": "3.3.1",
406 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
407 | "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
408 | "dev": true,
409 | "requires": {
410 | "anymatch": "~3.1.1",
411 | "braces": "~3.0.2",
412 | "fsevents": "~2.1.2",
413 | "glob-parent": "~5.1.0",
414 | "is-binary-path": "~2.1.0",
415 | "is-glob": "~4.0.1",
416 | "normalize-path": "~3.0.0",
417 | "readdirp": "~3.3.0"
418 | }
419 | },
420 | "clang-format": {
421 | "version": "1.4.0",
422 | "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.4.0.tgz",
423 | "integrity": "sha512-NrdyUnHJOGvMa60vbWk7GJTvOdhibj3uK5C0FlwdNG4301OUvqEJTFce9I9x8qw2odBbIVrJ+9xbsFS3a4FbDA==",
424 | "dev": true,
425 | "requires": {
426 | "async": "^1.5.2",
427 | "glob": "^7.0.0",
428 | "resolve": "^1.1.6"
429 | }
430 | },
431 | "cliui": {
432 | "version": "5.0.0",
433 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
434 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
435 | "dev": true,
436 | "requires": {
437 | "string-width": "^3.1.0",
438 | "strip-ansi": "^5.2.0",
439 | "wrap-ansi": "^5.1.0"
440 | },
441 | "dependencies": {
442 | "ansi-regex": {
443 | "version": "4.1.0",
444 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
445 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
446 | "dev": true
447 | },
448 | "string-width": {
449 | "version": "3.1.0",
450 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
451 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
452 | "dev": true,
453 | "requires": {
454 | "emoji-regex": "^7.0.1",
455 | "is-fullwidth-code-point": "^2.0.0",
456 | "strip-ansi": "^5.1.0"
457 | }
458 | },
459 | "strip-ansi": {
460 | "version": "5.2.0",
461 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
462 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
463 | "dev": true,
464 | "requires": {
465 | "ansi-regex": "^4.1.0"
466 | }
467 | }
468 | }
469 | },
470 | "color-convert": {
471 | "version": "1.9.3",
472 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
473 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
474 | "requires": {
475 | "color-name": "1.1.3"
476 | }
477 | },
478 | "color-name": {
479 | "version": "1.1.3",
480 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
481 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
482 | },
483 | "combined-stream": {
484 | "version": "1.0.8",
485 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
486 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
487 | "requires": {
488 | "delayed-stream": "~1.0.0"
489 | }
490 | },
491 | "command-line-args": {
492 | "version": "5.1.1",
493 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz",
494 | "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==",
495 | "requires": {
496 | "array-back": "^3.0.1",
497 | "find-replace": "^3.0.0",
498 | "lodash.camelcase": "^4.3.0",
499 | "typical": "^4.0.0"
500 | },
501 | "dependencies": {
502 | "array-back": {
503 | "version": "3.1.0",
504 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
505 | "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="
506 | }
507 | }
508 | },
509 | "command-line-usage": {
510 | "version": "6.1.0",
511 | "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.0.tgz",
512 | "integrity": "sha512-Ew1clU4pkUeo6AFVDFxCbnN7GIZfXl48HIOQeFQnkO3oOqvpI7wdqtLRwv9iOCZ/7A+z4csVZeiDdEcj8g6Wiw==",
513 | "requires": {
514 | "array-back": "^4.0.0",
515 | "chalk": "^2.4.2",
516 | "table-layout": "^1.0.0",
517 | "typical": "^5.2.0"
518 | },
519 | "dependencies": {
520 | "typical": {
521 | "version": "5.2.0",
522 | "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
523 | "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="
524 | }
525 | }
526 | },
527 | "compressible": {
528 | "version": "2.0.18",
529 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
530 | "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
531 | "requires": {
532 | "mime-db": ">= 1.43.0 < 2"
533 | }
534 | },
535 | "compression": {
536 | "version": "1.7.4",
537 | "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
538 | "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
539 | "requires": {
540 | "accepts": "~1.3.5",
541 | "bytes": "3.0.0",
542 | "compressible": "~2.0.16",
543 | "debug": "2.6.9",
544 | "on-headers": "~1.0.2",
545 | "safe-buffer": "5.1.2",
546 | "vary": "~1.1.2"
547 | }
548 | },
549 | "concat-map": {
550 | "version": "0.0.1",
551 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
552 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
553 | "dev": true
554 | },
555 | "content-disposition": {
556 | "version": "0.5.3",
557 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
558 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
559 | "requires": {
560 | "safe-buffer": "5.1.2"
561 | }
562 | },
563 | "content-type": {
564 | "version": "1.0.4",
565 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
566 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
567 | },
568 | "cookie": {
569 | "version": "0.4.0",
570 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
571 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
572 | },
573 | "cookie-signature": {
574 | "version": "1.0.6",
575 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
576 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
577 | },
578 | "core-util-is": {
579 | "version": "1.0.2",
580 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
581 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
582 | },
583 | "dashdash": {
584 | "version": "1.14.1",
585 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
586 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
587 | "requires": {
588 | "assert-plus": "^1.0.0"
589 | }
590 | },
591 | "debug": {
592 | "version": "2.6.9",
593 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
594 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
595 | "requires": {
596 | "ms": "2.0.0"
597 | }
598 | },
599 | "decamelize": {
600 | "version": "1.2.0",
601 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
602 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
603 | "dev": true
604 | },
605 | "deep-eql": {
606 | "version": "3.0.1",
607 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
608 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
609 | "dev": true,
610 | "requires": {
611 | "type-detect": "^4.0.0"
612 | }
613 | },
614 | "deep-extend": {
615 | "version": "0.6.0",
616 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
617 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
618 | },
619 | "define-properties": {
620 | "version": "1.1.3",
621 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
622 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
623 | "dev": true,
624 | "requires": {
625 | "object-keys": "^1.0.12"
626 | }
627 | },
628 | "delayed-stream": {
629 | "version": "1.0.0",
630 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
631 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
632 | },
633 | "depd": {
634 | "version": "1.1.2",
635 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
636 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
637 | },
638 | "destroy": {
639 | "version": "1.0.4",
640 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
641 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
642 | },
643 | "diff": {
644 | "version": "4.0.2",
645 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
646 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
647 | "dev": true
648 | },
649 | "ecc-jsbn": {
650 | "version": "0.1.2",
651 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
652 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
653 | "requires": {
654 | "jsbn": "~0.1.0",
655 | "safer-buffer": "^2.1.0"
656 | }
657 | },
658 | "ee-first": {
659 | "version": "1.1.1",
660 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
661 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
662 | },
663 | "emoji-regex": {
664 | "version": "7.0.3",
665 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
666 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
667 | "dev": true
668 | },
669 | "encodeurl": {
670 | "version": "1.0.2",
671 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
672 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
673 | },
674 | "es-abstract": {
675 | "version": "1.17.6",
676 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
677 | "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
678 | "dev": true,
679 | "requires": {
680 | "es-to-primitive": "^1.2.1",
681 | "function-bind": "^1.1.1",
682 | "has": "^1.0.3",
683 | "has-symbols": "^1.0.1",
684 | "is-callable": "^1.2.0",
685 | "is-regex": "^1.1.0",
686 | "object-inspect": "^1.7.0",
687 | "object-keys": "^1.1.1",
688 | "object.assign": "^4.1.0",
689 | "string.prototype.trimend": "^1.0.1",
690 | "string.prototype.trimstart": "^1.0.1"
691 | }
692 | },
693 | "es-array-method-boxes-properly": {
694 | "version": "1.0.0",
695 | "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
696 | "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
697 | "dev": true
698 | },
699 | "es-get-iterator": {
700 | "version": "1.1.0",
701 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
702 | "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
703 | "dev": true,
704 | "requires": {
705 | "es-abstract": "^1.17.4",
706 | "has-symbols": "^1.0.1",
707 | "is-arguments": "^1.0.4",
708 | "is-map": "^2.0.1",
709 | "is-set": "^2.0.1",
710 | "is-string": "^1.0.5",
711 | "isarray": "^2.0.5"
712 | }
713 | },
714 | "es-to-primitive": {
715 | "version": "1.2.1",
716 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
717 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
718 | "dev": true,
719 | "requires": {
720 | "is-callable": "^1.1.4",
721 | "is-date-object": "^1.0.1",
722 | "is-symbol": "^1.0.2"
723 | }
724 | },
725 | "escape-html": {
726 | "version": "1.0.3",
727 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
728 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
729 | },
730 | "escape-string-regexp": {
731 | "version": "1.0.5",
732 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
733 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
734 | },
735 | "esprima": {
736 | "version": "4.0.1",
737 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
738 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
739 | "dev": true
740 | },
741 | "etag": {
742 | "version": "1.8.1",
743 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
744 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
745 | },
746 | "express": {
747 | "version": "4.17.1",
748 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
749 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
750 | "requires": {
751 | "accepts": "~1.3.7",
752 | "array-flatten": "1.1.1",
753 | "body-parser": "1.19.0",
754 | "content-disposition": "0.5.3",
755 | "content-type": "~1.0.4",
756 | "cookie": "0.4.0",
757 | "cookie-signature": "1.0.6",
758 | "debug": "2.6.9",
759 | "depd": "~1.1.2",
760 | "encodeurl": "~1.0.2",
761 | "escape-html": "~1.0.3",
762 | "etag": "~1.8.1",
763 | "finalhandler": "~1.1.2",
764 | "fresh": "0.5.2",
765 | "merge-descriptors": "1.0.1",
766 | "methods": "~1.1.2",
767 | "on-finished": "~2.3.0",
768 | "parseurl": "~1.3.3",
769 | "path-to-regexp": "0.1.7",
770 | "proxy-addr": "~2.0.5",
771 | "qs": "6.7.0",
772 | "range-parser": "~1.2.1",
773 | "safe-buffer": "5.1.2",
774 | "send": "0.17.1",
775 | "serve-static": "1.14.1",
776 | "setprototypeof": "1.1.1",
777 | "statuses": "~1.5.0",
778 | "type-is": "~1.6.18",
779 | "utils-merge": "1.0.1",
780 | "vary": "~1.1.2"
781 | },
782 | "dependencies": {
783 | "statuses": {
784 | "version": "1.5.0",
785 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
786 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
787 | }
788 | }
789 | },
790 | "extend": {
791 | "version": "3.0.2",
792 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
793 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
794 | },
795 | "extsprintf": {
796 | "version": "1.3.0",
797 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
798 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
799 | },
800 | "fast-deep-equal": {
801 | "version": "3.1.3",
802 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
803 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
804 | },
805 | "fast-json-stable-stringify": {
806 | "version": "2.1.0",
807 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
808 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
809 | },
810 | "fill-range": {
811 | "version": "7.0.1",
812 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
813 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
814 | "dev": true,
815 | "requires": {
816 | "to-regex-range": "^5.0.1"
817 | }
818 | },
819 | "finalhandler": {
820 | "version": "1.1.2",
821 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
822 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
823 | "requires": {
824 | "debug": "2.6.9",
825 | "encodeurl": "~1.0.2",
826 | "escape-html": "~1.0.3",
827 | "on-finished": "~2.3.0",
828 | "parseurl": "~1.3.3",
829 | "statuses": "~1.5.0",
830 | "unpipe": "~1.0.0"
831 | },
832 | "dependencies": {
833 | "statuses": {
834 | "version": "1.5.0",
835 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
836 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
837 | }
838 | }
839 | },
840 | "find-replace": {
841 | "version": "3.0.0",
842 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
843 | "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
844 | "requires": {
845 | "array-back": "^3.0.1"
846 | },
847 | "dependencies": {
848 | "array-back": {
849 | "version": "3.1.0",
850 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
851 | "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="
852 | }
853 | }
854 | },
855 | "find-up": {
856 | "version": "4.1.0",
857 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
858 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
859 | "dev": true,
860 | "requires": {
861 | "locate-path": "^5.0.0",
862 | "path-exists": "^4.0.0"
863 | }
864 | },
865 | "flat": {
866 | "version": "4.1.0",
867 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
868 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
869 | "dev": true,
870 | "requires": {
871 | "is-buffer": "~2.0.3"
872 | }
873 | },
874 | "forever-agent": {
875 | "version": "0.6.1",
876 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
877 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
878 | },
879 | "form-data": {
880 | "version": "2.3.3",
881 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
882 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
883 | "requires": {
884 | "asynckit": "^0.4.0",
885 | "combined-stream": "^1.0.6",
886 | "mime-types": "^2.1.12"
887 | }
888 | },
889 | "forwarded": {
890 | "version": "0.1.2",
891 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
892 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
893 | },
894 | "fresh": {
895 | "version": "0.5.2",
896 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
897 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
898 | },
899 | "fs.realpath": {
900 | "version": "1.0.0",
901 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
902 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
903 | "dev": true
904 | },
905 | "fsevents": {
906 | "version": "2.1.3",
907 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
908 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
909 | "dev": true,
910 | "optional": true
911 | },
912 | "function-bind": {
913 | "version": "1.1.1",
914 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
915 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
916 | "dev": true
917 | },
918 | "get-caller-file": {
919 | "version": "2.0.5",
920 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
921 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
922 | "dev": true
923 | },
924 | "get-func-name": {
925 | "version": "2.0.0",
926 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
927 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
928 | "dev": true
929 | },
930 | "getpass": {
931 | "version": "0.1.7",
932 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
933 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
934 | "requires": {
935 | "assert-plus": "^1.0.0"
936 | }
937 | },
938 | "glob": {
939 | "version": "7.1.6",
940 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
941 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
942 | "dev": true,
943 | "requires": {
944 | "fs.realpath": "^1.0.0",
945 | "inflight": "^1.0.4",
946 | "inherits": "2",
947 | "minimatch": "^3.0.4",
948 | "once": "^1.3.0",
949 | "path-is-absolute": "^1.0.0"
950 | }
951 | },
952 | "glob-parent": {
953 | "version": "5.1.2",
954 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
955 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
956 | "dev": true,
957 | "requires": {
958 | "is-glob": "^4.0.1"
959 | }
960 | },
961 | "growl": {
962 | "version": "1.10.5",
963 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
964 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
965 | "dev": true
966 | },
967 | "har-schema": {
968 | "version": "2.0.0",
969 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
970 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
971 | },
972 | "har-validator": {
973 | "version": "5.1.5",
974 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
975 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
976 | "requires": {
977 | "ajv": "^6.12.3",
978 | "har-schema": "^2.0.0"
979 | }
980 | },
981 | "has": {
982 | "version": "1.0.3",
983 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
984 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
985 | "dev": true,
986 | "requires": {
987 | "function-bind": "^1.1.1"
988 | }
989 | },
990 | "has-flag": {
991 | "version": "3.0.0",
992 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
993 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
994 | },
995 | "has-symbols": {
996 | "version": "1.0.1",
997 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
998 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
999 | "dev": true
1000 | },
1001 | "he": {
1002 | "version": "1.2.0",
1003 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
1004 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1005 | "dev": true
1006 | },
1007 | "http-errors": {
1008 | "version": "1.8.0",
1009 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
1010 | "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
1011 | "requires": {
1012 | "depd": "~1.1.2",
1013 | "inherits": "2.0.4",
1014 | "setprototypeof": "1.2.0",
1015 | "statuses": ">= 1.5.0 < 2",
1016 | "toidentifier": "1.0.0"
1017 | },
1018 | "dependencies": {
1019 | "inherits": {
1020 | "version": "2.0.4",
1021 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1022 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
1023 | },
1024 | "setprototypeof": {
1025 | "version": "1.2.0",
1026 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1027 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1028 | },
1029 | "statuses": {
1030 | "version": "1.5.0",
1031 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1032 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1033 | }
1034 | }
1035 | },
1036 | "http-signature": {
1037 | "version": "1.2.0",
1038 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
1039 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
1040 | "requires": {
1041 | "assert-plus": "^1.0.0",
1042 | "jsprim": "^1.2.2",
1043 | "sshpk": "^1.7.0"
1044 | }
1045 | },
1046 | "iconv-lite": {
1047 | "version": "0.4.24",
1048 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1049 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1050 | "requires": {
1051 | "safer-buffer": ">= 2.1.2 < 3"
1052 | }
1053 | },
1054 | "inflight": {
1055 | "version": "1.0.6",
1056 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1057 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
1058 | "dev": true,
1059 | "requires": {
1060 | "once": "^1.3.0",
1061 | "wrappy": "1"
1062 | }
1063 | },
1064 | "inherits": {
1065 | "version": "2.0.3",
1066 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
1067 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
1068 | },
1069 | "ipaddr.js": {
1070 | "version": "1.9.1",
1071 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1072 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
1073 | },
1074 | "is-arguments": {
1075 | "version": "1.0.4",
1076 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
1077 | "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
1078 | "dev": true
1079 | },
1080 | "is-binary-path": {
1081 | "version": "2.1.0",
1082 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1083 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1084 | "dev": true,
1085 | "requires": {
1086 | "binary-extensions": "^2.0.0"
1087 | }
1088 | },
1089 | "is-buffer": {
1090 | "version": "2.0.4",
1091 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
1092 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
1093 | "dev": true
1094 | },
1095 | "is-callable": {
1096 | "version": "1.2.0",
1097 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
1098 | "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
1099 | "dev": true
1100 | },
1101 | "is-date-object": {
1102 | "version": "1.0.2",
1103 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
1104 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
1105 | "dev": true
1106 | },
1107 | "is-extglob": {
1108 | "version": "2.1.1",
1109 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1110 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
1111 | "dev": true
1112 | },
1113 | "is-fullwidth-code-point": {
1114 | "version": "2.0.0",
1115 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
1116 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
1117 | "dev": true
1118 | },
1119 | "is-glob": {
1120 | "version": "4.0.1",
1121 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
1122 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
1123 | "dev": true,
1124 | "requires": {
1125 | "is-extglob": "^2.1.1"
1126 | }
1127 | },
1128 | "is-map": {
1129 | "version": "2.0.1",
1130 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
1131 | "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
1132 | "dev": true
1133 | },
1134 | "is-number": {
1135 | "version": "7.0.0",
1136 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1137 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1138 | "dev": true
1139 | },
1140 | "is-plain-obj": {
1141 | "version": "1.1.0",
1142 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
1143 | "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
1144 | "dev": true
1145 | },
1146 | "is-regex": {
1147 | "version": "1.1.1",
1148 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
1149 | "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
1150 | "dev": true,
1151 | "requires": {
1152 | "has-symbols": "^1.0.1"
1153 | }
1154 | },
1155 | "is-set": {
1156 | "version": "2.0.1",
1157 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
1158 | "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
1159 | "dev": true
1160 | },
1161 | "is-string": {
1162 | "version": "1.0.5",
1163 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
1164 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
1165 | "dev": true
1166 | },
1167 | "is-symbol": {
1168 | "version": "1.0.3",
1169 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
1170 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
1171 | "dev": true,
1172 | "requires": {
1173 | "has-symbols": "^1.0.1"
1174 | }
1175 | },
1176 | "is-typedarray": {
1177 | "version": "1.0.0",
1178 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
1179 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
1180 | },
1181 | "isarray": {
1182 | "version": "2.0.5",
1183 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
1184 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
1185 | "dev": true
1186 | },
1187 | "isexe": {
1188 | "version": "2.0.0",
1189 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1190 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
1191 | "dev": true
1192 | },
1193 | "isstream": {
1194 | "version": "0.1.2",
1195 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
1196 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
1197 | },
1198 | "iterate-iterator": {
1199 | "version": "1.0.1",
1200 | "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
1201 | "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
1202 | "dev": true
1203 | },
1204 | "iterate-value": {
1205 | "version": "1.0.2",
1206 | "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
1207 | "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
1208 | "dev": true,
1209 | "requires": {
1210 | "es-get-iterator": "^1.0.2",
1211 | "iterate-iterator": "^1.0.1"
1212 | }
1213 | },
1214 | "js-yaml": {
1215 | "version": "3.13.1",
1216 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
1217 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
1218 | "dev": true,
1219 | "requires": {
1220 | "argparse": "^1.0.7",
1221 | "esprima": "^4.0.0"
1222 | }
1223 | },
1224 | "jsbn": {
1225 | "version": "0.1.1",
1226 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
1227 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
1228 | },
1229 | "json-schema": {
1230 | "version": "0.2.3",
1231 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
1232 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
1233 | },
1234 | "json-schema-traverse": {
1235 | "version": "0.4.1",
1236 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
1237 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
1238 | },
1239 | "json-stringify-safe": {
1240 | "version": "5.0.1",
1241 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
1242 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
1243 | },
1244 | "jsprim": {
1245 | "version": "1.4.1",
1246 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
1247 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
1248 | "requires": {
1249 | "assert-plus": "1.0.0",
1250 | "extsprintf": "1.3.0",
1251 | "json-schema": "0.2.3",
1252 | "verror": "1.10.0"
1253 | }
1254 | },
1255 | "locate-path": {
1256 | "version": "5.0.0",
1257 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
1258 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
1259 | "dev": true,
1260 | "requires": {
1261 | "p-locate": "^4.1.0"
1262 | }
1263 | },
1264 | "lodash.camelcase": {
1265 | "version": "4.3.0",
1266 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
1267 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
1268 | },
1269 | "log-symbols": {
1270 | "version": "3.0.0",
1271 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
1272 | "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
1273 | "dev": true,
1274 | "requires": {
1275 | "chalk": "^2.4.2"
1276 | }
1277 | },
1278 | "media-typer": {
1279 | "version": "0.3.0",
1280 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1281 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
1282 | },
1283 | "merge-descriptors": {
1284 | "version": "1.0.1",
1285 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1286 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
1287 | },
1288 | "methods": {
1289 | "version": "1.1.2",
1290 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1291 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
1292 | },
1293 | "mime": {
1294 | "version": "1.6.0",
1295 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1296 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1297 | },
1298 | "mime-db": {
1299 | "version": "1.44.0",
1300 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
1301 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
1302 | },
1303 | "mime-types": {
1304 | "version": "2.1.27",
1305 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
1306 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
1307 | "requires": {
1308 | "mime-db": "1.44.0"
1309 | }
1310 | },
1311 | "minimatch": {
1312 | "version": "3.0.4",
1313 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1314 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1315 | "dev": true,
1316 | "requires": {
1317 | "brace-expansion": "^1.1.7"
1318 | }
1319 | },
1320 | "mocha": {
1321 | "version": "8.1.1",
1322 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz",
1323 | "integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==",
1324 | "dev": true,
1325 | "requires": {
1326 | "ansi-colors": "4.1.1",
1327 | "browser-stdout": "1.3.1",
1328 | "chokidar": "3.3.1",
1329 | "debug": "3.2.6",
1330 | "diff": "4.0.2",
1331 | "escape-string-regexp": "1.0.5",
1332 | "find-up": "4.1.0",
1333 | "glob": "7.1.6",
1334 | "growl": "1.10.5",
1335 | "he": "1.2.0",
1336 | "js-yaml": "3.13.1",
1337 | "log-symbols": "3.0.0",
1338 | "minimatch": "3.0.4",
1339 | "ms": "2.1.2",
1340 | "object.assign": "4.1.0",
1341 | "promise.allsettled": "1.0.2",
1342 | "serialize-javascript": "4.0.0",
1343 | "strip-json-comments": "3.0.1",
1344 | "supports-color": "7.1.0",
1345 | "which": "2.0.2",
1346 | "wide-align": "1.1.3",
1347 | "workerpool": "6.0.0",
1348 | "yargs": "13.3.2",
1349 | "yargs-parser": "13.1.2",
1350 | "yargs-unparser": "1.6.1"
1351 | },
1352 | "dependencies": {
1353 | "debug": {
1354 | "version": "3.2.6",
1355 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
1356 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
1357 | "dev": true,
1358 | "requires": {
1359 | "ms": "^2.1.1"
1360 | }
1361 | },
1362 | "has-flag": {
1363 | "version": "4.0.0",
1364 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
1365 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
1366 | "dev": true
1367 | },
1368 | "ms": {
1369 | "version": "2.1.2",
1370 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1371 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1372 | "dev": true
1373 | },
1374 | "supports-color": {
1375 | "version": "7.1.0",
1376 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
1377 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
1378 | "dev": true,
1379 | "requires": {
1380 | "has-flag": "^4.0.0"
1381 | }
1382 | }
1383 | }
1384 | },
1385 | "ms": {
1386 | "version": "2.0.0",
1387 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1388 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
1389 | },
1390 | "negotiator": {
1391 | "version": "0.6.2",
1392 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
1393 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
1394 | },
1395 | "normalize-path": {
1396 | "version": "3.0.0",
1397 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1398 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1399 | "dev": true
1400 | },
1401 | "oauth-sign": {
1402 | "version": "0.9.0",
1403 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
1404 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
1405 | },
1406 | "object-inspect": {
1407 | "version": "1.8.0",
1408 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
1409 | "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
1410 | "dev": true
1411 | },
1412 | "object-keys": {
1413 | "version": "1.1.1",
1414 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
1415 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
1416 | "dev": true
1417 | },
1418 | "object.assign": {
1419 | "version": "4.1.0",
1420 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
1421 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
1422 | "dev": true,
1423 | "requires": {
1424 | "define-properties": "^1.1.2",
1425 | "function-bind": "^1.1.1",
1426 | "has-symbols": "^1.0.0",
1427 | "object-keys": "^1.0.11"
1428 | }
1429 | },
1430 | "on-finished": {
1431 | "version": "2.3.0",
1432 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1433 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
1434 | "requires": {
1435 | "ee-first": "1.1.1"
1436 | }
1437 | },
1438 | "on-headers": {
1439 | "version": "1.0.2",
1440 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
1441 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
1442 | },
1443 | "once": {
1444 | "version": "1.4.0",
1445 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1446 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1447 | "dev": true,
1448 | "requires": {
1449 | "wrappy": "1"
1450 | }
1451 | },
1452 | "p-limit": {
1453 | "version": "2.3.0",
1454 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
1455 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
1456 | "dev": true,
1457 | "requires": {
1458 | "p-try": "^2.0.0"
1459 | }
1460 | },
1461 | "p-locate": {
1462 | "version": "4.1.0",
1463 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
1464 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
1465 | "dev": true,
1466 | "requires": {
1467 | "p-limit": "^2.2.0"
1468 | }
1469 | },
1470 | "p-try": {
1471 | "version": "2.2.0",
1472 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
1473 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
1474 | "dev": true
1475 | },
1476 | "parseurl": {
1477 | "version": "1.3.3",
1478 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1479 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1480 | },
1481 | "path-exists": {
1482 | "version": "4.0.0",
1483 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
1484 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
1485 | "dev": true
1486 | },
1487 | "path-is-absolute": {
1488 | "version": "1.0.1",
1489 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1490 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
1491 | "dev": true
1492 | },
1493 | "path-parse": {
1494 | "version": "1.0.7",
1495 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1496 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1497 | "dev": true
1498 | },
1499 | "path-to-regexp": {
1500 | "version": "0.1.7",
1501 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1502 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
1503 | },
1504 | "pathval": {
1505 | "version": "1.1.0",
1506 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
1507 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
1508 | "dev": true
1509 | },
1510 | "performance-now": {
1511 | "version": "2.1.0",
1512 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
1513 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
1514 | },
1515 | "picomatch": {
1516 | "version": "2.2.2",
1517 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
1518 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
1519 | "dev": true
1520 | },
1521 | "promise.allsettled": {
1522 | "version": "1.0.2",
1523 | "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
1524 | "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
1525 | "dev": true,
1526 | "requires": {
1527 | "array.prototype.map": "^1.0.1",
1528 | "define-properties": "^1.1.3",
1529 | "es-abstract": "^1.17.0-next.1",
1530 | "function-bind": "^1.1.1",
1531 | "iterate-value": "^1.0.0"
1532 | }
1533 | },
1534 | "proxy-addr": {
1535 | "version": "2.0.6",
1536 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
1537 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
1538 | "requires": {
1539 | "forwarded": "~0.1.2",
1540 | "ipaddr.js": "1.9.1"
1541 | }
1542 | },
1543 | "psl": {
1544 | "version": "1.8.0",
1545 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
1546 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
1547 | },
1548 | "punycode": {
1549 | "version": "2.1.1",
1550 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
1551 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
1552 | },
1553 | "qs": {
1554 | "version": "6.7.0",
1555 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
1556 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
1557 | },
1558 | "randombytes": {
1559 | "version": "2.1.0",
1560 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
1561 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
1562 | "dev": true,
1563 | "requires": {
1564 | "safe-buffer": "^5.1.0"
1565 | }
1566 | },
1567 | "range-parser": {
1568 | "version": "1.2.1",
1569 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1570 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1571 | },
1572 | "raw-body": {
1573 | "version": "2.4.0",
1574 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
1575 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
1576 | "requires": {
1577 | "bytes": "3.1.0",
1578 | "http-errors": "1.7.2",
1579 | "iconv-lite": "0.4.24",
1580 | "unpipe": "1.0.0"
1581 | },
1582 | "dependencies": {
1583 | "bytes": {
1584 | "version": "3.1.0",
1585 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
1586 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
1587 | },
1588 | "http-errors": {
1589 | "version": "1.7.2",
1590 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
1591 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
1592 | "requires": {
1593 | "depd": "~1.1.2",
1594 | "inherits": "2.0.3",
1595 | "setprototypeof": "1.1.1",
1596 | "statuses": ">= 1.5.0 < 2",
1597 | "toidentifier": "1.0.0"
1598 | }
1599 | },
1600 | "statuses": {
1601 | "version": "1.5.0",
1602 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1603 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1604 | }
1605 | }
1606 | },
1607 | "readdirp": {
1608 | "version": "3.3.0",
1609 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
1610 | "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
1611 | "dev": true,
1612 | "requires": {
1613 | "picomatch": "^2.0.7"
1614 | }
1615 | },
1616 | "reduce-flatten": {
1617 | "version": "2.0.0",
1618 | "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
1619 | "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w=="
1620 | },
1621 | "rendertron-middleware": {
1622 | "version": "0.1.5",
1623 | "resolved": "https://registry.npmjs.org/rendertron-middleware/-/rendertron-middleware-0.1.5.tgz",
1624 | "integrity": "sha512-YTwWJ+vP07w0jdpHsKMhM8RQ6q7Xa2AKmZMMIUzFFbqFnAtso6o6nxWEk/S0axLFPEFfrGMxTqewOtM4Cy8KNQ==",
1625 | "requires": {
1626 | "@types/express": "^4.16.0",
1627 | "request": "^2.87.0"
1628 | }
1629 | },
1630 | "request": {
1631 | "version": "2.88.2",
1632 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
1633 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
1634 | "requires": {
1635 | "aws-sign2": "~0.7.0",
1636 | "aws4": "^1.8.0",
1637 | "caseless": "~0.12.0",
1638 | "combined-stream": "~1.0.6",
1639 | "extend": "~3.0.2",
1640 | "forever-agent": "~0.6.1",
1641 | "form-data": "~2.3.2",
1642 | "har-validator": "~5.1.3",
1643 | "http-signature": "~1.2.0",
1644 | "is-typedarray": "~1.0.0",
1645 | "isstream": "~0.1.2",
1646 | "json-stringify-safe": "~5.0.1",
1647 | "mime-types": "~2.1.19",
1648 | "oauth-sign": "~0.9.0",
1649 | "performance-now": "^2.1.0",
1650 | "qs": "~6.5.2",
1651 | "safe-buffer": "^5.1.2",
1652 | "tough-cookie": "~2.5.0",
1653 | "tunnel-agent": "^0.6.0",
1654 | "uuid": "^3.3.2"
1655 | },
1656 | "dependencies": {
1657 | "qs": {
1658 | "version": "6.5.2",
1659 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
1660 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
1661 | }
1662 | }
1663 | },
1664 | "require-directory": {
1665 | "version": "2.1.1",
1666 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1667 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
1668 | "dev": true
1669 | },
1670 | "require-main-filename": {
1671 | "version": "2.0.0",
1672 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
1673 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
1674 | "dev": true
1675 | },
1676 | "resolve": {
1677 | "version": "1.17.0",
1678 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
1679 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
1680 | "dev": true,
1681 | "requires": {
1682 | "path-parse": "^1.0.6"
1683 | }
1684 | },
1685 | "safe-buffer": {
1686 | "version": "5.1.2",
1687 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1688 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
1689 | },
1690 | "safer-buffer": {
1691 | "version": "2.1.2",
1692 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1693 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1694 | },
1695 | "semver": {
1696 | "version": "5.7.1",
1697 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1698 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1699 | "dev": true
1700 | },
1701 | "send": {
1702 | "version": "0.17.1",
1703 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
1704 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
1705 | "requires": {
1706 | "debug": "2.6.9",
1707 | "depd": "~1.1.2",
1708 | "destroy": "~1.0.4",
1709 | "encodeurl": "~1.0.2",
1710 | "escape-html": "~1.0.3",
1711 | "etag": "~1.8.1",
1712 | "fresh": "0.5.2",
1713 | "http-errors": "~1.7.2",
1714 | "mime": "1.6.0",
1715 | "ms": "2.1.1",
1716 | "on-finished": "~2.3.0",
1717 | "range-parser": "~1.2.1",
1718 | "statuses": "~1.5.0"
1719 | },
1720 | "dependencies": {
1721 | "http-errors": {
1722 | "version": "1.7.3",
1723 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
1724 | "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
1725 | "requires": {
1726 | "depd": "~1.1.2",
1727 | "inherits": "2.0.4",
1728 | "setprototypeof": "1.1.1",
1729 | "statuses": ">= 1.5.0 < 2",
1730 | "toidentifier": "1.0.0"
1731 | }
1732 | },
1733 | "inherits": {
1734 | "version": "2.0.4",
1735 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1736 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
1737 | },
1738 | "ms": {
1739 | "version": "2.1.1",
1740 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
1741 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
1742 | },
1743 | "statuses": {
1744 | "version": "1.5.0",
1745 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
1746 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
1747 | }
1748 | }
1749 | },
1750 | "serialize-javascript": {
1751 | "version": "4.0.0",
1752 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
1753 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
1754 | "dev": true,
1755 | "requires": {
1756 | "randombytes": "^2.1.0"
1757 | }
1758 | },
1759 | "serve-static": {
1760 | "version": "1.14.1",
1761 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
1762 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
1763 | "requires": {
1764 | "encodeurl": "~1.0.2",
1765 | "escape-html": "~1.0.3",
1766 | "parseurl": "~1.3.3",
1767 | "send": "0.17.1"
1768 | }
1769 | },
1770 | "set-blocking": {
1771 | "version": "2.0.0",
1772 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1773 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
1774 | "dev": true
1775 | },
1776 | "setprototypeof": {
1777 | "version": "1.1.1",
1778 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
1779 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
1780 | },
1781 | "source-map": {
1782 | "version": "0.6.1",
1783 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
1784 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
1785 | "dev": true
1786 | },
1787 | "source-map-support": {
1788 | "version": "0.5.19",
1789 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
1790 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
1791 | "dev": true,
1792 | "requires": {
1793 | "buffer-from": "^1.0.0",
1794 | "source-map": "^0.6.0"
1795 | }
1796 | },
1797 | "sprintf-js": {
1798 | "version": "1.0.3",
1799 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
1800 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
1801 | "dev": true
1802 | },
1803 | "sshpk": {
1804 | "version": "1.16.1",
1805 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
1806 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
1807 | "requires": {
1808 | "asn1": "~0.2.3",
1809 | "assert-plus": "^1.0.0",
1810 | "bcrypt-pbkdf": "^1.0.0",
1811 | "dashdash": "^1.12.0",
1812 | "ecc-jsbn": "~0.1.1",
1813 | "getpass": "^0.1.1",
1814 | "jsbn": "~0.1.0",
1815 | "safer-buffer": "^2.0.2",
1816 | "tweetnacl": "~0.14.0"
1817 | }
1818 | },
1819 | "statuses": {
1820 | "version": "2.0.0",
1821 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.0.tgz",
1822 | "integrity": "sha512-w9jNUUQdpuVoYqXxnyOakhckBbOxRaoYqJscyIBYCS5ixyCnO7nQn7zBZvP9zf5QOPZcz2DLUpE3KsNPbJBOFA=="
1823 | },
1824 | "string-width": {
1825 | "version": "2.1.1",
1826 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
1827 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
1828 | "dev": true,
1829 | "requires": {
1830 | "is-fullwidth-code-point": "^2.0.0",
1831 | "strip-ansi": "^4.0.0"
1832 | }
1833 | },
1834 | "string.prototype.trimend": {
1835 | "version": "1.0.1",
1836 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
1837 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
1838 | "dev": true,
1839 | "requires": {
1840 | "define-properties": "^1.1.3",
1841 | "es-abstract": "^1.17.5"
1842 | }
1843 | },
1844 | "string.prototype.trimstart": {
1845 | "version": "1.0.1",
1846 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
1847 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
1848 | "dev": true,
1849 | "requires": {
1850 | "define-properties": "^1.1.3",
1851 | "es-abstract": "^1.17.5"
1852 | }
1853 | },
1854 | "strip-ansi": {
1855 | "version": "4.0.0",
1856 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
1857 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
1858 | "dev": true,
1859 | "requires": {
1860 | "ansi-regex": "^3.0.0"
1861 | }
1862 | },
1863 | "strip-json-comments": {
1864 | "version": "3.0.1",
1865 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
1866 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
1867 | "dev": true
1868 | },
1869 | "supports-color": {
1870 | "version": "5.5.0",
1871 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1872 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1873 | "requires": {
1874 | "has-flag": "^3.0.0"
1875 | }
1876 | },
1877 | "table-layout": {
1878 | "version": "1.0.1",
1879 | "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz",
1880 | "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==",
1881 | "requires": {
1882 | "array-back": "^4.0.1",
1883 | "deep-extend": "~0.6.0",
1884 | "typical": "^5.2.0",
1885 | "wordwrapjs": "^4.0.0"
1886 | },
1887 | "dependencies": {
1888 | "typical": {
1889 | "version": "5.2.0",
1890 | "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
1891 | "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="
1892 | }
1893 | }
1894 | },
1895 | "to-regex-range": {
1896 | "version": "5.0.1",
1897 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1898 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1899 | "dev": true,
1900 | "requires": {
1901 | "is-number": "^7.0.0"
1902 | }
1903 | },
1904 | "toidentifier": {
1905 | "version": "1.0.0",
1906 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
1907 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
1908 | },
1909 | "tough-cookie": {
1910 | "version": "2.5.0",
1911 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
1912 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
1913 | "requires": {
1914 | "psl": "^1.1.28",
1915 | "punycode": "^2.1.1"
1916 | }
1917 | },
1918 | "tsc-then": {
1919 | "version": "1.1.0",
1920 | "resolved": "https://registry.npmjs.org/tsc-then/-/tsc-then-1.1.0.tgz",
1921 | "integrity": "sha512-830G8SK8tewOxfKVBC5YWqZ2C7wS1D4dh9267ebLxR7Gvh3UuI6aKU5Hxo+L3Kkcy6CuxZp4PMGl066DZ3Hlmw==",
1922 | "dev": true,
1923 | "requires": {
1924 | "@types/node": "^8.0.53",
1925 | "semver": "^5.4.1"
1926 | },
1927 | "dependencies": {
1928 | "@types/node": {
1929 | "version": "8.10.62",
1930 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.62.tgz",
1931 | "integrity": "sha512-76fupxOYVxk36kb7O/6KtrAPZ9jnSK3+qisAX4tQMEuGNdlvl7ycwatlHqjoE6jHfVtXFM3pCrCixZOidc5cuw==",
1932 | "dev": true
1933 | }
1934 | }
1935 | },
1936 | "tunnel-agent": {
1937 | "version": "0.6.0",
1938 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1939 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
1940 | "requires": {
1941 | "safe-buffer": "^5.0.1"
1942 | }
1943 | },
1944 | "tweetnacl": {
1945 | "version": "0.14.5",
1946 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
1947 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
1948 | },
1949 | "type-detect": {
1950 | "version": "4.0.8",
1951 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
1952 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
1953 | "dev": true
1954 | },
1955 | "type-is": {
1956 | "version": "1.6.18",
1957 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1958 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1959 | "requires": {
1960 | "media-typer": "0.3.0",
1961 | "mime-types": "~2.1.24"
1962 | }
1963 | },
1964 | "typescript": {
1965 | "version": "3.9.7",
1966 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
1967 | "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
1968 | "dev": true
1969 | },
1970 | "typical": {
1971 | "version": "4.0.0",
1972 | "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
1973 | "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw=="
1974 | },
1975 | "ua-parser-js": {
1976 | "version": "0.7.28",
1977 | "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz",
1978 | "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g=="
1979 | },
1980 | "unpipe": {
1981 | "version": "1.0.0",
1982 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1983 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
1984 | },
1985 | "uri-js": {
1986 | "version": "4.2.2",
1987 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
1988 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
1989 | "requires": {
1990 | "punycode": "^2.1.0"
1991 | }
1992 | },
1993 | "utils-merge": {
1994 | "version": "1.0.1",
1995 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1996 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
1997 | },
1998 | "uuid": {
1999 | "version": "3.4.0",
2000 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
2001 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
2002 | },
2003 | "valid-url": {
2004 | "version": "1.0.9",
2005 | "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
2006 | "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
2007 | },
2008 | "vary": {
2009 | "version": "1.1.2",
2010 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
2011 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
2012 | },
2013 | "verror": {
2014 | "version": "1.10.0",
2015 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
2016 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
2017 | "requires": {
2018 | "assert-plus": "^1.0.0",
2019 | "core-util-is": "1.0.2",
2020 | "extsprintf": "^1.2.0"
2021 | }
2022 | },
2023 | "which": {
2024 | "version": "2.0.2",
2025 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
2026 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
2027 | "dev": true,
2028 | "requires": {
2029 | "isexe": "^2.0.0"
2030 | }
2031 | },
2032 | "which-module": {
2033 | "version": "2.0.0",
2034 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
2035 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
2036 | "dev": true
2037 | },
2038 | "wide-align": {
2039 | "version": "1.1.3",
2040 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
2041 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
2042 | "dev": true,
2043 | "requires": {
2044 | "string-width": "^1.0.2 || 2"
2045 | }
2046 | },
2047 | "wordwrapjs": {
2048 | "version": "4.0.0",
2049 | "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz",
2050 | "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==",
2051 | "requires": {
2052 | "reduce-flatten": "^2.0.0",
2053 | "typical": "^5.0.0"
2054 | },
2055 | "dependencies": {
2056 | "typical": {
2057 | "version": "5.2.0",
2058 | "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
2059 | "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg=="
2060 | }
2061 | }
2062 | },
2063 | "workerpool": {
2064 | "version": "6.0.0",
2065 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
2066 | "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
2067 | "dev": true
2068 | },
2069 | "wrap-ansi": {
2070 | "version": "5.1.0",
2071 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
2072 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
2073 | "dev": true,
2074 | "requires": {
2075 | "ansi-styles": "^3.2.0",
2076 | "string-width": "^3.0.0",
2077 | "strip-ansi": "^5.0.0"
2078 | },
2079 | "dependencies": {
2080 | "ansi-regex": {
2081 | "version": "4.1.0",
2082 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2083 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2084 | "dev": true
2085 | },
2086 | "string-width": {
2087 | "version": "3.1.0",
2088 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
2089 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
2090 | "dev": true,
2091 | "requires": {
2092 | "emoji-regex": "^7.0.1",
2093 | "is-fullwidth-code-point": "^2.0.0",
2094 | "strip-ansi": "^5.1.0"
2095 | }
2096 | },
2097 | "strip-ansi": {
2098 | "version": "5.2.0",
2099 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2100 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2101 | "dev": true,
2102 | "requires": {
2103 | "ansi-regex": "^4.1.0"
2104 | }
2105 | }
2106 | }
2107 | },
2108 | "wrappy": {
2109 | "version": "1.0.2",
2110 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2111 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
2112 | "dev": true
2113 | },
2114 | "y18n": {
2115 | "version": "4.0.1",
2116 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
2117 | "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
2118 | "dev": true
2119 | },
2120 | "yargs": {
2121 | "version": "13.3.2",
2122 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
2123 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
2124 | "dev": true,
2125 | "requires": {
2126 | "cliui": "^5.0.0",
2127 | "find-up": "^3.0.0",
2128 | "get-caller-file": "^2.0.1",
2129 | "require-directory": "^2.1.1",
2130 | "require-main-filename": "^2.0.0",
2131 | "set-blocking": "^2.0.0",
2132 | "string-width": "^3.0.0",
2133 | "which-module": "^2.0.0",
2134 | "y18n": "^4.0.0",
2135 | "yargs-parser": "^13.1.2"
2136 | },
2137 | "dependencies": {
2138 | "ansi-regex": {
2139 | "version": "4.1.0",
2140 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2141 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2142 | "dev": true
2143 | },
2144 | "find-up": {
2145 | "version": "3.0.0",
2146 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
2147 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
2148 | "dev": true,
2149 | "requires": {
2150 | "locate-path": "^3.0.0"
2151 | }
2152 | },
2153 | "locate-path": {
2154 | "version": "3.0.0",
2155 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
2156 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
2157 | "dev": true,
2158 | "requires": {
2159 | "p-locate": "^3.0.0",
2160 | "path-exists": "^3.0.0"
2161 | }
2162 | },
2163 | "p-locate": {
2164 | "version": "3.0.0",
2165 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
2166 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
2167 | "dev": true,
2168 | "requires": {
2169 | "p-limit": "^2.0.0"
2170 | }
2171 | },
2172 | "path-exists": {
2173 | "version": "3.0.0",
2174 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
2175 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
2176 | "dev": true
2177 | },
2178 | "string-width": {
2179 | "version": "3.1.0",
2180 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
2181 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
2182 | "dev": true,
2183 | "requires": {
2184 | "emoji-regex": "^7.0.1",
2185 | "is-fullwidth-code-point": "^2.0.0",
2186 | "strip-ansi": "^5.1.0"
2187 | }
2188 | },
2189 | "strip-ansi": {
2190 | "version": "5.2.0",
2191 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2192 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2193 | "dev": true,
2194 | "requires": {
2195 | "ansi-regex": "^4.1.0"
2196 | }
2197 | }
2198 | }
2199 | },
2200 | "yargs-parser": {
2201 | "version": "13.1.2",
2202 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
2203 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
2204 | "dev": true,
2205 | "requires": {
2206 | "camelcase": "^5.0.0",
2207 | "decamelize": "^1.2.0"
2208 | }
2209 | },
2210 | "yargs-unparser": {
2211 | "version": "1.6.1",
2212 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
2213 | "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
2214 | "dev": true,
2215 | "requires": {
2216 | "camelcase": "^5.3.1",
2217 | "decamelize": "^1.2.0",
2218 | "flat": "^4.1.0",
2219 | "is-plain-obj": "^1.1.0",
2220 | "yargs": "^14.2.3"
2221 | },
2222 | "dependencies": {
2223 | "ansi-regex": {
2224 | "version": "4.1.0",
2225 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
2226 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
2227 | "dev": true
2228 | },
2229 | "find-up": {
2230 | "version": "3.0.0",
2231 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
2232 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
2233 | "dev": true,
2234 | "requires": {
2235 | "locate-path": "^3.0.0"
2236 | }
2237 | },
2238 | "locate-path": {
2239 | "version": "3.0.0",
2240 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
2241 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
2242 | "dev": true,
2243 | "requires": {
2244 | "p-locate": "^3.0.0",
2245 | "path-exists": "^3.0.0"
2246 | }
2247 | },
2248 | "p-locate": {
2249 | "version": "3.0.0",
2250 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
2251 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
2252 | "dev": true,
2253 | "requires": {
2254 | "p-limit": "^2.0.0"
2255 | }
2256 | },
2257 | "path-exists": {
2258 | "version": "3.0.0",
2259 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
2260 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
2261 | "dev": true
2262 | },
2263 | "string-width": {
2264 | "version": "3.1.0",
2265 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
2266 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
2267 | "dev": true,
2268 | "requires": {
2269 | "emoji-regex": "^7.0.1",
2270 | "is-fullwidth-code-point": "^2.0.0",
2271 | "strip-ansi": "^5.1.0"
2272 | }
2273 | },
2274 | "strip-ansi": {
2275 | "version": "5.2.0",
2276 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
2277 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
2278 | "dev": true,
2279 | "requires": {
2280 | "ansi-regex": "^4.1.0"
2281 | }
2282 | },
2283 | "yargs": {
2284 | "version": "14.2.3",
2285 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
2286 | "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
2287 | "dev": true,
2288 | "requires": {
2289 | "cliui": "^5.0.0",
2290 | "decamelize": "^1.2.0",
2291 | "find-up": "^3.0.0",
2292 | "get-caller-file": "^2.0.1",
2293 | "require-directory": "^2.1.1",
2294 | "require-main-filename": "^2.0.0",
2295 | "set-blocking": "^2.0.0",
2296 | "string-width": "^3.0.0",
2297 | "which-module": "^2.0.0",
2298 | "y18n": "^4.0.0",
2299 | "yargs-parser": "^15.0.1"
2300 | }
2301 | },
2302 | "yargs-parser": {
2303 | "version": "15.0.1",
2304 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
2305 | "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
2306 | "dev": true,
2307 | "requires": {
2308 | "camelcase": "^5.0.0",
2309 | "decamelize": "^1.2.0"
2310 | }
2311 | }
2312 | }
2313 | }
2314 | }
2315 | }
2316 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prpl-server",
3 | "version": "1.4.2",
4 | "description": "A Node implementation of the PRPL pattern for serving Progressive Web Apps",
5 | "repository": "git+https://github.com/Polymer/prpl-server-node.git",
6 | "main": "lib/prpl.js",
7 | "types": "lib/prpl.d.ts",
8 | "bin": "bin/prpl-server",
9 | "engines": {
10 | "node": ">=10.0"
11 | },
12 | "author": "The Polymer Project Authors",
13 | "license": "BSD-3-Clause",
14 | "scripts": {
15 | "build": "rm -Rf lib/ && tsc",
16 | "build:watch": "tsc --watch",
17 | "format": "find src -name '*.ts' | xargs clang-format --style=file -i",
18 | "prepack": "npm run build",
19 | "prepublishOnly": "npm test",
20 | "test": "npm run build && mocha lib/test/**/*_test.js",
21 | "test:watch": "tsc-then -- npm run test"
22 | },
23 | "files": [
24 | "bin/**/*",
25 | "lib/**/*"
26 | ],
27 | "dependencies": {
28 | "ansi-escape-sequences": "^5.1.2",
29 | "browser-capabilities": "^1.1.4",
30 | "command-line-args": "^5.1.1",
31 | "command-line-usage": "^6.1.0",
32 | "compression": "^1.7.4",
33 | "express": "^4.17.1",
34 | "http-errors": "^1.8.0",
35 | "rendertron-middleware": "^0.1.5",
36 | "send": "^0.17.1",
37 | "statuses": "^2.0.0",
38 | "valid-url": "^1.0.9"
39 | },
40 | "devDependencies": {
41 | "@types/chai": "^4.2.12",
42 | "@types/compression": "^1.7.0",
43 | "@types/express": "^4.17.7",
44 | "@types/http-errors": "^1.8.0",
45 | "@types/mocha": "^8.0.2",
46 | "@types/node": "^14.0.27",
47 | "@types/send": "^0.14.5",
48 | "@types/statuses": "^2.0.0",
49 | "@types/valid-url": "^1.0.3",
50 | "chai": "^4.2.0",
51 | "clang-format": "^1.4.0",
52 | "mocha": "^8.1.1",
53 | "source-map-support": "^0.5.19",
54 | "tsc-then": "^1.1.0",
55 | "typescript": "^3.9.7"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/cli.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 | * This code may only be used under the BSD style license found at
5 | * http://polymer.github.io/LICENSE.txt
6 | * The complete set of authors may be found at
7 | * http://polymer.github.io/AUTHORS.txt
8 | * The complete set of contributors may be found at
9 | * http://polymer.github.io/CONTRIBUTORS.txt
10 | * Code distributed by Google as part of the polymer project is also
11 | * subject to an additional IP rights grant found at
12 | * http://polymer.github.io/PATENTS.txt
13 | */
14 |
15 | import * as compression from 'compression';
16 | import * as express from 'express';
17 | import * as fs from 'fs';
18 | import * as path from 'path';
19 | import type {AddressInfo} from 'net';
20 |
21 | import * as prpl from './prpl';
22 |
23 | const commandLineArgs = require('command-line-args') as any;
24 | const commandLineUsage = require('command-line-usage') as any;
25 | const ansi = require('ansi-escape-sequences') as any;
26 | const rendertron = require('rendertron-middleware') as any;
27 |
28 | const argDefs = [
29 | {
30 | name: 'help',
31 | type: Boolean,
32 | description: 'Print this help text.',
33 | },
34 | {
35 | name: 'version',
36 | type: Boolean,
37 | description: 'Print the installed version.',
38 | },
39 | {
40 | name: 'host',
41 | type: String,
42 | defaultValue: '127.0.0.1',
43 | description: 'Listen on this hostname (default 127.0.0.1).',
44 | },
45 | {
46 | name: 'port',
47 | type: Number,
48 | defaultValue: 8080,
49 | description: 'Listen on this port; 0 for random (default 8080).'
50 | },
51 | {
52 | name: 'root',
53 | type: String,
54 | defaultValue: '.',
55 | description: 'Serve files relative to this directory (default ".").',
56 | },
57 | {
58 | name: 'config',
59 | type: String,
60 | description:
61 | 'JSON configuration file (default "/polymer.json" if exists).',
62 | },
63 | {
64 | name: 'https-redirect',
65 | type: Boolean,
66 | description:
67 | 'Redirect HTTP requests to HTTPS with a 301. Assumes same hostname ' +
68 | 'and default port (443). Trusts X-Forwarded-* headers for detecting ' +
69 | 'protocol and hostname.',
70 | },
71 | {
72 | name: 'bot-proxy',
73 | type: String,
74 | description: 'Proxy requests from bots/crawlers to this URL. See ' +
75 | 'https://github.com/GoogleChrome/rendertron for more details.',
76 | },
77 | {
78 | name: 'cache-control',
79 | type: String,
80 | description:
81 | 'The Cache-Control header to send for all requests except the ' +
82 | 'entrypoint (default from config file or "max-age=60").',
83 | },
84 | ];
85 |
86 | export function run(argv: string[]) {
87 | const args = commandLineArgs(argDefs, {argv});
88 |
89 | if (args.help) {
90 | console.log(commandLineUsage([
91 | {
92 | header: `[magenta]{prpl-server}`,
93 | content: 'https://github.com/Polymer/prpl-server-node',
94 | },
95 | {
96 | header: `Options`,
97 | optionList: argDefs,
98 | }
99 | ]));
100 | return;
101 | }
102 |
103 | if (args.version) {
104 | console.log(require('../package.json').version);
105 | return;
106 | }
107 |
108 | if (!args.host) {
109 | throw new Error('invalid --host');
110 | }
111 | if (isNaN(args.port)) {
112 | throw new Error('invalid --port');
113 | }
114 | if (!args.root) {
115 | throw new Error('invalid --root');
116 | }
117 |
118 | // If specified explicitly, a missing config file will error. Otherwise, try
119 | // the default location and only warn when it's missing.
120 | if (!args.config) {
121 | const p = path.join(args.root, 'polymer.json');
122 | if (fs.existsSync(p)) {
123 | args.config = p;
124 | } else {
125 | console.warn('WARNING: No config found.');
126 | }
127 | }
128 | let config: prpl.Config = {};
129 | if (args.config) {
130 | console.info(`Loading config from "${args.config}".`);
131 | config = JSON.parse(fs.readFileSync(args.config, 'utf8')) as prpl.Config;
132 | }
133 |
134 | if (args['cache-control']) {
135 | config.cacheControl = args['cache-control'];
136 | };
137 |
138 | const app = express();
139 |
140 | // Trust X-Forwarded-* headers so that when we are behind a reverse proxy,
141 | // our connection information is that of the original client (according to
142 | // the proxy), not of the proxy itself. We need this for HTTPS redirection
143 | // and bot rendering.
144 | app.set('trust proxy', true);
145 |
146 | if (args['https-redirect']) {
147 | console.info(`Redirecting HTTP requests to HTTPS.`);
148 | app.use((req, res, next) => {
149 | if (req.secure) {
150 | next();
151 | return;
152 | }
153 | res.redirect(301, `https://${req.hostname}${req.url}`);
154 | });
155 | }
156 |
157 | app.use(compression());
158 |
159 | if (args['bot-proxy']) {
160 | console.info(`Proxying bots to "${args['bot-proxy']}".`);
161 | app.use(rendertron.makeMiddleware({
162 | proxyUrl: args['bot-proxy'],
163 | injectShadyDom: true,
164 | }));
165 | }
166 |
167 | app.use(prpl.makeHandler(args.root, config));
168 |
169 | const server = app.listen(args.port, args.host, () => {
170 | const addr = server.address() as AddressInfo;
171 | let urlHost = addr.address;
172 | if (addr.family === 'IPv6') {
173 | urlHost = '[' + urlHost + ']';
174 | }
175 | console.log();
176 | console.log(ansi.format('[magenta bold]{prpl-server} listening'));
177 | console.log(ansi.format(`[blue]{http://${urlHost}:${addr.port}}`));
178 | console.log();
179 | });
180 | }
181 |
--------------------------------------------------------------------------------
/src/prpl.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 | * This code may only be used under the BSD style license found at
5 | * http://polymer.github.io/LICENSE.txt
6 | * The complete set of authors may be found at
7 | * http://polymer.github.io/AUTHORS.txt
8 | * The complete set of contributors may be found at
9 | * http://polymer.github.io/CONTRIBUTORS.txt
10 | * Code distributed by Google as part of the polymer project is also
11 | * subject to an additional IP rights grant found at
12 | * http://polymer.github.io/PATENTS.txt
13 | */
14 |
15 | import * as capabilities from 'browser-capabilities';
16 | import * as express from 'express';
17 | import * as fs from 'fs';
18 | import * as http from 'http';
19 | import * as httpErrors from 'http-errors';
20 | import * as path from 'path';
21 | import * as send from 'send';
22 | import * as statuses from 'statuses';
23 | import * as url from 'url';
24 |
25 | import * as push from './push';
26 |
27 | export interface Config {
28 | // The Cache-Control header to send for all requests except the entrypoint.
29 | //
30 | // Defaults to `max-age=60`.
31 | cacheControl?: string;
32 |
33 | // If `true`, when a 404 or other HTTP error occurs, the Express `next`
34 | // function will be called with the error, so that it can be handled by
35 | // downstream error handling middleware.
36 | //
37 | // If `false` (or if there was no `next` function because Express is not
38 | // being used), a minimal `text/plain` error will be returned.
39 | //
40 | // Defaults to `false`.
41 | forwardErrors?: boolean;
42 |
43 | // Serves a tiny self-unregistering service worker for any request path
44 | // ending with `service-worker.js` that would otherwise have had a 404 Not
45 | // Found response.
46 | //
47 | // This can be useful when the location of a service worker has changed, as
48 | // it will prevent clients from getting stuck with an old service worker
49 | // indefinitely.
50 | //
51 | // This problem arises because when a service worker updates, a 404 is
52 | // treated as a failed update. It does not cause the service worker to be
53 | // unregistered. See https://github.com/w3c/ServiceWorker/issues/204 for more
54 | // discussion of this problem.
55 | //
56 | // Defaults to `true`.
57 | unregisterMissingServiceWorkers?: boolean;
58 |
59 | // Below is the subset of the polymer.json specification that we care about
60 | // for serving. https://www.polymer-project.org/2.0/docs/tools/polymer-json
61 | // https://github.com/Polymer/polymer-project-config/blob/master/src/index.ts
62 | entrypoint?: string;
63 | builds?: {
64 | name?: string,
65 | browserCapabilities?: capabilities.BrowserCapability[],
66 | }[];
67 | }
68 |
69 | // Matches URLs like "/foo/bar.png" but not "/foo.png/bar".
70 | const hasFileExtension = /\.[^/]*$/;
71 |
72 | // TODO Service worker location should be configurable.
73 | const isServiceWorker = /service-worker.js$/;
74 |
75 | /**
76 | * Return a new HTTP handler to serve a PRPL-style application.
77 | */
78 | export function makeHandler(root?: string, config?: Config): (
79 | request: http.IncomingMessage,
80 | response: http.ServerResponse,
81 | next?: express.NextFunction) => void {
82 | const absRoot = path.resolve(root || '.');
83 | console.info(`Serving files from "${absRoot}".`);
84 |
85 | const builds = loadBuilds(absRoot, config);
86 |
87 | const cacheControl = (config && config.cacheControl) || 'max-age=60';
88 | const unregisterMissingServiceWorkers =
89 | (config && config.unregisterMissingServiceWorkers != undefined) ?
90 | config.unregisterMissingServiceWorkers :
91 | true;
92 | const forwardErrors = config && config.forwardErrors;
93 |
94 | return async function prplHandler(request, response, next) {
95 | const handleError = (err: httpErrors.HttpError) => {
96 | if (forwardErrors && next) {
97 | next(err);
98 | } else {
99 | writePlainTextError(response, err);
100 | }
101 | };
102 |
103 | const urlPath = url.parse(request.url || '/').pathname || '/';
104 |
105 | // Let's be extra careful about directory traversal attacks, even though
106 | // the `send` library should already ensure we don't serve any file outside
107 | // our root. This should also prevent the file existence check we do next
108 | // from leaking any file existence information (whether you got the
109 | // entrypoint or a 403 from `send` might tell you if a file outside our
110 | // root exists). Add the trailing path separator because otherwise "/foo"
111 | // is a prefix of "/foo-secrets".
112 | const absFilepath = path.normalize(path.join(absRoot, urlPath));
113 | if (!absFilepath.startsWith(addTrailingPathSep(absRoot))) {
114 | handleError(httpErrors(403, 'Forbidden'));
115 | return;
116 | }
117 |
118 | // Serve the entrypoint for the root path, and for all other paths that
119 | // don't have a corresponding static resource on disk. As a special
120 | // case, paths with file extensions are always excluded because they are
121 | // likely to be not-found static resources rather than application
122 | // routes.
123 | const serveEntrypoint = urlPath === '/' ||
124 | (!hasFileExtension.test(urlPath) && !(await fileExists(absFilepath)));
125 |
126 | // Find the highest ranked build suitable for this user agent.
127 | const clientCapabilities = capabilities.browserCapabilities(
128 | request.headers['user-agent'] as string);
129 | const build = builds.find((b) => b.canServe(clientCapabilities));
130 |
131 | // We warned about this at startup. You should probably provide a fallback
132 | // build with no capabilities, at least to nicely inform the user. Note
133 | // that we only return this error for the entrypoint; we always serve fully
134 | // qualified static resources.
135 | if (!build && serveEntrypoint) {
136 | handleError(httpErrors(500, 'This browser is not supported.'));
137 | return;
138 | }
139 |
140 | const fileToSend = (build && serveEntrypoint) ? build.entrypoint : urlPath;
141 |
142 | if (isServiceWorker.test(fileToSend)) {
143 | // A service worker may only register with a scope above its own path if
144 | // permitted by this header.
145 | // https://www.w3.org/TR/service-workers-1/#service-worker-allowed
146 | response.setHeader('Service-Worker-Allowed', '/');
147 |
148 | // Don't cache SW (unless cache header is otherwise set).
149 | if (!response.getHeader('Cache-Control')) {
150 | response.setHeader('Cache-Control', 'max-age=0');
151 | }
152 |
153 | // Automatically unregister service workers that no longer exist to
154 | // prevent clients getting stuck with old service workers indefinitely.
155 | if (unregisterMissingServiceWorkers && !(await fileExists(absFilepath))) {
156 | response.setHeader('Content-Type', 'application/javascript');
157 | response.writeHead(200);
158 | response.end(
159 | `self.addEventListener('install', () => self.skipWaiting());
160 | self.addEventListener('activate', () => self.registration.unregister());`);
161 | return;
162 | }
163 | }
164 |
165 | // Don't set the Cache-Control header if it's already set. This way another
166 | // middleware can control caching, and we won't touch it.
167 | if (!response.getHeader('Cache-Control')) {
168 | response.setHeader(
169 | 'Cache-Control', serveEntrypoint ? 'max-age=0' : cacheControl);
170 | }
171 |
172 | if (build && build.pushManifest) {
173 | // Set nopush attribute if the client doesn't support push. This will
174 | // still set preload headers, but it provides a signal to the server to
175 | // not use server push.
176 | const nopush = !clientCapabilities.has('push');
177 | const linkHeaders = build.pushManifest.linkHeaders(urlPath, nopush);
178 | if (urlPath !== fileToSend) {
179 | // Also check the filename against the push manifest. In the case of
180 | // the entrypoint, these will be different (e.g. "/my/app/route" vs
181 | // "/es2015/index.html"), and we want to support configuring pushes in
182 | // terms of both.
183 | linkHeaders.push(...build.pushManifest.linkHeaders(fileToSend, nopush));
184 | }
185 | response.setHeader('Link', linkHeaders);
186 | }
187 |
188 | const sendOpts = {
189 | root: absRoot,
190 | // We handle the caching header ourselves.
191 | cacheControl: false,
192 | };
193 | send(request, fileToSend, sendOpts)
194 | .on('error',
195 | (err: httpErrors.HttpError) => {
196 | // `send` puts a lot of detail in the error message, like the
197 | // absolute system path of the missing file for a 404. We don't
198 | // want that to leak out, so let's use a generic message instead.
199 | err.message = statuses.message[err.status] || String(err.status);
200 | handleError(err);
201 | })
202 | .pipe(response);
203 | };
204 | }
205 |
206 | /**
207 | * Return a promise for the existence of a file.
208 | */
209 | function fileExists(filepath: string): Promise {
210 | return new Promise((resolve) => fs.access(filepath, (err) => resolve(!err)));
211 | }
212 |
213 | /**
214 | * Write a plain text HTTP error response.
215 | */
216 | function writePlainTextError(
217 | response: http.ServerResponse, error: httpErrors.HttpError) {
218 | response.statusCode = error.status;
219 | response.setHeader('Content-Type', 'text/plain');
220 | response.end(error.message);
221 | }
222 |
223 | function addTrailingPathSep(p: string): string {
224 | return p.endsWith(path.sep) ? p : p + path.sep;
225 | }
226 |
227 | class Build {
228 | public pushManifest?: push.PushManifest;
229 |
230 | constructor(
231 | private configOrder: number,
232 | public requirements: Set,
233 | public entrypoint: string,
234 | buildDir: string,
235 | serverRoot: string) {
236 | // TODO Push manifest location should be configurable.
237 | const pushManifestPath = path.join(buildDir, 'push-manifest.json');
238 | const relPath = path.relative(serverRoot, pushManifestPath);
239 | if (fs.existsSync(pushManifestPath)) {
240 | console.info(`Detected push manifest "${relPath}".`);
241 | // Note this constructor throws if invalid.
242 | this.pushManifest = new push.PushManifest(
243 | JSON.parse(fs.readFileSync(pushManifestPath, 'utf8')) as
244 | push.PushManifestData,
245 | path.relative(serverRoot, buildDir));
246 | }
247 | }
248 |
249 | /**
250 | * Order builds with more capabililties first -- a heuristic that assumes
251 | * builds with more features are better. Ties are broken by the order the
252 | * build appeared in the original configuration file.
253 | */
254 | compare(that: Build): number {
255 | if (this.requirements.size !== that.requirements.size) {
256 | return that.requirements.size - this.requirements.size;
257 | }
258 | return this.configOrder - that.configOrder;
259 | }
260 |
261 | /**
262 | * Return whether all requirements of this build are met by the given client
263 | * browser capabilities.
264 | */
265 | canServe(client: Set): boolean {
266 | for (const r of this.requirements) {
267 | if (!client.has(r)) {
268 | return false;
269 | }
270 | }
271 | return true;
272 | }
273 | }
274 |
275 | function loadBuilds(root: string, config: Config|undefined): Build[] {
276 | const builds: Build[] = [];
277 | const entrypoint = (config ? config.entrypoint : null) || 'index.html';
278 |
279 | if (!config || !config.builds || !config.builds.length) {
280 | // No builds were specified. Try to serve an entrypoint from the root
281 | // directory, with no capability requirements.
282 | console.warn(`WARNING: No builds configured.`);
283 | builds.push(new Build(0, new Set(), entrypoint, root, root));
284 |
285 | } else {
286 | for (let i = 0; i < config.builds.length; i++) {
287 | const build = config.builds[i];
288 | if (!build.name) {
289 | console.warn(`WARNING: Build at offset ${i} has no name; skipping.`);
290 | continue;
291 | }
292 | builds.push(new Build(
293 | i,
294 | new Set(build.browserCapabilities),
295 | path.posix.join(build.name, entrypoint),
296 | path.join(root, build.name),
297 | root));
298 | }
299 | }
300 |
301 | // Sort builds by preference in case multiple builds could be served to
302 | // the same client.
303 | builds.sort((a, b) => a.compare(b));
304 |
305 | // Sanity check.
306 | for (const build of builds) {
307 | const requirements = Array.from(build.requirements.values());
308 | console.info(
309 | `Registered entrypoint "${build.entrypoint}" with capabilities ` +
310 | `[${requirements.join(',')}].`);
311 | // Note `build.entrypoint` is relative to the server root, but that's not
312 | // neccessarily our cwd.
313 | // TODO Refactor to make filepath vs URL path and relative vs absolute
314 | // values clearer.
315 | if (!fs.existsSync(path.join(root, build.entrypoint))) {
316 | console.warn(`WARNING: Entrypoint "${build.entrypoint}" does not exist.`);
317 | }
318 | }
319 | if (!builds.find((b) => b.requirements.size === 0)) {
320 | console.warn(
321 | 'WARNING: All builds have a capability requirement. ' +
322 | 'Some browsers will display an error. Consider a fallback build.');
323 | }
324 |
325 | return builds;
326 | }
327 |
--------------------------------------------------------------------------------
/src/push.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 | * This code may only be used under the BSD style license found at
5 | * http://polymer.github.io/LICENSE.txt
6 | * The complete set of authors may be found at
7 | * http://polymer.github.io/AUTHORS.txt
8 | * The complete set of contributors may be found at
9 | * http://polymer.github.io/CONTRIBUTORS.txt
10 | * Code distributed by Google as part of the polymer project is also
11 | * subject to an additional IP rights grant found at
12 | * http://polymer.github.io/PATENTS.txt
13 | */
14 |
15 | import * as path from 'path';
16 | import * as validUrl from 'valid-url';
17 |
18 | /**
19 | * JSON format for a multi-file push manifest.
20 | */
21 | export interface PushManifestData {
22 | [pattern: string]: {
23 | [resource: string]: {type: string; crossorigin?: string; weight?: number;}
24 | }
25 | }
26 |
27 | /**
28 | * Maps from an HTTP request path to the set of additional resources that
29 | * should be pre-emptively pushed to the client via HTTP/2 server push.
30 | */
31 | export class PushManifest {
32 | private mapping = new Array<[
33 | RegExp,
34 | Map
39 | ]>();
40 |
41 | /**
42 | * Create a new `PushManifest` from a JSON object which is expected to match
43 | * the multi-file variant of the format described at
44 | * https://github.com/GoogleChrome/http2-push-manifest.
45 | *
46 | * The keys of this object are exact-match regular expression patterns that
47 | * will be tested against the request URL path.
48 | *
49 | * If `basePath` is set, relative paths in the push manifest (both patterns
50 | * and resources) will be interpreted as relative to this directory.
51 | * Typically it should be set to the path from the server file root to the
52 | * push manifest file.
53 | *
54 | * Throws an exception if the given object does not match the manifest
55 | * format, if a resource is not a valid URI path, or if `type` is not one of
56 | * the valid request destinations
57 | * (https://fetch.spec.whatwg.org/#concept-request-destination).
58 | *
59 | * Note that this class does not validate that resources exist on disk, since
60 | * we can't assume if or how the server maps resources to disk.
61 | */
62 | constructor(manifest: PushManifestData, basePath: string = '/') {
63 | for (const pattern of Object.keys(manifest)) {
64 | const resources = new Map();
65 | for (const resource of Object.keys(manifest[pattern])) {
66 | validatePath(resource);
67 | const type = manifest[pattern][resource].type || '';
68 | const crossorigin = manifest[pattern][resource].crossorigin || null;
69 | if (!requestDestinations.has(type)) {
70 | throw new Error(`invalid type: ${type}`);
71 | }
72 | resources.set(normalizePath(resource, basePath), {type, crossorigin});
73 | }
74 | if (resources.size) {
75 | let normalizedPattern;
76 | if (pattern.startsWith('^')) {
77 | normalizedPattern = pattern;
78 | } else {
79 | normalizedPattern = '^' + normalizePath(pattern, basePath);
80 | if (!normalizedPattern.endsWith('$')) {
81 | normalizedPattern += '$';
82 | }
83 | }
84 | this.mapping.push([new RegExp(normalizedPattern), resources]);
85 | }
86 | }
87 | }
88 |
89 | /**
90 | * Generate `Link: rel=preload` headers for each push resource associated
91 | * with `path`.
92 | *
93 | * A cooperating HTTP/2 server may intercept these headers and intiate a
94 | * server push for each resource.
95 | *
96 | * See https://w3c.github.io/preload/#server-push-http-2.
97 | */
98 | linkHeaders(path: string, nopush: boolean = false): string[] {
99 | const headers = [];
100 | const normalizedPattern = addLeadingSlash(path);
101 | for (const [pattern, resources] of this.mapping) {
102 | if (!resources || !pattern.test(normalizedPattern)) {
103 | continue;
104 | }
105 | for (const [resource, {type, crossorigin}] of resources.entries()) {
106 | let header = `<${resource}>; rel=preload`;
107 | if (type) {
108 | header += `; as=${type}`;
109 | }
110 | if (crossorigin) {
111 | header += `; crossorigin=${crossorigin}`;
112 | }
113 | if (nopush) {
114 | header += `; nopush`;
115 | }
116 | headers.push(header);
117 | }
118 | }
119 | return headers;
120 | }
121 | }
122 |
123 | function normalizePath(s: string, basePath: string) {
124 | return s.startsWith('/') ? s : path.posix.join(addLeadingSlash(basePath), s);
125 | }
126 |
127 | function addLeadingSlash(s: string) {
128 | return s.startsWith('/') ? s : '/' + s;
129 | }
130 |
131 | function validatePath(s: string) {
132 | if (!validUrl.isUri('http://example.com' + addLeadingSlash(s))) {
133 | throw new Error(`invalid resource: ${s}`);
134 | }
135 | }
136 |
137 | // From https://fetch.spec.whatwg.org/#concept-request-destination.
138 | const requestDestinations = new Set([
139 | '',
140 | 'audio',
141 | 'document',
142 | 'embed',
143 | 'fetch',
144 | 'font',
145 | 'image',
146 | 'manifest',
147 | 'object',
148 | 'report',
149 | 'script',
150 | 'serviceworker',
151 | 'sharedworker',
152 | 'style',
153 | 'track',
154 | 'video',
155 | 'worker',
156 | 'xslt'
157 | ]);
158 |
--------------------------------------------------------------------------------
/src/test/prpl_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 | * This code may only be used under the BSD style license found at
5 | * http://polymer.github.io/LICENSE.txt
6 | * The complete set of authors may be found at
7 | * http://polymer.github.io/AUTHORS.txt
8 | * The complete set of contributors may be found at
9 | * http://polymer.github.io/CONTRIBUTORS.txt
10 | * Code distributed by Google as part of the polymer project is also
11 | * subject to an additional IP rights grant found at
12 | * http://polymer.github.io/PATENTS.txt
13 | */
14 |
15 | import {assert} from 'chai';
16 | import * as express from 'express';
17 | import * as http from 'http';
18 | import * as httpErrors from 'http-errors';
19 | import * as path from 'path';
20 | import type {AddressInfo} from 'net';
21 |
22 | import * as prpl from '../prpl';
23 |
24 | const chrome = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) ' +
25 | 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36';
26 |
27 | suite('prpl server', function() {
28 | let server: http.Server;
29 | let host: string;
30 | let port: number;
31 |
32 | function startServer(root: string, config?: prpl.Config): Promise {
33 | const handler = prpl.makeHandler(root, config);
34 | server = http.createServer(
35 | (request: http.IncomingMessage, response: http.ServerResponse) => {
36 | // To help test caching behavior, if the request URL includes this
37 | // magic string, we'll set the cache-control header to something
38 | // custom before calling prpl-handler. This is how we allow users to
39 | // take over control of the cache-control header.
40 | if (request.url && request.url.includes('custom-cache')) {
41 | response.setHeader('Cache-Control', 'custom-cache');
42 | }
43 | handler(request, response);
44 | });
45 | return new Promise((resolve) => {
46 | server.listen(/* random */ 0, '127.0.0.1', () => {
47 | const addr = server.address() as AddressInfo;
48 | host = addr.address;
49 | port = addr.port;
50 | resolve();
51 | });
52 | });
53 | }
54 |
55 | type GetResponse = {
56 | code: number|undefined; data: string; headers: http.IncomingHttpHeaders;
57 | };
58 |
59 | function get(path: string, ua?: string, headers?: http.OutgoingHttpHeaders):
60 | Promise {
61 | return new Promise((resolve) => {
62 | const getHeaders = Object.assign({'user-agent': ua || ''}, headers);
63 | http.get({host, port, path, headers: getHeaders}, (response) => {
64 | const code = response.statusCode;
65 | const headers = response.headers;
66 | let data = '';
67 | response.on('data', (chunk) => data += chunk);
68 | response.on('end', () => resolve({code, data, headers}));
69 | });
70 | });
71 | }
72 |
73 | function checkPlainTextError(
74 | expectCode: number, expectData: string, res: GetResponse) {
75 | assert.equal(res.code, expectCode);
76 | assert.equal(res.data, expectData);
77 | assert.equal(res.headers['content-type'], 'text/plain');
78 | assert.equal(res.headers['content-length'], String(expectData.length));
79 | }
80 |
81 | suite('configured with multiple builds', () => {
82 | suiteSetup(async () => {
83 | await startServer(path.join('src', 'test', 'static'), {
84 | builds: [
85 | {
86 | name: 'fallback',
87 | },
88 | {
89 | name: 'es2015',
90 | browserCapabilities: ['es2015'],
91 | },
92 | ],
93 | });
94 | });
95 |
96 | suiteTeardown((done) => {
97 | server.close(done);
98 | });
99 |
100 | suite('with low capability user agent', () => {
101 | test('serves entrypoint from root', async () => {
102 | const {code, data} = await get('/');
103 | assert.equal(code, 200);
104 | assert.include(data, 'fallback entrypoint');
105 | });
106 |
107 | test('serves entrypoint for application route', async () => {
108 | const {code, data} = await get('/foo/bar');
109 | assert.equal(code, 200);
110 | assert.include(data, 'fallback entrypoint');
111 | });
112 |
113 | test('serves a fragment resource', async () => {
114 | const {code, data} = await get('/fallback/fragment.html');
115 | assert.equal(code, 200);
116 | assert.include(data, 'fallback fragment');
117 | });
118 |
119 | test('serves a 404 for missing file with extension', async () => {
120 | checkPlainTextError(404, 'Not Found', await get('/foo.png'));
121 | });
122 |
123 | test('forbids traversal outside root', async () => {
124 | checkPlainTextError(403, 'Forbidden', await get('/../secrets'));
125 | });
126 |
127 | test('forbids traversal outside root with matching prefix', async () => {
128 | // Edge case where the resolved request path naively matches the root
129 | // directory by prefix even though it's actually a sibling, not a child
130 | // ("/static-secrets" begins with "/static").
131 | checkPlainTextError(403, 'Forbidden', await get('/../static-secrets'));
132 | });
133 | });
134 |
135 | suite('with high capability user agent', () => {
136 | test('serves entrypoint from root', async () => {
137 | const {code, data} = await get('/', chrome);
138 | assert.equal(code, 200);
139 | assert.include(data, 'es2015 entrypoint');
140 | });
141 |
142 | test('serves entrypoint for application route', async () => {
143 | const {code, data} = await get('/foo/bar', chrome);
144 | assert.equal(code, 200);
145 | assert.include(data, 'es2015 entrypoint');
146 | });
147 |
148 | test('serves a fragment resource', async () => {
149 | const {code, data} = await get('/es2015/fragment.html', chrome);
150 | assert.equal(code, 200);
151 | assert.include(data, 'es2015 fragment');
152 | });
153 |
154 | test('serves a 404 for missing file with extension', async () => {
155 | checkPlainTextError(404, 'Not Found', await get('/foo.png'));
156 | });
157 |
158 | test('sets push headers for fragment', async () => {
159 | const {headers} = await get('/es2015/fragment.html', chrome);
160 | assert.equal(
161 | headers['link'], '; rel=preload; as=document');
162 | });
163 |
164 | test('sets push headers for explicit entrypoint', async () => {
165 | const {headers} = await get('/es2015/index.html', chrome);
166 | assert.equal(
167 | headers['link'],
168 | ('; rel=preload; as=document, ' +
169 | '; rel=preload; as=script'));
170 | });
171 |
172 | test('sets push headers for application route', async () => {
173 | const {headers} = await get('/foo/bar', chrome);
174 | assert.equal(
175 | headers['link'],
176 | // Note these headers are both those defined for the entrypoint by
177 | // filename, and by application route.
178 | ('; rel=preload; as=document, ' +
179 | '; rel=preload; as=document, ' +
180 | '; rel=preload; as=script'));
181 | });
182 |
183 | test('sets service-worker-allowed header', async () => {
184 | const {headers} = await get('/es2015/service-worker.js', chrome);
185 | assert.equal(headers['service-worker-allowed'], '/');
186 | });
187 |
188 | test('sets zero cache header on SW', async () => {
189 | const {headers} = await get('/es2015/service-worker.js', chrome);
190 | assert.equal(headers['cache-control'], 'max-age=0');
191 | });
192 |
193 | test('doesn\'t set cache header on SW if already set', async () => {
194 | // See above explanation of `custom-cache` magic.
195 | const {headers} =
196 | await get('/es2015/service-worker.js?custom-cache', chrome);
197 | assert.equal(headers['cache-control'], 'custom-cache');
198 | });
199 |
200 | test('automatically unregister missing service workers', async () => {
201 | const {code, data, headers} = await get('/service-worker.js', chrome);
202 | assert.equal(code, 200);
203 | assert.equal(headers['content-type'], 'application/javascript');
204 | assert.equal(headers['service-worker-allowed'], '/');
205 | assert.include(data, 'registration.unregister');
206 | });
207 |
208 | test('sets default cache header on static file', async () => {
209 | const {headers} = await get('/es2015/fragment.html', chrome);
210 | assert.equal(headers['cache-control'], 'max-age=60');
211 | });
212 |
213 | test('sets zero cache header on entrypoint', async () => {
214 | const {headers} = await get('/foo/bar', chrome);
215 | assert.equal(headers['cache-control'], 'max-age=0');
216 | });
217 |
218 | test('doesn\'t set cache header if already set', async () => {
219 | // See above explanation of `custom-cache` magic.
220 | const {headers} = await get('/foo/bar?custom-cache', chrome);
221 | assert.equal(headers['cache-control'], 'custom-cache');
222 | });
223 |
224 | test('sends etag response header', async () => {
225 | const {headers} = await get('/es2015/fragment.html', chrome);
226 | assert.isNotEmpty(headers['etag']);
227 | });
228 |
229 | test('respects etag request header', async () => {
230 | const {headers} = await get('/es2015/fragment.html', chrome);
231 | const {code, data} = await get('/es2015/fragment.html', chrome, {
232 | 'If-None-Match': headers['etag'] as string,
233 | });
234 | assert.equal(code, 304);
235 | assert.equal(data, '');
236 | });
237 | });
238 | });
239 |
240 | suite('configured with no fallback build', () => {
241 | suiteSetup(async () => {
242 | await startServer(path.join('src', 'test', 'static'), {
243 | builds: [
244 | {
245 | name: 'es2015',
246 | browserCapabilities: ['es2015'],
247 | },
248 | ],
249 | });
250 | });
251 |
252 | suiteTeardown((done) => {
253 | server.close(done);
254 | });
255 |
256 | test('serves 500 error to unsupported browser', async () => {
257 | checkPlainTextError(
258 | 500, 'This browser is not supported.', await get('/'));
259 | });
260 | });
261 |
262 | suite('configured with unregisterMissingServiceWorkers disabled', () => {
263 | suiteSetup(async () => {
264 | await startServer(path.join('src', 'test', 'static'), {
265 | builds: [
266 | {
267 | name: 'es2015',
268 | browserCapabilities: ['es2015'],
269 | },
270 | ],
271 | unregisterMissingServiceWorkers: false,
272 | });
273 | });
274 |
275 | suiteTeardown((done) => {
276 | server.close(done);
277 | });
278 |
279 | test('sends 404 for missing service worker', async () => {
280 | const {code} = await get('/service-worker.js', chrome);
281 | assert.equal(code, 404);
282 | });
283 | });
284 |
285 | suite('standalone with no builds', () => {
286 | suiteSetup(async () => {
287 | await startServer(path.join('src', 'test', 'static', 'standalone'));
288 | });
289 |
290 | suiteTeardown((done) => {
291 | server.close(done);
292 | });
293 |
294 | test('serves index.html by default', async () => {
295 | const {code, data} = await get('/');
296 | assert.equal(code, 200);
297 | assert.include(data, 'standalone entrypoint');
298 | });
299 |
300 | test('services static files', async () => {
301 | const {code, data} = await get('/fragment.html');
302 | assert.equal(code, 200);
303 | assert.include(data, 'standalone fragment');
304 | });
305 |
306 | test('sets push manifest link headers', async () => {
307 | const {headers} = await get('/', chrome);
308 | assert.equal(
309 | headers['link'], '; rel=preload; as=document');
310 | });
311 | });
312 |
313 | suite('configured with express error forwarding', () => {
314 | suiteSetup((done) => {
315 | const app = express();
316 |
317 | app.use(prpl.makeHandler(path.join('src', 'test', 'static'), {
318 | forwardErrors: true,
319 | builds: [
320 | {
321 | name: 'es2015',
322 | browserCapabilities: ['es2015'],
323 | },
324 | ]
325 | }));
326 |
327 | app.use(
328 | (error: httpErrors.HttpError,
329 | _request: any,
330 | response: any,
331 | _next: express.NextFunction) => {
332 | response.statusCode = error.status;
333 | response.setHeader('Content-Type', 'text/plain');
334 | response.end(`custom ${error.status}: ${error.message}`);
335 | });
336 |
337 | server = app.listen(/* random */ 0, '127.0.0.1', () => {
338 | const addr = server.address() as AddressInfo;
339 | host = addr.address;
340 | port = addr.port;
341 | done();
342 | });
343 | });
344 |
345 | suiteTeardown((done) => {
346 | server.close(done);
347 | });
348 |
349 | test('forwards error for 404 not found', async () => {
350 | checkPlainTextError(
351 | 404, 'custom 404: Not Found', await get('/fragment/error.html'));
352 | });
353 |
354 | test('forwards error for directory traversal 403', async () => {
355 | checkPlainTextError(
356 | 403, 'custom 403: Forbidden', await get('/../secrets'));
357 | });
358 |
359 | test('forwards error for unsupported browser 500', async () => {
360 | checkPlainTextError(
361 | 500, 'custom 500: This browser is not supported.', await get('/'));
362 | });
363 | });
364 | });
365 |
--------------------------------------------------------------------------------
/src/test/push_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 | * This code may only be used under the BSD style license found at
5 | * http://polymer.github.io/LICENSE.txt
6 | * The complete set of authors may be found at
7 | * http://polymer.github.io/AUTHORS.txt
8 | * The complete set of contributors may be found at
9 | * http://polymer.github.io/CONTRIBUTORS.txt
10 | * Code distributed by Google as part of the polymer project is also
11 | * subject to an additional IP rights grant found at
12 | * http://polymer.github.io/PATENTS.txt
13 | */
14 |
15 | import {assert} from 'chai';
16 | import * as push from '../push';
17 |
18 | suite('PushManifest', function() {
19 | test('validates types', () => {
20 | assert.doesNotThrow(() => {
21 | new push.PushManifest({'/a.html': {'/b.html': {type: 'document'}}});
22 | new push.PushManifest({'/a.html': {'/api': {type: 'fetch'}}});
23 | new push.PushManifest({'/a.js': {'/b.js': {type: 'script'}}});
24 | });
25 | assert.throws(() => {
26 | new push.PushManifest({'/a.html': {'/b.html': {type: 'INVALID'}}});
27 | });
28 | });
29 |
30 | test('validates resources', () => {
31 | const valid = (t: string) => assert.doesNotThrow(
32 | () => new push.PushManifest({'/a.html': {[t]: {type: 'document'}}}));
33 | const invalid = (t: string) => assert.throws(
34 | () => new push.PushManifest({'/a.html': {[t]: {type: 'document'}}}));
35 |
36 | valid('b.html');
37 | valid('/b.html');
38 | invalid('');
39 | });
40 |
41 | test('normalizes leading slashes', () => {
42 | const manifest = new push.PushManifest({
43 | 'a.html': {
44 | 'b.html': {type: 'document'},
45 | },
46 | });
47 | const expect = [
48 | '; rel=preload; as=document',
49 | ];
50 | assert.deepEqual(manifest.linkHeaders('/a.html'), expect);
51 | assert.deepEqual(manifest.linkHeaders('a.html'), expect);
52 | });
53 |
54 | test('respects base path', () => {
55 | const manifest = new push.PushManifest(
56 | {
57 | '/abs.html': {
58 | 'rel.html': {type: 'document'},
59 | '/abs.html': {type: 'document'},
60 | },
61 | 'rel.html': {
62 | 'rel.html': {type: 'document'},
63 | '/abs.html': {type: 'document'},
64 | },
65 | },
66 | 'subdir');
67 |
68 | assert.deepEqual(manifest.linkHeaders('/subdir/abs.html'), []);
69 | assert.deepEqual(manifest.linkHeaders('/abs.html'), [
70 | '; rel=preload; as=document',
71 | '; rel=preload; as=document',
72 | ]);
73 |
74 | assert.deepEqual(manifest.linkHeaders('/rel.html'), []);
75 | assert.deepEqual(manifest.linkHeaders('/subdir/rel.html'), [
76 | '; rel=preload; as=document',
77 | '; rel=preload; as=document',
78 | ]);
79 | });
80 |
81 | test('supports patterns', () => {
82 | const manifest = new push.PushManifest({
83 | '/foo.*': {
84 | '/dep.html': {type: 'document'},
85 | },
86 | });
87 | const expect = [
88 | '; rel=preload; as=document',
89 | ];
90 | assert.deepEqual(manifest.linkHeaders('/foo'), expect);
91 | assert.deepEqual(manifest.linkHeaders('/foo/'), expect);
92 | assert.deepEqual(manifest.linkHeaders('/foo/bar'), expect);
93 | });
94 |
95 | test('patterns are forced exact', () => {
96 | const manifest = new push.PushManifest({
97 | '/foo.html': {
98 | '/dep.html': {type: 'document'},
99 | },
100 | });
101 | assert.deepEqual(manifest.linkHeaders('/qux/foo.html'), []);
102 | assert.deepEqual(manifest.linkHeaders('/foo.html.x'), []);
103 | });
104 |
105 | test('explicit exact patterns work', () => {
106 | const manifest = new push.PushManifest(
107 | {
108 | '^/foo$': {
109 | '/dep.html': {type: 'document'},
110 | },
111 | },
112 | 'subdir');
113 | const expect = [
114 | '; rel=preload; as=document',
115 | ];
116 | assert.deepEqual(manifest.linkHeaders('/foo'), expect);
117 | assert.deepEqual(manifest.linkHeaders('/foo/bar'), []);
118 | assert.deepEqual(manifest.linkHeaders('/qux/foo/bar'), []);
119 | });
120 |
121 | test('relative patterns work', () => {
122 | const manifest = new push.PushManifest(
123 | {
124 | 'foo.*': {
125 | '/dep.html': {type: 'document'},
126 | },
127 | },
128 | 'subdir');
129 | const expect = [
130 | '; rel=preload; as=document',
131 | ];
132 | assert.deepEqual(manifest.linkHeaders('/subdir/foo/bar'), expect);
133 | });
134 |
135 | test('nopush attribute setting works', () => {
136 | const manifest = new push.PushManifest({
137 | '/foo': {
138 | '/dep.html': {type: 'document'},
139 | },
140 | });
141 | assert.deepEqual(manifest.linkHeaders('/foo', true), [
142 | '; rel=preload; as=document; nopush',
143 | ]);
144 | });
145 |
146 | test('crossorigin setting works', () => {
147 | const manifest = new push.PushManifest({
148 | '/foo': {
149 | '/a.html': {type: 'document'},
150 | '/b.html': {type: 'document', crossorigin: ''},
151 | '/c.html': {type: 'document', crossorigin: 'anonymous'},
152 | '/d.html': {type: 'document', crossorigin: 'use-credentials'},
153 | },
154 | });
155 | assert.deepEqual(manifest.linkHeaders('/foo'), [
156 | '; rel=preload; as=document',
157 | '; rel=preload; as=document',
158 | '; rel=preload; as=document; crossorigin=anonymous',
159 | '; rel=preload; as=document; crossorigin=use-credentials',
160 | ]);
161 | });
162 | });
163 |
--------------------------------------------------------------------------------
/src/test/static/es2015/fragment.html:
--------------------------------------------------------------------------------
1 | es2015 fragment
2 |
--------------------------------------------------------------------------------
/src/test/static/es2015/index.html:
--------------------------------------------------------------------------------
1 | es2015 entrypoint
2 |
--------------------------------------------------------------------------------
/src/test/static/es2015/push-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "index.html": {
3 | "fragment.html": {
4 | "type": "document"
5 | },
6 | "/es2015/serviceworker.js": {
7 | "type": "script"
8 | }
9 | },
10 | "/foo/bar": {
11 | "foo.html": {
12 | "type": "document"
13 | }
14 | },
15 | "fragment.html": {
16 | "baz.html": {
17 | "type": "document"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/static/es2015/service-worker.js:
--------------------------------------------------------------------------------
1 | es2015 serviceworker
2 |
--------------------------------------------------------------------------------
/src/test/static/fallback/fragment.html:
--------------------------------------------------------------------------------
1 | fallback fragment
2 |
--------------------------------------------------------------------------------
/src/test/static/fallback/index.html:
--------------------------------------------------------------------------------
1 | fallback entrypoint
2 |
--------------------------------------------------------------------------------
/src/test/static/standalone/fragment.html:
--------------------------------------------------------------------------------
1 | standalone fragment
2 |
--------------------------------------------------------------------------------
/src/test/static/standalone/index.html:
--------------------------------------------------------------------------------
1 | standalone entrypoint
2 |
--------------------------------------------------------------------------------
/src/test/static/standalone/push-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/index.html": {
3 | "/fragment.html": {
4 | "type": "document"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "lib": [
5 | "es2016"
6 | ],
7 | "outDir": "lib",
8 | "module": "commonjs",
9 | "strict": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "preserveConstEnums": true,
13 | "sourceMap": true,
14 | "pretty": true,
15 | "declaration": true,
16 | "skipLibCheck": true
17 | },
18 | "include": [
19 | "src/**/*.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------