├── .eslintrc.js ├── .gcloudignore ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app.yaml ├── package.json ├── permissions-policy-header.mjs ├── public ├── demos │ ├── animations.html │ ├── autoplay.html │ ├── geolocation.html │ ├── oversized-images.html │ ├── picture-in-picture.html │ ├── sync-script.html │ ├── sync-xhr.html │ ├── unoptimized-lossy-images.html │ ├── unsized-media.css │ ├── unsized-media.html │ └── vertical-scroll.html ├── img │ ├── emilio-large.jpg │ ├── emilio.bmp │ ├── emilio.jpg │ ├── flag-24px.svg │ ├── github.svg │ ├── lossless-emilio.webp │ ├── lossy-emilio.webp │ ├── menu24px.svg │ └── sink.svg ├── index.html ├── js │ ├── app.js │ ├── policies.json │ ├── resources │ │ ├── async-script.js │ │ ├── defer-script.js │ │ ├── module.mjs │ │ └── parser-blocking-script.js │ └── shared.js ├── styles │ ├── header.css │ └── main.css ├── test.json ├── testpage.html └── video │ └── mixkit-rain-falling-from-the-roof-on-a-rainy-day-2716.mp4 ├── server.mjs └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // start with google standard style 3 | // https://github.com/google/eslint-config-google/blob/master/index.js 4 | "extends": ["eslint:recommended", "google"], 5 | "env": { 6 | "node": true, 7 | "es6": true, 8 | "browser": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8, 12 | "ecmaFeatures": { 13 | "jsx": false, 14 | }, 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | // 2 == error, 1 == warning, 0 == off 19 | "indent": [2, 2, { 20 | "SwitchCase": 1, 21 | "VariableDeclarator": 2, 22 | "MemberExpression": 2, 23 | "outerIIFEBody": 0 24 | }], 25 | "max-len": [2, 100, { 26 | "ignoreComments": true, 27 | "ignoreUrls": true, 28 | "tabWidth": 2 29 | }], 30 | "no-var": 2, 31 | "no-console": 1, 32 | "prefer-const": 2, 33 | 34 | // Disable rules 35 | "arrow-parens": 0, 36 | // "require-jsdoc": 0 37 | } 38 | } -------------------------------------------------------------------------------- /.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | # Node.js dependencies: 17 | node_modules/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). -------------------------------------------------------------------------------- /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. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Permissions Policy and Document Policy Demos 2 | 3 | Kitchen sink of [Permissions Policy](https://www.chromestatus.com/feature/5745992911552512) and [Document Policy](https://www.chromestatus.com/feature/5756689661820928) demos. 4 | 5 | ### Development 6 | 7 | First install the dependencies: 8 | 9 | `npm i` 10 | 11 | Then run the web server: 12 | 13 | `npm run start` 14 | 15 | ### Deployment 16 | 17 | `npm run deploy` 18 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | runtime: nodejs10 2 | #instance_class: F4 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feature-policy-demos", 3 | "version": "1.0.0", 4 | "author": "Eric Bidelman ", 5 | "main": "server.mjs", 6 | "license": "Apache-2.0", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "dependencies": { 11 | "express": "^4.16.4", 12 | "lit-html": "^0.14.0" 13 | }, 14 | "scripts": { 15 | "start": "node --experimental-modules server.mjs", 16 | "deploy": "gcloud app deploy app.yaml --project feature-policy-demos", 17 | "lint": "./node_modules/eslint/bin/eslint.js --ext .js,.mjs ." 18 | }, 19 | "devDependencies": { 20 | "eslint": "^5.10.0", 21 | "eslint-config-google": "^0.11.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /permissions-policy-header.mjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Copyright 2020 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | export class FeaturePolicyHeader { 19 | /** 20 | * @param {Map} policies 21 | */ 22 | constructor(policies) { 23 | this.policies = policies; 24 | } 25 | 26 | /** 27 | * Parse Feature-Policy header string 28 | * 29 | * @param {string} header_string e.g. unsized-images 'none'; geolocation *; usb foo.com bar.com 30 | * @returns {FeaturePolicyHeader} 31 | */ 32 | static parse(header_string) { 33 | if (!header_string) 34 | return new FeaturePolicyHeader(new Map()); 35 | 36 | return new FeaturePolicyHeader(header_string.split(';') 37 | .reduce((acc, item) => { 38 | const [policyName, ...allowlist] = item.trim().split(' ').filter(s => s !== ''); 39 | acc.set(policyName, allowlist); 40 | return acc; 41 | }, new Map())); 42 | } 43 | 44 | serialize() { 45 | return [...this.policies.entries()] 46 | .map(([policyName, allowlist]) => `${policyName} ${allowlist.join(' ')}`) 47 | .join('; '); 48 | } 49 | }; 50 | 51 | export class PermissionsPolicyHeader { 52 | /** 53 | * @param {Map} policies 54 | */ 55 | constructor(policies) { 56 | this.policies = policies; 57 | } 58 | /** 59 | * Parse Permissions-Policy header string. 60 | * 61 | * Note: Permissions-Policy header uses Dictionary in [structured header syntax] 62 | *(https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html). 63 | * 64 | * Note: This function does not provide full structured header syntax parsing. 65 | * When incomplete syntax is received, it can lead to unexpected behaviour. 66 | * 67 | * @param {string} header_string e.g. p1=(), p2=("foo.com" "bar.com"), p3=self 68 | * @returns {PermissionsPolicyHeader} 69 | */ 70 | static parse(header_string) { 71 | if (!header_string) 72 | return new PermissionsPolicyHeader(new Map()); 73 | 74 | return new PermissionsPolicyHeader(header_string.split(',') 75 | .reduce((acc, item) => { 76 | const [policyName, allowlist_string] = item.split('=').map(s => s.trim()); 77 | 78 | const match_result = allowlist_string.match(/\((.*)\)/); 79 | // Bracket is optional when there is only single item in SH's dictionary 80 | // value. 81 | const allowlist = match_result ? ( 82 | match_result.length > 1 ? match_result[1].split(' ') : [''] 83 | ) : [allowlist_string]; 84 | acc.set(policyName, allowlist); 85 | return acc; 86 | }, new Map())); 87 | } 88 | 89 | serialize() { 90 | return [...this.policies.entries()] 91 | .map(([policyName, allowlist]) => `${policyName}=(${allowlist.join(' ')})`) 92 | .join(', '); 93 | } 94 | 95 | /** 96 | * @returns {FeaturePolicyHeader} 97 | */ 98 | toFeaturePolicy() { 99 | /** 100 | * Convert permissions policy allowlist item to feature policy allowlist 101 | * item. 102 | * @param {string} item 103 | * @returns {string} 104 | */ 105 | function mapAllowlistItem(item) { 106 | switch (item) { 107 | case '*': 108 | return '*'; 109 | case "self": 110 | return "'self'"; 111 | case "": 112 | return "'none'"; 113 | default: 114 | // Remove leading and trailing '"' in permissions header syntax. 115 | return item.slice(1, -1); 116 | } 117 | } 118 | return new FeaturePolicyHeader( 119 | new Map( 120 | [...this.policies.entries()] 121 | .map(([feature, allowlist]) => [feature, allowlist.map(mapAllowlistItem)]) 122 | )); 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /public/demos/animations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Policy: animations example 8 | 9 | 10 | 11 | 76 | 77 | 78 | 79 |
80 | 81 |
82 | 83 |

84 | ... 85 |

86 | 87 |

This page shows how animations are affected by this policy.

88 | 89 |
90 |

CSS keyframe animation on transform property

91 |

This first animation continues when the policy is active because it is GPU accelerated. It's 92 | animating one of the three properties (opacity, transform, filter) that can be 93 | reliably hardware accelerated.

94 |
95 |
96 |
97 |

CSS keyframe animation on left property

98 |

This second animation jumps when the policy is 99 | active because it's animating the left property. Don't do that.

100 |
101 |
102 |
103 |

CSS keyframe animation on left and transform properties

104 |

This one shows what happens if you animate more than one property and 105 | one of the properties is non accelerated. Some of the animation still continues.

106 |
107 |
108 |
109 |

requestionAnimationFrame-based animation on left property

110 |

JS-based animations using requestionAnimationFrame are not affected 111 | by the policy, even if they do terrible things like animate CSS left.

112 |
113 |
114 |
115 |

CSS Transition on height property

116 |

Hover over this box. The CSS transition on the height/width won't animate when the policy is active.

117 |
Hover on me.
118 |
119 | 120 | 136 | 137 | 143 | 144 | -------------------------------------------------------------------------------- /public/demos/autoplay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Permissions Policy: autoplay example 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

19 | ... 20 |

21 | 22 |

The video below uses autoplay attribute. Reload the page with the policy on/off to see the effect on it auto playing.

23 | 24 |
25 | 26 |
27 |

Source: https://mixkit.co/free-stock-video/rain-falling-from-the-roof-on-a-rainy-day-2716/

28 | 29 | 33 | 34 | 40 | 41 | -------------------------------------------------------------------------------- /public/demos/geolocation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Permissions Policy: geolocation example 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 |
24 | 25 |
26 | 27 |

28 | ... 29 |

30 | 31 |

32 | The demo attempts to use the 33 | Geolocation API 34 | to display the user's current location. 35 |

36 | 37 |
38 |   navigator.geolocation.getCurrentPosition(
39 |     p => {
40 |       displayText(`Successfully retrieved geolocation(${p.coords.latitude}, ${p.coords.longitude})`);
41 |     },
42 |     err => {
43 |       displayText(`Failed to retrieve geolocation: ${err.message}`);
44 |     });
45 | 46 |
47 | 48 | 65 | 66 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /public/demos/oversized-images.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Policy: oversized-images example 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

Click the buttons above to see how the page loads with different policy values. 19 | Images where the ratio of intrinsic dimensions to container size exceeds the given 20 | threshold should render as placeholder images.

21 | 22 | 31 | 32 |
33 |

34 | When the downscaling ratio does not exceed the limit (2.0) in either direction, the image renders normally. 35 |

36 |
37 | 38 |

39 | Intrinsic size: 800px x 500px
40 | Container size: 800px x 500px 41 |

42 |
43 |
44 |
45 |

46 | When the downscaling ratio exceeds the limit (2.0) for width, the image renders as a placeholder image. 47 |

48 |
49 | For devices with device pixel ratio of 1 (e.g. tablets, monitors, etc), the downscaling ratio (800/380) > 2, the image renders as a placeholder image. 50 | 51 |

52 | Intrinsic size: 800px x 500px
53 | Container size: 380px x 500px 54 |

55 |
56 | 57 |
58 | For devices with device pixel ratio of 2 (e.g. iPad, MacBook, smartphones, etc), the downscaling ratio (800/(180 * 2)) > 2, the image renders as a placeholder image. 59 | 60 |

61 | Intrinsic size: 800px x 500px
62 | Container size: 180px x 500px 63 |

64 |
65 | 66 |
67 | For devices with device pixel ratio between 3 to 5 (e.g. iPhoneX, LG G3+, Samsung Galaxy S4, etc), 68 | the downscaling ratio (800 / (60 * 5)) > 2, the image renders as a placeholder image. 69 | 70 |

71 | Intrinsic size: 800px x 500px
72 | Container size: 60px x 500px 73 |

74 |
75 |
76 |
77 |

78 | When the downscaling ratio exceeds the limit (2.0) for height, the image as a placeholder image. 79 |

80 |
81 | For devices with device pixel ratio of 1 (e.g. tablets, monitors, etc), the downscaling ratio (500/200) > 2, the image renders as a placeholder image. 82 | 83 |

84 | Intrinsic size: 800px x 500px
85 | Container size: 800px x 200px 86 |

87 |
88 | 89 |
90 | For devices with device pixel ratio of 2 (e.g. iPad, MacBook, smartphones, etc), the downscaling ratio (800/(200 * 2)) > 2, the image renders as a placeholder image. 91 | 92 |

93 | Intrinsic size: 800px x 500px
94 | Container size: 800px x 100px 95 |

96 |
97 | 98 |
99 | For devices with device pixel ratio between 3 to 5 (e.g. iPhoneX, LG G3+, Samsung Galaxy S4, etc), 100 | the downscaling ratio (500 / (40 * 5)) > 2, the image renders as a placeholder image. 101 | 102 |

103 | Intrinsic size: 800px x 500px
104 | Container size: 800px x 40px 105 |

106 |
107 |
108 |
109 |

110 | When the downscaling ratio exceeds the limit (2.0) in both direction, the image renders as a placeholder image. 111 |

112 |
113 | For devices with device pixel ratio of 1 (e.g. tablets, monitors, etc), the downscaling ratio > 2, the image renders as a placeholder image. 114 | 115 |

116 | Intrinsic size: 800px x 500px
117 | Container size: 380px x 200px 118 |

119 |
120 | 121 |
122 | For devices with device pixel ratio of 2 (e.g. iPad, MacBook, smartphones, etc), the downscaling ratio > 2, the image renders as a placeholder image. 123 | 124 |

125 | Intrinsic size: 800px x 500px
126 | Container size: 180px x 100px 127 |

128 |
129 | 130 |
131 | For devices with device pixel ratio between 3 to 5 (e.g. iPhoneX, LG G3+, Samsung Galaxy S4, etc), 132 | the downscaling ratio > 2, image renders as a placeholder image. 133 | 134 |

135 | Intrinsic size: 800px x 500px
136 | Container size: 60px x 40px 137 |

138 |
139 |
140 | 141 | 146 | 147 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /public/demos/picture-in-picture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Permissions Policy: Picture-in-Picture example 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

19 | ... 20 |

21 | 22 |

The video below uses native controls. Reload the page with the permissions policy 23 | on/off to see the effect on its controls.

24 | 25 |

Note: At the moment, you may need to enable the 26 | --enable-picture-in-picture, --enable-surfaces-for-videos, 27 | and --enable-experimental-web-platform-features flags in Chrome 69+ 28 | chrome://flags for this demo to work.

29 | 30 |
31 | 32 |
33 |

Source: https://mixkit.co/free-stock-video/rain-falling-from-the-roof-on-a-rainy-day-2716/

34 | 35 | 39 | 40 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/demos/sync-script.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Policy: sync-script example 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 |
31 | 32 |
33 | 34 |

35 | ... 36 |

37 | 38 |

Below, you'll see the list of scripts that executed on the page. When the 39 | policy is active, some scripts will not run.

40 | 41 |
42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 103 | 104 | 105 | 111 | 112 | -------------------------------------------------------------------------------- /public/demos/sync-xhr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Feature Policy: sync-xhr example 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 |

31 | ... 32 |

33 | 34 |

Reload the page. When the page allows synchronous XHRs, you will eventually see a list of web 35 | features supported in Chrome. Note that the banner doesn't render until the JSON request has 36 | finished. This is because the main thread is blocked by the request + parsing of JSON data. When the 37 | page turns on this policy, the browser simply blocks the sync XHR request.

38 | 39 |

Chrome features

40 |
    41 | Fetching list of Chrome web features... 42 |
43 | 44 | 71 | 72 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /public/demos/unoptimized-lossy-images.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Policy: unoptimized-{lossy,lossless}-images example 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 |
17 | 18 |

Click the buttons above to see how the page loads with different policy values. 19 | Images where the compression ratio (bytes per pixel) exceeds the threshold should render as placeholder images. 20 |

21 | 22 | 31 | 32 |
  • For lossy images, (image size - 1KB) / image resolution should be under 0.5
  • 33 |
    34 |
    35 | 36 |

    37 | emilio.jpg
    38 | Image file size: 111K
    39 | Image resolution: 400px x 300px
    40 | Compression ratio (byte-per-pixel): (111KB - 1KB) / (400px * 300px) = 0.9 (> 0.5) 41 |

    42 |
    43 |
    44 | 45 |

    46 | lossy-emilio.webp
    47 | Image file size: 17.3K
    48 | Image resolution: 400px x 300px
    49 | Compression ratio (byte-per-pixel): (17.3KB - 1KB) / (400px * 300px) = 0.14 (<= 0.5) 50 |

    51 |
    52 |
    53 | 54 |
  • For lossless images, (image size - 10KB) / image resolution should be under 1
  • 55 |
    56 |
    57 | 58 |

    59 | emilio.bmp
    60 | Image file size: 351K
    61 | Image resolution: 400px x 300px
    62 | Compression ratio (byte-per-pixel): (351KB - 10KB) / (400px * 300px) = 2.9 (> 1.0) 63 |

    64 |
    65 |
    66 | 67 |

    68 | lossless-emilio.webp
    69 | Image file size: 111K
    70 | Image resolution: 400px x 300px
    71 | Compression ratio (byte-per-pixel): (111KB - 10KB) / (400px * 300px) = 0.86 (<= 1.0) 72 |

    73 |
    74 |
    75 | 76 |
  • For lossless images in strict mode, (image size - 1KB) / image resolution should be under 1
  • 77 |
    78 |
    79 | 80 |

    81 | emilio.bmp
    82 | Image file size: 351K
    83 | Image resolution: 400px x 300px
    84 | Compression ratio (byte-per-pixel): (351KB - 1KB) / (400px * 300px) = 2.99 (> 1.0) 85 |

    86 |
    87 |
    88 | 89 |

    90 | lossless-emilio.webp
    91 | Image file size: 147K
    92 | Image resolution: 400px x 300px
    93 | Compression ratio (byte-per-pixel): (111KB - 1KB) / (400px * 300px) = 0.94 (<= 0.5) 94 |

    95 |
    96 |
    97 | 98 | 103 | 104 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /public/demos/unsized-media.css: -------------------------------------------------------------------------------- 1 | .height { 2 | height: 400px; 3 | } 4 | .sized { 5 | width: 300px; 6 | height: 300px; 7 | } 8 | img { 9 | border-radius: 3px; 10 | } 11 | .round { 12 | shape-outside: circle(); 13 | shape-margin: 16px; 14 | border-radius: 50%; 15 | } 16 | .right { 17 | float: right; 18 | } 19 | .left { 20 | float: left; 21 | } 22 | .center { 23 | text-align: center; 24 | } 25 | -------------------------------------------------------------------------------- /public/demos/unsized-media.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Policy: unsized-media example 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 17 |
    18 | 19 | 20 |

    Click the disallow/allow buttons above to see how the page loads with the policy on and off. 21 | When the policy is on, you won't see layout thrashing as large images load. Media without 22 | explicit sizes (either as HTML attributes ore defined in CSS) causes layout and text reflows 23 | as the page loads.

    24 | 25 | 39 | 40 | 41 |

    Lorem ipsum dolor sit amet, pretium urna faucibus etiam enim sollicitudin, 42 | sagittis phasellus nec suspendisse aenean. Orci nec nam ipsum lobortis ipsum, 43 | at nunc, semper in suscipit sem et, mauris amet amet accumsan risus quis, urna 44 | quam. Lacus mi elit lectus tortor nec enim, sed ornare est praesent est id ut, 45 | mi at. Vestibulum varius, diam posuere tristique est adipiscing, proin quam orci, 46 | nunc tincidunt purus accumsan erat, nec amet magna donec. Consectetuer vivamus 47 | pharetra nulla vivamus, quam sit conubia ligula turpis, condimentum purus mauris 48 | felis scelerisque id. Tincidunt nullam tristique mattis sit vivamus viverra, 49 | velit orci convallis, suspendisse urna sed.

    50 | 51 | 52 | 53 | 54 | 55 |

    Lorem ipsum dolor sit amet, pretium urna faucibus etiam enim sollicitudin, 56 | sagittis phasellus nec suspendisse aenean. Orci nec nam ipsum lobortis ipsum, 57 | at nunc, semper in suscipit sem et, mauris amet amet accumsan risus quis, urna 58 | quam. Lacus mi elit lectus tortor nec enim, sed ornare est praesent est id ut, 59 | mi at. Vestibulum varius, diam posuere tristique est adipiscing, proin quam orci, 60 | nunc tincidunt purus accumsan erat, nec amet magna donec. Consectetuer vivamus 61 | pharetra nulla vivamus, quam sit conubia ligula turpis, condimentum purus mauris 62 | felis scelerisque id. Tincidunt nullam tristique mattis sit vivamus viverra, 63 | velit orci convallis, suspendisse urna sed.

    64 | 65 |

    Lorem ipsum dolor sit amet, pretium urna faucibus etiam enim sollicitudin, 66 | sagittis phasellus nec suspendisse aenean. Orci nec nam ipsum lobortis ipsum, 67 | at nunc, semper in suscipit sem et, mauris amet amet accumsan risus quis, urna 68 | quam. Lacus mi elit lectus tortor nec enim, sed ornare est praesent est id ut, 69 | mi at. Vestibulum varius, diam posuere tristique est adipiscing, proin quam orci, 70 | nunc tincidunt purus accumsan erat, nec amet magna donec. Consectetuer vivamus 71 | pharetra nulla vivamus, quam sit conubia ligula turpis, condimentum purus mauris 72 | felis scelerisque id. Tincidunt nullam tristique mattis sit vivamus viverra, 73 | velit orci convallis, suspendisse urna sed.

    74 | 75 | 76 |
    77 | 78 |
    79 | 80 |

    Lorem ipsum dolor sit amet, pretium urna faucibus etiam enim sollicitudin, 81 | sagittis phasellus nec suspendisse aenean. Orci nec nam ipsum lobortis ipsum, 82 | at nunc, semper in suscipit sem et, mauris amet amet accumsan risus quis, urna 83 | quam. Lacus mi elit lectus tortor nec enim, sed ornare est praesent est id ut, 84 | mi at. Vestibulum varius, diam posuere tristique est adipiscing, proin quam orci, 85 | nunc tincidunt purus accumsan erat, nec amet magna donec. Consectetuer vivamus 86 | pharetra nulla vivamus, quam sit conubia ligula turpis, condimentum purus mauris 87 | felis scelerisque id. Tincidunt nullam tristique mattis sit vivamus viverra, 88 | velit orci convallis, suspendisse urna sed.

    89 | 90 | 94 | 95 | 96 |

    Lorem ipsum dolor sit amet, pretium urna faucibus etiam enim sollicitudin, 97 | sagittis phasellus nec suspendisse aenean. Orci nec nam ipsum lobortis ipsum, 98 | at nunc, semper in suscipit sem et, mauris amet amet accumsan risus quis, urna 99 | quam. Lacus mi elit lectus tortor nec enim, sed ornare est praesent est id ut, 100 | mi at. Vestibulum varius, diam posuere tristique est adipiscing, proin quam orci, 101 | nunc tincidunt purus accumsan erat, nec amet magna donec. Consectetuer vivamus 102 | pharetra nulla vivamus, quam sit conubia ligula turpis, condimentum purus mauris 103 | felis scelerisque id. Tincidunt nullam tristique mattis sit vivamus viverra, 104 | velit orci convallis, suspendisse urna sed.

    105 | 106 | 117 | 118 | 124 | 125 | -------------------------------------------------------------------------------- /public/demos/vertical-scroll.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Permissions Policy: vertical-scroll example 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 |
    26 | 27 |
    28 | 29 |

    30 | ... 31 |

    32 | 33 |

    When this policy is active, a frame or main page content will not 34 | be able prevent its content from scrolling vertically. This is done by:

    35 | 36 |
      37 |
    • Ensuring all scroll related input events (e.g. touchstart) are non-cancelable.
    • 38 |
    • Treat touch-action: none as if it were touch-action: pan-y). Meaning the page still blocks pan-x and zoom.
    • 39 |
    • Ensuring programmatic scrolling using the DOM scroll APIs is handled within the scope of the frame, i.e., calls to el.scrollIntoView() do not propagate outside of the frame.
    • 40 |
    • Ensuring that no scrollable layer is targeted for scroll gestures (to avoid a frame from consuming 41 | all the scroll gestures).
    • 42 |
    43 | 44 |

    The frame below uses body{ touch-action: none; } to 45 | prevent its content from scrolling. However, when the policy is "on", it 46 | will be scrollable!. In other words, the policy prevents 47 | the page from blocking vertical scroll by treating the touch-action: none 48 | as touch-action: pan-y.

    49 | 50 |

    Try scrolling the iframe below on mobile (e.g. using touch events).

    51 | 52 |
    53 | 70 |
    71 | 72 | 79 | 80 | 86 | 87 | -------------------------------------------------------------------------------- /public/img/emilio-large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/img/emilio-large.jpg -------------------------------------------------------------------------------- /public/img/emilio.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/img/emilio.bmp -------------------------------------------------------------------------------- /public/img/emilio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/img/emilio.jpg -------------------------------------------------------------------------------- /public/img/flag-24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/lossless-emilio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/img/lossless-emilio.webp -------------------------------------------------------------------------------- /public/img/lossy-emilio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/img/lossy-emilio.webp -------------------------------------------------------------------------------- /public/img/menu24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/img/sink.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 12 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Permissions Policy Kitchen Sink 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 28 | 29 | 30 | 31 |
    32 |
    33 |
    34 | It's a kitchen sink! 35 |
    36 | 37 |

    Permissions/
    Document Policy

    38 |

    Demos

    39 |
    40 |
    41 | 44 |
    45 | 46 |
    47 |
    48 | 49 |
    50 |
    51 | 52 |
    53 |

    Select a policy to begin.

    54 | 55 | 56 | 57 |
    58 |
    59 | 60 |
    61 |
    62 | 63 |

    Permissions Policy 64 | allows web developers to selectively enable, disable, and modify the behavior 65 | of various features and APIs in the browser. This can provide a well-lit path towards good performance 66 | and prevent common footguns when building and maintaining web apps.

    67 |

    Permissions policy is not supported in your browser. Try Chrome Canary and enable the --enable-experimental-web-platform-features flag.

    68 |

    You control permissions policies through an HTTP header:

    69 |

    70 |

    Permissions-Policy: geolocation=*
     71 | Permissions-Policy: geolocation=()
     72 | Permissions-Policy: geolocation=self
     73 | Permissions-Policy: geolocation="https://google-developers.appspot.com"
    74 |

    75 |

    or by adding the allow attribute on iframes:

    76 |

    77 |

    <iframe allow="geolocation">
     78 | <iframe allow="geolocation 'self'">
     79 | <iframe allow="geolocation 'none'">
     80 | <iframe allow="geolocation https://google-developers.appspot.com">
    81 |

    82 |
    83 | 84 |
    85 | 90 |
    91 | 92 | 93 | 99 | 100 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | /* global gtag */ 2 | 3 | /** 4 | * Copyright 2018 Google Inc. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import {html, render} from '/lit-html/lit-html.js'; 20 | import {repeat} from '/lit-html/directives/repeat.js'; 21 | import {until} from '/lit-html/directives/until.js'; 22 | import {fetchPolicies, updateDetailsHeader, getPolicy, permissionsPolicyAPISupported} from '/js/shared.js'; 23 | 24 | const POLICY_TYPE_TO_LABEL = { 25 | performance: 'Performance', 26 | images: 'Images', 27 | granular: 'Granular control', 28 | other: 'Coming soon...', 29 | }; 30 | 31 | /** 32 | * Dynamically loads policy demo page based off current (deep link) url. 33 | * @return {!Object} Policy info. 34 | */ 35 | async function loadPage() { 36 | let policy = null; 37 | const url = new URL(location); 38 | 39 | if (url.pathname !== '/') { 40 | const demoPage = url.pathname.slice(1).split('.')[0]; 41 | 42 | policy = await getPolicy(demoPage); 43 | 44 | // Remove intro banner. 45 | const intro = document.querySelector('#intro-summary'); 46 | if (intro) { 47 | intro.remove(); 48 | } 49 | 50 | const contentFrame = document.querySelector('iframe.content-view'); 51 | const pagePath = policy.url + url.search; 52 | contentFrame.src = pagePath; 53 | 54 | gtag('config', 'UA-120357238-1', {page_path: pagePath}); 55 | } 56 | 57 | return policy; 58 | } 59 | 60 | /** 61 | * Updates the iframe content when policy value selector is clicked. 62 | * 63 | * @param {!HTMLAnchorElement} anchor 64 | */ 65 | function updatePolicyValue(anchor) { 66 | anchor.parentElement.querySelectorAll('a').forEach(a => a.removeAttribute('active')); 67 | anchor.setAttribute('active', ''); 68 | const href = anchor.getAttribute('href').replace('/demos', ''); 69 | window.history.pushState(null, null, href); 70 | loadPage(); 71 | } 72 | 73 | /** 74 | * Updates the dynamic portions of the page. 75 | * @param {!HTMLAnchorElement} anchor 76 | * @param {string} id Policy id. 77 | */ 78 | async function updatePage(anchor, id) { 79 | // Update window URL first, so that |updateDetailsHeader| can observe 80 | // the latest URL. 81 | const href = anchor.getAttribute('href').replace('/demos', ''); 82 | window.history.pushState(null, null, href); 83 | 84 | updateDetailsHeader(await getPolicy(id)); 85 | loadPage(); 86 | } 87 | 88 | /** 89 | * Toggles the app drawer. 90 | * @param {boolean=} forClose True to force close the drawer. Default toggles. 91 | */ 92 | function toggleDrawer(forClose = false) { 93 | const el = document.querySelector('.drawer-list'); 94 | if (forClose) { 95 | el.classList.remove('active'); 96 | } else { 97 | el.classList.toggle('active'); 98 | } 99 | } 100 | 101 | /** 102 | * @param {Array} implementedPolicies 103 | */ 104 | async function leftOverPolicies(implementedPolicies) { 105 | const permissionsPolicy = 106 | document.permissionsPolicy || document.featurePolicy; 107 | const allPolicies = permissionsPolicy.allowedFeatures().sort(); 108 | 109 | const policies = []; 110 | allPolicies.forEach((item, i) => { 111 | const found = implementedPolicies.find(p => p.id === item); 112 | if (!found) { 113 | policies.push(item); 114 | } 115 | }); 116 | 117 | return repeat(policies, null, (p, i) => { 118 | return html`
    ${p}
    `; 119 | }); 120 | } 121 | 122 | 123 | (async () => { 124 | if (!permissionsPolicyAPISupported) { 125 | document.querySelector('.notsupported').classList.add('show'); 126 | return; 127 | } 128 | 129 | const fetchedPolicies = await fetchPolicies(); 130 | 131 | const buildImplementedPolicies = () => { 132 | const orderedPolicies = fetchedPolicies.sort((a, b) => { 133 | return a.name < b.name ? -1 : 134 | a.name > b.name ? 1 : 0; 135 | }); 136 | 137 | const categoryMapping = new Map(); 138 | fetchedPolicies.forEach(policy => { 139 | const item = categoryMapping.get(policy.type); 140 | if (item) { 141 | item.push(policy); 142 | } else { 143 | categoryMapping.set(policy.type, [policy]); 144 | } 145 | }); 146 | 147 | const categories = Array.from(categoryMapping.entries()); 148 | const markup = repeat(categories, (item) => item[0], ([cat, policies], i) => { 149 | const items = repeat(policies, (p) => p.id, (p, i) => { 150 | const firstUsage = Object.keys(p.usage).sort()[0]; 151 | return html` 152 | 153 | ${p.name} 154 | 156 | `; 157 | }); 158 | return html` 159 |

    ${POLICY_TYPE_TO_LABEL[cat]}

    160 | ${items} 161 | `; 162 | }); 163 | return {markup, policies: orderedPolicies}; 164 | }; 165 | 166 | loadPage().then(updateDetailsHeader); 167 | 168 | const implementedPolicies = buildImplementedPolicies(); 169 | 170 | render(html`${implementedPolicies.markup}`, 171 | document.querySelector('#policy-list')); 172 | render( 173 | html` 174 |

    ${POLICY_TYPE_TO_LABEL['other']}

    175 | ${until(leftOverPolicies(implementedPolicies.policies))} 176 | `, document.querySelector('#all-policy-list')); 177 | 178 | document.addEventListener('click', e => { 179 | const button = document.querySelector('.menu-button'); 180 | if (!button.contains(e.target)) { 181 | e.stopPropagation(); 182 | toggleDrawer(true); 183 | } 184 | }); 185 | })(); 186 | 187 | window.updatePage = updatePage; 188 | window.updatePolicyValue = updatePolicyValue; 189 | window.toggleDrawer = toggleDrawer; 190 | -------------------------------------------------------------------------------- /public/js/policies.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "unsized-media", 3 | "name": "Unsized Media", 4 | "type": "images", 5 | "url": "/demos/unsized-media.html", 6 | "what": "Allows developers to enforce that image/video elements have explicit dimensions. If dimensions aren't specified on the element, the browser sets a default size of 300x150 when this policy is active.", 7 | "why": "Reduces the layout work the browser has to perform.", 8 | "policyType": "Document-Policy", 9 | "usage": { 10 | "off": "unsized-media", 11 | "on": "unsized-media=?0" 12 | }, 13 | "examples": { 14 | "header": [ 15 | "Document-Policy: unsized-media", 16 | "Document-Policy: unsized-media=?0" 17 | ] 18 | } 19 | }, { 20 | "id": "sync-xhr", 21 | "name": "Synchronous XHR", 22 | "type": "performance", 23 | "url": "/demos/sync-xhr.html", 24 | "chromeStatusLink": "https://www.chromestatus.com/feature/5154875084111872", 25 | "what": "Disallows the use of synchronous XMLHttpRequests.", 26 | "why": "Using synchronous XHRs can cause jank on the main thread and be detrimental to user experience. Although the HTML spec has deprecated sync XHRs, developers are still able to use it for XHR requests where responseType=\"text\".", 27 | "policyType": "Permissions-Policy", 28 | "usage": { 29 | "off": "sync-xhr=self", 30 | "on": "sync-xhr=()" 31 | }, 32 | "examples": { 33 | "header": [ 34 | "Permissions-Policy: sync-xhr=()" 35 | ] 36 | } 37 | }, { 38 | "id": "autoplay", 39 | "name": "Autoplay media", 40 | "type": "granular", 41 | "url": "/demos/autoplay.html", 42 | "chromeStatusLink": "https://www.chromestatus.com/feature/5100524789563392", 43 | "what": "Allows cross-origin videos and movies to autoplay.", 44 | "why": "By default, Chrome allows the `autoplay` attribute on videos within same-origin iframes. To enable cross-origin videos to autoplay (or disallow same-origin videos from auto playing), sites can use this permissions policy.", 45 | "policyType": "Permissions-Policy", 46 | "usage": { 47 | "off": "autoplay=self", 48 | "on": "autoplay=()" 49 | }, 50 | "examples": { 51 | "header": [ 52 | "Permissions-Policy: autoplay=()", 53 | "Permissions-Policy: autoplay=self" 54 | ] 55 | } 56 | }, { 57 | "id": "geolocation", 58 | "name": "Geolocation", 59 | "type": "granular", 60 | "url": "/demos/geolocation.html", 61 | "chromeStatusLink": null, 62 | "what": "Allows/disables the use of the Geolocation API.", 63 | "why": "By default, Chrome blocks the usage of geolocation in cross-origin iframes. Developers can control this behavior (or geolocation access in general) using this permissions policy.", 64 | "policyType": "Permissions-Policy", 65 | "usage": { 66 | "off": "geolocation=self", 67 | "on": "geolocation=()" 68 | }, 69 | "examples": { 70 | "header": [ 71 | "Permissions-Policy: geolocation=self", 72 | "Permissions-Policy: geolocation https://google-developers.appspot.com" 73 | ], 74 | "iframe": [ 75 | "<iframe src=\"...\" allow=\"geolocation https://google-developers.appspot.com\"><\/iframe>" 76 | ] 77 | } 78 | }, { 79 | "id": "picture-in-picture", 80 | "name": "Picture-in-Picture", 81 | "type": "granular", 82 | "url": "/demos/picture-in-picture.html", 83 | "chromeStatusLink": "https://www.chromestatus.com/features/5729206566649856", 84 | "what": "Controls access to Picture in Picture.", 85 | "why": "By default, Chrome allows the usage of Picture-in-Picture in cross-origin iframes. Developers can disable it using this permissions policy.", 86 | "policyType": "Permissions-Policy", 87 | "usage": { 88 | "on": "picture-in-picture=self", 89 | "off": "picture-in-picture=()" 90 | }, 91 | "examples": { 92 | "header": [ 93 | "Permissions-Policy: picture-in-picture=self", 94 | "Permissions-Policy: picture-in-picture=()" 95 | ] 96 | } 97 | }, { 98 | "id": "animations", 99 | "name": "Fast animations", 100 | "type": "performance", 101 | "url": "/demos/animations.html", 102 | "what": "Restricts the set of CSS properties which can be animated to opacity, transform, and filter.", 103 | "why": "Ensures smooth animations, by only allowing those properties which can be animated on the GPU using the hardware acceleration.", 104 | "policyType": "Document-Policy", 105 | "usage": { 106 | "off": "animations", 107 | "on": "animations=?0" 108 | }, 109 | "examples": { 110 | "header": [ 111 | "Document-Policy: animations=?0" 112 | ] 113 | } 114 | }, { 115 | "id": "oversized-images", 116 | "name": "Oversized images", 117 | "type": "images", 118 | "url": "/demos/oversized-images.html", 119 | "what": "Ensures instrinsic size of images are not more than X times larger than their container size.", 120 | "why": "Image bloat is a large problem on the web. Sending unnecessarily large images is bad for performance, UX, and wastes bandwidth.", 121 | "policyType": "Document-Policy", 122 | "usage_desc": "Maximum image_size / container_size ratio", 123 | "usage": { 124 | "1.0": "oversized-images=1.0", 125 | "2.0": "oversized-images=2.0", 126 | "4.0": "oversized-images=4.0" 127 | }, 128 | "examples": { 129 | "header": [ 130 | "Document-Policy: oversized-images=2.0" 131 | ] 132 | } 133 | }, { 134 | "id": "unoptimized-lossy-images", 135 | "name": "Unoptimized images", 136 | "type": "images", 137 | "url": "/demos/unoptimized-lossy-images.html", 138 | "what": "Requires the data size of images (in bytes) to be no more than X times bigger than its rendering area (in pixels). Images violating this policy render as placeholder images.", 139 | "why": "Ensures optimized performance with images by minimizing file size, reducing image bloat and saving bandwidth.", 140 | "policyType": "Document-Policy", 141 | "usage_desc": "Maximum byte per pixel", 142 | "usage": { 143 | "1.0": "unoptimized-lossy-images=1.0, unoptimized-lossless-images=1.0, unoptimized-lossless-images-strict=1.0", 144 | "2.0": "unoptimized-lossy-images=2.0, unoptimized-lossless-images=2.0, unoptimized-lossless-images-strict=2.0", 145 | "4.0": "unoptimized-lossy-images=4.0, unoptimized-lossless-images=4.0, unoptimized-lossless-images-strict=4.0" 146 | }, 147 | "examples": { 148 | "header": [ 149 | "Document-Policy: unoptimized-lossy-images=1.0", 150 | "Document-Policy: unoptimized-lossless-images=1.0", 151 | "Document-Policy: unoptimized-lossless-images-strict=1.0" 152 | ] 153 | } 154 | }, { 155 | "id": "sync-script", 156 | "name": "Synchronous scripts", 157 | "type": "performance", 158 | "url": "/demos/sync-script.html", 159 | "what": "Prevents synchronous, parsing blocking scripts from executing.", 160 | "why": "Inline scripts and <script src> without the defer/async attributes block the parser. This can lead to bad performance and poor UX. Instead, use defer/async when loading scripts, dynamically inject them into the page using JS, or use ES Modules (which are defer loaded by default). These solutions will not violate this document policy.", 161 | "policyType": "Document-Policy", 162 | "usage": { 163 | "off": "sync-script", 164 | "on": "sync-script=?0" 165 | }, 166 | "examples": { 167 | "header": [ 168 | "Document-Policy: sync-script=?0" 169 | ] 170 | } 171 | }, { 172 | "id": "vertical-scroll", 173 | "name": "Vertical scroll", 174 | "type": "granular", 175 | "url": "/demos/vertical-scroll.html", 176 | "what": "Controls whether embedded content can interfere with vertical scrolling.", 177 | "why": "By default, iframe content can use touch-action: none, e.preventDefault() in touch events, and the DOM Scroll APIs to prevent and/or alter how content scrolls vertically. This policy ensures vertical scrolling is not blocked by preventing these features from working.", 178 | "policyType": "Permissions-Policy", 179 | "usage": { 180 | "off": "vertical-scroll=self", 181 | "on": "vertical-scroll=()" 182 | }, 183 | "examples": { 184 | "header": [ 185 | "Permissions-Policy: vertical-scroll=()" 186 | ], 187 | "iframe": [ 188 | "<iframe allow=\"vertical-scroll 'none'\" src=\"...\"><\/iframe>" 189 | ] 190 | } 191 | }] 192 | -------------------------------------------------------------------------------- /public/js/resources/async-script.js: -------------------------------------------------------------------------------- 1 | window.didExecuteAsyncScript= true; 2 | console.log('didExecuteAsyncScript'); 3 | document.currentScript.dataset.ran = true; 4 | -------------------------------------------------------------------------------- /public/js/resources/defer-script.js: -------------------------------------------------------------------------------- 1 | window.didExecuteDeferScript= true; 2 | console.log('didExecuteDeferScript'); 3 | document.currentScript.dataset.ran = true; 4 | -------------------------------------------------------------------------------- /public/js/resources/module.mjs: -------------------------------------------------------------------------------- 1 | window.didExecuteModule = true; 2 | console.log('didExecuteModule'); 3 | const src = new URL(import.meta.url).pathname; 4 | 5 | const thisScriptTag = document.querySelector(`script[src*="${src}"]`); 6 | thisScriptTag.dataset.ran = true; 7 | -------------------------------------------------------------------------------- /public/js/resources/parser-blocking-script.js: -------------------------------------------------------------------------------- 1 | window.didExecuteExternalParsingBlockingScript = true; 2 | console.log('didExecuteExternalParsingBlockingScript'); 3 | document.currentScript.dataset.ran = true; 4 | -------------------------------------------------------------------------------- /public/js/shared.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import {html, render} from '/lit-html/lit-html.js'; 18 | import {unsafeHTML} from '/lit-html/directives/unsafe-html.js'; 19 | import {ifDefined} from '/lit-html/directives/if-defined.js'; 20 | 21 | export const policyOn = new URL(location).searchParams.has('on'); 22 | export const currentPolicyId = new URL(location).pathname.split('/').slice(-1)[0].split('.')[0]; 23 | 24 | export const permissionsPolicyAPISupported = 25 | 'featurePolicy' in document || 26 | 'permissionsPolicy' in document; 27 | 28 | const permissionsPolicy = 29 | document.featurePolicy || 30 | document.permissionsPolicy; 31 | 32 | let policies = []; 33 | let fetchingPoliciesPromise = null; 34 | 35 | /** 36 | * @return {boolean} True if page is top-level and not an iframe. 37 | */ 38 | function inFrame() { 39 | return window.self !== window.top; 40 | } 41 | 42 | /** 43 | * Fetches the list of feature policies and metadata. 44 | * @return {!Promise} List of policies. 45 | */ 46 | async function fetchPolicies() { 47 | if (policies.length) { 48 | return policies; 49 | } 50 | 51 | // Create singleton to fetch json. 52 | if (fetchingPoliciesPromise) { 53 | return await fetchingPoliciesPromise; 54 | } 55 | 56 | fetchingPoliciesPromise = fetch('/js/policies.json').then(resp => resp.json()); 57 | policies = await fetchingPoliciesPromise; 58 | 59 | // Determine if policy is supported in browser. 60 | policies.forEach(policy => { 61 | policy.supported = policySupported(policy.id); 62 | }); 63 | 64 | return policies; 65 | } 66 | 67 | /** 68 | * Lookups up a policy by id 69 | * @param {string} id 70 | * @return {!Promise} Found policy 71 | */ 72 | async function getPolicy(id) { 73 | const policies = await fetchPolicies(); 74 | return policies.find(item => item.id === id); 75 | } 76 | 77 | /** 78 | * Returns true if the policy is supported by the browser. 79 | * @param {string} id 80 | * @return {!Boolean} 81 | */ 82 | function policySupported(id) { 83 | if (!permissionsPolicyAPISupported) { 84 | return false; 85 | } 86 | return permissionsPolicy.allowedFeatures().findIndex(el => el === id) !== -1; 87 | } 88 | 89 | /** 90 | * Shows the policy metadata UI element. 91 | */ 92 | function showDetails() { 93 | const details = document.querySelector('details'); 94 | if (details) { 95 | details.style.display = 'block'; 96 | } 97 | } 98 | 99 | /** 100 | * Render the policy value selection bar html string based on policy object 101 | * given. 102 | * 103 | * @param {Object} policy 104 | * @return {string} 105 | */ 106 | function policyValueSelector(policy) { 107 | const desc = policy.usage_desc ? html `${policy.usage_desc}:` : ''; 108 | const optionButtons = Object.entries(policy.usage) 109 | .sort(([ka, va], [kb, vb]) => ka < kb) 110 | .map(([policyValue, _]) => { 111 | const currentPolicyValue = new URL(location).search.slice(1); 112 | const active = policyValue.localeCompare(currentPolicyValue) === 0 ? 'active' : undefined; 113 | return html ` 114 | ${policyValue}`; 117 | }); 118 | 119 | return html ` 120 | 121 | ${desc} 122 | ${optionButtons} 123 | 124 | `; 125 | } 126 | 127 | /** 128 | * Render the not supported banner html string based on policy object given. 129 | * @param {!Object} policy 130 | * @return {string} 131 | */ 132 | function notSupportedBanner(policy) { 133 | if ('Document-Policy'.localeCompare(policy.policyType) === 0) { 134 | return html ` 135 |
    136 | This policy is experimental
    137 | 138 | Please make sure you are running Chrome Canary with the 139 | --enable-experimental-web-platform-features flag.
    140 |
    `; 141 | } else { 142 | return html ` 143 |
    144 | This policy is not supported in your browser.
    145 | Try running Chrome Canary with the 146 | --enable-experimental-web-platform-features flag.
    147 |
    `; 148 | } 149 | } 150 | 151 | 152 | /** 153 | * Updates the UI metadata header when a policy is selected. 154 | * @param {!Object} policy 155 | */ 156 | function updateDetailsHeader(policy) { 157 | if (!policy) { 158 | return; 159 | } 160 | 161 | const examples = [...policy.examples.header, ...policy.examples.iframe || []] 162 | .map(item => `
    ${item}
    `).join(''); 163 | 164 | const tmpl = html` 165 | 166 | ${policy.name} 167 | ${policyValueSelector(policy)} 168 | 169 | 170 |
      171 |
    • ${unsafeHTML(policy.what)}
    • 172 |
    • ${unsafeHTML(policy.why)}
    • 173 |
    • ${unsafeHTML(examples)}
    • 174 |
    175 | ${notSupportedBanner(policy)}`; 176 | 177 | render(tmpl, document.querySelector('.details')); 178 | } 179 | 180 | /** 181 | * Adds a banner to the page to show if a policy is enable/disabled. 182 | * @param {string} policyId 183 | * @return {boolean} True if the current policy is enabled. 184 | */ 185 | function updateAllowBanner(policyId) { 186 | const allowsFeature = permissionsPolicy.allowsFeature(policyId); 187 | const allows = allowsFeature ? 'enables' : 'disables'; 188 | const banner = document.querySelector('#feature-allowed-banner'); 189 | if (!banner) { 190 | return; 191 | } 192 | banner.classList.toggle('allows', allowsFeature); 193 | banner.classList.add('show'); 194 | document.querySelector('#allowfeature').textContent = `Page ${allows} ${currentPolicyId}.`; 195 | 196 | return allowsFeature; 197 | } 198 | 199 | /** 200 | * Inits top header/banner for demos pages. 201 | */ 202 | function initPage() { 203 | if (!inFrame()) { 204 | showDetails(); 205 | getPolicy(currentPolicyId).then(policy => { 206 | updateDetailsHeader(policy); 207 | }); 208 | } 209 | updateAllowBanner(currentPolicyId); 210 | } 211 | 212 | export { 213 | fetchPolicies, 214 | getPolicy, 215 | initPage, 216 | updateDetailsHeader, 217 | }; 218 | -------------------------------------------------------------------------------- /public/styles/header.css: -------------------------------------------------------------------------------- 1 | details.details { 2 | display: none; 3 | margin-bottom: var(--default-padding-4x); 4 | } 5 | .details { 6 | background: var(--md-blue-grey-50); 7 | border-radius: 5px; 8 | padding: var(--default-padding-4x); 9 | } 10 | .details .trylinks { 11 | position: absolute; 12 | right: 0; 13 | } 14 | .details .trylinks a { 15 | margin-left: var(--default-padding-2x); 16 | text-transform: uppercase; 17 | font-size: 18px; 18 | border-radius: var(--default-border-radius); 19 | padding: var(--default-padding) var(--default-padding-2x); 20 | background: white; 21 | text-decoration: none; 22 | } 23 | .details .trylinks .try-button { 24 | /* border-bottom: 4px solid transparent; */ 25 | --disable-color: var(--md-blue-grey-100); 26 | --enable-color: var(--md-blue-grey-500); 27 | } 28 | .details .trylinks .try-button:hover { 29 | border-bottom: 4px solid var(--enable-color) !important; 30 | } 31 | .details .trylinks a[active] { 32 | border-bottom: 4px solid var(--enable-color); 33 | font-weight: 600; 34 | } 35 | .details .trylinks a:not([active]) { 36 | border-bottom: 4px solid var(--disable-color); 37 | } 38 | .details summary { 39 | position: relative; 40 | color: var(--title-color); 41 | font-size: var(--default-padding-3x); 42 | } 43 | .details ul { 44 | list-style: none; 45 | padding: 0; 46 | margin: var(--default-padding-3x) 0 0 0; 47 | } 48 | .details li { 49 | display: flex; 50 | margin-bottom: 8px; 51 | flex: 0 0 auto; 52 | } 53 | .details .policyname { 54 | font-weight: 600; 55 | } 56 | .details .menu-button { 57 | position: absolute; 58 | top: 0; 59 | right: 0; 60 | display: none; 61 | } 62 | .details-info label { 63 | flex: 0 0 150px; 64 | display: inline-block; 65 | font-weight: 600; 66 | } 67 | .details-info pre { 68 | margin: 0 0 var(--default-padding) 0; 69 | white-space: pre-wrap; 70 | } 71 | .details-info pre:last-child { 72 | margin-bottom: 0; 73 | } 74 | .details .flag-icon { 75 | opacity: 1; 76 | } 77 | @media only screen and (max-width: 450px) { 78 | .details { 79 | padding: var(--default-padding) var(--default-padding-2x); 80 | } 81 | .details .trylinks { 82 | position: relative; 83 | margin: var(--default-padding) 0; 84 | display: block; 85 | } 86 | .details .trylinks a { 87 | margin: 0 var(--default-padding) 0 0; 88 | } 89 | .details li { 90 | display: block; 91 | } 92 | .details li label { 93 | margin-right: var(--default-padding); 94 | } 95 | .details .menu-button { 96 | display: block; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /public/styles/main.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --app-drawer-width: 305px; 3 | --app-primary-color: #E91E63; 4 | --app-secondary-color: #293237; 5 | --app-dark-text-color: var(--app-secondary-color); 6 | --app-light-text-color: white; 7 | --app-section-even-color: #f7f7f7; 8 | --app-section-odd-color: white; 9 | --app-header-background-color: white; 10 | --app-header-text-color: var(--app-dark-text-color); 11 | --app-header-selected-color: var(--app-primary-color); 12 | --app-drawer-background-color: var(--app-secondary-color); 13 | --app-drawer-text-color: var(--app-light-text-color); 14 | --app-drawer-selected-color: #78909C; 15 | 16 | --title-color: var(--md-blue-grey-900); 17 | --md-blue-grey-50: #ECEFF1; 18 | --md-blue-grey-100: #CFD8DC; 19 | --md-blue-grey-200: #B0BEC5; 20 | --md-blue-grey-300: #90A4AE; 21 | --md-blue-grey-400: #78909C; 22 | --md-blue-grey-500: #607D8B; 23 | --md-blue-grey-700: #455A64; 24 | --md-blue-grey-900: #263238; 25 | --md-green-500: #4caf50; 26 | --md-red-500: #f44336; 27 | --md-blue-700: #0288d1; 28 | --md-blue-800: #1565c0; 29 | --default-padding: 8px; 30 | --default-padding-2x: 16px; 31 | --default-padding-3x: 24px; 32 | --default-padding-4x: 32px; 33 | 34 | --default-border-radius: 3px; 35 | } 36 | * { 37 | box-sizing: border-box; 38 | } 39 | html, body { 40 | margin: 0; 41 | padding: 0; 42 | font-family: "Google Sans", "Roboto", sans-serif; 43 | -webkit-font-smoothing: antialiased; 44 | line-height: 1.6; 45 | font-weight: 300; 46 | color: var(--md-blue-grey-700); 47 | overflow-x: hidden; 48 | } 49 | a { 50 | color: inherit; 51 | } 52 | h1, h2, h3, h4 { 53 | margin: 0; 54 | font-weight: inherit; 55 | } 56 | .layout { 57 | display: flex; 58 | } 59 | .layout.center { 60 | justify-content: center; 61 | } 62 | .layout.center-center { 63 | justify-content: center; 64 | align-items: center; 65 | } 66 | .layout.vertical { 67 | flex-direction: column; 68 | } 69 | .layout.wrap { 70 | flex-wrap: wrap; 71 | } 72 | .flex { 73 | flex: 1 0 auto; 74 | } 75 | .relative { 76 | position: relative; 77 | } 78 | .body-padding { 79 | margin: var(--default-padding-4x); 80 | } 81 | #feature-allowed-banner { 82 | padding: var(--default-padding); 83 | margin-bottom: var(--default-padding-2x); 84 | color: #fff; 85 | background-color: var(--md-blue-800); 86 | text-align: center; 87 | visibility: hidden; 88 | border-radius: var(--default-border-radius); 89 | } 90 | #feature-allowed-banner.show { 91 | visibility: visible; 92 | } 93 | .label { 94 | font-weight: 600; 95 | } 96 | .drawer-list { 97 | color: var(--app-drawer-text-color); 98 | position: fixed; 99 | width: var(--app-drawer-width); 100 | height: 100%; 101 | padding: var(--default-padding-4x); 102 | background: var(--app-drawer-background-color); 103 | line-height: 40px; 104 | overflow-y: auto; 105 | overscroll-behavior: contain; 106 | transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1); 107 | z-index: 2; 108 | } 109 | .drawer-list.active { 110 | transform: none; 111 | } 112 | .drawer-list header { 113 | line-height: 1; 114 | display: flex; 115 | flex: 0 0 auto; 116 | margin-bottom: var(--default-padding-3x); 117 | } 118 | .drawer-list img { 119 | margin-right: var(--default-padding-2x); 120 | } 121 | .drawer-list .drawer-title { 122 | font-size: 20px; 123 | } 124 | .drawer-list .drawer-subtitle { 125 | font-size: 37px; 126 | text-indent: -2px; 127 | margin-top: 4px; 128 | text-transform: uppercase; 129 | } 130 | .drawer-list header img { 131 | width: 56px; 132 | height: 56px; 133 | } 134 | .drawer-list a { 135 | text-decoration: none; 136 | } 137 | .drawer-list .flag-icon { 138 | display: inline-block; 139 | } 140 | .drawer-list .flag-icon.supported { 141 | display: none; 142 | } 143 | .main-view { 144 | padding-left: var(--app-drawer-width); 145 | height: 100vh; 146 | overflow-y: hidden; 147 | } 148 | .main-view iframe { 149 | width: 100%; 150 | height: 100vh; 151 | border: none; 152 | padding-bottom: 350px; 153 | } 154 | .policy-name { 155 | display: block; 156 | padding-left: var(--default-padding-2x); 157 | } 158 | .policy-id { 159 | white-space: nowrap; 160 | color: var(--md-blue-grey-400); 161 | } 162 | .policy-type { 163 | text-transform: uppercase; 164 | letter-spacing: 0.05em; 165 | font-weight: 600; 166 | } 167 | .policy-type { 168 | margin-top: var(--default-padding); 169 | } 170 | #policy-list .policy-type:first-of-type { 171 | margin-top: 0; 172 | } 173 | footer { 174 | z-index: 1; 175 | position: fixed; 176 | left: 0; 177 | right: 0; 178 | bottom: 0; 179 | padding: var(--default-padding-2x); 180 | background-color: var(--md-blue-grey-50); 181 | padding-left: var(--app-drawer-width); 182 | } 183 | footer a { 184 | line-height: 1; 185 | opacity: 0.8; 186 | } 187 | footer a:hover { 188 | opacity: 1; 189 | } 190 | footer .logo { 191 | height: 25px; 192 | width: auto; 193 | } 194 | pre.snippet { 195 | padding: var(--default-padding-2x); 196 | background: var(--md-blue-grey-400); 197 | color: #fff; 198 | font-family: "Courier New"; 199 | font-weight: 600; 200 | font-size: 14px; 201 | border-radius: var(--default-border-radius); 202 | } 203 | .flag-icon { 204 | height: 24px; 205 | width: 24px; 206 | vertical-align: middle; 207 | margin-right: 3px; 208 | opacity: 0.25; 209 | } 210 | @media only screen and (max-width: 450px) { 211 | #intro-summary { 212 | font-size: 11px; 213 | } 214 | .drawer-list { 215 | transform: translate3d(-100%,0,0); 216 | } 217 | .main-view { 218 | padding-left: 0; 219 | } 220 | .body-padding { 221 | margin: var(--default-padding-2x); 222 | } 223 | footer { 224 | padding-left: var(--default-padding-2x); 225 | } 226 | } -------------------------------------------------------------------------------- /public/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } -------------------------------------------------------------------------------- /public/testpage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Feature policy examples 8 | 35 | 36 | 37 | 38 |
    39 | Test stuff: 40 | 41 | 42 | 43 | 44 | 45 |
    46 | 47 |
    48 | 49 |
    50 |
    51 | 52 |
    FAILS unsized-media because image is not sized 53 | explicity in HTML or CSS. Image will be sized to 300x150.
    54 |
    55 |
    56 | 57 | 58 |
    FAILS max-downscaling-image because image is 59 | scaled down > 0.5 it's natural size. Image colors will be inverted.
    60 |
    61 |
    62 | 63 |
    64 |
    65 | 66 |
    FAILS image-compression because image 67 | contains EXIF metdata making filesize greater than width x height. Image 68 | colors will be inverted.
    69 |
    70 |
    71 | 72 |
    PASSES image-compression b/c because image 73 | contains no EXIF metdata.
    74 |
    75 |
    76 | 77 |
    78 |
    79 | 80 |
    FAILS legacy-image-formats because it's a BMP. Image colors will be inverted.
    81 |
    82 |
    83 |
    84 | 85 |
    86 |
    Shows geolocation failing when policy prevents it.
    87 |
    88 |
    89 | 90 |
    91 |
    92 | 93 |
    FAILS autoplay when policy is set.
    94 |
    95 |
    96 | 97 |
    FAILS camera/microphone access when policy is set.
    98 |
    99 |
    100 | 101 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /public/video/mixkit-rain-falling-from-the-roof-on-a-rainy-day-2716.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/feature-policy-demos/89d7e1db151fcbbe6415784d3d8fd0baa399df59/public/video/mixkit-rain-falling-from-the-roof-on-a-rainy-day-2716.mp4 -------------------------------------------------------------------------------- /server.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | import fs from 'fs'; 20 | import express from 'express'; 21 | import bodyParser from 'body-parser'; 22 | 23 | import {PermissionsPolicyHeader} from './permissions-policy-header.mjs'; 24 | 25 | const OT_TOKEN_UNOPT_IMAGES = 'AiGYzl8A17mcET9ObZ6QbC5vmOlCWk+4jZwZptDwKw8Iguu3jX2e6WVzUbHZpW0zPgqZdq/WSSUysH2chjMtCA4AAAByeyJvcmlnaW4iOiJodHRwczovL2ZlYXR1cmUtcG9saWN5LWRlbW9zLmFwcHNwb3QuY29tOjQ0MyIsImZlYXR1cmUiOiJVbm9wdGltaXplZEltYWdlUG9saWNpZXMiLCJleHBpcnkiOjE1NjQ2Nzg1NjF9'; 26 | const OT_TOKEN_UNOPT_IMAGES_LOCALHOST = 'AuqelXxw7r91rz8mkV5fJnMkjNXY6vtmpd8lzATN2KGpwd0D6akFg7GBtigifHHuqk7zAnOvo2NlUnmAQTmSTQkAAABbeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJmZWF0dXJlIjoiVW5vcHRpbWl6ZWRJbWFnZVBvbGljaWVzIiwiZXhwaXJ5IjoxNTY0Njc4MzU1fQ=='; 27 | 28 | /* eslint-disable */ 29 | function errorHandler(err, req, res, next) { 30 | if (res.headersSent) { 31 | return next(err); 32 | } 33 | console.error('errorHandler', err); 34 | res.status(500).send({errors: `Error running your code. ${err}`}); 35 | } 36 | /* eslint-enable */ 37 | 38 | const app = express(); 39 | app.use(bodyParser.urlencoded({ 40 | extended: false, 41 | })); 42 | 43 | app.use(function forceSSL(req, res, next) { 44 | const fromCron = req.get('X-Appengine-Cron'); 45 | if (!fromCron && req.hostname !== 'localhost' && req.get('X-Forwarded-Proto') === 'http') { 46 | return res.redirect(`https://${req.hostname}${req.url}`); 47 | } 48 | next(); 49 | }); 50 | 51 | app.use(function commonHeaders(req, res, next) { 52 | res.set('Access-Control-Allow-Origin', '*'); 53 | // TODO: Re-enable when OT working correctly 54 | // res.set('Origin-Trial', OT_TOKEN_UNOPT_IMAGES); 55 | // res.set('Origin-Trial', OT_TOKEN_UNOPT_IMAGES_LOCALHOST); 56 | next(); 57 | }); 58 | 59 | app.use('/demos/:demoPage', function attachHeader(req, res, next) { 60 | const demoPage = req.params.demoPage; 61 | const policies = app.get('policies'); 62 | 63 | const targetPolicies = policies.filter(p => 64 | `/demos/${demoPage}`.localeCompare(p.url) === 0); 65 | if (targetPolicies.length == 1) { 66 | const targetPolicy = targetPolicies[0]; 67 | const requestParamNames = Object.keys(req.query); 68 | 69 | if (requestParamNames.empty) next(); 70 | const usageKey = requestParamNames[0]; 71 | 72 | if (usageKey in targetPolicy.usage) { 73 | const policyType = targetPolicy.policyType; 74 | const policyValue = targetPolicy.usage[usageKey]; 75 | 76 | res.append(policyType, policyValue); 77 | 78 | // Attach 'Feature-Policy' header as well for backward compatibility. 79 | if (policyType === 'Permissions-Policy') { 80 | const featurePolicyValue = 81 | PermissionsPolicyHeader.parse(policyValue).toFeaturePolicy().serialize(); 82 | res.append('Feature-Policy', featurePolicyValue); 83 | } 84 | } 85 | } 86 | 87 | next(); 88 | }); 89 | 90 | app.get('/test', (req, res, next) => { 91 | const on = 'on' in req.query; 92 | if (on) { 93 | res.append('Feature-Policy', `max-downscaling-image 'none'`); 94 | res.append('Feature-Policy', `image-compression 'none'`); 95 | res.append('Feature-Policy', `legacy-image-formats 'none'`); 96 | res.append('Feature-Policy', `geolocation 'none'`); 97 | res.append('Feature-Policy', `camera 'none'; microphone 'none'`); 98 | res.append('Feature-Policy', `autoplay 'self'`); 99 | res.append('Feature-Policy', `fullscreen 'none'`); 100 | res.append('Feature-Policy', `geolocation 'self'`); 101 | res.append('Feature-Policy', `midi 'none'`); 102 | res.append('Feature-Policy', `sync-xhr 'none'`); 103 | res.append('Feature-Policy', `vr 'none'`); 104 | res.append('Feature-Policy', `usb 'none'`); 105 | res.append('Feature-Policy', `payment 'none'`); 106 | } 107 | res.send(fs.readFileSync('./public/testpage.html', {encoding: 'utf-8'})); 108 | }); 109 | 110 | app.get('/:demoPage', (req, res, next) => { 111 | res.send(fs.readFileSync('./public/index.html', {encoding: 'utf-8'})); 112 | }); 113 | 114 | app.use(express.static('public', {extensions: ['html', 'htm']})); 115 | app.use(express.static('node_modules')); 116 | 117 | app.use(errorHandler); 118 | 119 | const PORT = process.env.PORT || 8080; 120 | fs.promises.readFile('./public/js/policies.json') 121 | .then(f => JSON.parse(f)) 122 | .then(policies => { 123 | app.set('policies', policies); 124 | app.listen(PORT, () => { 125 | console.log(`App listening on port ${PORT}`); /* eslint-disable-line */ 126 | console.log('Press Ctrl+C to quit.'); /* eslint-disable-line */ 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.0.0" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" 8 | integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== 9 | dependencies: 10 | "@babel/highlight" "^7.0.0" 11 | 12 | "@babel/highlight@^7.0.0": 13 | version "7.0.0" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" 15 | integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | accepts@~1.3.5: 22 | version "1.3.5" 23 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" 24 | integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= 25 | dependencies: 26 | mime-types "~2.1.18" 27 | negotiator "0.6.1" 28 | 29 | acorn-jsx@^5.0.0: 30 | version "5.0.1" 31 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" 32 | integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== 33 | 34 | acorn@^6.0.2: 35 | version "6.4.1" 36 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" 37 | integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== 38 | 39 | ajv@^6.5.3: 40 | version "6.5.4" 41 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.4.tgz#247d5274110db653706b550fcc2b797ca28cfc59" 42 | integrity sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg== 43 | dependencies: 44 | fast-deep-equal "^2.0.1" 45 | fast-json-stable-stringify "^2.0.0" 46 | json-schema-traverse "^0.4.1" 47 | uri-js "^4.2.2" 48 | 49 | ajv@^6.6.1: 50 | version "6.6.2" 51 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d" 52 | integrity sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g== 53 | dependencies: 54 | fast-deep-equal "^2.0.1" 55 | fast-json-stable-stringify "^2.0.0" 56 | json-schema-traverse "^0.4.1" 57 | uri-js "^4.2.2" 58 | 59 | ansi-escapes@^3.0.0: 60 | version "3.1.0" 61 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" 62 | integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== 63 | 64 | ansi-regex@^3.0.0: 65 | version "3.0.0" 66 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 67 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 68 | 69 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 70 | version "3.2.1" 71 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 72 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 73 | dependencies: 74 | color-convert "^1.9.0" 75 | 76 | argparse@^1.0.7: 77 | version "1.0.10" 78 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 79 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 80 | dependencies: 81 | sprintf-js "~1.0.2" 82 | 83 | array-flatten@1.1.1: 84 | version "1.1.1" 85 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 86 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 87 | 88 | array-union@^1.0.1: 89 | version "1.0.2" 90 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 91 | integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= 92 | dependencies: 93 | array-uniq "^1.0.1" 94 | 95 | array-uniq@^1.0.1: 96 | version "1.0.3" 97 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 98 | integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= 99 | 100 | arrify@^1.0.0: 101 | version "1.0.1" 102 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 103 | integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= 104 | 105 | astral-regex@^1.0.0: 106 | version "1.0.0" 107 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" 108 | integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== 109 | 110 | balanced-match@^1.0.0: 111 | version "1.0.0" 112 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 113 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 114 | 115 | body-parser@1.18.3: 116 | version "1.18.3" 117 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" 118 | integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= 119 | dependencies: 120 | bytes "3.0.0" 121 | content-type "~1.0.4" 122 | debug "2.6.9" 123 | depd "~1.1.2" 124 | http-errors "~1.6.3" 125 | iconv-lite "0.4.23" 126 | on-finished "~2.3.0" 127 | qs "6.5.2" 128 | raw-body "2.3.3" 129 | type-is "~1.6.16" 130 | 131 | brace-expansion@^1.1.7: 132 | version "1.1.11" 133 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 134 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 135 | dependencies: 136 | balanced-match "^1.0.0" 137 | concat-map "0.0.1" 138 | 139 | bytes@3.0.0: 140 | version "3.0.0" 141 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" 142 | integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= 143 | 144 | caller-path@^0.1.0: 145 | version "0.1.0" 146 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 147 | integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= 148 | dependencies: 149 | callsites "^0.2.0" 150 | 151 | callsites@^0.2.0: 152 | version "0.2.0" 153 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 154 | integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= 155 | 156 | chalk@^2.0.0, chalk@^2.1.0: 157 | version "2.4.1" 158 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 159 | integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== 160 | dependencies: 161 | ansi-styles "^3.2.1" 162 | escape-string-regexp "^1.0.5" 163 | supports-color "^5.3.0" 164 | 165 | chardet@^0.7.0: 166 | version "0.7.0" 167 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 168 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== 169 | 170 | circular-json@^0.3.1: 171 | version "0.3.3" 172 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 173 | integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== 174 | 175 | cli-cursor@^2.1.0: 176 | version "2.1.0" 177 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 178 | integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= 179 | dependencies: 180 | restore-cursor "^2.0.0" 181 | 182 | cli-width@^2.0.0: 183 | version "2.2.0" 184 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 185 | integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= 186 | 187 | color-convert@^1.9.0: 188 | version "1.9.1" 189 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" 190 | integrity sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ== 191 | dependencies: 192 | color-name "^1.1.1" 193 | 194 | color-name@^1.1.1: 195 | version "1.1.3" 196 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 197 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 198 | 199 | concat-map@0.0.1: 200 | version "0.0.1" 201 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 202 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 203 | 204 | content-disposition@0.5.2: 205 | version "0.5.2" 206 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" 207 | integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= 208 | 209 | content-type@~1.0.4: 210 | version "1.0.4" 211 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 212 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 213 | 214 | cookie-signature@1.0.6: 215 | version "1.0.6" 216 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 217 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 218 | 219 | cookie@0.3.1: 220 | version "0.3.1" 221 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 222 | integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= 223 | 224 | cross-spawn@^6.0.5: 225 | version "6.0.5" 226 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 227 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 228 | dependencies: 229 | nice-try "^1.0.4" 230 | path-key "^2.0.1" 231 | semver "^5.5.0" 232 | shebang-command "^1.2.0" 233 | which "^1.2.9" 234 | 235 | debug@2.6.9: 236 | version "2.6.9" 237 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 238 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 239 | dependencies: 240 | ms "2.0.0" 241 | 242 | debug@^4.0.1: 243 | version "4.1.0" 244 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" 245 | integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== 246 | dependencies: 247 | ms "^2.1.1" 248 | 249 | deep-is@~0.1.3: 250 | version "0.1.3" 251 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 252 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 253 | 254 | del@^2.0.2: 255 | version "2.2.2" 256 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 257 | integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag= 258 | dependencies: 259 | globby "^5.0.0" 260 | is-path-cwd "^1.0.0" 261 | is-path-in-cwd "^1.0.0" 262 | object-assign "^4.0.1" 263 | pify "^2.0.0" 264 | pinkie-promise "^2.0.0" 265 | rimraf "^2.2.8" 266 | 267 | depd@~1.1.2: 268 | version "1.1.2" 269 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 270 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 271 | 272 | destroy@~1.0.4: 273 | version "1.0.4" 274 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 275 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 276 | 277 | doctrine@^2.1.0: 278 | version "2.1.0" 279 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 280 | integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== 281 | dependencies: 282 | esutils "^2.0.2" 283 | 284 | ee-first@1.1.1: 285 | version "1.1.1" 286 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 287 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 288 | 289 | encodeurl@~1.0.2: 290 | version "1.0.2" 291 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 292 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 293 | 294 | escape-html@~1.0.3: 295 | version "1.0.3" 296 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 297 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 298 | 299 | escape-string-regexp@^1.0.5: 300 | version "1.0.5" 301 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 302 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 303 | 304 | eslint-config-google@^0.11.0: 305 | version "0.11.0" 306 | resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.11.0.tgz#fd0fc70be2e9114df097cac93a9b5b0d4e1c6830" 307 | integrity sha512-z541Fs5TFaY7/35v/z100InQ2f3V2J7e3u/0yKrnImgsHjh6JWgSRngfC/mZepn/+XN16jUydt64k//kxXc1fw== 308 | 309 | eslint-scope@^4.0.0: 310 | version "4.0.0" 311 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" 312 | integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== 313 | dependencies: 314 | esrecurse "^4.1.0" 315 | estraverse "^4.1.1" 316 | 317 | eslint-utils@^1.3.1: 318 | version "1.4.3" 319 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" 320 | integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== 321 | dependencies: 322 | eslint-visitor-keys "^1.1.0" 323 | 324 | eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: 325 | version "1.1.0" 326 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" 327 | integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== 328 | 329 | eslint@^5.10.0: 330 | version "5.10.0" 331 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.10.0.tgz#24adcbe92bf5eb1fc2d2f2b1eebe0c5e0713903a" 332 | integrity sha512-HpqzC+BHULKlnPwWae9MaVZ5AXJKpkxCVXQHrFaRw3hbDj26V/9ArYM4Rr/SQ8pi6qUPLXSSXC4RBJlyq2Z2OQ== 333 | dependencies: 334 | "@babel/code-frame" "^7.0.0" 335 | ajv "^6.5.3" 336 | chalk "^2.1.0" 337 | cross-spawn "^6.0.5" 338 | debug "^4.0.1" 339 | doctrine "^2.1.0" 340 | eslint-scope "^4.0.0" 341 | eslint-utils "^1.3.1" 342 | eslint-visitor-keys "^1.0.0" 343 | espree "^5.0.0" 344 | esquery "^1.0.1" 345 | esutils "^2.0.2" 346 | file-entry-cache "^2.0.0" 347 | functional-red-black-tree "^1.0.1" 348 | glob "^7.1.2" 349 | globals "^11.7.0" 350 | ignore "^4.0.6" 351 | imurmurhash "^0.1.4" 352 | inquirer "^6.1.0" 353 | js-yaml "^3.12.0" 354 | json-stable-stringify-without-jsonify "^1.0.1" 355 | levn "^0.3.0" 356 | lodash "^4.17.5" 357 | minimatch "^3.0.4" 358 | mkdirp "^0.5.1" 359 | natural-compare "^1.4.0" 360 | optionator "^0.8.2" 361 | path-is-inside "^1.0.2" 362 | pluralize "^7.0.0" 363 | progress "^2.0.0" 364 | regexpp "^2.0.1" 365 | require-uncached "^1.0.3" 366 | semver "^5.5.1" 367 | strip-ansi "^4.0.0" 368 | strip-json-comments "^2.0.1" 369 | table "^5.0.2" 370 | text-table "^0.2.0" 371 | 372 | espree@^5.0.0: 373 | version "5.0.0" 374 | resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c" 375 | integrity sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA== 376 | dependencies: 377 | acorn "^6.0.2" 378 | acorn-jsx "^5.0.0" 379 | eslint-visitor-keys "^1.0.0" 380 | 381 | esprima@^4.0.0: 382 | version "4.0.1" 383 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 384 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 385 | 386 | esquery@^1.0.1: 387 | version "1.0.1" 388 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 389 | integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== 390 | dependencies: 391 | estraverse "^4.0.0" 392 | 393 | esrecurse@^4.1.0: 394 | version "4.2.1" 395 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 396 | integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== 397 | dependencies: 398 | estraverse "^4.1.0" 399 | 400 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 401 | version "4.2.0" 402 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 403 | integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= 404 | 405 | esutils@^2.0.2: 406 | version "2.0.2" 407 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 408 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 409 | 410 | etag@~1.8.1: 411 | version "1.8.1" 412 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 413 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 414 | 415 | express@^4.16.4: 416 | version "4.16.4" 417 | resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" 418 | integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== 419 | dependencies: 420 | accepts "~1.3.5" 421 | array-flatten "1.1.1" 422 | body-parser "1.18.3" 423 | content-disposition "0.5.2" 424 | content-type "~1.0.4" 425 | cookie "0.3.1" 426 | cookie-signature "1.0.6" 427 | debug "2.6.9" 428 | depd "~1.1.2" 429 | encodeurl "~1.0.2" 430 | escape-html "~1.0.3" 431 | etag "~1.8.1" 432 | finalhandler "1.1.1" 433 | fresh "0.5.2" 434 | merge-descriptors "1.0.1" 435 | methods "~1.1.2" 436 | on-finished "~2.3.0" 437 | parseurl "~1.3.2" 438 | path-to-regexp "0.1.7" 439 | proxy-addr "~2.0.4" 440 | qs "6.5.2" 441 | range-parser "~1.2.0" 442 | safe-buffer "5.1.2" 443 | send "0.16.2" 444 | serve-static "1.13.2" 445 | setprototypeof "1.1.0" 446 | statuses "~1.4.0" 447 | type-is "~1.6.16" 448 | utils-merge "1.0.1" 449 | vary "~1.1.2" 450 | 451 | external-editor@^3.0.0: 452 | version "3.0.3" 453 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" 454 | integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== 455 | dependencies: 456 | chardet "^0.7.0" 457 | iconv-lite "^0.4.24" 458 | tmp "^0.0.33" 459 | 460 | fast-deep-equal@^2.0.1: 461 | version "2.0.1" 462 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" 463 | integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= 464 | 465 | fast-json-stable-stringify@^2.0.0: 466 | version "2.0.0" 467 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 468 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 469 | 470 | fast-levenshtein@~2.0.4: 471 | version "2.0.6" 472 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 473 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 474 | 475 | figures@^2.0.0: 476 | version "2.0.0" 477 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 478 | integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= 479 | dependencies: 480 | escape-string-regexp "^1.0.5" 481 | 482 | file-entry-cache@^2.0.0: 483 | version "2.0.0" 484 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 485 | integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= 486 | dependencies: 487 | flat-cache "^1.2.1" 488 | object-assign "^4.0.1" 489 | 490 | finalhandler@1.1.1: 491 | version "1.1.1" 492 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" 493 | integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== 494 | dependencies: 495 | debug "2.6.9" 496 | encodeurl "~1.0.2" 497 | escape-html "~1.0.3" 498 | on-finished "~2.3.0" 499 | parseurl "~1.3.2" 500 | statuses "~1.4.0" 501 | unpipe "~1.0.0" 502 | 503 | flat-cache@^1.2.1: 504 | version "1.3.0" 505 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" 506 | integrity sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE= 507 | dependencies: 508 | circular-json "^0.3.1" 509 | del "^2.0.2" 510 | graceful-fs "^4.1.2" 511 | write "^0.2.1" 512 | 513 | forwarded@~0.1.2: 514 | version "0.1.2" 515 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 516 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= 517 | 518 | fresh@0.5.2: 519 | version "0.5.2" 520 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 521 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 522 | 523 | fs.realpath@^1.0.0: 524 | version "1.0.0" 525 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 526 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 527 | 528 | functional-red-black-tree@^1.0.1: 529 | version "1.0.1" 530 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 531 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 532 | 533 | glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: 534 | version "7.1.2" 535 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 536 | integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== 537 | dependencies: 538 | fs.realpath "^1.0.0" 539 | inflight "^1.0.4" 540 | inherits "2" 541 | minimatch "^3.0.4" 542 | once "^1.3.0" 543 | path-is-absolute "^1.0.0" 544 | 545 | globals@^11.7.0: 546 | version "11.7.0" 547 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" 548 | integrity sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg== 549 | 550 | globby@^5.0.0: 551 | version "5.0.0" 552 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 553 | integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0= 554 | dependencies: 555 | array-union "^1.0.1" 556 | arrify "^1.0.0" 557 | glob "^7.0.3" 558 | object-assign "^4.0.1" 559 | pify "^2.0.0" 560 | pinkie-promise "^2.0.0" 561 | 562 | graceful-fs@^4.1.2: 563 | version "4.1.11" 564 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 565 | integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= 566 | 567 | has-flag@^3.0.0: 568 | version "3.0.0" 569 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 570 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 571 | 572 | http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: 573 | version "1.6.3" 574 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" 575 | integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= 576 | dependencies: 577 | depd "~1.1.2" 578 | inherits "2.0.3" 579 | setprototypeof "1.1.0" 580 | statuses ">= 1.4.0 < 2" 581 | 582 | iconv-lite@0.4.23: 583 | version "0.4.23" 584 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" 585 | integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== 586 | dependencies: 587 | safer-buffer ">= 2.1.2 < 3" 588 | 589 | iconv-lite@^0.4.24: 590 | version "0.4.24" 591 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 592 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 593 | dependencies: 594 | safer-buffer ">= 2.1.2 < 3" 595 | 596 | ignore@^4.0.6: 597 | version "4.0.6" 598 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 599 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 600 | 601 | imurmurhash@^0.1.4: 602 | version "0.1.4" 603 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 604 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 605 | 606 | inflight@^1.0.4: 607 | version "1.0.6" 608 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 609 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 610 | dependencies: 611 | once "^1.3.0" 612 | wrappy "1" 613 | 614 | inherits@2, inherits@2.0.3: 615 | version "2.0.3" 616 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 617 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 618 | 619 | inquirer@^6.1.0: 620 | version "6.2.0" 621 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" 622 | integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== 623 | dependencies: 624 | ansi-escapes "^3.0.0" 625 | chalk "^2.0.0" 626 | cli-cursor "^2.1.0" 627 | cli-width "^2.0.0" 628 | external-editor "^3.0.0" 629 | figures "^2.0.0" 630 | lodash "^4.17.10" 631 | mute-stream "0.0.7" 632 | run-async "^2.2.0" 633 | rxjs "^6.1.0" 634 | string-width "^2.1.0" 635 | strip-ansi "^4.0.0" 636 | through "^2.3.6" 637 | 638 | ipaddr.js@1.8.0: 639 | version "1.8.0" 640 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" 641 | integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= 642 | 643 | is-fullwidth-code-point@^2.0.0: 644 | version "2.0.0" 645 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 646 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 647 | 648 | is-path-cwd@^1.0.0: 649 | version "1.0.0" 650 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 651 | integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= 652 | 653 | is-path-in-cwd@^1.0.0: 654 | version "1.0.1" 655 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" 656 | integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ== 657 | dependencies: 658 | is-path-inside "^1.0.0" 659 | 660 | is-path-inside@^1.0.0: 661 | version "1.0.1" 662 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" 663 | integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= 664 | dependencies: 665 | path-is-inside "^1.0.1" 666 | 667 | is-promise@^2.1.0: 668 | version "2.1.0" 669 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 670 | integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= 671 | 672 | isexe@^2.0.0: 673 | version "2.0.0" 674 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 675 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 676 | 677 | js-tokens@^4.0.0: 678 | version "4.0.0" 679 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 680 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 681 | 682 | js-yaml@^3.12.0: 683 | version "3.13.1" 684 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 685 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 686 | dependencies: 687 | argparse "^1.0.7" 688 | esprima "^4.0.0" 689 | 690 | json-schema-traverse@^0.4.1: 691 | version "0.4.1" 692 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 693 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 694 | 695 | json-stable-stringify-without-jsonify@^1.0.1: 696 | version "1.0.1" 697 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 698 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 699 | 700 | levn@^0.3.0, levn@~0.3.0: 701 | version "0.3.0" 702 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 703 | integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= 704 | dependencies: 705 | prelude-ls "~1.1.2" 706 | type-check "~0.3.2" 707 | 708 | lit-html@^0.14.0: 709 | version "0.14.0" 710 | resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-0.14.0.tgz#d6830e8f55fb923b0f5824decf7da082a1d22997" 711 | integrity sha512-+xqUPzzJGEDqb0F5DOnMXvL0jxpkKebAMlXycKZxFtzlmD+qePEmYrEUPF9XVXcc5eYBzYXTCOUcjSCod4YvgQ== 712 | 713 | lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5: 714 | version "4.17.21" 715 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 716 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 717 | 718 | media-typer@0.3.0: 719 | version "0.3.0" 720 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 721 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 722 | 723 | merge-descriptors@1.0.1: 724 | version "1.0.1" 725 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 726 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 727 | 728 | methods@~1.1.2: 729 | version "1.1.2" 730 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 731 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 732 | 733 | mime-db@~1.33.0: 734 | version "1.33.0" 735 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" 736 | integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== 737 | 738 | mime-types@~2.1.18: 739 | version "2.1.18" 740 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" 741 | integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== 742 | dependencies: 743 | mime-db "~1.33.0" 744 | 745 | mime@1.4.1: 746 | version "1.4.1" 747 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" 748 | integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== 749 | 750 | mimic-fn@^1.0.0: 751 | version "1.2.0" 752 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 753 | integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== 754 | 755 | minimatch@^3.0.4: 756 | version "3.0.4" 757 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 758 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 759 | dependencies: 760 | brace-expansion "^1.1.7" 761 | 762 | minimist@0.0.8: 763 | version "0.0.8" 764 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 765 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 766 | 767 | mkdirp@^0.5.1: 768 | version "0.5.1" 769 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 770 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 771 | dependencies: 772 | minimist "0.0.8" 773 | 774 | ms@2.0.0: 775 | version "2.0.0" 776 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 777 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 778 | 779 | ms@^2.1.1: 780 | version "2.1.1" 781 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 782 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 783 | 784 | mute-stream@0.0.7: 785 | version "0.0.7" 786 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 787 | integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= 788 | 789 | natural-compare@^1.4.0: 790 | version "1.4.0" 791 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 792 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 793 | 794 | negotiator@0.6.1: 795 | version "0.6.1" 796 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 797 | integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= 798 | 799 | nice-try@^1.0.4: 800 | version "1.0.4" 801 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" 802 | integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA== 803 | 804 | object-assign@^4.0.1: 805 | version "4.1.1" 806 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 807 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 808 | 809 | on-finished@~2.3.0: 810 | version "2.3.0" 811 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 812 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 813 | dependencies: 814 | ee-first "1.1.1" 815 | 816 | once@^1.3.0: 817 | version "1.4.0" 818 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 819 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 820 | dependencies: 821 | wrappy "1" 822 | 823 | onetime@^2.0.0: 824 | version "2.0.1" 825 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 826 | integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= 827 | dependencies: 828 | mimic-fn "^1.0.0" 829 | 830 | optionator@^0.8.2: 831 | version "0.8.2" 832 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 833 | integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= 834 | dependencies: 835 | deep-is "~0.1.3" 836 | fast-levenshtein "~2.0.4" 837 | levn "~0.3.0" 838 | prelude-ls "~1.1.2" 839 | type-check "~0.3.2" 840 | wordwrap "~1.0.0" 841 | 842 | os-tmpdir@~1.0.2: 843 | version "1.0.2" 844 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 845 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 846 | 847 | parseurl@~1.3.2: 848 | version "1.3.2" 849 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 850 | integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= 851 | 852 | path-is-absolute@^1.0.0: 853 | version "1.0.1" 854 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 855 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 856 | 857 | path-is-inside@^1.0.1, path-is-inside@^1.0.2: 858 | version "1.0.2" 859 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 860 | integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= 861 | 862 | path-key@^2.0.1: 863 | version "2.0.1" 864 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 865 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 866 | 867 | path-to-regexp@0.1.7: 868 | version "0.1.7" 869 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 870 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 871 | 872 | pify@^2.0.0: 873 | version "2.3.0" 874 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 875 | integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= 876 | 877 | pinkie-promise@^2.0.0: 878 | version "2.0.1" 879 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 880 | integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= 881 | dependencies: 882 | pinkie "^2.0.0" 883 | 884 | pinkie@^2.0.0: 885 | version "2.0.4" 886 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 887 | integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= 888 | 889 | pluralize@^7.0.0: 890 | version "7.0.0" 891 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 892 | integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== 893 | 894 | prelude-ls@~1.1.2: 895 | version "1.1.2" 896 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 897 | integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= 898 | 899 | progress@^2.0.0: 900 | version "2.0.0" 901 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" 902 | integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8= 903 | 904 | proxy-addr@~2.0.4: 905 | version "2.0.4" 906 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" 907 | integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== 908 | dependencies: 909 | forwarded "~0.1.2" 910 | ipaddr.js "1.8.0" 911 | 912 | punycode@^2.1.0: 913 | version "2.1.1" 914 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 915 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 916 | 917 | qs@6.5.2: 918 | version "6.5.2" 919 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" 920 | integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== 921 | 922 | range-parser@~1.2.0: 923 | version "1.2.0" 924 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 925 | integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= 926 | 927 | raw-body@2.3.3: 928 | version "2.3.3" 929 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" 930 | integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== 931 | dependencies: 932 | bytes "3.0.0" 933 | http-errors "1.6.3" 934 | iconv-lite "0.4.23" 935 | unpipe "1.0.0" 936 | 937 | regexpp@^2.0.1: 938 | version "2.0.1" 939 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" 940 | integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== 941 | 942 | require-uncached@^1.0.3: 943 | version "1.0.3" 944 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 945 | integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= 946 | dependencies: 947 | caller-path "^0.1.0" 948 | resolve-from "^1.0.0" 949 | 950 | resolve-from@^1.0.0: 951 | version "1.0.1" 952 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 953 | integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= 954 | 955 | restore-cursor@^2.0.0: 956 | version "2.0.0" 957 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 958 | integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= 959 | dependencies: 960 | onetime "^2.0.0" 961 | signal-exit "^3.0.2" 962 | 963 | rimraf@^2.2.8: 964 | version "2.6.2" 965 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 966 | integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== 967 | dependencies: 968 | glob "^7.0.5" 969 | 970 | run-async@^2.2.0: 971 | version "2.3.0" 972 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 973 | integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= 974 | dependencies: 975 | is-promise "^2.1.0" 976 | 977 | rxjs@^6.1.0: 978 | version "6.3.2" 979 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.2.tgz#6a688b16c4e6e980e62ea805ec30648e1c60907f" 980 | integrity sha512-hV7criqbR0pe7EeL3O66UYVg92IR0XsA97+9y+BWTePK9SKmEI5Qd3Zj6uPnGkNzXsBywBQWTvujPl+1Kn9Zjw== 981 | dependencies: 982 | tslib "^1.9.0" 983 | 984 | safe-buffer@5.1.2: 985 | version "5.1.2" 986 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 987 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 988 | 989 | "safer-buffer@>= 2.1.2 < 3": 990 | version "2.1.2" 991 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 992 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 993 | 994 | semver@^5.5.0: 995 | version "5.5.0" 996 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 997 | integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== 998 | 999 | semver@^5.5.1: 1000 | version "5.5.1" 1001 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" 1002 | integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== 1003 | 1004 | send@0.16.2: 1005 | version "0.16.2" 1006 | resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" 1007 | integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== 1008 | dependencies: 1009 | debug "2.6.9" 1010 | depd "~1.1.2" 1011 | destroy "~1.0.4" 1012 | encodeurl "~1.0.2" 1013 | escape-html "~1.0.3" 1014 | etag "~1.8.1" 1015 | fresh "0.5.2" 1016 | http-errors "~1.6.2" 1017 | mime "1.4.1" 1018 | ms "2.0.0" 1019 | on-finished "~2.3.0" 1020 | range-parser "~1.2.0" 1021 | statuses "~1.4.0" 1022 | 1023 | serve-static@1.13.2: 1024 | version "1.13.2" 1025 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" 1026 | integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== 1027 | dependencies: 1028 | encodeurl "~1.0.2" 1029 | escape-html "~1.0.3" 1030 | parseurl "~1.3.2" 1031 | send "0.16.2" 1032 | 1033 | setprototypeof@1.1.0: 1034 | version "1.1.0" 1035 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" 1036 | integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== 1037 | 1038 | shebang-command@^1.2.0: 1039 | version "1.2.0" 1040 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 1041 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 1042 | dependencies: 1043 | shebang-regex "^1.0.0" 1044 | 1045 | shebang-regex@^1.0.0: 1046 | version "1.0.0" 1047 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1048 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 1049 | 1050 | signal-exit@^3.0.2: 1051 | version "3.0.2" 1052 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 1053 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= 1054 | 1055 | slice-ansi@2.0.0: 1056 | version "2.0.0" 1057 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.0.0.tgz#5373bdb8559b45676e8541c66916cdd6251612e7" 1058 | integrity sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ== 1059 | dependencies: 1060 | ansi-styles "^3.2.0" 1061 | astral-regex "^1.0.0" 1062 | is-fullwidth-code-point "^2.0.0" 1063 | 1064 | sprintf-js@~1.0.2: 1065 | version "1.0.3" 1066 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 1067 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 1068 | 1069 | "statuses@>= 1.4.0 < 2": 1070 | version "1.5.0" 1071 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 1072 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 1073 | 1074 | statuses@~1.4.0: 1075 | version "1.4.0" 1076 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" 1077 | integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== 1078 | 1079 | string-width@^2.1.0, string-width@^2.1.1: 1080 | version "2.1.1" 1081 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 1082 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 1083 | dependencies: 1084 | is-fullwidth-code-point "^2.0.0" 1085 | strip-ansi "^4.0.0" 1086 | 1087 | strip-ansi@^4.0.0: 1088 | version "4.0.0" 1089 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1090 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 1091 | dependencies: 1092 | ansi-regex "^3.0.0" 1093 | 1094 | strip-json-comments@^2.0.1: 1095 | version "2.0.1" 1096 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1097 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 1098 | 1099 | supports-color@^5.3.0: 1100 | version "5.4.0" 1101 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 1102 | integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== 1103 | dependencies: 1104 | has-flag "^3.0.0" 1105 | 1106 | table@^5.0.2: 1107 | version "5.1.1" 1108 | resolved "https://registry.yarnpkg.com/table/-/table-5.1.1.tgz#92030192f1b7b51b6eeab23ed416862e47b70837" 1109 | integrity sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw== 1110 | dependencies: 1111 | ajv "^6.6.1" 1112 | lodash "^4.17.11" 1113 | slice-ansi "2.0.0" 1114 | string-width "^2.1.1" 1115 | 1116 | text-table@^0.2.0: 1117 | version "0.2.0" 1118 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1119 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 1120 | 1121 | through@^2.3.6: 1122 | version "2.3.8" 1123 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1124 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= 1125 | 1126 | tmp@^0.0.33: 1127 | version "0.0.33" 1128 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 1129 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== 1130 | dependencies: 1131 | os-tmpdir "~1.0.2" 1132 | 1133 | tslib@^1.9.0: 1134 | version "1.9.3" 1135 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" 1136 | integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== 1137 | 1138 | type-check@~0.3.2: 1139 | version "0.3.2" 1140 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1141 | integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= 1142 | dependencies: 1143 | prelude-ls "~1.1.2" 1144 | 1145 | type-is@~1.6.16: 1146 | version "1.6.16" 1147 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" 1148 | integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== 1149 | dependencies: 1150 | media-typer "0.3.0" 1151 | mime-types "~2.1.18" 1152 | 1153 | unpipe@1.0.0, unpipe@~1.0.0: 1154 | version "1.0.0" 1155 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1156 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 1157 | 1158 | uri-js@^4.2.2: 1159 | version "4.2.2" 1160 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 1161 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 1162 | dependencies: 1163 | punycode "^2.1.0" 1164 | 1165 | utils-merge@1.0.1: 1166 | version "1.0.1" 1167 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 1168 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 1169 | 1170 | vary@~1.1.2: 1171 | version "1.1.2" 1172 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1173 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 1174 | 1175 | which@^1.2.9: 1176 | version "1.3.1" 1177 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1178 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1179 | dependencies: 1180 | isexe "^2.0.0" 1181 | 1182 | wordwrap@~1.0.0: 1183 | version "1.0.0" 1184 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1185 | integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= 1186 | 1187 | wrappy@1: 1188 | version "1.0.2" 1189 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1190 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1191 | 1192 | write@^0.2.1: 1193 | version "0.2.1" 1194 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 1195 | integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= 1196 | dependencies: 1197 | mkdirp "^0.5.1" 1198 | --------------------------------------------------------------------------------