├── .gitignore ├── .npmignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo ├── index.html ├── logo-114.png ├── logo-120.png ├── logo-128.png ├── logo-144.png ├── logo-152.png ├── logo-16.png ├── logo-180.png ├── logo-192.png ├── logo-32.png ├── manifest.json ├── nocode.html ├── other.html └── sw.js ├── package.json ├── pwacompat.min.js ├── src ├── externs.js ├── pwacompat.js └── test.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | suite.* 3 | testrunner.* 4 | yarn.lock 5 | test/ 6 | src/suite.* 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | dist: trusty 3 | node_js: 4 | - "node" 5 | addons: 6 | chrome: stable 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 6 | (CLA), which you can do online. The CLA is necessary mainly because you own the 7 | copyright to your changes, even after your contribution becomes part of our 8 | codebase, so we need your permission to use and distribute your code. We also 9 | need to be sure of various other things—for instance that you'll tell us if you 10 | know that your code infringes on other people's patents. You don't have to sign 11 | the CLA until after you've submitted your code for review and a member has 12 | approved it, but you must do it before we can put your code into our codebase. 13 | Before you start working on a larger contribution, you should get in touch with 14 | us first through the issue tracker with your idea so that we can help out and 15 | possibly guide you. Coordinating up front makes it much easier to avoid 16 | frustration later on. 17 | 18 | ### Code reviews 19 | All submissions, including submissions by project members, require review. We 20 | use Github pull requests for this purpose. 21 | 22 | ### The small print 23 | Contributions made by corporations are covered by a different agreement than 24 | the one above, the Software Grant and Corporate Contributor License Agreement. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/GoogleChromeLabs/pwacompat.svg?branch=master)](https://travis-ci.org/GoogleChromeLabs/pwacompat) 2 | 3 | PWACompat is a library that brings the [Web App Manifest](https://developers.google.com/web/fundamentals/web-app-manifest/) to non-compliant browsers for better [Progressive Web Apps](https://en.wikipedia.org/wiki/Progressive_Web_Apps). 4 | This mostly means creating splash screens and icons for Mobile Safari, as well as supporting IE/Edge's Pinned Sites feature. 5 | 6 | So, if you've created a `manifest.webmanifest` but want to have wide support everywhere else—through legacy HTML tags for icons and theming—look no further. 7 | We recommend including it from a CDN to get the latest version, or [bundling it yourself](https://npmjs.com/package/pwacompat): 8 | 9 | ```html 10 | 11 | 12 | 13 | 14 | ``` 15 | 16 | And you're done^! 🎉📄 17 | 18 | For more on the Web App Manifest, read 📖 [how to add a Web App Manifest and mobile-proof your site](https://medium.com/dev-channel/how-to-add-a-web-app-manifest-and-mobile-proof-your-site-450e6e485638), watch 📹 [theming as part of The Standard](https://www.youtube.com/watch?v=5fEMTxpA6BA), or check out 📬 [the Web Fundamentals post on PWACompat](https://developers.google.com/web/updates/2018/07/pwacompat). 19 | 20 |

21 | PWACompat explainer
22 | PWACompat takes your regular manifest and enhances other browsers 23 |

24 | 25 | # ^Best Practice & Caveats 26 | 27 | While PWACompat can generate most icons, meta tags etc that your PWA might need, it's best practice to include at least one ``. 28 | This is standardized and older browsers, along with search engines, may use it from your page to display an icon. 29 | For example: 30 | 31 | ```html 32 | 33 | 34 | 35 | 36 | ``` 37 | 38 | You should also consider only loading PWACompat after your site is loaded, as adding your site to a homescreen is a pretty rare operation. 39 | This is the approach taken on [v8.dev](https://github.com/v8/v8.dev/pull/310/files) and [Emojityper](https://github.com/emojityper/emojityper/blob/master/src/loader.js#L8). 40 | 41 | ## iOS 42 | 43 | PWACompat looks for a viewport tag which includes `viewport-fit=cover`, such as ``. 44 | If this tag is detected, PWACompat will generate a meta tag that makes your PWA load in fullscreen mode—this is particularly useful for devices with a notch. 45 | 46 | You can customize the generated splash screen's font by using a CSS Variable. 47 | For example: 48 | 49 | ```html 50 | 55 | ``` 56 | 57 | This is set directly as a [canvas font](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font), so you must as a minimum include size _and_ family. 58 | The default value is "24px HelveticaNeue-CondensedBold". 59 | 60 | ⚠️ PWACompat won't wait for your fonts to load, so if you're using custom fonts, be sure to only load the library after they're ready. 61 | 62 | ### Old Versions 63 | 64 | Prior [to iOS 12.2](https://twitter.com/mhartington/status/1089293403089784832), Mobile Safari opens external sites in the regular browser, meaning that flows like Oauth won't complete correctly. 65 | This [isn't a problem with PWACompat](https://github.com/GoogleChromeLabs/pwacompat/issues/15), but is an issue with PWAs on iOS generally. 66 | 67 | ## Session Storage 68 | 69 | PWACompat uses `window.sessionStorage` to cache your site's manifest (and on iOS, any updated icons and generated splash screens). 70 | This expires after a user navigates away from your page or closes their tab. 71 | 72 | # Details 73 | 74 | What does PWACompat actually do? 75 | If you provide a Web App Manifest, PWACompat will update your page and: 76 | 77 | * Create meta icon tags for all icons in the manifest (e.g., for a favicon, older browsers) 78 | * Create fallback meta tags for various browsers (e.g., iOS, WebKit/Chromium forks etc) describing how a PWA should open 79 | * Sets [the theme color](https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android) based on the manifest 80 | 81 | For Safari, PWACompat also: 82 | 83 | * Sets `apple-mobile-web-app-capable` (opening without a browser chrome) for display modes `standalone`, `fullscreen` or `minimal-ui` 84 | * Creates `apple-touch-icon` images, adding the manifest background to transparent icons: otherwise, iOS renders transparency as black 85 | * Creates dynamic splash images, closely matching the splash images generated [for Chromium-based browsers](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java?type=cs&q=webappsplash&sq=package:chromium&g=0&l=70) 86 | 87 | For IE and Edge: 88 | 89 | * Adds meta tags for the [Pinned Sites](https://blogs.msdn.microsoft.com/jennifer/2011/04/20/ie-pinned-sites-part-1-what-are-pinned-sites/) feature 90 | 91 | For PWAs on Windows with access to UWP APIs: 92 | 93 | * Sets the titlebar color 94 | 95 | Do you think PWACompat should support backfilling more HTML tags needed for older browsers? 96 | [Let us know!](https://github.com/GoogleChromeLabs/pwacompat/issues) 97 | 98 | ## Demo 99 | 100 | For a demo, try adding [Emojityper](https://emojityper.com/) or [the demo site](https://googlechromelabs.github.io/pwacompat/demo/) to an iOS home screen (to see splash screens and icons). 101 | You can also install Emojityper from the [Microsoft Store](https://www.microsoft.com/p/emojityper/9np2xx3sxmct) (where the titlebar color is automatically set the manifest's `theme_color`). 102 | 103 | ## Support 104 | 105 | This is supported in most modern browsers (UC Browser, Safari, Firefox, Chrome, IE10+), and fails silenty when unsupported. 106 | Mobile Safari benefits the most from PWACompat, as generating [a large number of splash screens](https://google.com/search?q=ios%20webapp%20splash%20screens) manually is a complex task. 107 | 108 | # Web App Manifest 109 | 110 | Your Web App Manifest is: 111 | 112 | * normally named `manifest.webmanifest` (although some folks name it `manifest.json`) 113 | * referenced from all pages on your site like `` 114 | * and should look a bit like this: 115 | 116 | ```js 117 | { 118 | "name": "Always Be Progressive", 119 | "short_name": "Progressive!", 120 | "display": "browser", 121 | "start_url": "/", 122 | "background_color": "#102a48", 123 | "icons": [ 124 | { 125 | "src": "res/icon-256.png", 126 | "sizes": "256x256" 127 | }, 128 | { 129 | "src": "res/icon-128.png", 130 | "sizes": "128x128" 131 | } 132 | ] 133 | } 134 | ``` 135 | 136 | For more information on the Web App Manifest, and how e.g., modern browsers will prompt engaged users to install your site to their home screen, check out [Web Fundamentals](https://developers.google.com/web/fundamentals/web-app-manifest/). 137 | There's also a number of [online generators](https://www.google.com/search?q=web+app+manifest+generator). 138 | 139 | # Release 140 | 141 | Compile code with [Closure Compiler](https://closure-compiler.appspot.com/home). 142 | 143 | ``` 144 | // ==ClosureCompiler== 145 | // @compilation_level ADVANCED_OPTIMIZATIONS 146 | // @output_file_name pwacompat.min.js 147 | // ==/ClosureCompiler== 148 | 149 | // code here 150 | ``` 151 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 24 | PWA Compat 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |

34 | This is a test of PWA Compat. 35 | Try adding this site to your home screen on an iOS device to see correctly rendered icons and dynamic splash screens! 36 | It uses the uncompiled file, for testing. 37 |

38 |

39 | Links to local page, page without PWA Compat or an external site. 40 |

41 |

42 | Change Hash 1 43 | Change Hash 2 44 |

45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/logo-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-114.png -------------------------------------------------------------------------------- /demo/logo-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-120.png -------------------------------------------------------------------------------- /demo/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-128.png -------------------------------------------------------------------------------- /demo/logo-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-144.png -------------------------------------------------------------------------------- /demo/logo-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-152.png -------------------------------------------------------------------------------- /demo/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-16.png -------------------------------------------------------------------------------- /demo/logo-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-180.png -------------------------------------------------------------------------------- /demo/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-192.png -------------------------------------------------------------------------------- /demo/logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/pwacompat/b24effcc909b6a8d8b50e0f3b1e8157425dfb30c/demo/logo-32.png -------------------------------------------------------------------------------- /demo/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "long long `PWA Compat` long long", 3 | "short_name": "short `PWA Compat` short", 4 | "icons": [ 5 | { 6 | "src": "logo-192.png", 7 | "sizes": "192x192" 8 | }, 9 | { 10 | "src": "logo-180.png", 11 | "sizes": "180x180" 12 | }, 13 | { 14 | "src": "logo-152.png", 15 | "sizes": "152x152" 16 | }, 17 | { 18 | "src": "logo-144.png", 19 | "sizes": "144x144" 20 | }, 21 | { 22 | "src": "logo-128.png", 23 | "sizes": "128x128" 24 | }, 25 | { 26 | "src": "logo-128.png", 27 | "sizes": "128x128" 28 | }, 29 | { 30 | "src": "logo-120.png", 31 | "sizes": "120x120" 32 | }, 33 | { 34 | "src": "logo-114.png", 35 | "sizes": "114x114" 36 | }, 37 | { 38 | "src": "logo-32.png", 39 | "sizes": "32x32" 40 | }, 41 | { 42 | "src": "logo-16.png", 43 | "sizes": "16x16" 44 | } 45 | ], 46 | "display": "standalone", 47 | "scope": "/", 48 | "start_url": "./?homescreen=true", 49 | "orientation": "portrait", 50 | "theme_color": "#1abc9c", 51 | "background_color": "#2c3e50", 52 | "related_applications": [ 53 | { 54 | "platform": "play", 55 | "url": "https://play.google.com/store/apps/details?id=com.example.app1", 56 | "id": "com.example.app1" 57 | }, 58 | { 59 | "platform": "itunes", 60 | "url": "https://itunes.apple.com/app/example-app1/id123456789" 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /demo/nocode.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | PWA Compat 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |

28 | This is a test of PWA Compat. 29 | This does NOT include the PWA Compat code, however. 30 |

31 |

32 | Links to home page. 33 |

34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/other.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | Other — PWA Compat 20 | 21 | 22 | 23 | 24 | 29 | 48 | 49 | 50 | 51 | 52 |

53 | Different page for the PWA Compat Test. 54 | This page registers a zero Service Worker. 55 |

56 |

57 | Link back to the starter page. Reload 58 |

59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /demo/sw.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('fetch', () => { 2 | // literally does nothing 3 | }); 4 | 5 | console.info('SW for test running'); 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwacompat", 3 | "version": "2.0.17", 4 | "description": "library to bring Web App Manifest contents to non-compliant browsers", 5 | "main": "pwacompat.min.js", 6 | "module": "src/pwacompat.js", 7 | "jsnext:main": "src/pwacompat.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/GoogleChrome/pwacompat.git" 11 | }, 12 | "keywords": [ 13 | "pwa", 14 | "compatibility", 15 | "ios", 16 | "homescreen", 17 | "manifest" 18 | ], 19 | "author": "The Chromium Authors", 20 | "license": "Apache-2.0", 21 | "bugs": { 22 | "url": "https://github.com/GoogleChrome/pwacompat/issues" 23 | }, 24 | "homepage": "https://github.com/GoogleChrome/pwacompat", 25 | "scripts": { 26 | "test": "headless-test src/test.js" 27 | }, 28 | "devDependencies": { 29 | "chai": "^4.1.2", 30 | "headless-test": "^1.0.2", 31 | "mocha": "^5.2.0" 32 | }, 33 | "resolutions": { 34 | "**/minimist": "^1.2.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pwacompat.min.js: -------------------------------------------------------------------------------- 1 | (function(){function B(a){try{return document.head.querySelector(a)}catch(b){return null}}function v(a,b){a="__pwacompat_"+a;void 0!==b&&(w[a]=b);return w[a]}function F(){var a=(x=B('link[rel="manifest"]'))?x.href:"";if(!a)throw'can\'t find \'';var b=Q([a,location]),d=v("manifest");if(d)try{var g=JSON.parse(d);G(g,b)}catch(r){console.warn("PWACompat error",r)}else{var n=new XMLHttpRequest;n.open("GET",a);n.withCredentials="use-credentials"===x.getAttribute("crossorigin"); 2 | n.onload=function(){try{var r=JSON.parse(n.responseText);v("manifest",n.responseText);G(r,b)}catch(t){console.warn("PWACompat error",t)}};n.send(null)}}function Q(a){for(var b={},d=0;d.6*f)l.fillText(H,I/-2,0),l.translate(0,1.2*m),e.splice(0, 5 | c),c=0}return function(){var J=l.canvas.toDataURL();g(h,J);return J}}function g(f,c){var h=document.createElement("link");h.setAttribute("rel","apple-touch-startup-image");h.setAttribute("media","(orientation: "+f+")");h.setAttribute("href",c);document.head.appendChild(h)}function n(f,c){var h=window.screen,m=d(h.width,h.height,"portrait",f),e=d(h.height,h.width,"landscape",f);setTimeout(function(){u.p=m();setTimeout(function(){u.l=e();c()},10)},10)}function r(f){function c(){--h||f()}var h=z.length+ 6 | 1;c();z.forEach(function(m){var e=new Image;e.crossOrigin="anonymous";e.onerror=c;e.onload=function(){e.onload=null;m.href=K(e,y,!0);u.i[e.src]=m.href;c()};e.src=m.href})}function t(){v("iOS",JSON.stringify(u))}function L(){var f=z.shift();if(f){var c=new Image;c.crossOrigin="anonymous";c.onerror=function(){return void L()};c.onload=function(){c.onload=null;n(c,function(){var h=a.background_color&&K(c,y);h?(f.href=h,u.i[c.src]=h,r(t)):t()})};c.src=f.href}else n(null,t)}var p=(a.icons||[]).map(R).sort(function(f, 7 | c){return c.h-f.h}),q=p.filter(function(f){return-1f.h))return c.rel="apple-touch-icon",C("link",c,'[rel="apple-touch-icon"]'+h)}).filter(Boolean);p=B('meta[name="viewport"]');var T=!!(p&&p.content||"").match(/\bviewport-fit\s*=\s*cover\b/),M=a.display;p=-1!==U.indexOf(M); 8 | k("mobile-web-app-capable",p);V(a.theme_color||"black",T);W&&(k("application-name",a.short_name),k("msapplication-tooltip",a.description),k("msapplication-starturl",b(a.start_url||".")),k("msapplication-navbutton-color",a.theme_color),(q=q[0])&&k("msapplication-TileImage",b(q.src)),k("msapplication-TileColor",a.background_color));k("theme-color",a.theme_color);if(A){var y=a.background_color||"#f8f9fa",S=N(y);(q=X(a.related_applications))&&k("apple-itunes-app","app-id="+q);k("apple-mobile-web-app-capable", 9 | p);k("apple-mobile-web-app-title",a.short_name||a.name);if(q=v("iOS"))try{var E=JSON.parse(q);g("portrait",E.p);g("landscape",E.l);z.forEach(function(f){var c=E.i[f.href];c&&(f.href=c)});return}catch(f){}var u={i:{}};L()}else q={por:"portrait",lan:"landscape"}[String(a.orientation||"").substr(0,3)]||"",k("x5-orientation",q),k("screen-orientation",q),"fullscreen"===M?(k("x5-fullscreen","true"),k("full-screen","yes")):p&&(k("x5-page-mode","app"),k("browsermode","application"))}function X(a){var b;(a|| 10 | []).filter(function(d){return"itunes"===d.platform}).forEach(function(d){d.id?b=d.id:(d=d.url.match(/id(\d+)/))&&(b=d[1])});return b}function V(a,b){if(A||Y){var d=N(a);if(A)k("apple-mobile-web-app-status-bar-style",b?"black-translucent":d?"black":"default");else{a:{try{var g=Windows.UI.ViewManagement.ApplicationView.getForCurrentView().titleBar;break a}catch(n){}g=void 0}if(b=g)d=d?255:0,b.foregroundColor={r:d,g:d,b:d,a:255},a=O(a),b.backgroundColor={r:a[0],g:a[1],b:a[2],a:a[3]}}}}function O(a){var b= 11 | D();b.fillStyle=a;b.fillRect(0,0,1,1);return b.getImageData(0,0,1,1).data||[]}function N(a){a=O(a).map(function(b){b/=255;return.03928>b?b/12.92:Math.pow((b+.055)/1.055,2.4)});return 3'`; 94 | } 95 | 96 | const hrefFactory = buildHrefFactory([manifestHref, location]); 97 | const storedResponse = store('manifest'); 98 | if (storedResponse) { 99 | try { 100 | const data = /** @type {!Object} */ (JSON.parse(storedResponse)); 101 | process(data, hrefFactory); 102 | } catch (err) { 103 | console.warn('PWACompat error', err); 104 | } 105 | return; 106 | } 107 | 108 | const xhr = new XMLHttpRequest(); 109 | xhr.open('GET', manifestHref); 110 | 111 | // nb. use getAttribute for older brower safety 112 | xhr.withCredentials = (manifestEl.getAttribute('crossorigin') === 'use-credentials'); 113 | 114 | // this is IE10+ 115 | xhr.onload = () => { 116 | try { 117 | const data = /** @type {!Object} */ (JSON.parse(xhr.responseText)); 118 | store('manifest', xhr.responseText); 119 | process(data, hrefFactory); 120 | } catch (err) { 121 | console.warn('PWACompat error', err); 122 | } 123 | }; 124 | xhr.send(null); 125 | } 126 | 127 | /** 128 | * @param {!Array} options 129 | * @return {function(string): string} 130 | */ 131 | function buildHrefFactory(options) { 132 | for (let i = 0; i < options.length; ++i) { 133 | const opt = options[i]; 134 | try { 135 | new URL('', opt); 136 | return (part) => (new URL(part || '', opt)).toString(); 137 | } catch (e) {} 138 | } 139 | return (part) => part || ''; 140 | } 141 | 142 | /** 143 | * Adds an element in the if it's not present already based on the passed check. 144 | * @param {string} localName tag name 145 | * @param {!Object} attr key-value collection of attributes 146 | * @param {string} check to apply to the tag 147 | */ 148 | function push(localName, attr, check) { 149 | if (getElementInHead(localName + check)) { 150 | return; 151 | } 152 | const node = document.createElement(localName); 153 | for (const k in attr) { 154 | node.setAttribute(k, attr[k]); 155 | } 156 | document.head.appendChild(node); 157 | return node; 158 | } 159 | 160 | function meta(name, content) { 161 | if (content) { 162 | if (content === true) { 163 | content = 'yes'; 164 | } 165 | push('meta', {name, content}, `[name="${name}"]`); 166 | } 167 | } 168 | 169 | /** 170 | * Pre-process the icon sizes and purpose into a tuple and array. 171 | */ 172 | function normalizeIcon(icon) { 173 | const parsedSizes = icon.sizes.split(/\s+/g).map((size) => { 174 | if (size === 'any') { 175 | return Infinity; 176 | } 177 | return parseInt(size, 10) || 0; // NaN is falsey 178 | }); 179 | 180 | return { 181 | src: icon.src, 182 | type: icon.type, 183 | sizes: icon.sizes, 184 | // Get the largest size from a processed icon. 185 | largestSize: Math.max.apply(null, parsedSizes), 186 | purpose: icon.purpose ? icon.purpose.split(/\s+/g) : ['any'], 187 | }; 188 | } 189 | 190 | /** 191 | * @param {!Object} manifest 192 | * @param {function(string): string} urlFactory 193 | */ 194 | function process(manifest, urlFactory) { 195 | // largest first 196 | const allIcons = (manifest['icons'] || []) 197 | .map(normalizeIcon) 198 | .sort((a, b) => b.largestSize - a.largestSize); 199 | 200 | const icons = allIcons.filter((icon) => icon.purpose.indexOf('any') > -1) 201 | const maskable = allIcons.filter((icon) => icon.purpose.indexOf('maskable') > -1); 202 | 203 | const appleTouchIcons = (maskable.length > 0 ? maskable : icons).map((icon) => { 204 | // create regular link icons as byproduct 205 | const attr = {'rel': 'icon', 'href': urlFactory(icon['src']), 'sizes': icon['sizes']}; 206 | // This checks for matching "rel" and "sizes". We don't check for the same image file, as 207 | // it is used literally by ourselves (and could be set by users for another icon). 208 | const querySuffix = `[sizes="${icon['sizes']}"]`; 209 | push('link', attr, '[rel="icon"]' + querySuffix); 210 | if (!isSafariMobile) { 211 | return; 212 | } 213 | if (icon.largestSize < appleIconSizeMin) { 214 | return; 215 | } 216 | attr['rel'] = 'apple-touch-icon'; 217 | 218 | // nb. we used to call `removeAttribute('sizes')` here, which crashed iOS 8 219 | // ... sizes has been supported since iOS 4.2 (!) 220 | return push('link', attr, '[rel="apple-touch-icon"]' + querySuffix); 221 | }).filter(Boolean); 222 | 223 | // nb. only for iOS, but watch for future CSS rule `@viewport { viewport-fit: cover; }` 224 | const metaViewport = getElementInHead('meta[name="viewport"]'); 225 | const viewport = metaViewport && metaViewport.content || ''; 226 | const viewportFitCover = Boolean(viewport.match(/\bviewport-fit\s*=\s*cover\b/)); 227 | 228 | const display = manifest['display']; 229 | const isCapable = capableDisplayModes.indexOf(display) !== -1; 230 | meta('mobile-web-app-capable', isCapable); 231 | updateThemeColorRender(/** @type {string} */ (manifest['theme_color']) || 'black', viewportFitCover); 232 | 233 | if (isIEOrEdge) { 234 | // Pinned Sites, largely from https://technet.microsoft.com/en-us/windows/dn255024(v=vs.60) 235 | meta('application-name', manifest['short_name']); 236 | meta('msapplication-tooltip', manifest['description']); 237 | meta('msapplication-starturl', urlFactory(/** @type {string} */ (manifest['start_url']) || '.')); 238 | meta('msapplication-navbutton-color', manifest['theme_color']); 239 | 240 | const largest = icons[0]; 241 | if (largest) { 242 | meta('msapplication-TileImage', urlFactory(largest['src'])); 243 | } 244 | meta('msapplication-TileColor', manifest['background_color']); 245 | } 246 | 247 | meta('theme-color', manifest['theme_color']); 248 | 249 | if (!isSafariMobile) { 250 | // TODO(samthor): We don't detect QQ or UC, we just set the vars anyway. 251 | const orientation = simpleOrientationFor(manifest['orientation']); 252 | meta('x5-orientation', orientation); // QQ 253 | meta('screen-orientation', orientation); // UC 254 | if (display === 'fullscreen') { 255 | meta('x5-fullscreen', 'true'); // QQ 256 | meta('full-screen', 'yes'); // UC 257 | } else if (isCapable) { 258 | meta('x5-page-mode', 'app'); // QQ 259 | meta('browsermode', 'application'); // UC 260 | } 261 | 262 | return; // the rest of this file is for Mobile Safari 263 | } 264 | 265 | const backgroundColor = 266 | /** @type {string} */ (manifest['background_color']) || defaultSplashColor; 267 | const backgroundIsLight = shouldUseLightForeground(backgroundColor); 268 | 269 | // Add related iTunes app from manifest. 270 | const itunes = findAppleId(manifest['related_applications']); 271 | itunes && meta('apple-itunes-app', `app-id=${itunes}`); 272 | 273 | // General iOS meta tags. 274 | meta('apple-mobile-web-app-capable', isCapable); 275 | meta('apple-mobile-web-app-title', manifest['short_name'] || manifest['name']); 276 | 277 | /** 278 | * @param {number} width 279 | * @param {number} height 280 | * @param {string} orientation 281 | * @param {?Image} icon 282 | * @return {function(): string} 283 | */ 284 | function splashFor(width, height, orientation, icon) { 285 | const ratio = window.devicePixelRatio; 286 | const ctx = contextForCanvas({width: width * ratio, height: height * ratio}); 287 | 288 | ctx.scale(ratio, ratio); 289 | ctx.fillStyle = backgroundColor; 290 | ctx.fillRect(0, 0, width, height); 291 | ctx.translate(width / 2, (height - splashIconPadding) / 2); 292 | 293 | if (icon) { 294 | // nb: on Chrome, we need the image >=48px, use the big layout >=80dp, ideal is >=128dp 295 | let iconWidth = (icon.width / ratio); 296 | let iconHeight = (icon.height / ratio); 297 | if (iconHeight > idealSplashIconSize) { 298 | // clamp to 128px height max 299 | iconWidth /= (iconHeight / idealSplashIconSize); 300 | iconHeight = idealSplashIconSize; 301 | } 302 | 303 | if (iconWidth >= minimumSplashIconSize && iconHeight >= minimumSplashIconSize) { 304 | ctx.drawImage(icon, iconWidth / -2, iconHeight / -2, iconWidth, iconHeight); 305 | ctx.translate(0, iconHeight / 2 + splashIconPadding); 306 | } 307 | } 308 | 309 | ctx.fillStyle = backgroundIsLight ? 'white' : 'black'; 310 | ctx.font = `${defaultSplashTextSize}px ${defaultFontName}`; 311 | 312 | // Set the user-requested font; if it's invalid, the set will fail. 313 | const s = getComputedStyle(manifestEl); 314 | ctx.font = s.getPropertyValue('--pwacompat-splash-font'); // blank for old browsers 315 | 316 | const title = manifest['name'] || manifest['short_name'] || document.title; 317 | const measure = ctx.measureText(title); 318 | const textHeight = (measure.actualBoundingBoxAscent || defaultSplashTextSize); 319 | ctx.translate(0, textHeight); 320 | 321 | if (measure.width < width * 0.8) { 322 | // short-circuit, just draw entire string 323 | ctx.fillText(title, measure.width / -2, 0); 324 | } else { 325 | // longer wrap case, draw once we have >0.7 width accumulated 326 | const words = title.split(/\s+/g); 327 | for (let i = 1; i <= words.length; ++i) { 328 | const cand = words.slice(0, i).join(' '); 329 | const measureWidth = ctx.measureText(cand).width; 330 | if (i === words.length || measureWidth > width * 0.6) { 331 | // render accumulated words 332 | ctx.fillText(cand, measureWidth / -2, 0); 333 | ctx.translate(0, textHeight * 1.2); 334 | words.splice(0, i); 335 | i = 0; 336 | } 337 | } 338 | } 339 | 340 | return () => { 341 | const data = ctx.canvas.toDataURL(); 342 | if (debug) { 343 | const img = document.createElement('img'); 344 | img.src = data; 345 | document.body.append(img); 346 | } 347 | appendSplash(orientation, data); 348 | return data; 349 | }; 350 | } 351 | 352 | /** 353 | * @param {string} orientation 354 | * @param {string} data 355 | */ 356 | function appendSplash(orientation, data) { 357 | const generatedSplash = /** @type {!HTMLLinkElement} */ (document.createElement('link')); 358 | generatedSplash.setAttribute('rel', 'apple-touch-startup-image'); 359 | generatedSplash.setAttribute('media', `(orientation: ${orientation})`); 360 | generatedSplash.setAttribute('href', data); 361 | document.head.appendChild(generatedSplash); 362 | } 363 | 364 | // fetch previous (session) iOS image updates 365 | const rendered = store('iOS'); 366 | if (!debug && rendered) { 367 | try { 368 | const prev = /** @type {!Object} */ (JSON.parse(rendered)); 369 | appendSplash('portrait', prev['p']); 370 | appendSplash('landscape', prev['l']); 371 | appleTouchIcons.forEach((icon) => { 372 | const change = prev['i'][icon.href]; 373 | if (change) { 374 | icon.href = change; 375 | } 376 | }); 377 | return; 378 | } catch (e) { 379 | // ignore, some problem with the JSON 380 | } 381 | } 382 | const update = {'i': {}}; 383 | 384 | /** 385 | * @param {?Image} applicationIcon 386 | * @param {function(): void} done 387 | */ 388 | function renderBothSplash(applicationIcon, done) { 389 | const s = window.screen; 390 | const portrait = splashFor(s.width, s.height, 'portrait', applicationIcon); 391 | const landscape = splashFor(s.height, s.width, 'landscape', applicationIcon); 392 | 393 | // this is particularly egregious setTimeout use, but the .toDataURL() is one of the 394 | // "bottlenecks" of PWACompat, so don't elongate any single frame more than needed. 395 | 396 | setTimeout(() => { 397 | update['p'] = portrait(); 398 | setTimeout(() => { 399 | update['l'] = landscape(); 400 | done(); 401 | }, 10); 402 | }, 10); 403 | } 404 | 405 | // fetches and redraws any remaining icons in appleTouchIcons (to have proper bg) 406 | function redrawRemainingIcons(done) { 407 | let left = appleTouchIcons.length + 1; 408 | const check = () => { 409 | if (!--left) { 410 | done(); 411 | } 412 | }; 413 | check(); 414 | appleTouchIcons.forEach((icon) => { 415 | const img = new Image(); 416 | img.crossOrigin = 'anonymous'; 417 | img.onerror = check; 418 | img.onload = () => { 419 | img.onload = null; 420 | icon.href = updateTransparent(img, backgroundColor, true); 421 | update['i'][img.src] = icon.href; 422 | check(); 423 | }; 424 | img.src = icon.href; 425 | }); 426 | } 427 | 428 | // write the update to sessionStorage 429 | function saveUpdate() { 430 | store('iOS', JSON.stringify(update)); 431 | } 432 | 433 | // called repeatedly until a valid icon is found 434 | function fetchIconAndBuildSplash() { 435 | const icon = appleTouchIcons.shift(); 436 | if (!icon) { 437 | renderBothSplash(null, saveUpdate); // ran out of icons, render without one 438 | return; 439 | } 440 | 441 | const img = new Image(); 442 | img.crossOrigin = 'anonymous'; 443 | img.onerror = () => void fetchIconAndBuildSplash(); // try again 444 | img.onload = () => { 445 | img.onload = null; // iOS Safari might call this many times 446 | renderBothSplash(img, () => { 447 | // ... if the icon used for splash changed, redraw others too 448 | const redrawn = manifest['background_color'] && updateTransparent(img, backgroundColor); 449 | if (redrawn) { 450 | icon.href = redrawn; 451 | update['i'][img.src] = redrawn; 452 | redrawRemainingIcons(saveUpdate); 453 | } else { 454 | saveUpdate(); 455 | } 456 | }); 457 | }; 458 | 459 | img.src = icon.href; // trigger load 460 | } 461 | fetchIconAndBuildSplash(); 462 | } 463 | 464 | function findAppleId(related) { 465 | let itunes; 466 | (related || []) 467 | .filter((app) => app['platform'] === 'itunes') 468 | .forEach((app) => { 469 | if (app['id']) { 470 | itunes = app['id']; 471 | } else { 472 | const match = app['url'].match(/id(\d+)/); 473 | if (match) { 474 | itunes = match[1]; 475 | } 476 | } 477 | }); 478 | return itunes; 479 | } 480 | 481 | function simpleOrientationFor(v) { 482 | const prefix = String(v || '').substr(0, 3); 483 | return {'por': 'portrait', 'lan': 'landscape'}[prefix] || ''; 484 | } 485 | 486 | /** 487 | * @param {string} color 488 | * @param {boolean} viewportFitCover 489 | */ 490 | function updateThemeColorRender(color, viewportFitCover) { 491 | if (!(isSafariMobile || isEdgePWA)) { 492 | return; 493 | } 494 | 495 | const themeIsLight = shouldUseLightForeground(color); 496 | if (isSafariMobile) { 497 | // nb. Safari 11.3+ gives a deprecation warning about this meta tag. 498 | const content = viewportFitCover ? 'black-translucent' : (themeIsLight ? 'black' : 'default'); 499 | meta('apple-mobile-web-app-status-bar-style', content); 500 | } else { 501 | // Edge PWA 502 | const t = getEdgeTitleBar(); 503 | if (!t) { 504 | return; // something went wrong, we had a UWP without titleBar 505 | } 506 | // Foreground is black if theme is light, otherwise white. 507 | const v = themeIsLight ? 255 : 0; 508 | t.foregroundColor = /** @type {WindowsColor} */ ({'r': v, 'g': v, 'b': v, 'a': 255}); 509 | t.backgroundColor = colorToWindowsRGBA(color); 510 | } 511 | } 512 | 513 | /** 514 | * @return {!ApplicationViewTitleBar|undefined} 515 | */ 516 | function getEdgeTitleBar() { 517 | try { 518 | return Windows.UI.ViewManagement.ApplicationView.getForCurrentView().titleBar; 519 | } catch (e) { 520 | // implicit return undefined 521 | } 522 | } 523 | 524 | /** 525 | * The Windows titlebar APIs expect an object of {r, g, b, a}. 526 | * 527 | * @param {string} color 528 | * @return {WindowsColor} 529 | */ 530 | function colorToWindowsRGBA(color) { 531 | const data = readColor(color); 532 | return /** @type {WindowsColor} */ ({ 533 | 'r': data[0], 534 | 'g': data[1], 535 | 'b': data[2], 536 | 'a': data[3], 537 | }); 538 | } 539 | 540 | /** 541 | * @param {string} color 542 | * @return {!Uint8ClampedArray} 543 | */ 544 | function readColor(color) { 545 | const c = contextForCanvas(); 546 | c.fillStyle = color; 547 | c.fillRect(0, 0, 1, 1); 548 | return c.getImageData(0, 0, 1, 1).data || []; // incase this fails for some reason 549 | } 550 | 551 | /** 552 | * @param {string} color 553 | * @return {boolean} 554 | */ 555 | function shouldUseLightForeground(color) { 556 | const pixelData = readColor(color); 557 | 558 | // From https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/util/ColorUtils.java 559 | const data = pixelData.map((v) => { 560 | const f = v / 255; 561 | return (f < 0.03928) ? f / 12.92 : Math.pow((f + 0.055) / 1.055, 2.4); 562 | }); 563 | const lum = 0.2126 * data[0] + 0.7152 * data[1] + 0.0722 * data[2]; 564 | const contrast = Math.abs((1.05) / (lum + 0.05)); 565 | return contrast > 3; 566 | } 567 | 568 | function updateTransparent(image, background, force=false) { 569 | const context = contextForCanvas(image); 570 | context.drawImage(image, 0, 0); 571 | 572 | // look for transparent pixel in top-left 573 | // TODO: Chrome actually checks the four corners for some cases. 574 | if (!force) { 575 | const imageData = context.getImageData(0, 0, 1, 1); 576 | if (imageData.data[3] === 255) { 577 | return; 578 | } 579 | } 580 | 581 | context.globalCompositeOperation = 'destination-over'; // only replace transparent areas 582 | context.fillStyle = background; 583 | context.fillRect(0, 0, image.width, image.height); 584 | return context.canvas.toDataURL(); 585 | } 586 | 587 | function contextForCanvas({width, height} = {width: 1, height: 1}) { 588 | const canvas = document.createElement('canvas'); 589 | canvas.width = width; 590 | canvas.height = height; 591 | return canvas.getContext('2d'); 592 | } 593 | 594 | // actually run PWACompat here 595 | if (document.readyState === 'complete') { 596 | setup(); 597 | } else { 598 | window.addEventListener('load', setup); 599 | } 600 | }()); 601 | -------------------------------------------------------------------------------- /src/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const assert = self.assert; 18 | 19 | const sources = new WeakMap(); 20 | window.addEventListener('message', (ev) => { 21 | const resolve = sources.get(ev.source); 22 | if (!resolve) { 23 | throw new TypeError('unexpected source'); 24 | } 25 | resolve(ev.data); 26 | }); 27 | 28 | /** 29 | * @param {!Object} options.manifest 30 | * @param {!Object} options.override 31 | * @param {string} options.head 32 | * @param {number} options.delay 33 | * @return {!Promise} 34 | */ 35 | function testManifest(options) { 36 | options = Object.assign({ 37 | manifest: {}, 38 | head: '', 39 | override: {}, 40 | delay: 25, 41 | }, options); 42 | 43 | const rigsource = ` 44 | 45 | 46 | 47 | 48 | ${options.head} 49 | 50 | 51 | 78 | 79 | 80 | 81 | `; 82 | 83 | const iframe = document.createElement('iframe'); 84 | iframe.hidden = true; 85 | iframe.src = `data:text/html;base64,${btoa(rigsource)}`; 86 | iframe.sandbox = 'allow-scripts'; 87 | document.body.appendChild(iframe); 88 | 89 | const p = new Promise((resolve, reject) => { 90 | sources.set(iframe.contentWindow, resolve); 91 | window.setTimeout(reject, 500); 92 | }); 93 | 94 | const cleanup = () => iframe.remove(); 95 | p.then(cleanup, cleanup); 96 | 97 | return p.then((all) => { 98 | // rehydrate DOM 99 | const frag = document.createDocumentFragment(); 100 | all.forEach(({name, attr}) => { 101 | const node = document.createElement(name); 102 | for (const n in attr) { 103 | node.setAttribute(n, attr[n]); 104 | } 105 | frag.appendChild(node); 106 | }); 107 | return frag; 108 | }); 109 | } 110 | 111 | suite('pwacompat', () => { 112 | test('theme_color', async () => { 113 | const manifest = { 114 | 'theme_color': 'red', 115 | }; 116 | let r = await testManifest({manifest}); 117 | assert.isNotNull(r.querySelector('meta[name="theme-color"][content="red"]')); 118 | 119 | r = await testManifest({manifest, head: ''}); 120 | assert.isNotNull(r.querySelector('meta[name="theme-color"][content="blue"]')); 121 | assert.isNull(r.querySelector('meta[name="theme-color"][content="red"]'), 122 | 'red should not be created'); 123 | }); 124 | 125 | test('icons', async () => { 126 | const manifest = { 127 | 'icons': [ 128 | {'src': 'logo-192.png', 'sizes': '192x192'}, 129 | {'src': 'logo-128.png', 'sizes': '128x128'}, 130 | ], 131 | }; 132 | const r = await testManifest({manifest}); 133 | assert.isNotNull(r.querySelector('link[rel="icon"][href="logo-128.png"][sizes="128x128"]')); 134 | }); 135 | 136 | test('should add meta `mobile-web-app-capable`', async () => { 137 | const manifest = { 138 | display: 'standalone', 139 | }; 140 | const r = await testManifest({manifest}); 141 | assert.isNotNull(r.querySelector('meta[name="mobile-web-app-capable"][content="yes"]')); 142 | }); 143 | 144 | test('should not add meta `mobile-web-app-capable` if it was present beforehand', async () => { 145 | const manifest = { 146 | display: 'standalone', // pwacompat should add 'meta[name="mobile-web-app-capable"][content="yes"]' 147 | }; 148 | const r = await testManifest({manifest, head: ''}); 149 | assert.isNotNull(r.querySelector('meta[name="mobile-web-app-capable"][content="existing"]')); 150 | assert.isNull(r.querySelector('meta[name="mobile-web-app-capable"][content="yes"]')); 151 | assert.lengthOf(r.querySelectorAll('meta[name="mobile-web-app-capable"]'), 1, 'found only one node'); 152 | }); 153 | 154 | test('should not add link icon if it was present beforehand', async () => { 155 | const manifest = { 156 | 'icons': [ 157 | { 158 | 'src': 'NEW-192.png', 159 | 'sizes': '192x192', 160 | }, 161 | ], 162 | }; 163 | const r = await testManifest({manifest, head: ''}); 164 | assert.isNotNull(r.querySelector('link[rel="icon"][href="EXISTING-192.png"][sizes="192x192"]')); 165 | assert.isNull(r.querySelector('link[rel="icon"][href="NEW-192.png"][sizes="192x192"]')); 166 | }); 167 | 168 | test('iOS splash', async () => { 169 | const manifest = { 170 | 'short_name': 'Test', 171 | 'icons': [ 172 | { 173 | 'src': window.location.origin + '/demo/logo-192.png', 174 | 'sizes': '192x192', 175 | }, 176 | ], 177 | 'background_color': 'red', 178 | 'display': 'standalone', 179 | }; 180 | 181 | const override = { 182 | vendor: 'Apple', 183 | userAgent: 'Mobile/', 184 | standalone: false, 185 | }; 186 | 187 | const r = await testManifest({manifest, override, delay: 100}); 188 | 189 | assert.isNotNull(r.querySelector('meta[name="apple-mobile-web-app-title"]'), 'should have title'); 190 | assert.isNotNull(r.querySelector('meta[name="apple-mobile-web-app-capable"]'), 'should be capable'); 191 | 192 | const images = r.querySelectorAll('link[rel="apple-touch-startup-image"]'); 193 | assert.lengthOf(images, 2); 194 | 195 | const [portrait, landscape] = images; 196 | assert.strictEqual(portrait.media, '(orientation: portrait)'); 197 | assert.strictEqual(landscape.media, '(orientation: landscape)'); 198 | 199 | assert.notEqual(portrait.href, landscape.href); 200 | 201 | const portraitImage = new Image(); 202 | portraitImage.src = portrait.href; 203 | 204 | const landscapeImage = new Image(); 205 | landscapeImage.src = landscape.href; 206 | 207 | await Promise.resolve(); // wait for images to be ready 208 | 209 | assert.notStrictEqual(portraitImage.naturalWidth, 0); 210 | assert.strictEqual(portraitImage.naturalWidth, landscapeImage.naturalHeight); 211 | assert.strictEqual(portraitImage.naturalHeight, landscapeImage.naturalWidth); 212 | 213 | assert.strictEqual(portraitImage.naturalWidth, window.screen.width * window.devicePixelRatio); 214 | assert.strictEqual(landscapeImage.naturalHeight, window.screen.width * window.devicePixelRatio); 215 | 216 | const canvas = document.createElement('canvas'); 217 | canvas.width = 1; 218 | canvas.height = 1; 219 | const context = canvas.getContext('2d'); 220 | 221 | context.drawImage(portraitImage, 0, 0); 222 | const pixel = context.getImageData(0, 0, 1, 1); 223 | assert.deepStrictEqual(pixel.data, new Uint8ClampedArray([255, 0, 0, 255]), 'background should be red'); 224 | }); 225 | 226 | test('skip for installed iOS', async () => { 227 | const manifest = { 228 | 'short_name': 'Test', 229 | 'icons': [ 230 | { 231 | 'src': window.location.origin + '/demo/logo-192.png', 232 | 'sizes': '192x192', 233 | }, 234 | ], 235 | 'display': 'standalone', 236 | }; 237 | 238 | const override = { 239 | vendor: 'Apple', 240 | userAgent: 'Mobile/', 241 | standalone: true, 242 | }; 243 | 244 | const r = await testManifest({manifest, override, delay: 100}); 245 | const all = r.querySelectorAll('*'); 246 | assert.lengthOf(all, 1, 'should only have manifest itself'); 247 | assert.strictEqual(all[0].rel, 'manifest'); 248 | 249 | // This basically confirms that nothing else is added and we bail out early. 250 | }); 251 | 252 | // TODO(samthor): Test Edge and non-iOS environments with overrides. 253 | }); 254 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/color-name@^1.1.1": 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" 8 | integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== 9 | 10 | "@types/mime-types@^2.1.0": 11 | version "2.1.0" 12 | resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73" 13 | integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM= 14 | 15 | agent-base@5: 16 | version "5.1.1" 17 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" 18 | integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== 19 | 20 | ansi-colors@3.2.3: 21 | version "3.2.3" 22 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" 23 | integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== 24 | 25 | ansi-regex@^3.0.0: 26 | version "3.0.0" 27 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 28 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 29 | 30 | ansi-regex@^4.1.0: 31 | version "4.1.0" 32 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 33 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 34 | 35 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 36 | version "3.2.1" 37 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 38 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 39 | dependencies: 40 | color-convert "^1.9.0" 41 | 42 | ansi-styles@^4.1.0: 43 | version "4.2.1" 44 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" 45 | integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== 46 | dependencies: 47 | "@types/color-name" "^1.1.1" 48 | color-convert "^2.0.1" 49 | 50 | anymatch@~3.1.1: 51 | version "3.1.1" 52 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" 53 | integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== 54 | dependencies: 55 | normalize-path "^3.0.0" 56 | picomatch "^2.0.4" 57 | 58 | arch@^2.1.0: 59 | version "2.1.1" 60 | resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" 61 | integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== 62 | 63 | argparse@^1.0.7: 64 | version "1.0.10" 65 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 66 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 67 | dependencies: 68 | sprintf-js "~1.0.2" 69 | 70 | assertion-error@^1.0.1, assertion-error@^1.1.0: 71 | version "1.1.0" 72 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 73 | 74 | async-limiter@~1.0.0: 75 | version "1.0.1" 76 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" 77 | integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== 78 | 79 | balanced-match@^1.0.0: 80 | version "1.0.0" 81 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 82 | 83 | binary-extensions@^2.0.0: 84 | version "2.0.0" 85 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" 86 | integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== 87 | 88 | brace-expansion@^1.1.7: 89 | version "1.1.11" 90 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 91 | dependencies: 92 | balanced-match "^1.0.0" 93 | concat-map "0.0.1" 94 | 95 | braces@~3.0.2: 96 | version "3.0.2" 97 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 98 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 99 | dependencies: 100 | fill-range "^7.0.1" 101 | 102 | browser-stdout@1.3.1: 103 | version "1.3.1" 104 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 105 | 106 | buffer-crc32@~0.2.3: 107 | version "0.2.13" 108 | resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" 109 | integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= 110 | 111 | buffer-from@^1.0.0: 112 | version "1.1.1" 113 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 114 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 115 | 116 | bytes@^3.1.0: 117 | version "3.1.0" 118 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 119 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 120 | 121 | camelcase@^5.0.0: 122 | version "5.3.1" 123 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 124 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 125 | 126 | chai@^4: 127 | version "4.2.0" 128 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" 129 | integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== 130 | dependencies: 131 | assertion-error "^1.1.0" 132 | check-error "^1.0.2" 133 | deep-eql "^3.0.1" 134 | get-func-name "^2.0.0" 135 | pathval "^1.1.0" 136 | type-detect "^4.0.5" 137 | 138 | chai@^4.1.2: 139 | version "4.1.2" 140 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" 141 | dependencies: 142 | assertion-error "^1.0.1" 143 | check-error "^1.0.1" 144 | deep-eql "^3.0.0" 145 | get-func-name "^2.0.0" 146 | pathval "^1.0.0" 147 | type-detect "^4.0.0" 148 | 149 | chalk@^2.4.2: 150 | version "2.4.2" 151 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 152 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 153 | dependencies: 154 | ansi-styles "^3.2.1" 155 | escape-string-regexp "^1.0.5" 156 | supports-color "^5.3.0" 157 | 158 | chalk@^3.0.0: 159 | version "3.0.0" 160 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" 161 | integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== 162 | dependencies: 163 | ansi-styles "^4.1.0" 164 | supports-color "^7.1.0" 165 | 166 | check-error@^1.0.1, check-error@^1.0.2: 167 | version "1.0.2" 168 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 169 | 170 | chokidar@3.3.0: 171 | version "3.3.0" 172 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" 173 | integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== 174 | dependencies: 175 | anymatch "~3.1.1" 176 | braces "~3.0.2" 177 | glob-parent "~5.1.0" 178 | is-binary-path "~2.1.0" 179 | is-glob "~4.0.1" 180 | normalize-path "~3.0.0" 181 | readdirp "~3.2.0" 182 | optionalDependencies: 183 | fsevents "~2.1.1" 184 | 185 | clipboardy@^1.2.3: 186 | version "1.2.3" 187 | resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" 188 | integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA== 189 | dependencies: 190 | arch "^2.1.0" 191 | execa "^0.8.0" 192 | 193 | cliui@^5.0.0: 194 | version "5.0.0" 195 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" 196 | integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== 197 | dependencies: 198 | string-width "^3.1.0" 199 | strip-ansi "^5.2.0" 200 | wrap-ansi "^5.1.0" 201 | 202 | color-convert@^1.9.0: 203 | version "1.9.3" 204 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 205 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 206 | dependencies: 207 | color-name "1.1.3" 208 | 209 | color-convert@^2.0.1: 210 | version "2.0.1" 211 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 212 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 213 | dependencies: 214 | color-name "~1.1.4" 215 | 216 | color-name@1.1.3: 217 | version "1.1.3" 218 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 219 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 220 | 221 | color-name@~1.1.4: 222 | version "1.1.4" 223 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 224 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 225 | 226 | commander@2.15.1: 227 | version "2.15.1" 228 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" 229 | 230 | concat-map@0.0.1: 231 | version "0.0.1" 232 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 233 | 234 | concat-stream@^1.6.2: 235 | version "1.6.2" 236 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 237 | integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== 238 | dependencies: 239 | buffer-from "^1.0.0" 240 | inherits "^2.0.3" 241 | readable-stream "^2.2.2" 242 | typedarray "^0.0.6" 243 | 244 | core-util-is@~1.0.0: 245 | version "1.0.2" 246 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 247 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 248 | 249 | cross-spawn@^5.0.1: 250 | version "5.1.0" 251 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 252 | integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= 253 | dependencies: 254 | lru-cache "^4.0.1" 255 | shebang-command "^1.2.0" 256 | which "^1.2.9" 257 | 258 | debug@3.1.0: 259 | version "3.1.0" 260 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 261 | dependencies: 262 | ms "2.0.0" 263 | 264 | debug@3.2.6: 265 | version "3.2.6" 266 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 267 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 268 | dependencies: 269 | ms "^2.1.1" 270 | 271 | debug@4, debug@^4.1.0: 272 | version "4.1.1" 273 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 274 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 275 | dependencies: 276 | ms "^2.1.1" 277 | 278 | debug@^2.6.9: 279 | version "2.6.9" 280 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 281 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 282 | dependencies: 283 | ms "2.0.0" 284 | 285 | decamelize@^1.2.0: 286 | version "1.2.0" 287 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 288 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 289 | 290 | deep-eql@^3.0.0, deep-eql@^3.0.1: 291 | version "3.0.1" 292 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 293 | dependencies: 294 | type-detect "^4.0.0" 295 | 296 | define-properties@^1.1.2, define-properties@^1.1.3: 297 | version "1.1.3" 298 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 299 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 300 | dependencies: 301 | object-keys "^1.0.12" 302 | 303 | dhost@^0.1.9: 304 | version "0.1.9" 305 | resolved "https://registry.yarnpkg.com/dhost/-/dhost-0.1.9.tgz#1421803a088a79b6a45ba63a5be51cf9d998cfa9" 306 | integrity sha512-/MYMqdeIst8n5h6/nUwGu1WqwU5zwQAIP9W9X2u2NjKqPIx0fBFdoCm+4BeQ5hSt8zdJMX2JJUqnHyM/+hXrHQ== 307 | dependencies: 308 | bytes "^3.1.0" 309 | chalk "^2.4.2" 310 | clipboardy "^1.2.3" 311 | he "^1.2.0" 312 | mime "^2.4.0" 313 | mri "^1.1.4" 314 | 315 | diff@3.5.0: 316 | version "3.5.0" 317 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 318 | 319 | emoji-regex@^7.0.1: 320 | version "7.0.3" 321 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 322 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 323 | 324 | es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: 325 | version "1.17.5" 326 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" 327 | integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== 328 | dependencies: 329 | es-to-primitive "^1.2.1" 330 | function-bind "^1.1.1" 331 | has "^1.0.3" 332 | has-symbols "^1.0.1" 333 | is-callable "^1.1.5" 334 | is-regex "^1.0.5" 335 | object-inspect "^1.7.0" 336 | object-keys "^1.1.1" 337 | object.assign "^4.1.0" 338 | string.prototype.trimleft "^2.1.1" 339 | string.prototype.trimright "^2.1.1" 340 | 341 | es-to-primitive@^1.2.1: 342 | version "1.2.1" 343 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 344 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 345 | dependencies: 346 | is-callable "^1.1.4" 347 | is-date-object "^1.0.1" 348 | is-symbol "^1.0.2" 349 | 350 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: 351 | version "1.0.5" 352 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 353 | 354 | esprima@^4.0.0: 355 | version "4.0.1" 356 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 357 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 358 | 359 | execa@^0.8.0: 360 | version "0.8.0" 361 | resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" 362 | integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= 363 | dependencies: 364 | cross-spawn "^5.0.1" 365 | get-stream "^3.0.0" 366 | is-stream "^1.1.0" 367 | npm-run-path "^2.0.0" 368 | p-finally "^1.0.0" 369 | signal-exit "^3.0.0" 370 | strip-eof "^1.0.0" 371 | 372 | extract-zip@^1.6.6: 373 | version "1.7.0" 374 | resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" 375 | integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== 376 | dependencies: 377 | concat-stream "^1.6.2" 378 | debug "^2.6.9" 379 | mkdirp "^0.5.4" 380 | yauzl "^2.10.0" 381 | 382 | fd-slicer@~1.1.0: 383 | version "1.1.0" 384 | resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" 385 | integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= 386 | dependencies: 387 | pend "~1.2.0" 388 | 389 | fill-range@^7.0.1: 390 | version "7.0.1" 391 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 392 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 393 | dependencies: 394 | to-regex-range "^5.0.1" 395 | 396 | find-up@3.0.0, find-up@^3.0.0: 397 | version "3.0.0" 398 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" 399 | integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== 400 | dependencies: 401 | locate-path "^3.0.0" 402 | 403 | flat@^4.1.0: 404 | version "4.1.0" 405 | resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" 406 | integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== 407 | dependencies: 408 | is-buffer "~2.0.3" 409 | 410 | fs.realpath@^1.0.0: 411 | version "1.0.0" 412 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 413 | 414 | fsevents@~2.1.1: 415 | version "2.1.3" 416 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" 417 | integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== 418 | 419 | function-bind@^1.1.1: 420 | version "1.1.1" 421 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 422 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 423 | 424 | get-caller-file@^2.0.1: 425 | version "2.0.5" 426 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 427 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 428 | 429 | get-func-name@^2.0.0: 430 | version "2.0.0" 431 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 432 | 433 | get-stream@^3.0.0: 434 | version "3.0.0" 435 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" 436 | integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= 437 | 438 | glob-parent@~5.1.0: 439 | version "5.1.1" 440 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" 441 | integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== 442 | dependencies: 443 | is-glob "^4.0.1" 444 | 445 | glob@7.1.2: 446 | version "7.1.2" 447 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 448 | dependencies: 449 | fs.realpath "^1.0.0" 450 | inflight "^1.0.4" 451 | inherits "2" 452 | minimatch "^3.0.4" 453 | once "^1.3.0" 454 | path-is-absolute "^1.0.0" 455 | 456 | glob@7.1.3: 457 | version "7.1.3" 458 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 459 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== 460 | dependencies: 461 | fs.realpath "^1.0.0" 462 | inflight "^1.0.4" 463 | inherits "2" 464 | minimatch "^3.0.4" 465 | once "^1.3.0" 466 | path-is-absolute "^1.0.0" 467 | 468 | glob@^7.1.3: 469 | version "7.1.6" 470 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 471 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 472 | dependencies: 473 | fs.realpath "^1.0.0" 474 | inflight "^1.0.4" 475 | inherits "2" 476 | minimatch "^3.0.4" 477 | once "^1.3.0" 478 | path-is-absolute "^1.0.0" 479 | 480 | growl@1.10.5: 481 | version "1.10.5" 482 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" 483 | 484 | has-flag@^3.0.0: 485 | version "3.0.0" 486 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 487 | 488 | has-flag@^4.0.0: 489 | version "4.0.0" 490 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 491 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 492 | 493 | has-symbols@^1.0.0, has-symbols@^1.0.1: 494 | version "1.0.1" 495 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" 496 | integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== 497 | 498 | has@^1.0.3: 499 | version "1.0.3" 500 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 501 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 502 | dependencies: 503 | function-bind "^1.1.1" 504 | 505 | he@1.1.1: 506 | version "1.1.1" 507 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 508 | 509 | he@1.2.0, he@^1.2.0: 510 | version "1.2.0" 511 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 512 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 513 | 514 | headless-test@^1.0.2: 515 | version "1.0.2" 516 | resolved "https://registry.yarnpkg.com/headless-test/-/headless-test-1.0.2.tgz#6237bf1d0300c026a8c088291364b75df34bf1ca" 517 | integrity sha512-4QbDxTYYnPXyzURvrHVFOWvUnVbDjYcxwbDzFAREGV4j3hFKsTzO1u/X04IOVIJue0lPdHSCTYiy7ze7kKNjPw== 518 | dependencies: 519 | chai "^4" 520 | chalk "^3.0.0" 521 | dhost "^0.1.9" 522 | mocha "^7" 523 | mri "^1" 524 | puppeteer "^2" 525 | 526 | https-proxy-agent@^4.0.0: 527 | version "4.0.0" 528 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" 529 | integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== 530 | dependencies: 531 | agent-base "5" 532 | debug "4" 533 | 534 | inflight@^1.0.4: 535 | version "1.0.6" 536 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 537 | dependencies: 538 | once "^1.3.0" 539 | wrappy "1" 540 | 541 | inherits@2: 542 | version "2.0.3" 543 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 544 | 545 | inherits@^2.0.3, inherits@~2.0.3: 546 | version "2.0.4" 547 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 548 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 549 | 550 | is-binary-path@~2.1.0: 551 | version "2.1.0" 552 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 553 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 554 | dependencies: 555 | binary-extensions "^2.0.0" 556 | 557 | is-buffer@~2.0.3: 558 | version "2.0.4" 559 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" 560 | integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== 561 | 562 | is-callable@^1.1.4, is-callable@^1.1.5: 563 | version "1.1.5" 564 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" 565 | integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== 566 | 567 | is-date-object@^1.0.1: 568 | version "1.0.2" 569 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" 570 | integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== 571 | 572 | is-extglob@^2.1.1: 573 | version "2.1.1" 574 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 575 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 576 | 577 | is-fullwidth-code-point@^2.0.0: 578 | version "2.0.0" 579 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 580 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 581 | 582 | is-glob@^4.0.1, is-glob@~4.0.1: 583 | version "4.0.1" 584 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 585 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 586 | dependencies: 587 | is-extglob "^2.1.1" 588 | 589 | is-number@^7.0.0: 590 | version "7.0.0" 591 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 592 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 593 | 594 | is-regex@^1.0.5: 595 | version "1.0.5" 596 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" 597 | integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== 598 | dependencies: 599 | has "^1.0.3" 600 | 601 | is-stream@^1.1.0: 602 | version "1.1.0" 603 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 604 | integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= 605 | 606 | is-symbol@^1.0.2: 607 | version "1.0.3" 608 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" 609 | integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== 610 | dependencies: 611 | has-symbols "^1.0.1" 612 | 613 | isarray@~1.0.0: 614 | version "1.0.0" 615 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 616 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 617 | 618 | isexe@^2.0.0: 619 | version "2.0.0" 620 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 621 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 622 | 623 | js-yaml@3.13.1: 624 | version "3.13.1" 625 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 626 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 627 | dependencies: 628 | argparse "^1.0.7" 629 | esprima "^4.0.0" 630 | 631 | locate-path@^3.0.0: 632 | version "3.0.0" 633 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" 634 | integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== 635 | dependencies: 636 | p-locate "^3.0.0" 637 | path-exists "^3.0.0" 638 | 639 | lodash@^4.17.15: 640 | version "4.17.19" 641 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" 642 | integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== 643 | 644 | log-symbols@3.0.0: 645 | version "3.0.0" 646 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" 647 | integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== 648 | dependencies: 649 | chalk "^2.4.2" 650 | 651 | lru-cache@^4.0.1: 652 | version "4.1.5" 653 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" 654 | integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== 655 | dependencies: 656 | pseudomap "^1.0.2" 657 | yallist "^2.1.2" 658 | 659 | mime-db@1.44.0: 660 | version "1.44.0" 661 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" 662 | integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== 663 | 664 | mime-types@^2.1.25: 665 | version "2.1.27" 666 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" 667 | integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== 668 | dependencies: 669 | mime-db "1.44.0" 670 | 671 | mime@^2.0.3, mime@^2.4.0: 672 | version "2.4.5" 673 | resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.5.tgz#d8de2ecb92982dedbb6541c9b6841d7f218ea009" 674 | integrity sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w== 675 | 676 | minimatch@3.0.4, minimatch@^3.0.4: 677 | version "3.0.4" 678 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 679 | dependencies: 680 | brace-expansion "^1.1.7" 681 | 682 | minimist@0.0.8, minimist@^1.2.5: 683 | version "1.2.5" 684 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 685 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 686 | 687 | mkdirp@0.5.1: 688 | version "0.5.1" 689 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 690 | dependencies: 691 | minimist "0.0.8" 692 | 693 | mkdirp@0.5.5, mkdirp@^0.5.4: 694 | version "0.5.5" 695 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 696 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 697 | dependencies: 698 | minimist "^1.2.5" 699 | 700 | mocha@^5.2.0: 701 | version "5.2.0" 702 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" 703 | dependencies: 704 | browser-stdout "1.3.1" 705 | commander "2.15.1" 706 | debug "3.1.0" 707 | diff "3.5.0" 708 | escape-string-regexp "1.0.5" 709 | glob "7.1.2" 710 | growl "1.10.5" 711 | he "1.1.1" 712 | minimatch "3.0.4" 713 | mkdirp "0.5.1" 714 | supports-color "5.4.0" 715 | 716 | mocha@^7: 717 | version "7.1.2" 718 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" 719 | integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== 720 | dependencies: 721 | ansi-colors "3.2.3" 722 | browser-stdout "1.3.1" 723 | chokidar "3.3.0" 724 | debug "3.2.6" 725 | diff "3.5.0" 726 | escape-string-regexp "1.0.5" 727 | find-up "3.0.0" 728 | glob "7.1.3" 729 | growl "1.10.5" 730 | he "1.2.0" 731 | js-yaml "3.13.1" 732 | log-symbols "3.0.0" 733 | minimatch "3.0.4" 734 | mkdirp "0.5.5" 735 | ms "2.1.1" 736 | node-environment-flags "1.0.6" 737 | object.assign "4.1.0" 738 | strip-json-comments "2.0.1" 739 | supports-color "6.0.0" 740 | which "1.3.1" 741 | wide-align "1.1.3" 742 | yargs "13.3.2" 743 | yargs-parser "13.1.2" 744 | yargs-unparser "1.6.0" 745 | 746 | mri@^1, mri@^1.1.4: 747 | version "1.1.5" 748 | resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.5.tgz#ce21dba2c69f74a9b7cf8a1ec62307e089e223e0" 749 | integrity sha512-d2RKzMD4JNyHMbnbWnznPaa8vbdlq/4pNZ3IgdaGrVbBhebBsGUUE/6qorTMYNS6TwuH3ilfOlD2bf4Igh8CKg== 750 | 751 | ms@2.0.0: 752 | version "2.0.0" 753 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 754 | 755 | ms@2.1.1: 756 | version "2.1.1" 757 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 758 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 759 | 760 | ms@^2.1.1: 761 | version "2.1.2" 762 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 763 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 764 | 765 | node-environment-flags@1.0.6: 766 | version "1.0.6" 767 | resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" 768 | integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== 769 | dependencies: 770 | object.getownpropertydescriptors "^2.0.3" 771 | semver "^5.7.0" 772 | 773 | normalize-path@^3.0.0, normalize-path@~3.0.0: 774 | version "3.0.0" 775 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 776 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 777 | 778 | npm-run-path@^2.0.0: 779 | version "2.0.2" 780 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 781 | integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= 782 | dependencies: 783 | path-key "^2.0.0" 784 | 785 | object-inspect@^1.7.0: 786 | version "1.7.0" 787 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" 788 | integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== 789 | 790 | object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: 791 | version "1.1.1" 792 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 793 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 794 | 795 | object.assign@4.1.0, object.assign@^4.1.0: 796 | version "4.1.0" 797 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" 798 | integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== 799 | dependencies: 800 | define-properties "^1.1.2" 801 | function-bind "^1.1.1" 802 | has-symbols "^1.0.0" 803 | object-keys "^1.0.11" 804 | 805 | object.getownpropertydescriptors@^2.0.3: 806 | version "2.1.0" 807 | resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" 808 | integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== 809 | dependencies: 810 | define-properties "^1.1.3" 811 | es-abstract "^1.17.0-next.1" 812 | 813 | once@^1.3.0: 814 | version "1.4.0" 815 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 816 | dependencies: 817 | wrappy "1" 818 | 819 | p-finally@^1.0.0: 820 | version "1.0.0" 821 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 822 | integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= 823 | 824 | p-limit@^2.0.0: 825 | version "2.3.0" 826 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 827 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 828 | dependencies: 829 | p-try "^2.0.0" 830 | 831 | p-locate@^3.0.0: 832 | version "3.0.0" 833 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" 834 | integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== 835 | dependencies: 836 | p-limit "^2.0.0" 837 | 838 | p-try@^2.0.0: 839 | version "2.2.0" 840 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 841 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 842 | 843 | path-exists@^3.0.0: 844 | version "3.0.0" 845 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 846 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 847 | 848 | path-is-absolute@^1.0.0: 849 | version "1.0.1" 850 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 851 | 852 | path-key@^2.0.0: 853 | version "2.0.1" 854 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 855 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 856 | 857 | pathval@^1.0.0, pathval@^1.1.0: 858 | version "1.1.0" 859 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 860 | 861 | pend@~1.2.0: 862 | version "1.2.0" 863 | resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 864 | integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= 865 | 866 | picomatch@^2.0.4: 867 | version "2.2.2" 868 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" 869 | integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== 870 | 871 | process-nextick-args@~2.0.0: 872 | version "2.0.1" 873 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 874 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 875 | 876 | progress@^2.0.1: 877 | version "2.0.3" 878 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 879 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 880 | 881 | proxy-from-env@^1.0.0: 882 | version "1.1.0" 883 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 884 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 885 | 886 | pseudomap@^1.0.2: 887 | version "1.0.2" 888 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 889 | integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= 890 | 891 | puppeteer@^2: 892 | version "2.1.1" 893 | resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.1.tgz#ccde47c2a688f131883b50f2d697bd25189da27e" 894 | integrity sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg== 895 | dependencies: 896 | "@types/mime-types" "^2.1.0" 897 | debug "^4.1.0" 898 | extract-zip "^1.6.6" 899 | https-proxy-agent "^4.0.0" 900 | mime "^2.0.3" 901 | mime-types "^2.1.25" 902 | progress "^2.0.1" 903 | proxy-from-env "^1.0.0" 904 | rimraf "^2.6.1" 905 | ws "^6.1.0" 906 | 907 | readable-stream@^2.2.2: 908 | version "2.3.7" 909 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 910 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 911 | dependencies: 912 | core-util-is "~1.0.0" 913 | inherits "~2.0.3" 914 | isarray "~1.0.0" 915 | process-nextick-args "~2.0.0" 916 | safe-buffer "~5.1.1" 917 | string_decoder "~1.1.1" 918 | util-deprecate "~1.0.1" 919 | 920 | readdirp@~3.2.0: 921 | version "3.2.0" 922 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" 923 | integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== 924 | dependencies: 925 | picomatch "^2.0.4" 926 | 927 | require-directory@^2.1.1: 928 | version "2.1.1" 929 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 930 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 931 | 932 | require-main-filename@^2.0.0: 933 | version "2.0.0" 934 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 935 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 936 | 937 | rimraf@^2.6.1: 938 | version "2.7.1" 939 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 940 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 941 | dependencies: 942 | glob "^7.1.3" 943 | 944 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 945 | version "5.1.2" 946 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 947 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 948 | 949 | semver@^5.7.0: 950 | version "5.7.1" 951 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 952 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 953 | 954 | set-blocking@^2.0.0: 955 | version "2.0.0" 956 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 957 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 958 | 959 | shebang-command@^1.2.0: 960 | version "1.2.0" 961 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 962 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 963 | dependencies: 964 | shebang-regex "^1.0.0" 965 | 966 | shebang-regex@^1.0.0: 967 | version "1.0.0" 968 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 969 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 970 | 971 | signal-exit@^3.0.0: 972 | version "3.0.3" 973 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 974 | integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 975 | 976 | sprintf-js@~1.0.2: 977 | version "1.0.3" 978 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 979 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 980 | 981 | "string-width@^1.0.2 || 2": 982 | version "2.1.1" 983 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 984 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 985 | dependencies: 986 | is-fullwidth-code-point "^2.0.0" 987 | strip-ansi "^4.0.0" 988 | 989 | string-width@^3.0.0, string-width@^3.1.0: 990 | version "3.1.0" 991 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 992 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 993 | dependencies: 994 | emoji-regex "^7.0.1" 995 | is-fullwidth-code-point "^2.0.0" 996 | strip-ansi "^5.1.0" 997 | 998 | string.prototype.trimend@^1.0.0: 999 | version "1.0.1" 1000 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" 1001 | integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== 1002 | dependencies: 1003 | define-properties "^1.1.3" 1004 | es-abstract "^1.17.5" 1005 | 1006 | string.prototype.trimleft@^2.1.1: 1007 | version "2.1.2" 1008 | resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" 1009 | integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== 1010 | dependencies: 1011 | define-properties "^1.1.3" 1012 | es-abstract "^1.17.5" 1013 | string.prototype.trimstart "^1.0.0" 1014 | 1015 | string.prototype.trimright@^2.1.1: 1016 | version "2.1.2" 1017 | resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" 1018 | integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== 1019 | dependencies: 1020 | define-properties "^1.1.3" 1021 | es-abstract "^1.17.5" 1022 | string.prototype.trimend "^1.0.0" 1023 | 1024 | string.prototype.trimstart@^1.0.0: 1025 | version "1.0.1" 1026 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" 1027 | integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== 1028 | dependencies: 1029 | define-properties "^1.1.3" 1030 | es-abstract "^1.17.5" 1031 | 1032 | string_decoder@~1.1.1: 1033 | version "1.1.1" 1034 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1035 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 1036 | dependencies: 1037 | safe-buffer "~5.1.0" 1038 | 1039 | strip-ansi@^4.0.0: 1040 | version "4.0.0" 1041 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1042 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 1043 | dependencies: 1044 | ansi-regex "^3.0.0" 1045 | 1046 | strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: 1047 | version "5.2.0" 1048 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 1049 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 1050 | dependencies: 1051 | ansi-regex "^4.1.0" 1052 | 1053 | strip-eof@^1.0.0: 1054 | version "1.0.0" 1055 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 1056 | integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= 1057 | 1058 | strip-json-comments@2.0.1: 1059 | version "2.0.1" 1060 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1061 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 1062 | 1063 | supports-color@5.4.0: 1064 | version "5.4.0" 1065 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 1066 | dependencies: 1067 | has-flag "^3.0.0" 1068 | 1069 | supports-color@6.0.0: 1070 | version "6.0.0" 1071 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" 1072 | integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== 1073 | dependencies: 1074 | has-flag "^3.0.0" 1075 | 1076 | supports-color@^5.3.0: 1077 | version "5.5.0" 1078 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1079 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1080 | dependencies: 1081 | has-flag "^3.0.0" 1082 | 1083 | supports-color@^7.1.0: 1084 | version "7.1.0" 1085 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" 1086 | integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== 1087 | dependencies: 1088 | has-flag "^4.0.0" 1089 | 1090 | to-regex-range@^5.0.1: 1091 | version "5.0.1" 1092 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1093 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1094 | dependencies: 1095 | is-number "^7.0.0" 1096 | 1097 | type-detect@^4.0.0, type-detect@^4.0.5: 1098 | version "4.0.8" 1099 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 1100 | 1101 | typedarray@^0.0.6: 1102 | version "0.0.6" 1103 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1104 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 1105 | 1106 | util-deprecate@~1.0.1: 1107 | version "1.0.2" 1108 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1109 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1110 | 1111 | which-module@^2.0.0: 1112 | version "2.0.0" 1113 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 1114 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 1115 | 1116 | which@1.3.1, which@^1.2.9: 1117 | version "1.3.1" 1118 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1119 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1120 | dependencies: 1121 | isexe "^2.0.0" 1122 | 1123 | wide-align@1.1.3: 1124 | version "1.1.3" 1125 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 1126 | integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== 1127 | dependencies: 1128 | string-width "^1.0.2 || 2" 1129 | 1130 | wrap-ansi@^5.1.0: 1131 | version "5.1.0" 1132 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" 1133 | integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== 1134 | dependencies: 1135 | ansi-styles "^3.2.0" 1136 | string-width "^3.0.0" 1137 | strip-ansi "^5.0.0" 1138 | 1139 | wrappy@1: 1140 | version "1.0.2" 1141 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1142 | 1143 | ws@^6.1.0: 1144 | version "6.2.1" 1145 | resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" 1146 | integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== 1147 | dependencies: 1148 | async-limiter "~1.0.0" 1149 | 1150 | y18n@^4.0.0: 1151 | version "4.0.0" 1152 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" 1153 | integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== 1154 | 1155 | yallist@^2.1.2: 1156 | version "2.1.2" 1157 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1158 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= 1159 | 1160 | yargs-parser@13.1.2, yargs-parser@^13.1.2: 1161 | version "13.1.2" 1162 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" 1163 | integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== 1164 | dependencies: 1165 | camelcase "^5.0.0" 1166 | decamelize "^1.2.0" 1167 | 1168 | yargs-unparser@1.6.0: 1169 | version "1.6.0" 1170 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" 1171 | integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== 1172 | dependencies: 1173 | flat "^4.1.0" 1174 | lodash "^4.17.15" 1175 | yargs "^13.3.0" 1176 | 1177 | yargs@13.3.2, yargs@^13.3.0: 1178 | version "13.3.2" 1179 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" 1180 | integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== 1181 | dependencies: 1182 | cliui "^5.0.0" 1183 | find-up "^3.0.0" 1184 | get-caller-file "^2.0.1" 1185 | require-directory "^2.1.1" 1186 | require-main-filename "^2.0.0" 1187 | set-blocking "^2.0.0" 1188 | string-width "^3.0.0" 1189 | which-module "^2.0.0" 1190 | y18n "^4.0.0" 1191 | yargs-parser "^13.1.2" 1192 | 1193 | yauzl@^2.10.0: 1194 | version "2.10.0" 1195 | resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" 1196 | integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= 1197 | dependencies: 1198 | buffer-crc32 "~0.2.3" 1199 | fd-slicer "~1.1.0" 1200 | --------------------------------------------------------------------------------