@samitouri / QOS-React-2 / commits / 675a29c3e9

[ci] Rebuild sizebot on the GitHub Actions token (#37322)

The build size comparison comment was posted by Danger, which authenticated with a personal access token hardcoded in `scripts/tasks/danger.js`. That token has since been revoked, so sizebot has been posting nothing at all (due to e.g. https://github.com/react/react/actions/runs/32181295467/job/95855395224?pr=37315). This change rebuilds it on the short-lived `GITHUB_TOKEN` that Actions mints per run and a new workflow only responsible for rendering untrusted JSON input as markdown in a PR comment. A straight token swap would not have worked. Fork pull requests did receive sizebot comments, but only because the token was in checked-out source: the sizebot job runs on the `pull_request` trigger, where a fork's `GITHUB_TOKEN` is read-only and cannot comment. The comment therefore moves to a new `workflow_run` workflow, `runtime_sizebot_comment.yml`, which runs in this repository with a writable token no matter where the pull request came from. It posts a placeholder when a build is requested and rewrites it in place when the build completes, fails, is cancelled, or is held for maintainer approval. The measurement stays on the unprivileged side of that boundary which are recorded as raw sizes into a `sizebot-results` artifact, and the new workflow downloads only that JSON and renders it from a default-branch checkout. The job holding `pull-requests: write` never unpacks a build produced by a fork, which matters because the existing base-build download justifies using an unverified artifact on the grounds that the job has restricted permissions. Thresholds, the critical bundle list, and the comment template all live on the trusted side, and the renderer validates every field it reads out of the artifact so that a crafted build path cannot inject markdown. The pull request number is resolved from the API rather than from the artifact, since a number read from fork-controlled data would let any contributor post a bot comment on an arbitrary pull request. Resolving that number needs a branch lookup rather than any of the obvious approaches. `workflow_run.pull_requests` is empty for fork runs, and neither `commits/{sha}/pulls` nor the search API indexes fork pull request head commits, so the workflow looks the pull request up by `owner:ref` instead. A comment is only ever left alone in one situation: when it already describes the pull request's current head and the event being handled belongs to an older commit. Everything else is written, and marked stale whenever the report does not describe the current head. That single rule covers both an old run finishing after a force push and a new build superseding a report already on display, and in the latter case the previous numbers stay visible instead of being blanked back to a placeholder. The results file carries a `version` field. Its writer is whatever `compare-sizes.js` a pull request branch happens to carry, while its reader is on the default branch, so the two can mismatch and the renderer needs to be able to say so instead of misrendering a table. Porting the table fixed a longstanding bug in `change()`. Testing `decimal < 0.0001` reported every size decrease as unchanged, which is why `signDisplay: 'exceptZero'` never had a negative number to render: a 709.04 kB to 708.68 kB drop printed as `=`. It now compares the magnitude. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>

Sebastian "Sebbie" Silbermann committed Aug 23, 2026 at 17:29 UTC 675a29c3e9869d0706dc5ed08779ae04186bd44f
11 files changed +990 -664
.eslintrc.js
-1
@@ -433,7 +433,6 @@ module.exports = {
433 'packages/*/npm/**/*.js',
434 'packages/dom-event-testing-library/**/*.js',
435 'packages/react-devtools*/**/*.js',
436 - 'dangerfile.js',
436 'fixtures',
437 'packages/react-dom/src/test-utils/*.js',
438 ],
.github/workflows/runtime_build_and_test.yml
+13 -4
@@ -1,3 +1,7 @@
1 +# Keep this name in sync with the `workflow_run.workflows` list in
2 +# runtime_sizebot_comment.yml, which matches on this exact string rather than on
3 +# the file name. Renaming it here alone stops sizebot from ever commenting again,
4 +# and nothing fails: the comment workflow simply never triggers.
5 name: (Runtime) Build and Test
6
7 on:
@@ -930,10 +934,15 @@ jobs:
934 - name: Display structure of build for PR
935 run: ls -R build
936 - run: echo ${{ github.event.pull_request.head.sha || github.sha }} >> build/COMMIT_SHA
933 - - run: node ./scripts/tasks/danger
937 + - name: Measure size changes
938 + # Only measures and records the numbers. The comment is rendered and
939 + # posted by runtime_sizebot_comment.yml, which runs on the workflow_run
940 + # trigger because this job's token is read-only for pull requests from
941 + # forks and so cannot comment.
942 + run: node ./scripts/sizebot/compare-sizes.js
943 - name: Archive sizebot results
944 uses: actions/upload-artifact@v4
945 with:
937 - name: sizebot-message
938 - path: sizebot-message.md
939 - if-no-files-found: ignore
946 + name: sizebot-results
947 + path: sizebot-results.json
948 + if-no-files-found: error
.github/workflows/runtime_sizebot_comment.yml new
+105
@@ -0,0 +1,105 @@
1 +name: (Runtime) Sizebot Comment
2 +
3 +# Posts the build size comparison comment on pull requests.
4 +#
5 +# This has to be a separate `workflow_run` workflow rather than a job inside
6 +# (Runtime) Build and Test: that workflow runs on the `pull_request` trigger, so
7 +# a pull request from a fork gets a read-only token and cannot comment. A
8 +# `workflow_run` workflow always runs in the context of this repository, on the
9 +# default branch, with a writable token.
10 +#
11 +# The measurement happens on the other side of that boundary, in the unprivileged
12 +# sizebot job, which uploads a `sizebot-results` artifact. This workflow only
13 +# downloads that small JSON file and renders it. It deliberately never unpacks a
14 +# build produced by a fork, because it holds a token that can write to the
15 +# repository.
16 +
17 +on:
18 + workflow_run:
19 + workflows: ['(Runtime) Build and Test']
20 + types: [requested, completed]
21 +
22 +permissions: {}
23 +
24 +concurrency:
25 + # Serialize per pull request. Both the requested and completed handlers read
26 + # the existing comment, decide against it and write it back, so they must not
27 + # interleave. Never cancel: every event either updates the comment or is
28 + # deliberately skipped, and dropping one loses a state transition.
29 + group: ${{ github.workflow }}-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
30 + cancel-in-progress: false
31 +
32 +env:
33 + TZ: /usr/share/zoneinfo/America/Los_Angeles
34 +
35 +jobs:
36 + comment:
37 + # Only pull request builds get a size comment. Runs from `push` and
38 + # `workflow_dispatch` have no pull request to comment on.
39 + if: ${{ github.event.workflow_run.event == 'pull_request' }}
40 + name: Comment with size changes
41 + runs-on: ubuntu-latest
42 + permissions:
43 + # We use github.token to download the sizebot results artifact from the
44 + # triggering runtime_build_and_test.yml run
45 + actions: read
46 + # Used to check out the renderer this workflow runs
47 + contents: read
48 + # Used to create and update the sizebot comment on the pull request
49 + pull-requests: write
50 + steps:
51 + # No `ref`, so this is the default branch rather than the pull request.
52 + # The thresholds, the critical bundle list and the comment template all
53 + # come from here and cannot be changed by the pull request being measured.
54 + - uses: actions/checkout@v4
55 + with:
56 + # This job holds a token that can write to the repository, and it has
57 + # no use for git credentials after the checkout.
58 + persist-credentials: false
59 +
60 + - name: Resolve pull request and existing comment
61 + id: resolve
62 + uses: actions/github-script@v7
63 + with:
64 + script: |
65 + const {resolve} = require(`${process.env.GITHUB_WORKSPACE}/scripts/sizebot/pull-request-comment.js`);
66 + await resolve({github, context, core});
67 +
68 + - name: Download sizebot results
69 + if: ${{ steps.resolve.outputs.action == 'continue' && steps.resolve.outputs.download_results == 'true' }}
70 + continue-on-error: true
71 + uses: actions/download-artifact@v4
72 + with:
73 + name: sizebot-results
74 + run-id: ${{ github.event.workflow_run.id }}
75 + github-token: ${{ github.token }}
76 +
77 + - name: Render comment
78 + if: ${{ steps.resolve.outputs.action == 'continue' }}
79 + run: node ./scripts/sizebot/render-comment.js
80 +
81 + - name: Archive full size report
82 + # Only written when the report is too large to fit in a comment, in which
83 + # case the comment links to this artifact.
84 + if: ${{ steps.resolve.outputs.action == 'continue' && hashFiles('sizebot-message.md') != '' }}
85 + uses: actions/upload-artifact@v4
86 + with:
87 + name: sizebot-message
88 + path: sizebot-message.md
89 +
90 + - name: Post comment
91 + if: ${{ steps.resolve.outputs.action == 'continue' }}
92 + uses: actions/github-script@v7
93 + with:
94 + script: |
95 + const {post} = require(`${process.env.GITHUB_WORKSPACE}/scripts/sizebot/pull-request-comment.js`);
96 + await post({github, context, core});
97 +
98 + - name: Fail if the build configuration drifted
99 + # The comment is posted first, so it explains the problem on the pull
100 + # request itself. This step exists so the drift also shows up as a failed
101 + # run rather than only in a comment.
102 + if: ${{ steps.resolve.outputs.action == 'continue' && hashFiles('sizebot-problem.txt') != '' }}
103 + run: |
104 + cat sizebot-problem.txt
105 + exit 1
.gitignore
+6
@@ -8,6 +8,12 @@ scripts/flow/*/.flowconfig
8 _SpecRunner.html
9 __benchmarks__
10 build/
11 +base-build/
12 +sizebot-comment.md
13 +sizebot-context.json
14 +sizebot-message.md
15 +sizebot-problem.txt
16 +sizebot-results.json
17 remote-repo/
18 coverage/
19 .module-cache
dangerfile.js deleted
-282
@@ -1,282 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -'use strict';
9 -
10 -/* eslint-disable no-for-of-loops/no-for-of-loops */
11 -
12 -// Hi, if this is your first time editing/reading a Dangerfile, here's a summary:
13 -// It's a JS runtime which helps you provide continuous feedback inside GitHub.
14 -//
15 -// You can see the docs here: http://danger.systems/js/
16 -//
17 -// If you want to test changes Danger, I'd recommend checking out an existing PR
18 -// and then running the `danger pr` command.
19 -//
20 -// You'll need a GitHub token, you can re-use this one:
21 -//
22 -// 0a7d5c3cad9a6dbec2d9 9a5222cf49062a4c1ef7
23 -//
24 -// (Just remove the space)
25 -//
26 -// So, for example:
27 -//
28 -// `DANGER_GITHUB_API_TOKEN=[ENV_ABOVE] yarn danger pr https://github.com/facebook/react/pull/11865
29 -
30 -const {markdown, danger, warn} = require('danger');
31 -const {promisify} = require('util');
32 -const glob = promisify(require('glob'));
33 -const gzipSize = require('gzip-size');
34 -const {writeFileSync} = require('fs');
35 -
36 -const {readFileSync, statSync} = require('fs');
37 -
38 -const BASE_DIR = 'base-build';
39 -const HEAD_DIR = 'build';
40 -
41 -const CRITICAL_THRESHOLD = 0.02;
42 -const SIGNIFICANCE_THRESHOLD = 0.002;
43 -const CRITICAL_ARTIFACT_PATHS = new Set([
44 - // We always report changes to these bundles, even if the change is
45 - // insignificant or non-existent.
46 - 'oss-stable/react-dom/cjs/react-dom.production.js',
47 - 'oss-stable/react-dom/cjs/react-dom-client.production.js',
48 - 'oss-experimental/react-dom/cjs/react-dom.production.js',
49 - 'oss-experimental/react-dom/cjs/react-dom-client.production.js',
50 - 'facebook-www/ReactDOM-prod.classic.js',
51 - 'facebook-www/ReactDOM-prod.modern.js',
52 -]);
53 -
54 -const kilobyteFormatter = new Intl.NumberFormat('en', {
55 - style: 'unit',
56 - unit: 'kilobyte',
57 - minimumFractionDigits: 2,
58 - maximumFractionDigits: 2,
59 -});
60 -
61 -function kbs(bytes) {
62 - return kilobyteFormatter.format(bytes / 1000);
63 -}
64 -
65 -const percentFormatter = new Intl.NumberFormat('en', {
66 - style: 'percent',
67 - signDisplay: 'exceptZero',
68 - minimumFractionDigits: 2,
69 - maximumFractionDigits: 2,
70 -});
71 -
72 -function change(decimal) {
73 - if (decimal === Infinity) {
74 - return 'New file';
75 - }
76 - if (decimal === -1) {
77 - return 'Deleted';
78 - }
79 - if (decimal < 0.0001) {
80 - return '=';
81 - }
82 - return percentFormatter.format(decimal);
83 -}
84 -
85 -const header = `
86 - | Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
87 - | ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;
88 -
89 -function row(result, baseSha, headSha) {
90 - const diffViewUrl = `https://react-builds.vercel.app/commits/${headSha}/files/${result.path}?compare=${baseSha}`;
91 - const rowArr = [
92 - `| [${result.path}](${diffViewUrl})`,
93 - `**${change(result.change)}**`,
94 - `${kbs(result.baseSize)}`,
95 - `${kbs(result.headSize)}`,
96 - `${change(result.changeGzip)}`,
97 - `${kbs(result.baseSizeGzip)}`,
98 - `${kbs(result.headSizeGzip)}`,
99 - ];
100 - return rowArr.join(' | ');
101 -}
102 -
103 -(async function () {
104 - // Use git locally to grab the commit which represents the place
105 - // where the branches differ
106 -
107 - const upstreamRepo = danger.github.pr.base.repo.full_name;
108 - if (upstreamRepo !== 'react/react') {
109 - // Exit unless we're running in the main repo
110 - return;
111 - }
112 -
113 - let headSha;
114 - let baseSha;
115 - try {
116 - headSha = String(readFileSync(HEAD_DIR + '/COMMIT_SHA')).trim();
117 - baseSha = String(readFileSync(BASE_DIR + '/COMMIT_SHA')).trim();
118 - } catch {
119 - warn(
120 - "Failed to read build artifacts. It's possible a build configuration " +
121 - 'has changed upstream. Try pulling the latest changes from the ' +
122 - 'main branch.'
123 - );
124 - return;
125 - }
126 -
127 - // Disable sizeBot in a Devtools Pull Request. Because that doesn't affect production bundle size.
128 - const commitFiles = [
129 - ...danger.git.created_files,
130 - ...danger.git.deleted_files,
131 - ...danger.git.modified_files,
132 - ];
133 - if (
134 - commitFiles.every(filename => filename.includes('packages/react-devtools'))
135 - )
136 - return;
137 -
138 - const resultsMap = new Map();
139 -
140 - // Find all the head (current) artifacts paths.
141 - const headArtifactPaths = await glob('**/*.js', {cwd: 'build'});
142 - for (const artifactPath of headArtifactPaths) {
143 - try {
144 - // This will throw if there's no matching base artifact
145 - const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
146 - const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
147 -
148 - const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
149 - const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
150 - resultsMap.set(artifactPath, {
151 - path: artifactPath,
152 - headSize,
153 - headSizeGzip,
154 - baseSize,
155 - baseSizeGzip,
156 - change: (headSize - baseSize) / baseSize,
157 - changeGzip: (headSizeGzip - baseSizeGzip) / baseSizeGzip,
158 - });
159 - } catch {
160 - // There's no matching base artifact. This is a new file.
161 - const baseSize = 0;
162 - const baseSizeGzip = 0;
163 - const headSize = statSync(HEAD_DIR + '/' + artifactPath).size;
164 - const headSizeGzip = gzipSize.fileSync(HEAD_DIR + '/' + artifactPath);
165 - resultsMap.set(artifactPath, {
166 - path: artifactPath,
167 - headSize,
168 - headSizeGzip,
169 - baseSize,
170 - baseSizeGzip,
171 - change: Infinity,
172 - changeGzip: Infinity,
173 - });
174 - }
175 - }
176 -
177 - // Check for base artifacts that were deleted in the head.
178 - const baseArtifactPaths = await glob('**/*.js', {cwd: 'base-build'});
179 - for (const artifactPath of baseArtifactPaths) {
180 - if (!resultsMap.has(artifactPath)) {
181 - const baseSize = statSync(BASE_DIR + '/' + artifactPath).size;
182 - const baseSizeGzip = gzipSize.fileSync(BASE_DIR + '/' + artifactPath);
183 - const headSize = 0;
184 - const headSizeGzip = 0;
185 - resultsMap.set(artifactPath, {
186 - path: artifactPath,
187 - headSize,
188 - headSizeGzip,
189 - baseSize,
190 - baseSizeGzip,
191 - change: -1,
192 - changeGzip: -1,
193 - });
194 - }
195 - }
196 -
197 - const results = Array.from(resultsMap.values());
198 - results.sort((a, b) => b.change - a.change);
199 -
200 - let criticalResults = [];
201 - for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
202 - const result = resultsMap.get(artifactPath);
203 - if (result === undefined) {
204 - throw new Error(
205 - 'Missing expected bundle. If this was an intentional change to the ' +
206 - 'build configuration, update Dangerfile.js accordingly: ' +
207 - artifactPath
208 - );
209 - }
210 - criticalResults.push(row(result, baseSha, headSha));
211 - }
212 -
213 - let significantResults = [];
214 - for (const result of results) {
215 - // If result exceeds critical threshold, add to top section.
216 - if (
217 - (result.change > CRITICAL_THRESHOLD ||
218 - 0 - result.change > CRITICAL_THRESHOLD ||
219 - // New file
220 - result.change === Infinity ||
221 - // Deleted file
222 - result.change === -1) &&
223 - // Skip critical artifacts. We added those earlier, in a fixed order.
224 - !CRITICAL_ARTIFACT_PATHS.has(result.path)
225 - ) {
226 - criticalResults.push(row(result, baseSha, headSha));
227 - }
228 -
229 - // Do the same for results that exceed the significant threshold. These
230 - // will go into the bottom, collapsed section. Intentionally including
231 - // critical artifacts in this section, too.
232 - if (
233 - result.change > SIGNIFICANCE_THRESHOLD ||
234 - 0 - result.change > SIGNIFICANCE_THRESHOLD ||
235 - result.change === Infinity ||
236 - result.change === -1
237 - ) {
238 - significantResults.push(row(result, baseSha, headSha));
239 - }
240 - }
241 -
242 - const message = `
243 -Comparing: ${baseSha}...${headSha}
244 -
245 -## Critical size changes
246 -
247 -Includes critical production bundles, as well as any change greater than ${
248 - CRITICAL_THRESHOLD * 100
249 - }%:
250 -
251 -${header}
252 -${criticalResults.join('\n')}
253 -
254 -## Significant size changes
255 -
256 -Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:
257 -
258 -${
259 - significantResults.length > 0
260 - ? `
261 -<details>
262 -<summary>Expand to show</summary>
263 -${header}
264 -${significantResults.join('\n')}
265 -</details>
266 -`
267 - : '(No significant changes)'
268 -}
269 -`;
270 -
271 - // GitHub comments are limited to 65536 characters.
272 - if (message.length > 65536) {
273 - // Make message available as an artifact
274 - writeFileSync('sizebot-message.md', message);
275 - markdown(
276 - 'The size diff is too large to display in a single comment. ' +
277 - `The GitHub action for this pull request contains an artifact called 'sizebot-message.md' with the full message.`
278 - );
279 - } else {
280 - markdown(message);
281 - }
282 -})();
package.json
-1
@@ -59,7 +59,6 @@
59 "confusing-browser-globals": "^1.0.9",
60 "core-js": "^3.6.4",
61 "create-react-class": "^15.6.3",
62 - "danger": "^11.2.3",
62 "error-stack-parser": "^2.0.6",
63 "eslint": "^7.7.0",
64 "eslint-config-prettier": "^6.9.0",
scripts/sizebot/compare-sizes.js new
+114
@@ -0,0 +1,114 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +'use strict';
9 +
10 +/* eslint-disable no-for-of-loops/no-for-of-loops */
11 +
12 +// Measures every build artifact in `build` against the base revision's artifacts
13 +// in `base-build` and writes the raw numbers to `sizebot-results.json`.
14 +//
15 +// This runs in the `pull_request` half of CI, where the GitHub token is
16 +// read-only, so it never talks to the API and never renders anything
17 +// user-facing. `render-comment.js` turns this JSON into the pull request comment
18 +// from a trusted checkout. See `.github/workflows/runtime_sizebot_comment.yml`
19 +// for why the two halves are separate.
20 +
21 +const {promisify} = require('util');
22 +const glob = promisify(require('glob'));
23 +const gzipSize = require('gzip-size');
24 +const {readFileSync, statSync, writeFileSync} = require('fs');
25 +
26 +// Bump on any incompatible change to the JSON below: added required fields,
27 +// renamed or removed fields, or a changed meaning for an existing one. Purely
28 +// additive optional fields do not need a bump. The reader lives on the default
29 +// branch while the writer lives on the pull request branch, so the two can
30 +// legitimately disagree and `render-comment.js` needs to be able to tell.
31 +const RESULTS_VERSION = 1;
32 +
33 +const RESULTS_PATH = 'sizebot-results.json';
34 +const BASE_DIR = 'base-build';
35 +const HEAD_DIR = 'build';
36 +
37 +function measure(dir, artifactPath) {
38 + const file = dir + '/' + artifactPath;
39 + return {
40 + size: statSync(file).size,
41 + sizeGzip: gzipSize.fileSync(file),
42 + };
43 +}
44 +
45 +function writeResults(results) {
46 + writeFileSync(RESULTS_PATH, JSON.stringify(results, null, 2) + '\n');
47 +}
48 +
49 +(async function () {
50 + let headSha;
51 + let baseSha;
52 + try {
53 + headSha = String(readFileSync(HEAD_DIR + '/COMMIT_SHA')).trim();
54 + baseSha = String(readFileSync(BASE_DIR + '/COMMIT_SHA')).trim();
55 + } catch {
56 + // Let the renderer explain this one. It is expected to happen whenever the
57 + // build configuration changes upstream, which is not a CI failure.
58 + writeResults({
59 + version: RESULTS_VERSION,
60 + status: 'base-artifacts-unavailable',
61 + });
62 + return;
63 + }
64 +
65 + // A missing size is recorded as null rather than 0, so the renderer can tell
66 + // "this artifact does not exist on that side" apart from "this artifact is
67 + // empty". It derives the new-file and deleted-file cases from those nulls.
68 + const artifactsByPath = new Map();
69 +
70 + const headArtifactPaths = await glob('**/*.js', {cwd: HEAD_DIR});
71 + for (const artifactPath of headArtifactPaths) {
72 + let base;
73 + try {
74 + base = measure(BASE_DIR, artifactPath);
75 + } catch {
76 + // There's no matching base artifact. This is a new file.
77 + base = null;
78 + }
79 + const head = measure(HEAD_DIR, artifactPath);
80 + artifactsByPath.set(artifactPath, {
81 + path: artifactPath,
82 + baseSize: base === null ? null : base.size,
83 + baseSizeGzip: base === null ? null : base.sizeGzip,
84 + headSize: head.size,
85 + headSizeGzip: head.sizeGzip,
86 + });
87 + }
88 +
89 + // Check for base artifacts that were deleted in the head.
90 + const baseArtifactPaths = await glob('**/*.js', {cwd: BASE_DIR});
91 + for (const artifactPath of baseArtifactPaths) {
92 + if (!artifactsByPath.has(artifactPath)) {
93 + const base = measure(BASE_DIR, artifactPath);
94 + artifactsByPath.set(artifactPath, {
95 + path: artifactPath,
96 + baseSize: base.size,
97 + baseSizeGzip: base.sizeGzip,
98 + headSize: null,
99 + headSizeGzip: null,
100 + });
101 + }
102 + }
103 +
104 + // Every artifact is reported, with no threshold filtering. The thresholds and
105 + // the critical bundle list belong to the renderer, so that a pull request
106 + // cannot quietly widen them to hide a regression.
107 + writeResults({
108 + version: RESULTS_VERSION,
109 + status: 'ok',
110 + baseSha,
111 + headSha,
112 + artifacts: Array.from(artifactsByPath.values()),
113 + });
114 +})();
scripts/sizebot/pull-request-comment.js new
+253
@@ -0,0 +1,253 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +'use strict';
9 +
10 +/* eslint-disable no-for-of-loops/no-for-of-loops */
11 +
12 +// The GitHub API half of sizebot, called from `actions/github-script` steps in
13 +// `.github/workflows/runtime_sizebot_comment.yml`.
14 +//
15 +// `resolve` figures out which pull request a `workflow_run` event belongs to,
16 +// finds any comment sizebot has already left on it, and decides whether this
17 +// event should write at all. `post` creates or updates the comment from the body
18 +// that `render-comment.js` produced in between.
19 +
20 +const {readFileSync, writeFileSync} = require('fs');
21 +const {
22 + MARKER_PREFIX,
23 + extractReport,
24 + parseReportHead,
25 +} = require('./render-comment');
26 +
27 +const CONTEXT_PATH = 'sizebot-context.json';
28 +const COMMENT_PATH = 'sizebot-comment.md';
29 +
30 +const COMMENT_AUTHOR = 'github-actions[bot]';
31 +
32 +// `pulls.listFiles` stops paginating here, so a pull request larger than this
33 +// cannot be shown to touch only DevTools.
34 +const MAX_LISTABLE_FILES = 3000;
35 +
36 +const DEVTOOLS_PATH = 'packages/react-devtools';
37 +
38 +async function findExistingComment(github, context, prNumber) {
39 + const comments = await github.paginate(github.rest.issues.listComments, {
40 + owner: context.repo.owner,
41 + repo: context.repo.repo,
42 + issue_number: prNumber,
43 + per_page: 100,
44 + });
45 + for (const comment of comments) {
46 + if (
47 + comment.user.login === COMMENT_AUTHOR &&
48 + comment.body.startsWith(MARKER_PREFIX)
49 + ) {
50 + return comment;
51 + }
52 + }
53 + return null;
54 +}
55 +
56 +// `workflow_run.pull_requests` is empty for runs triggered by a fork, and
57 +// neither `commits/{sha}/pulls` nor the search API index fork pull request head
58 +// commits. Looking the branch up by `owner:ref` is what actually works for both
59 +// fork and same-repo pull requests.
60 +async function findPullRequestNumber(github, context, workflowRun) {
61 + if (
62 + workflowRun.pull_requests != null &&
63 + workflowRun.pull_requests.length > 0
64 + ) {
65 + return workflowRun.pull_requests[0].number;
66 + }
67 +
68 + if (workflowRun.head_repository == null) {
69 + return null;
70 + }
71 +
72 + const {data: pulls} = await github.rest.pulls.list({
73 + owner: context.repo.owner,
74 + repo: context.repo.repo,
75 + head: `${workflowRun.head_repository.owner.login}:${workflowRun.head_branch}`,
76 + state: 'open',
77 + per_page: 100,
78 + });
79 + if (pulls.length === 0) {
80 + return null;
81 + }
82 + return pulls[0].number;
83 +}
84 +
85 +async function findPullRequest(github, context, workflowRun) {
86 + const number = await findPullRequestNumber(github, context, workflowRun);
87 + if (number === null) {
88 + return null;
89 + }
90 + // Always finish with `pulls.get`. The list endpoint omits `changed_files`,
91 + // which `isDevToolsOnly` needs, and that is the endpoint the fork path uses.
92 + const {data} = await github.rest.pulls.get({
93 + owner: context.repo.owner,
94 + repo: context.repo.repo,
95 + pull_number: number,
96 + });
97 + return data;
98 +}
99 +
100 +async function isDevToolsOnly(github, context, pullRequest) {
101 + if (
102 + !Number.isInteger(pullRequest.changed_files) ||
103 + // `listFiles` would silently truncate, and a truncated list can look
104 + // DevTools-only when it is not.
105 + pullRequest.changed_files > MAX_LISTABLE_FILES
106 + ) {
107 + return false;
108 + }
109 + const files = await github.paginate(github.rest.pulls.listFiles, {
110 + owner: context.repo.owner,
111 + repo: context.repo.repo,
112 + pull_number: pullRequest.number,
113 + per_page: 100,
114 + });
115 + if (files.length === 0) {
116 + return false;
117 + }
118 + return files.every(file => file.filename.includes(DEVTOOLS_PATH));
119 +}
120 +
121 +async function resolve({github, context, core}) {
122 + const workflowRun = context.payload.workflow_run;
123 + const action = context.payload.action;
124 +
125 + const pullRequest = await findPullRequest(github, context, workflowRun);
126 + if (pullRequest === null) {
127 + core.info('No open pull request for this run. Nothing to comment on.');
128 + core.setOutput('action', 'skip');
129 + return;
130 + }
131 +
132 + // A pull request number must never come from the build artifact, which the
133 + // fork controls. Confirm the one we resolved really does belong to this run.
134 + const runRepo = workflowRun.head_repository?.full_name ?? null;
135 + const pullRequestRepo = pullRequest.head.repo?.full_name ?? null;
136 + if (runRepo === null || pullRequestRepo === null) {
137 + // A deleted fork leaves us no way to check, so don't write anything.
138 + core.info('Head repository is unavailable. Nothing to comment on.');
139 + core.setOutput('action', 'skip');
140 + return;
141 + }
142 + if (pullRequestRepo !== runRepo) {
143 + core.setFailed(
144 + `Pull request #${pullRequest.number} has head repository ` +
145 + `${pullRequestRepo}, but the run came from ${runRepo}.`
146 + );
147 + return;
148 + }
149 +
150 + // Mirrors the sizebot job's own condition in runtime_build_and_test.yml.
151 + if (pullRequest.base.ref !== 'main') {
152 + core.info(
153 + `Pull request #${pullRequest.number} targets ${pullRequest.base.ref}, not main.`
154 + );
155 + core.setOutput('action', 'skip');
156 + return;
157 + }
158 +
159 + const existing = await findExistingComment(
160 + github,
161 + context,
162 + pullRequest.number
163 + );
164 + const existingReportHead =
165 + existing === null ? null : parseReportHead(existing.body);
166 +
167 + // The only event we ever drop: a comment already describes the current head,
168 + // and this event is about an older commit. Without this, a run cancelled by a
169 + // force push reports `cancelled` after the newer run's comment has landed and
170 + // replaces good numbers with a cancellation notice.
171 + if (
172 + existingReportHead !== null &&
173 + existingReportHead === pullRequest.head.sha &&
174 + workflowRun.head_sha !== pullRequest.head.sha
175 + ) {
176 + core.info(
177 + `Comment already reports on ${pullRequest.head.sha}; this run is for ` +
178 + `${workflowRun.head_sha}. Leaving it alone.`
179 + );
180 + core.setOutput('action', 'skip');
181 + return;
182 + }
183 +
184 + const devtoolsOnly =
185 + action === 'completed'
186 + ? await isDevToolsOnly(github, context, pullRequest)
187 + : false;
188 +
189 + writeFileSync(
190 + CONTEXT_PATH,
191 + JSON.stringify(
192 + {
193 + action,
194 + prNumber: pullRequest.number,
195 + prHeadSha: pullRequest.head.sha,
196 + runHeadSha: workflowRun.head_sha,
197 + runUrl: workflowRun.html_url,
198 + runStatus: workflowRun.status,
199 + runConclusion: workflowRun.conclusion,
200 + devtoolsOnly,
201 + existingCommentId: existing === null ? null : existing.id,
202 + existingReportHead,
203 + existingReport: existing === null ? null : extractReport(existing.body),
204 + commentRunUrl: `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
205 + },
206 + null,
207 + 2
208 + ) + '\n'
209 + );
210 +
211 + core.setOutput('action', 'continue');
212 + // Only a completed run can have produced results to download.
213 + core.setOutput('download_results', String(action === 'completed'));
214 +}
215 +
216 +async function post({github, context, core}) {
217 + const sizebotContext = JSON.parse(readFileSync(CONTEXT_PATH, 'utf8'));
218 + const body = readFileSync(COMMENT_PATH, 'utf8');
219 +
220 + async function create() {
221 + const {data} = await github.rest.issues.createComment({
222 + owner: context.repo.owner,
223 + repo: context.repo.repo,
224 + issue_number: sizebotContext.prNumber,
225 + body,
226 + });
227 + core.info(`Created ${data.html_url}`);
228 + }
229 +
230 + if (sizebotContext.existingCommentId === null) {
231 + await create();
232 + return;
233 + }
234 +
235 + try {
236 + const {data} = await github.rest.issues.updateComment({
237 + owner: context.repo.owner,
238 + repo: context.repo.repo,
239 + comment_id: sizebotContext.existingCommentId,
240 + body,
241 + });
242 + core.info(`Updated ${data.html_url}`);
243 + } catch (error) {
244 + if (error.status !== 404) {
245 + throw error;
246 + }
247 + // Someone deleted the comment between resolving it and writing to it.
248 + core.info('Existing comment is gone, posting a new one.');
249 + await create();
250 + }
251 +}
252 +
253 +module.exports = {post, resolve};
scripts/sizebot/render-comment.js new
+489
@@ -0,0 +1,489 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +'use strict';
9 +
10 +/* eslint-disable no-for-of-loops/no-for-of-loops */
11 +
12 +// Turns `sizebot-results.json` into the body of the sizebot pull request
13 +// comment. Runs from a checkout of the default branch, never from the pull
14 +// request branch, so the thresholds, the critical bundle list and the table
15 +// itself cannot be influenced by the pull request being measured. Everything it
16 +// reads out of the results file is therefore treated as untrusted input.
17 +//
18 +// Reads `sizebot-context.json` (written by the resolve step) and, when the build
19 +// produced one, `sizebot-results.json`. Writes `sizebot-comment.md`, plus
20 +// `sizebot-message.md` when the report is too large to fit in a comment and
21 +// `sizebot-problem.txt` when the build configuration no longer matches this
22 +// file's expectations.
23 +
24 +const {existsSync, readFileSync, writeFileSync} = require('fs');
25 +
26 +// Results shapes this file knows how to read. `compare-sizes.js` on the pull
27 +// request branch may be older or newer than this list.
28 +const SUPPORTED_VERSIONS = new Set([1]);
29 +const SUPPORTED_STATUSES = new Set(['ok', 'base-artifacts-unavailable']);
30 +
31 +const CRITICAL_THRESHOLD = 0.02;
32 +const SIGNIFICANCE_THRESHOLD = 0.002;
33 +const CRITICAL_ARTIFACT_PATHS = new Set([
34 + // We always report changes to these bundles, even if the change is
35 + // insignificant or non-existent.
36 + 'oss-stable/react-dom/cjs/react-dom.production.js',
37 + 'oss-stable/react-dom/cjs/react-dom-client.production.js',
38 + 'oss-experimental/react-dom/cjs/react-dom.production.js',
39 + 'oss-experimental/react-dom/cjs/react-dom-client.production.js',
40 + 'facebook-www/ReactDOM-prod.classic.js',
41 + 'facebook-www/ReactDOM-prod.modern.js',
42 +]);
43 +
44 +// GitHub comments are limited to 65536 characters.
45 +const MAX_COMMENT_LENGTH = 65536;
46 +
47 +// Both the notice and the report are delimited so each can be rewritten without
48 +// disturbing the other, and so a report can be read back out of a comment
49 +// verbatim when a newer build supersedes it. Relying on "everything after the
50 +// notice" instead would swallow the footer and append a second one every time.
51 +const MARKER_PREFIX = '<!-- sizebot-comment';
52 +const NOTICE_START = '<!-- sizebot-notice-start -->';
53 +const NOTICE_END = '<!-- sizebot-notice-end -->';
54 +const REPORT_START = '<!-- sizebot-report-start -->';
55 +const REPORT_END = '<!-- sizebot-report-end -->';
56 +
57 +const CONTEXT_PATH = 'sizebot-context.json';
58 +const RESULTS_PATH = 'sizebot-results.json';
59 +const COMMENT_PATH = 'sizebot-comment.md';
60 +const MESSAGE_PATH = 'sizebot-message.md';
61 +const PROBLEM_PATH = 'sizebot-problem.txt';
62 +
63 +// Build artifact paths end up inside markdown link text and inside a URL, so
64 +// anything that could break out of either is rejected rather than escaped.
65 +const SAFE_ARTIFACT_PATH = /^[A-Za-z0-9_@./+-]+$/;
66 +
67 +function isSafeArtifactPath(value) {
68 + return (
69 + typeof value === 'string' &&
70 + value.length > 0 &&
71 + value.length < 512 &&
72 + SAFE_ARTIFACT_PATH.test(value) &&
73 + !value.includes('..') &&
74 + !value.startsWith('/')
75 + );
76 +}
77 +
78 +function isSize(value) {
79 + return value === null || (Number.isFinite(value) && value >= 0);
80 +}
81 +
82 +function isSha(value) {
83 + return typeof value === 'string' && /^[0-9a-f]{7,40}$/.test(value);
84 +}
85 +
86 +const kilobyteFormatter = new Intl.NumberFormat('en', {
87 + style: 'unit',
88 + unit: 'kilobyte',
89 + minimumFractionDigits: 2,
90 + maximumFractionDigits: 2,
91 +});
92 +
93 +function kbs(bytes) {
94 + // An artifact that exists on only one side has no size on the other. The
95 + // report has always shown that as 0.00 kB rather than an empty cell.
96 + return kilobyteFormatter.format((bytes === null ? 0 : bytes) / 1000);
97 +}
98 +
99 +const percentFormatter = new Intl.NumberFormat('en', {
100 + style: 'percent',
101 + signDisplay: 'exceptZero',
102 + minimumFractionDigits: 2,
103 + maximumFractionDigits: 2,
104 +});
105 +
106 +function ratio(baseSize, headSize) {
107 + if (baseSize === null) {
108 + return Infinity;
109 + }
110 + if (headSize === null) {
111 + return -1;
112 + }
113 + return (headSize - baseSize) / baseSize;
114 +}
115 +
116 +function change(decimal) {
117 + if (decimal === Infinity) {
118 + return 'New file';
119 + }
120 + if (decimal === -1) {
121 + return 'Deleted';
122 + }
123 + // Compare the magnitude, not the signed value. Testing `decimal < 0.0001`
124 + // reported every size decrease as unchanged, which is why `signDisplay:
125 + // 'exceptZero'` above never had a negative number to render.
126 + if (Math.abs(decimal) < 0.0001) {
127 + return '=';
128 + }
129 + return percentFormatter.format(decimal);
130 +}
131 +
132 +const header = `| Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
133 +| ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;
134 +
135 +function row(result, baseSha, headSha) {
136 + const diffViewUrl = `https://react-builds.vercel.app/commits/${headSha}/files/${result.path}?compare=${baseSha}`;
137 + const rowArr = [
138 + `| [${result.path}](${diffViewUrl})`,
139 + `**${change(result.change)}**`,
140 + `${kbs(result.baseSize)}`,
141 + `${kbs(result.headSize)}`,
142 + `${change(result.changeGzip)}`,
143 + `${kbs(result.baseSizeGzip)}`,
144 + `${kbs(result.headSizeGzip)}`,
145 + ];
146 + return rowArr.join(' | ');
147 +}
148 +
149 +function validateResults(raw) {
150 + if (raw === null || typeof raw !== 'object') {
151 + return {ok: false, reason: 'malformed'};
152 + }
153 + // Checked before anything else so a shape this file cannot read produces a
154 + // clear message instead of a misrendered table.
155 + if (!SUPPORTED_VERSIONS.has(raw.version)) {
156 + return {ok: false, reason: 'unsupported-version'};
157 + }
158 + if (!SUPPORTED_STATUSES.has(raw.status)) {
159 + return {ok: false, reason: 'malformed'};
160 + }
161 + if (raw.status === 'base-artifacts-unavailable') {
162 + return {ok: true, results: {status: raw.status}};
163 + }
164 + if (!isSha(raw.baseSha) || !isSha(raw.headSha)) {
165 + return {ok: false, reason: 'malformed'};
166 + }
167 + if (!Array.isArray(raw.artifacts)) {
168 + return {ok: false, reason: 'malformed'};
169 + }
170 + for (const artifact of raw.artifacts) {
171 + if (artifact === null || typeof artifact !== 'object') {
172 + return {ok: false, reason: 'malformed'};
173 + }
174 + if (!isSafeArtifactPath(artifact.path)) {
175 + return {ok: false, reason: 'malformed'};
176 + }
177 + if (
178 + !isSize(artifact.baseSize) ||
179 + !isSize(artifact.baseSizeGzip) ||
180 + !isSize(artifact.headSize) ||
181 + !isSize(artifact.headSizeGzip)
182 + ) {
183 + return {ok: false, reason: 'malformed'};
184 + }
185 + if (artifact.baseSize === null && artifact.headSize === null) {
186 + return {ok: false, reason: 'malformed'};
187 + }
188 + }
189 + return {ok: true, results: raw};
190 +}
191 +
192 +function renderTable(results) {
193 + const {baseSha, headSha} = results;
194 +
195 + const resultsMap = new Map();
196 + for (const artifact of results.artifacts) {
197 + resultsMap.set(artifact.path, {
198 + ...artifact,
199 + change: ratio(artifact.baseSize, artifact.headSize),
200 + changeGzip: ratio(artifact.baseSizeGzip, artifact.headSizeGzip),
201 + });
202 + }
203 +
204 + const sorted = Array.from(resultsMap.values());
205 + sorted.sort((a, b) => b.change - a.change);
206 +
207 + const criticalResults = [];
208 + const missingCriticalPaths = [];
209 + for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
210 + const result = resultsMap.get(artifactPath);
211 + if (result === undefined) {
212 + missingCriticalPaths.push(artifactPath);
213 + continue;
214 + }
215 + criticalResults.push(row(result, baseSha, headSha));
216 + }
217 +
218 + const significantResults = [];
219 + for (const result of sorted) {
220 + // If result exceeds critical threshold, add to top section.
221 + if (
222 + (Math.abs(result.change) > CRITICAL_THRESHOLD ||
223 + // New file
224 + result.change === Infinity ||
225 + // Deleted file
226 + result.change === -1) &&
227 + // Skip critical artifacts. We added those earlier, in a fixed order.
228 + !CRITICAL_ARTIFACT_PATHS.has(result.path)
229 + ) {
230 + criticalResults.push(row(result, baseSha, headSha));
231 + }
232 +
233 + // Do the same for results that exceed the significant threshold. These
234 + // will go into the bottom, collapsed section. Intentionally including
235 + // critical artifacts in this section, too.
236 + if (
237 + Math.abs(result.change) > SIGNIFICANCE_THRESHOLD ||
238 + result.change === Infinity ||
239 + result.change === -1
240 + ) {
241 + significantResults.push(row(result, baseSha, headSha));
242 + }
243 + }
244 +
245 + const markdown = `Comparing: ${baseSha}...${headSha}
246 +
247 +## Critical size changes
248 +
249 +Includes critical production bundles, as well as any change greater than ${
250 + CRITICAL_THRESHOLD * 100
251 + }%:
252 +
253 +${header}
254 +${criticalResults.join('\n')}
255 +
256 +## Significant size changes
257 +
258 +Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:
259 +
260 +${
261 + significantResults.length > 0
262 + ? `<details>
263 +<summary>Expand to show</summary>
264 +
265 +${header}
266 +${significantResults.join('\n')}
267 +</details>`
268 + : '(No significant changes)'
269 +}`;
270 +
271 + return {markdown, missingCriticalPaths};
272 +}
273 +
274 +function renderCompletedReport(context) {
275 + const {runConclusion, runUrl, devtoolsOnly} = context;
276 +
277 + // The common outcome for a first-time contributor's pull request: the run is
278 + // created but held until a maintainer approves it.
279 + if (runConclusion === 'action_required') {
280 + return {
281 + markdown: `[The build for this commit](${runUrl}) needs maintainer approval before it can run, so there is no size report yet.`,
282 + missingCriticalPaths: [],
283 + };
284 + }
285 +
286 + if (runConclusion !== 'success') {
287 + return {
288 + markdown: `The build for this commit did not complete, so there is no size report. See [the workflow run](${runUrl}) for details.`,
289 + missingCriticalPaths: [],
290 + };
291 + }
292 +
293 + if (devtoolsOnly) {
294 + return {
295 + markdown:
296 + 'No size report: this pull request only touches `packages/react-devtools`, which does not affect production bundle size.',
297 + missingCriticalPaths: [],
298 + };
299 + }
300 +
301 + if (!existsSync(RESULTS_PATH)) {
302 + return {
303 + markdown: `The build succeeded but produced no size results, so there is no size report. See [the workflow run](${runUrl}) for details.`,
304 + missingCriticalPaths: [],
305 + };
306 + }
307 +
308 + let raw;
309 + try {
310 + raw = JSON.parse(readFileSync(RESULTS_PATH, 'utf8'));
311 + } catch {
312 + raw = null;
313 + }
314 +
315 + const validated = validateResults(raw);
316 + if (!validated.ok) {
317 + if (validated.reason === 'unsupported-version') {
318 + return {
319 + markdown:
320 + 'This pull request produced a size report in a format this repository no longer reads. ' +
321 + 'Merge the latest changes from the `main` branch to pick up the current one.',
322 + missingCriticalPaths: [],
323 + };
324 + }
325 + return {
326 + markdown: `The size results for this commit could not be read, so there is no size report. See [the workflow run](${runUrl}) for details.`,
327 + missingCriticalPaths: [],
328 + };
329 + }
330 +
331 + if (validated.results.status === 'base-artifacts-unavailable') {
332 + return {
333 + markdown:
334 + "Failed to read build artifacts. It's possible a build configuration has changed upstream. " +
335 + 'Try pulling the latest changes from the `main` branch.',
336 + missingCriticalPaths: [],
337 + };
338 + }
339 +
340 + return renderTable(validated.results);
341 +}
342 +
343 +function renderNotice(context, reportHead) {
344 + const {action, prHeadSha, runHeadSha, runStatus, runUrl} = context;
345 + const lines = [];
346 +
347 + // One rule covers both the case where an older run's results arrive after the
348 + // head moved, and the case where a new build supersedes a report already on
349 + // display: the report simply is not about the pull request's current head.
350 + if (reportHead !== null && reportHead !== prHeadSha) {
351 + lines.push(
352 + `These sizes are for ${reportHead}, which is no longer the head of this pull request.`
353 + );
354 + if (action === 'requested') {
355 + lines.push(`A build for ${runHeadSha} is in progress.`);
356 + }
357 + } else if (action === 'requested' && runStatus === 'waiting') {
358 + lines.push(
359 + `[The build for this commit](${runUrl}) is waiting for maintainer approval before it can run.`
360 + );
361 + }
362 +
363 + if (lines.length === 0) {
364 + return '';
365 + }
366 + return lines.map(line => `> ${line}`).join('\n> \n');
367 +}
368 +
369 +function renderBody(context) {
370 + let reportHead;
371 + let report;
372 + let missingCriticalPaths = [];
373 +
374 + if (context.action === 'requested') {
375 + // Only a comment that names the commit it describes holds real numbers. A
376 + // previous placeholder has body text too, but carrying that forward would
377 + // pin the comment to a stale run link instead of refreshing it.
378 + if (
379 + context.existingReportHead !== null &&
380 + context.existingReport !== null
381 + ) {
382 + // Keep the numbers from the previous build visible. The notice below
383 + // explains that they describe an older commit.
384 + reportHead = context.existingReportHead;
385 + report = context.existingReport;
386 + } else {
387 + reportHead = null;
388 + report = `A size report will appear here when [the build](${context.runUrl}) finishes.`;
389 + }
390 + } else {
391 + reportHead = context.runHeadSha;
392 + const rendered = renderCompletedReport(context);
393 + report = rendered.markdown;
394 + missingCriticalPaths = rendered.missingCriticalPaths;
395 + }
396 +
397 + if (missingCriticalPaths.length > 0) {
398 + report =
399 + '> [!CAUTION]\n' +
400 + '> These critical bundles are missing from the build. If that was an intentional\n' +
401 + '> change to the build configuration, update `CRITICAL_ARTIFACT_PATHS` in\n' +
402 + '> `scripts/sizebot/render-comment.js`:\n' +
403 + missingCriticalPaths.map(p => `> - \`${p}\``).join('\n') +
404 + '\n\n' +
405 + report;
406 + }
407 +
408 + const notice = renderNotice(context, reportHead);
409 + const footerSha = reportHead === null ? context.runHeadSha : reportHead;
410 +
411 + function assemble(reportRegion) {
412 + return `${MARKER_PREFIX} report-head=${
413 + reportHead === null ? 'none' : reportHead
414 + } -->
415 +${NOTICE_START}
416 +${notice === '' ? '' : `> [!WARNING]\n${notice}\n`}${NOTICE_END}
417 +${REPORT_START}
418 +${reportRegion}
419 +${REPORT_END}
420 +
421 +<sub>Generated by sizebot against ${footerSha}</sub>
422 +`;
423 + }
424 +
425 + return {
426 + body: assemble(report),
427 + report,
428 + assemble,
429 + reportHead,
430 + missingCriticalPaths,
431 + };
432 +}
433 +
434 +// Reads the report region back out of a comment, so a completed report can be
435 +// carried forward when a new build is requested for a newer commit.
436 +function extractReport(body) {
437 + const start = body.indexOf(REPORT_START);
438 + const end = body.indexOf(REPORT_END);
439 + if (start === -1 || end === -1 || end < start) {
440 + return null;
441 + }
442 + const report = body.slice(start + REPORT_START.length, end).trim();
443 + return report === '' ? null : report;
444 +}
445 +
446 +function parseReportHead(body) {
447 + const match =
448 + /<!-- sizebot-comment report-head=([0-9a-f]{7,40}|none) -->/.exec(body);
449 + if (match === null || match[1] === 'none') {
450 + return null;
451 + }
452 + return match[1];
453 +}
454 +
455 +function main() {
456 + const context = JSON.parse(readFileSync(CONTEXT_PATH, 'utf8'));
457 + const {body, report, assemble, missingCriticalPaths} = renderBody(context);
458 +
459 + let comment = body;
460 + if (body.length > MAX_COMMENT_LENGTH) {
461 + // The link resolves because the artifact is uploaded to this same run,
462 + // before the comment is posted.
463 + writeFileSync(MESSAGE_PATH, report + '\n');
464 + comment = assemble(
465 + `The size diff is too large to display in a single comment. [This workflow run](${context.commentRunUrl}) contains an artifact called \`sizebot-message.md\` with the full report.`
466 + );
467 + }
468 + writeFileSync(COMMENT_PATH, comment);
469 +
470 + if (missingCriticalPaths.length > 0) {
471 + writeFileSync(
472 + PROBLEM_PATH,
473 + `Missing expected bundles:\n${missingCriticalPaths.join('\n')}\n`
474 + );
475 + }
476 +
477 + process.stdout.write(comment);
478 +}
479 +
480 +module.exports = {
481 + MARKER_PREFIX,
482 + extractReport,
483 + parseReportHead,
484 + renderBody,
485 +};
486 +
487 +if (require.main === module) {
488 + main();
489 +}
scripts/tasks/danger.js deleted
-39
@@ -1,39 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -'use strict';
9 -
10 -const path = require('path');
11 -const spawn = require('child_process').spawn;
12 -
13 -const extension = process.platform === 'win32' ? '.cmd' : '';
14 -
15 -// sizebot public_repo token (this is publicly visible on purpose)
16 -const token = 'ghp_UfuUaoow8veN3ZV1' + 'sGquTDgiVjRDmL2qLY1D';
17 -spawn(
18 - path.join('node_modules', '.bin', 'danger-ci' + extension),
19 - [
20 - '--id',
21 - process.env.RELEASE_CHANNEL === 'experimental' ? 'experimental' : 'stable',
22 - ],
23 - {
24 - // Allow colors to pass through
25 - stdio: 'inherit',
26 - env: {
27 - ...process.env,
28 - DANGER_GITHUB_API_TOKEN: token,
29 - },
30 - }
31 -).on('close', function (code) {
32 - if (code !== 0) {
33 - console.error('Danger failed');
34 - } else {
35 - console.log('Danger passed');
36 - }
37 -
38 - process.exit(code);
39 -});
yarn.lock
+10 -337
@@ -2840,36 +2840,6 @@
2840 resolved "https://registry.yarnpkg.com/@fluent/syntax/-/syntax-0.19.0.tgz#43f882faba6908b0f1013f6a94e009d0dfbdcb77"
2841 integrity sha512-5D2qVpZrgpjtqU4eNOcWGp1gnUCgjfM+vKGE2y03kKN6z5EBhtx0qdRFbg8QuNNj8wXNoX93KJoYb+NqoxswmQ==
2842
2843 -"@gitbeaker/core@^21.7.0":
2844 - version "21.7.0"
2845 - resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-21.7.0.tgz#fcf7a12915d39f416e3f316d0a447a814179b8e5"
2846 - integrity sha512-cw72rE7tA27wc6JJe1WqeAj9v/6w0S7XJcEji+bRNjTlUfE1zgfW0Gf1mbGUi7F37SOABGCosQLfg9Qe63aIqA==
2847 - dependencies:
2848 - "@gitbeaker/requester-utils" "^21.7.0"
2849 - form-data "^3.0.0"
2850 - li "^1.3.0"
2851 - xcase "^2.0.1"
2852 -
2853 -"@gitbeaker/node@^21.3.0":
2854 - version "21.7.0"
2855 - resolved "https://registry.yarnpkg.com/@gitbeaker/node/-/node-21.7.0.tgz#2c19613f44ee497a8808c555abec614ebd2dfcad"
2856 - integrity sha512-OdM3VcTKYYqboOsnbiPcO0XimXXpYK4gTjARBZ6BWc+1LQXKmqo+OH6oUbyxOoaFu9hHECafIt3WZU3NM4sZTg==
2857 - dependencies:
2858 - "@gitbeaker/core" "^21.7.0"
2859 - "@gitbeaker/requester-utils" "^21.7.0"
2860 - form-data "^3.0.0"
2861 - got "^11.1.4"
2862 - xcase "^2.0.1"
2863 -
2864 -"@gitbeaker/requester-utils@^21.7.0":
2865 - version "21.7.0"
2866 - resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-21.7.0.tgz#e9a9cfaf268d2a99eb7bbdc930943240a5f88878"
2867 - integrity sha512-eLTaVXlBnh8Qimj6QuMMA06mu/mLcJm3dy8nqhhn/Vm/D25sPrvpGwmbfFyvzj6QujPqtHvFfsCHtyZddL01qA==
2868 - dependencies:
2869 - form-data "^3.0.0"
2870 - query-string "^6.12.1"
2871 - xcase "^2.0.1"
2872 -
2843 "@humanwhocodes/config-array@^0.11.14":
2844 version "0.11.14"
2845 resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
@@ -3409,7 +3379,7 @@
3379 node-fetch "^2.6.7"
3380 universal-user-agent "^6.0.0"
3381
3412 -"@octokit/rest@^16.43.0 || ^17.11.0 || ^18.12.0", "@octokit/rest@^18.12.0":
3382 +"@octokit/rest@^18.12.0":
3383 version "18.12.0"
3384 resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881"
3385 integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==
@@ -5163,13 +5133,6 @@ async-each@^1.0.1:
5133 resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
5134 integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
5135
5166 -async-retry@1.2.3:
5167 - version "1.2.3"
5168 - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0"
5169 - integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==
5170 - dependencies:
5171 - retry "0.12.0"
5172 -
5136 async@^2.0.0, async@^2.6.3:
5137 version "2.6.3"
5138 resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -5926,11 +5889,6 @@ buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
5889 resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
5890 integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
5891
5929 -buffer-equal-constant-time@^1.0.1:
5930 - version "1.0.1"
5931 - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
5932 - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
5933 -
5892 buffer-fill@^1.0.0:
5893 version "1.0.0"
5894 resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
@@ -6208,7 +6166,7 @@ chalk@^1.0.0, chalk@^1.1.3:
6166 strip-ansi "^3.0.0"
6167 supports-color "^2.0.0"
6168
6211 -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
6169 +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
6170 version "2.4.2"
6171 resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
6172 integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -6549,7 +6507,7 @@ colors@1.0.3:
6507 resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
6508 integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
6509
6552 -colors@1.4.0, colors@^1.1.2:
6510 +colors@1.4.0:
6511 version "1.4.0"
6512 resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
6513 integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
@@ -6581,7 +6539,7 @@ commander@^10.0.1:
6539 resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
6540 integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
6541
6584 -commander@^2.18.0, commander@^2.20.0, commander@^2.6.0, commander@^2.8.1:
6542 +commander@^2.20.0, commander@^2.6.0, commander@^2.8.1:
6543 version "2.20.3"
6544 resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
6545 integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -6833,11 +6791,6 @@ core-js@^3.6.4:
6791 resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
6792 integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
6793
6836 -core-js@^3.8.2:
6837 - version "3.27.2"
6838 - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.2.tgz#85b35453a424abdcacb97474797815f4d62ebbf7"
6839 - integrity sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==
6840 -
6794 core-util-is@~1.0.0:
6795 version "1.0.2"
6796 resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -7124,49 +7077,6 @@ currently-unhandled@^0.4.1:
7077 dependencies:
7078 array-find-index "^1.0.1"
7079
7127 -danger@^11.2.3:
7128 - version "11.2.3"
7129 - resolved "https://registry.yarnpkg.com/danger/-/danger-11.2.3.tgz#2a0a6d3581478005d0f2abf5b3995a8409165067"
7130 - integrity sha512-NNDOUDZWCi1fqEicSWbnk8lOOoqY+vwekB8twUiknEyyvDDKypWEcUolq6SNg9Kd6HMqnX80K8U8nqzmDRC1QQ==
7131 - dependencies:
7132 - "@gitbeaker/node" "^21.3.0"
7133 - "@octokit/rest" "^18.12.0"
7134 - async-retry "1.2.3"
7135 - chalk "^2.3.0"
7136 - commander "^2.18.0"
7137 - core-js "^3.8.2"
7138 - debug "^4.1.1"
7139 - fast-json-patch "^3.0.0-1"
7140 - get-stdin "^6.0.0"
7141 - http-proxy-agent "^5.0.0"
7142 - https-proxy-agent "^5.0.1"
7143 - hyperlinker "^1.0.0"
7144 - json5 "^2.1.0"
7145 - jsonpointer "^5.0.0"
7146 - jsonwebtoken "^9.0.0"
7147 - lodash.find "^4.6.0"
7148 - lodash.includes "^4.3.0"
7149 - lodash.isobject "^3.0.2"
7150 - lodash.keys "^4.0.8"
7151 - lodash.mapvalues "^4.6.0"
7152 - lodash.memoize "^4.1.2"
7153 - memfs-or-file-map-to-github-branch "^1.2.1"
7154 - micromatch "^4.0.4"
7155 - node-cleanup "^2.1.2"
7156 - node-fetch "^2.6.7"
7157 - override-require "^1.1.1"
7158 - p-limit "^2.1.0"
7159 - parse-diff "^0.7.0"
7160 - parse-git-config "^2.0.3"
7161 - parse-github-url "^1.0.2"
7162 - parse-link-header "^2.0.0"
7163 - pinpoint "^1.1.0"
7164 - prettyjson "^1.2.1"
7165 - readline-sync "^1.4.9"
7166 - regenerator-runtime "^0.13.9"
7167 - require-from-string "^2.0.2"
7168 - supports-hyperlinks "^1.0.1"
7169 -
7080 data-uri-to-buffer@^4.0.0:
7081 version "4.0.1"
7082 resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
@@ -7665,13 +7575,6 @@ eastasianwidth@^0.2.0:
7575 resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
7576 integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
7577
7668 -ecdsa-sig-formatter@1.0.11:
7669 - version "1.0.11"
7670 - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
7671 - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
7672 - dependencies:
7673 - safe-buffer "^5.0.1"
7674 -
7578 ee-first@1.1.1:
7579 version "1.1.1"
7580 resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -8583,13 +8486,6 @@ expand-brackets@^2.1.4:
8486 snapdragon "^0.8.1"
8487 to-regex "^3.0.1"
8488
8586 -expand-tilde@^2.0.2:
8587 - version "2.0.2"
8588 - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
8589 - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=
8590 - dependencies:
8591 - homedir-polyfill "^1.0.1"
8592 -
8489 expect@^29.7.0:
8490 version "29.7.0"
8491 resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
@@ -8751,7 +8647,7 @@ fast-glob@^3.2.9:
8647 merge2 "^1.3.0"
8648 micromatch "^4.0.4"
8649
8754 -fast-json-patch@3.1.1, fast-json-patch@^3.0.0-1:
8650 +fast-json-patch@3.1.1:
8651 version "3.1.1"
8652 resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947"
8653 integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==
@@ -8985,11 +8881,6 @@ fill-range@^7.0.1:
8881 dependencies:
8882 to-regex-range "^5.0.1"
8883
8988 -filter-obj@^1.1.0:
8989 - version "1.1.0"
8990 - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
8991 - integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
8992 -
8884 finalhandler@1.3.1:
8885 version "1.3.1"
8886 resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
@@ -9195,15 +9086,6 @@ form-data-encoder@^2.1.2:
9086 resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5"
9087 integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==
9088
9198 -form-data@^3.0.0:
9199 - version "3.0.1"
9200 - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
9201 - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
9202 - dependencies:
9203 - asynckit "^0.4.0"
9204 - combined-stream "^1.0.8"
9205 - mime-types "^2.1.12"
9206 -
9089 form-data@^4.0.0:
9090 version "4.0.0"
9091 resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
@@ -9250,11 +9132,6 @@ fs-constants@^1.0.0:
9132 resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
9133 integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
9134
9253 -fs-exists-sync@^0.1.0:
9254 - version "0.1.0"
9255 - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
9256 - integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=
9257 -
9135 fs-extra@11.2.0:
9136 version "11.2.0"
9137 resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
@@ -9446,15 +9323,6 @@ gifsicle@^5.0.0:
9323 execa "^5.0.0"
9324 logalot "^2.0.0"
9325
9449 -git-config-path@^1.0.1:
9450 - version "1.0.1"
9451 - resolved "https://registry.yarnpkg.com/git-config-path/-/git-config-path-1.0.1.tgz#6d33f7ed63db0d0e118131503bab3aca47d54664"
9452 - integrity sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=
9453 - dependencies:
9454 - extend-shallow "^2.0.1"
9455 - fs-exists-sync "^0.1.0"
9456 - homedir-polyfill "^1.0.0"
9457 -
9326 glob-parent@^3.1.0:
9327 version "3.1.0"
9328 resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -9714,7 +9582,7 @@ gopd@^1.0.1:
9582 dependencies:
9583 get-intrinsic "^1.1.3"
9584
9717 -got@^11.1.4, got@^11.8.5:
9585 +got@^11.8.5:
9586 version "11.8.6"
9587 resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
9588 integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
@@ -9873,11 +9741,6 @@ has-bigints@^1.0.1:
9741 resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
9742 integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
9743
9876 -has-flag@^2.0.0:
9877 - version "2.0.0"
9878 - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
9879 - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=
9880 -
9744 has-flag@^3.0.0:
9745 version "3.0.0"
9746 resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -10025,7 +9888,7 @@ hermes-parser@^0.25.1:
9888 dependencies:
9889 hermes-estree "0.25.1"
9890
10028 -homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
9891 +homedir-polyfill@^1.0.1:
9892 version "1.0.3"
9893 resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
9894 integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
@@ -10214,11 +10077,6 @@ human-signals@^4.3.0:
10077 resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
10078 integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==
10079
10217 -hyperlinker@^1.0.0:
10218 - version "1.0.0"
10219 - resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
10220 - integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
10221 -
10080 hyphenate-style-name@^1.0.2, hyphenate-style-name@^1.0.3:
10081 version "1.0.3"
10082 resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
@@ -10438,7 +10296,7 @@ ini@2.0.0, ini@~2.0.0:
10296 resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
10297 integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
10298
10441 -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0, ini@~1.3.3:
10299 +ini@^1.3.4, ini@~1.3.0, ini@~1.3.3:
10300 version "1.3.5"
10301 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
10302 integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
@@ -11738,13 +11596,6 @@ json5@^1.0.1:
11596 dependencies:
11597 minimist "^1.2.0"
11598
11741 -json5@^2.1.0:
11742 - version "2.1.0"
11743 - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"
11744 - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
11745 - dependencies:
11746 - minimist "^1.2.0"
11747 -
11599 json5@^2.1.2:
11600 version "2.1.3"
11601 resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
@@ -11778,21 +11629,6 @@ jsonify@~0.0.0:
11629 resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
11630 integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
11631
11781 -jsonpointer@^5.0.0:
11782 - version "5.0.1"
11783 - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
11784 - integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==
11785 -
11786 -jsonwebtoken@^9.0.0:
11787 - version "9.0.0"
11788 - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d"
11789 - integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==
11790 - dependencies:
11791 - jws "^3.2.2"
11792 - lodash "^4.17.21"
11793 - ms "^2.1.1"
11794 - semver "^7.3.8"
11795 -
11632 jsx-ast-utils@^1.3.4:
11633 version "1.4.1"
11634 resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
@@ -11813,23 +11649,6 @@ junk@^3.1.0:
11649 resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
11650 integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
11651
11816 -jwa@^1.4.2:
11817 - version "1.4.2"
11818 - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9"
11819 - integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==
11820 - dependencies:
11821 - buffer-equal-constant-time "^1.0.1"
11822 - ecdsa-sig-formatter "1.0.11"
11823 - safe-buffer "^5.0.1"
11824 -
11825 -jws@^3.2.2:
11826 - version "3.2.3"
11827 - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.3.tgz#5ac0690b460900a27265de24520526853c0b8ca1"
11828 - integrity sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==
11829 - dependencies:
11830 - jwa "^1.4.2"
11831 - safe-buffer "^5.0.1"
11832 -
11652 keyv@3.0.0:
11653 version "3.0.0"
11654 resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
@@ -11948,11 +11767,6 @@ levn@^0.4.1:
11767 prelude-ls "^1.2.1"
11768 type-check "~0.4.0"
11769
11951 -li@^1.3.0:
11952 - version "1.3.0"
11953 - resolved "https://registry.yarnpkg.com/li/-/li-1.3.0.tgz#22c59bcaefaa9a8ef359cf759784e4bf106aea1b"
11954 - integrity sha1-IsWbyu+qmo7zWc91l4TkvxBq6hs=
11955 -
11770 lie@~3.3.0:
11771 version "3.3.0"
11772 resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
@@ -12066,11 +11880,6 @@ lodash.difference@^4.5.0:
11880 resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
11881 integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=
11882
12069 -lodash.find@^4.6.0:
12070 - version "4.6.0"
12071 - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
12072 - integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=
12073 -
11883 lodash.flatten@^4.4.0:
11884 version "4.4.0"
11885 resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
@@ -12081,11 +11890,6 @@ lodash.hasin@4.5.2:
11890 resolved "https://registry.yarnpkg.com/lodash.hasin/-/lodash.hasin-4.5.2.tgz#f91e352378d21ef7090b9e7687c2ca35c5b4d52a"
11891 integrity sha1-+R41I3jSHvcJC552h8LKNcW01So=
11892
12084 -lodash.includes@^4.3.0:
12085 - version "4.3.0"
12086 - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
12087 - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
12088 -
11893 lodash.isempty@4.4.0:
11894 version "4.4.0"
11895 resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
@@ -12096,31 +11900,11 @@ lodash.isnil@4.0.0:
11900 resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c"
11901 integrity sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw=
11902
12099 -lodash.isobject@^3.0.2:
12100 - version "3.0.2"
12101 - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
12102 - integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=
12103 -
11903 lodash.isplainobject@^4.0.6:
11904 version "4.0.6"
11905 resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
11906 integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
11907
12109 -lodash.keys@^4.0.8:
12110 - version "4.2.0"
12111 - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205"
12112 - integrity sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=
12113 -
12114 -lodash.mapvalues@^4.6.0:
12115 - version "4.6.0"
12116 - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c"
12117 - integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=
12118 -
12119 -lodash.memoize@^4.1.2:
12120 - version "4.1.2"
12121 - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
12122 - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
12123 -
11908 lodash.merge@^4.6.2:
11909 version "4.6.2"
11910 resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -12384,13 +12168,6 @@ mem@^5.0.0:
12168 mimic-fn "^2.1.0"
12169 p-is-promise "^2.1.0"
12170
12387 -memfs-or-file-map-to-github-branch@^1.2.1:
12388 - version "1.2.1"
12389 - resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz#fdb9a85408262316a9bd5567409bf89be7d72f96"
12390 - integrity sha512-I/hQzJ2a/pCGR8fkSQ9l5Yx+FQ4e7X6blNHyWBm2ojeFLT3GVzGkTj7xnyWpdclrr7Nq4dmx3xrvu70m3ypzAQ==
12391 - dependencies:
12392 - "@octokit/rest" "^16.43.0 || ^17.11.0 || ^18.12.0"
12393 -
12171 memfs@^3.4.3:
12172 version "3.5.1"
12173 resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.1.tgz#f0cd1e2bfaef58f6fe09bfb9c2288f07fea099ec"
@@ -12756,11 +12533,6 @@ nice-try@^1.0.4:
12533 resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
12534 integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
12535
12759 -node-cleanup@^2.1.2:
12760 - version "2.1.2"
12761 - resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c"
12762 - integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=
12763 -
12536 node-domexception@^1.0.0:
12537 version "1.0.0"
12538 resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
@@ -13248,11 +13020,6 @@ osenv@0.0.3:
13020 resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.0.3.tgz#cd6ad8ddb290915ad9e22765576025d411f29cb6"
13021 integrity sha1-zWrY3bKQkVrZ4idlV2Al1BHynLY=
13022
13251 -override-require@^1.1.1:
13252 - version "1.1.1"
13253 - resolved "https://registry.yarnpkg.com/override-require/-/override-require-1.1.1.tgz#6ae22fadeb1f850ffb0cf4c20ff7b87e5eb650df"
13254 - integrity sha1-auIvresfhQ/7DPTCD/e4fl62UN8=
13255 -
13023 p-cancelable@^0.3.0:
13024 version "0.3.0"
13025 resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
@@ -13326,13 +13093,6 @@ p-limit@^2.0.0, p-limit@^2.2.0:
13093 dependencies:
13094 p-try "^2.0.0"
13095
13329 -p-limit@^2.1.0:
13330 - version "2.2.2"
13331 - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
13332 - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
13333 - dependencies:
13334 - p-try "^2.0.0"
13335 -
13096 p-limit@^3.0.2, p-limit@^3.1.0:
13097 version "3.1.0"
13098 resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@@ -13454,11 +13214,6 @@ parent-module@^1.0.0:
13214 dependencies:
13215 callsites "^3.0.0"
13216
13457 -parse-diff@^0.7.0:
13458 - version "0.7.1"
13459 - resolved "https://registry.yarnpkg.com/parse-diff/-/parse-diff-0.7.1.tgz#9b7a2451c3725baf2c87c831ba192d40ee2237d4"
13460 - integrity sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg==
13461 -
13217 parse-filepath@^1.0.2:
13218 version "1.0.2"
13219 resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891"
@@ -13468,20 +13223,6 @@ parse-filepath@^1.0.2:
13223 map-cache "^0.2.0"
13224 path-root "^0.1.1"
13225
13471 -parse-git-config@^2.0.3:
13472 - version "2.0.3"
13473 - resolved "https://registry.yarnpkg.com/parse-git-config/-/parse-git-config-2.0.3.tgz#6fb840d4a956e28b971c97b33a5deb73a6d5b6bb"
13474 - integrity sha512-Js7ueMZOVSZ3tP8C7E3KZiHv6QQl7lnJ+OkbxoaFazzSa2KyEHqApfGbU3XboUgUnq4ZuUmskUpYKTNx01fm5A==
13475 - dependencies:
13476 - expand-tilde "^2.0.2"
13477 - git-config-path "^1.0.1"
13478 - ini "^1.3.5"
13479 -
13480 -parse-github-url@^1.0.2:
13481 - version "1.0.2"
13482 - resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395"
13483 - integrity sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==
13484 -
13226 parse-json@7.1.1:
13227 version "7.1.1"
13228 resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-7.1.1.tgz#68f7e6f0edf88c54ab14c00eb700b753b14e2120"
@@ -13510,13 +13251,6 @@ parse-json@^5.2.0:
13251 json-parse-even-better-errors "^2.3.0"
13252 lines-and-columns "^1.1.6"
13253
13513 -parse-link-header@^2.0.0:
13514 - version "2.0.0"
13515 - resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-2.0.0.tgz#949353e284f8aa01f2ac857a98f692b57733f6b7"
13516 - integrity sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==
13517 - dependencies:
13518 - xtend "~4.0.1"
13519 -
13254 parse-node-version@^1.0.0:
13255 version "1.0.1"
13256 resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
@@ -13766,11 +13500,6 @@ pino@8.20.0:
13500 sonic-boom "^3.7.0"
13501 thread-stream "^2.0.0"
13502
13769 -pinpoint@^1.1.0:
13770 - version "1.1.0"
13771 - resolved "https://registry.yarnpkg.com/pinpoint/-/pinpoint-1.1.0.tgz#0cf7757a6977f1bf7f6a32207b709e377388e874"
13772 - integrity sha1-DPd1eml38b9/ajIge3CeN3OI6HQ=
13773 -
13503 pirates@^3.0.2:
13504 version "3.0.2"
13505 resolved "https://registry.yarnpkg.com/pirates/-/pirates-3.0.2.tgz#7e6f85413fd9161ab4e12b539b06010d85954bb9"
@@ -14008,14 +13737,6 @@ pretty-format@^29.7.0:
13737 ansi-styles "^5.0.0"
13738 react-is "^18.0.0"
13739
14011 -prettyjson@^1.2.1:
14012 - version "1.2.1"
14013 - resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
14014 - integrity sha1-/P+rQdGcq0365eV15kJGYZsS0ok=
14015 - dependencies:
14016 - colors "^1.1.2"
14017 - minimist "^1.2.0"
14018 -
13740 process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
13741 version "2.0.1"
13742 resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -14198,16 +13919,6 @@ query-string@^5.0.1:
13919 object-assign "^4.1.0"
13920 strict-uri-encode "^1.0.0"
13921
14201 -query-string@^6.12.1:
14202 - version "6.14.1"
14203 - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a"
14204 - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==
14205 - dependencies:
14206 - decode-uri-component "^0.2.0"
14207 - filter-obj "^1.1.0"
14208 - split-on-first "^1.0.0"
14209 - strict-uri-encode "^2.0.0"
14210 -
13922 querystring-es3@~0.2.0:
13923 version "0.2.1"
13924 resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -14517,11 +14228,6 @@ readdirp@~3.6.0:
14228 dependencies:
14229 picomatch "^2.2.1"
14230
14520 -readline-sync@^1.4.9:
14521 - version "1.4.10"
14522 - resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b"
14523 - integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==
14524 -
14231 real-require@^0.2.0:
14232 version "0.2.0"
14233 resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
@@ -14578,11 +14284,6 @@ regenerator-runtime@^0.13.4:
14284 resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
14285 integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
14286
14581 -regenerator-runtime@^0.13.9:
14582 - version "0.13.11"
14583 - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
14584 - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
14585 -
14287 regenerator-runtime@^0.14.0:
14288 version "0.14.1"
14289 resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
@@ -14872,11 +14573,6 @@ ret@~0.1.10:
14573 resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
14574 integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
14575
14875 -retry@0.12.0:
14876 - version "0.12.0"
14877 - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
14878 - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
14879 -
14576 retry@^0.13.1:
14577 version "0.13.1"
14578 resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
@@ -15234,7 +14930,7 @@ semver@^7.1.1:
14930 resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.1.tgz#29104598a197d6cbe4733eeecbe968f7b43a9667"
14931 integrity sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==
14932
15237 -semver@^7.2.1, semver@^7.3.8:
14933 +semver@^7.2.1:
14934 version "7.3.8"
14935 resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
14936 integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
@@ -15700,11 +15396,6 @@ spdy@^4.0.2:
15396 select-hose "^2.0.0"
15397 spdy-transport "^3.0.0"
15398
15703 -split-on-first@^1.0.0:
15704 - version "1.1.0"
15705 - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
15706 - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
15707 -
15399 split-string@^3.0.1, split-string@^3.0.2:
15400 version "3.1.0"
15401 resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -15806,11 +15497,6 @@ strict-uri-encode@^1.0.0:
15497 resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
15498 integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
15499
15809 -strict-uri-encode@^2.0.0:
15810 - version "2.0.0"
15811 - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
15812 - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
15813 -
15500 string-length@^4.0.1:
15501 version "4.0.2"
15502 resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
@@ -16088,7 +15774,7 @@ supports-color@^2.0.0:
15774 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
15775 integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
15776
16091 -supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0:
15777 +supports-color@^5.3.0, supports-color@^5.4.0:
15778 version "5.5.0"
15779 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
15780 integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
@@ -16109,14 +15795,6 @@ supports-color@^8.0.0:
15795 dependencies:
15796 has-flag "^4.0.0"
15797
16112 -supports-hyperlinks@^1.0.1:
16113 - version "1.0.1"
16114 - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7"
16115 - integrity sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==
16116 - dependencies:
16117 - has-flag "^2.0.0"
16118 - supports-color "^5.0.0"
16119 -
15798 supports-preserve-symlinks-flag@^1.0.0:
15799 version "1.0.0"
15800 resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
@@ -17532,11 +17210,6 @@ ws@^7:
17210 resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9"
17211 integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
17212
17535 -xcase@^2.0.1:
17536 - version "2.0.1"
17537 - resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9"
17538 - integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw==
17539 -
17213 xdg-basedir@^4.0.0:
17214 version "4.0.0"
17215 resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"