diff --git a/.gitignore b/.gitignore index e18ac6e95b..ae752d2179 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,4 @@ test-timings.json # Cache *.tsbuildinfo -.swc/ +.swc/ \ No newline at end of file diff --git a/bench/vercel/.env.dev b/bench/vercel/.env.dev new file mode 100644 index 0000000000..aaa722fd03 --- /dev/null +++ b/bench/vercel/.env.dev @@ -0,0 +1,8 @@ +# The Vercel team you want to deploy the project too +VERCEL_TEST_TEAM= + +# The corresponding Vercel token +VERCEL_TEST_TOKEN= + +# The Vercel project you want to deploy the test project too +VERCEL_TEST_PROJECT_NAME= diff --git a/bench/vercel/.gitignore b/bench/vercel/.gitignore new file mode 100644 index 0000000000..753f9c1a3b --- /dev/null +++ b/bench/vercel/.gitignore @@ -0,0 +1,5 @@ +.vercel +.next +*.tgz +yarn.lock +.env \ No newline at end of file diff --git a/bench/vercel/README.md b/bench/vercel/README.md new file mode 100644 index 0000000000..5d8bce4c7e --- /dev/null +++ b/bench/vercel/README.md @@ -0,0 +1,22 @@ +# Benchmarking Next.js on production + +This script allows you to measure some performance metrics of your local build of Next.js on production by uploading your current build to Vercel with an example app and running some basic benchmarks on it. + +## Requirements + +- the Vercel CLI + +## Setup + +Rename the provided `./env.local` file to `./env` and fill in the required `VERCEL_TEST_TOKEN` and `VERCEL_TEST_TEAM` values. You can find and generate those from vercel.com. + +Run `pnpm install`, `pnpm bench` and profit. + +Note: if you made some changes to Next.js, make sure you compiled them by running at the root of the monorepo either `pnpm dev` or `pnpm build --force`. + +## How it works + +- with the Vercel CLI, we setup a project +- we `npm pack` the local Next build and add it to the repo +- we upload the repo to Vercel and let it build +- once it builds, we get the deployment url and run some tests diff --git a/bench/vercel/bench.js b/bench/vercel/bench.js new file mode 100644 index 0000000000..7aea7631ac --- /dev/null +++ b/bench/vercel/bench.js @@ -0,0 +1,70 @@ +import { Command } from 'commander' +import console from 'console' + +import chalk from 'chalk' + +import PQueue from 'p-queue' +import { generateProjects, cleanupProjectFolders } from './project-utils.js' +import { printBenchmarkResults } from './chart.js' +import { genRetryableRequest } from './gen-request.js' + +const program = new Command() + +const queue = new PQueue({ concurrency: 25 }) +const TTFB_OUTLIERS_THRESHOLD = 250 + +program.option('-p, --path ') + +program.parse(process.argv) + +const options = program.opts() + +if (options.path) { + console.log('Running benchmark for path: ', options.path) +} + +try { + const [originDeploymentURL, headDeploymentURL] = await generateProjects() + + const originBenchmarkURL = `${originDeploymentURL}${options.path || ''}` + const headBenchmarkURL = `${headDeploymentURL}${options.path || ''}` + + console.log(`Origin deployment URL: ${originBenchmarkURL}`) + console.log(`Head deployment URL: ${headBenchmarkURL}`) + console.log(`Running benchmark...`) + + const benchResults = await runBenchmark(originBenchmarkURL) + + const headBenchResults = await runBenchmark(headBenchmarkURL) + + console.log(chalk.bold('Benchmark results for cold:')) + printBenchmarkResults( + { + origin: benchResults, + head: headBenchResults, + }, + (r) => r.cold && r.firstByte <= TTFB_OUTLIERS_THRESHOLD && r.firstByte + ) + console.log(chalk.bold('Benchmark results for hot:')) + printBenchmarkResults( + { + origin: benchResults, + head: headBenchResults, + }, + (r) => !r.cold && r.firstByte <= TTFB_OUTLIERS_THRESHOLD && r.firstByte + ) +} catch (err) { + console.log(chalk.red('Benchmark failed: ', err)) +} finally { + await cleanupProjectFolders() +} + +async function runBenchmark(url) { + return ( + await Promise.all( + Array.from({ length: 500 }).map(() => + queue.add(() => genRetryableRequest(url)) + ) + ) + ).filter(Boolean) +} diff --git a/bench/vercel/benchmark-app/.gitignore b/bench/vercel/benchmark-app/.gitignore new file mode 100644 index 0000000000..9a0fb41c7f --- /dev/null +++ b/bench/vercel/benchmark-app/.gitignore @@ -0,0 +1,2 @@ +.vercel +webpack-stats-client.json \ No newline at end of file diff --git a/bench/vercel/benchmark-app/app/layout.js b/bench/vercel/benchmark-app/app/layout.js new file mode 100644 index 0000000000..3a313aa413 --- /dev/null +++ b/bench/vercel/benchmark-app/app/layout.js @@ -0,0 +1,14 @@ +import * as React from 'react' + +export default function Root({ children }) { + return ( + + + {children} + + ) +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/benchmark-app/app/rsc/page.js b/bench/vercel/benchmark-app/app/rsc/page.js new file mode 100644 index 0000000000..7598427263 --- /dev/null +++ b/bench/vercel/benchmark-app/app/rsc/page.js @@ -0,0 +1,14 @@ +import * as React from 'react' + +// if (!('hot' in Math)) Math.hot = false + +export default function page() { + // const previous = Math.hot + // Math.hot = true + // return
{previous ? 'HOT' : 'COLD'}
+ return
hello
+} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/benchmark-app/next.config.js b/bench/vercel/benchmark-app/next.config.js new file mode 100644 index 0000000000..7041c1b531 --- /dev/null +++ b/bench/vercel/benchmark-app/next.config.js @@ -0,0 +1,34 @@ +const { StatsWriterPlugin } = require('webpack-stats-plugin') +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') + +module.exports = { + experimental: { + appDir: true, + }, + webpack: (config, options) => { + const { nextRuntime = 'client' } = options + if (process.env.ANALYZE) { + if (nextRuntime === 'edge') + config.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + openAnalyzer: true, + reportFilename: options.isServer + ? '../analyze/server.html' + : './analyze/client.html', + }) + ) + config.plugins.push( + new StatsWriterPlugin({ + filename: `../webpack-stats-${nextRuntime}.json`, + stats: { + assets: true, + chunks: true, + modules: true, + }, + }) + ) + } + return config + }, +} diff --git a/bench/vercel/benchmark-app/package.json b/bench/vercel/benchmark-app/package.json new file mode 100644 index 0000000000..883a323075 --- /dev/null +++ b/bench/vercel/benchmark-app/package.json @@ -0,0 +1,14 @@ +{ + "name": "stats-app", + "private": true, + "license": "MIT", + "dependencies": { + "webpack-bundle-analyzer": "^4.6.1", + "webpack-stats-plugin": "^1.1.0" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + } +} diff --git a/bench/vercel/benchmark-app/pages/index.js b/bench/vercel/benchmark-app/pages/index.js new file mode 100644 index 0000000000..7ab1d4cea6 --- /dev/null +++ b/bench/vercel/benchmark-app/pages/index.js @@ -0,0 +1,20 @@ +if (!('hot' in Math)) Math.hot = false + +export default function page({ hot }) { + return `${hot ? 'HOT' : 'COLD'}` +} + +export async function getServerSideProps() { + const wasHot = Math.hot + Math.hot = true + + return { + props: { + hot: wasHot, + }, + } +} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/bench/vercel/chart.js b/bench/vercel/chart.js new file mode 100644 index 0000000000..c35e07e9d7 --- /dev/null +++ b/bench/vercel/chart.js @@ -0,0 +1,99 @@ +import downsampler from 'downsample-lttb' +import asciichart from 'asciichart' +import terminalSize from 'term-size' + +const CHART_WIDTH = terminalSize().columns - 15 // space for the labels + +function getMetrics(data) { + const sorted = [...data].sort((a, b) => a - b) + const getPercentile = (percentile) => { + const index = Math.floor((sorted.length - 1) * percentile) + return sorted[index] + } + return { + hits: sorted.length, + confidenceInterval: round(getConfidenceInterval(sorted)), + median: getPercentile(0.5), + avg: sorted.reduce((a, b) => a + b, 0) / sorted.length, + p75: getPercentile(0.75), + p95: getPercentile(0.95), + p99: getPercentile(0.99), + p25: getPercentile(0.25), + min: sorted[0], + max: sorted[sorted.length - 1], + } +} + +function round(num) { + return Math.round(num * 100) / 100 +} + +// thanks Copilot +function getConfidenceInterval(data) { + const n = data.length + const m = data.reduce((a, b) => a + b) / n + const s = Math.sqrt( + data.map((x) => Math.pow(x - m, 2)).reduce((a, b) => a + b) / n + ) + const z = 1.96 // 95% confidence + const e = z * (s / Math.sqrt(n)) + return e +} + +export function downsample(data, maxPoints) { + const sortedData = [...data].sort((a, b) => a - b) + return downsampler + .processData( + // the downsampler expects a 2d array of [x, y] values, so we need to add an index + sortedData.map((p, i) => [p, i]), + maxPoints + ) + .map((p) => p[0]) +} + +export function printBenchmarkResults({ origin, head }, metricSelector) { + const [processedOriginData, processedHeadData] = [origin, head].map( + (results) => results.map(metricSelector).filter(Boolean) + ) + + const [originMetrics, headMetrics] = [ + processedOriginData, + processedHeadData, + ].map(getMetrics) + + const deltaMetrics = { + min: headMetrics.min - originMetrics.min, + max: headMetrics.max - originMetrics.max, + avg: headMetrics.avg - originMetrics.avg, + median: headMetrics.median - originMetrics.median, + p95: headMetrics.p95 - originMetrics.p95, + p99: headMetrics.p99 - originMetrics.p99, + p75: headMetrics.p75 - originMetrics.p75, + p25: headMetrics.p25 - originMetrics.p25, + } + + console.table({ + origin: originMetrics, + head: headMetrics, + delta: deltaMetrics, + }) + + const [originData, headData] = [processedOriginData, processedHeadData].map( + (data) => + downsample( + data, + Math.min( + CHART_WIDTH, + processedOriginData.length, + processedHeadData.length + ) + ) + ) + + console.log( + asciichart.plot([originData, headData], { + height: 15, + colors: [asciichart.blue, asciichart.red], + }) + ) +} diff --git a/bench/vercel/gen-request.js b/bench/vercel/gen-request.js new file mode 100644 index 0000000000..ac99226f55 --- /dev/null +++ b/bench/vercel/gen-request.js @@ -0,0 +1,41 @@ +import https from 'https' +import timer from '@szmarczak/http-timer' + +// a wrapper around genAsyncRequest that will retry the request 5 times if it fails +export async function genRetryableRequest(url) { + let retries = 0 + while (retries < 5) { + try { + return await genAsyncRequest(url) + } catch (err) {} + retries++ + await new Promise((r) => setTimeout(r, 1000)) + } + throw new Error(`Failed to fetch ${url}, too many retries`) +} + +// a wrapper around http.request that is enhanced with timing information +async function genAsyncRequest(url) { + return new Promise((resolve, reject) => { + const request = https.get(url) + timer(request) + request.on('response', (response) => { + let body = '' + response.on('data', (data) => { + body += data + }) + response.on('end', () => { + resolve({ + ...response.timings.phases, + cold: !body.includes('HOT'), + }) + }) + response.on('error', (err) => { + reject(err) + }) + }) + request.on('error', (err) => { + reject(err) + }) + }) +} diff --git a/bench/vercel/generate-package-json.js b/bench/vercel/generate-package-json.js new file mode 100644 index 0000000000..1d449d460c --- /dev/null +++ b/bench/vercel/generate-package-json.js @@ -0,0 +1,52 @@ +import execa from 'execa' +import fs from 'fs/promises' +import path from 'path' + +export async function generatePackageJson(folder, withLocalNext = false) { + const packageJson = JSON.parse( + await fs.readFile(path.join(folder, 'package.json')) + ) + + const currentVersions = await getCurrentRootReactPackagesVersions() + + packageJson.dependencies = packageJson.dependencies || {} + packageJson.dependencies['react'] = currentVersions.react + packageJson.dependencies['react-dom'] = currentVersions['react-dom'] + if (withLocalNext) { + packageJson.dependencies.next = await packNextBuild(folder) + } else { + packageJson.dependencies.next = await getCurrentNextVersion() + } + + await fs.writeFile( + path.join(folder, 'package.json'), + JSON.stringify(packageJson, null, 2) + ) +} + +export async function packNextBuild(folder) { + const process = await execa('npm', [ + 'pack', + '../../packages/next', + `--pack-destination=${folder}`, + ]) + + return `file:./${process.stdout}` +} + +async function getCurrentNextVersion() { + const packageJson = JSON.parse( + await fs.readFile('../../packages/next/package.json', 'utf8') + ) + return packageJson.version +} + +async function getCurrentRootReactPackagesVersions() { + const packageJson = JSON.parse( + await fs.readFile('../../package.json', 'utf8') + ) + return { + react: packageJson.devDependencies['react-exp'], + 'react-dom': packageJson.devDependencies['react-dom-exp'], + } +} diff --git a/bench/vercel/package.json b/bench/vercel/package.json new file mode 100644 index 0000000000..4ce5045df5 --- /dev/null +++ b/bench/vercel/package.json @@ -0,0 +1,24 @@ +{ + "name": "bench-production", + "version": "1.0.0", + "description": "Scripts for benchmarking in production.", + "main": "bench.js", + "type": "module", + "scripts": { + "bench": "node bench.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@szmarczak/http-timer": "5.0.1", + "asciichart": "1.5.25", + "commander": "2.20.0", + "dotenv": "10.0.0", + "downsample-lttb": "0.0.1", + "listr2": "5.0.5", + "p-queue": "7.3.0", + "term-size": "3.0.2", + "webpack-bundle-analyzer": "^4.6.1", + "webpack-stats-plugin": "^1.1.0" + } +} diff --git a/bench/vercel/project-utils.js b/bench/vercel/project-utils.js new file mode 100644 index 0000000000..6d069dc0a6 --- /dev/null +++ b/bench/vercel/project-utils.js @@ -0,0 +1,218 @@ +import { config } from 'dotenv' + +import fetch from 'node-fetch' +import chalk from 'chalk' +import execa from 'execa' +import path from 'path' +import url from 'url' +import { generatePackageJson } from './generate-package-json.js' +import { Listr } from 'listr2' + +config() + +const TEST_PROJECT_NAME = process.env.VERCEL_TEST_PROJECT_NAME +const ORIGIN_PROJECT_NAME = TEST_PROJECT_NAME + '-origin' +const HEAD_PROJECT_NAME = TEST_PROJECT_NAME + '-head' + +const TEST_TEAM_NAME = process.env.VERCEL_TEST_TEAM +const TEST_TOKEN = process.env.VERCEL_TEST_TOKEN +const VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG = + process.env.VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) + +const appFolder = path.join(__dirname, 'benchmark-app') +const originAppFolder = path.join(__dirname, 'benchmark-app-origin') +const headAppFolder = path.join(__dirname, 'benchmark-app-head') + +export async function generateProjects() { + const { originUrl, headUrl } = await new Listr( + [ + { + title: 'Origin project', + task: (ctx, task) => + task.newListr( + (parent) => [ + { + title: 'Resetting project', + task: async () => { + await resetProject(ORIGIN_PROJECT_NAME) + }, + }, + { + title: 'copying app', + task: async () => { + await execa('cp', ['-f', '-R', appFolder, originAppFolder]) + }, + }, + { + title: 'Set Next.js version in package.json', + task: async () => { + await generatePackageJson(originAppFolder) + }, + }, + { + title: 'deploying project', + task: async () => { + const url = await deployProject( + ORIGIN_PROJECT_NAME, + originAppFolder + ) + ctx.originUrl = url + }, + }, + ], + { concurrent: false } + ), + }, + { + title: 'Head project', + task: (ctx, task) => + task.newListr( + (parent) => [ + { + title: 'Resetting project', + task: async () => { + await resetProject(HEAD_PROJECT_NAME) + }, + }, + { + title: 'copying app', + task: async () => { + await execa('cp', ['-f', '-R', appFolder, headAppFolder]) + }, + }, + { + title: 'pack local Next.js version', + task: async () => { + await generatePackageJson(headAppFolder, true) + }, + }, + { + title: 'deploying project', + task: async () => { + const url = await deployProject( + HEAD_PROJECT_NAME, + headAppFolder + ) + ctx.headUrl = url + }, + }, + ], + { concurrent: false } + ), + }, + ], + { concurrent: true } + ).run() + + return [originUrl, headUrl] +} + +export async function cleanupProjectFolders() { + await Promise.all([ + execa('rm', ['-rf', originAppFolder]), + execa('rm', ['-rf', headAppFolder]), + ]) +} + +async function resetProject(projectName) { + const deleteRes = await fetch( + `https://vercel.com/api/v8/projects/${encodeURIComponent( + projectName + )}?teamId=${TEST_TEAM_NAME}`, + { + method: 'DELETE', + headers: { + Authorization: `Bearer ${TEST_TOKEN}`, + }, + } + ) + + if (!deleteRes.ok && deleteRes.status !== 404) { + throw new Error( + `Failed to delete project got status ${ + deleteRes.status + }, ${await deleteRes.text()}` + ) + } + + const createRes = await fetch( + `https://vercel.com/api/v8/projects?teamId=${TEST_TEAM_NAME}`, + { + method: 'POST', + headers: { + 'content-type': 'application/json', + Authorization: `Bearer ${TEST_TOKEN}`, + }, + body: JSON.stringify({ + framework: 'nextjs', + name: projectName, + }), + } + ) + + if (!createRes.ok) { + throw new Error( + `Failed to create project got status ${ + createRes.status + }, ${await createRes.text()}` + ) + } +} + +export async function deployProject(projectName, appFolder) { + try { + const vercelFlags = ['--scope', TEST_TEAM_NAME] + const vercelEnv = { ...process.env, TOKEN: TEST_TOKEN } + + // link the project + const linkRes = await execa( + 'vercel', + ['link', '-p', projectName, '--confirm', ...vercelFlags], + { + cwd: appFolder, + env: vercelEnv, + } + ) + + if (linkRes.exitCode !== 0) { + throw new Error( + `Failed to link project ${linkRes.stdout} ${linkRes.stderr} (${linkRes.exitCode})` + ) + } + + const deployRes = await execa( + 'vercel', + [ + 'deploy', + '--build-env', + 'NEXT_PRIVATE_TEST_MODE=1', + '--build-env', + 'NEXT_TELEMETRY_DISABLED=1', + ...(VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG + ? [ + '--build-env', + `VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG=${VERCEL_EDGE_FUNCTIONS_BRIDGE_PKG}`, + ] + : []), + '--force', + ...vercelFlags, + ], + { + cwd: appFolder, + env: vercelEnv, + } + ) + + if (deployRes.exitCode !== 0) { + throw new Error( + `Failed to deploy project ${linkRes.stdout} ${linkRes.stderr} (${linkRes.exitCode})` + ) + } + + return deployRes.stdout + } catch (err) { + console.log(chalk.red('Deployment failed: ', err)) + throw err + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b2c632861..3bff451d40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -325,6 +325,30 @@ importers: webpack: 5.74.0_@swc+core@1.2.203 webpack-bundle-analyzer: 4.3.0 + bench/vercel: + specifiers: + '@szmarczak/http-timer': 5.0.1 + asciichart: 1.5.25 + commander: 2.20.0 + dotenv: 10.0.0 + downsample-lttb: 0.0.1 + listr2: 5.0.5 + p-queue: 7.3.0 + term-size: 3.0.2 + webpack-bundle-analyzer: ^4.6.1 + webpack-stats-plugin: ^1.1.0 + dependencies: + '@szmarczak/http-timer': 5.0.1 + asciichart: 1.5.25 + commander: 2.20.0 + dotenv: 10.0.0 + downsample-lttb: 0.0.1 + listr2: 5.0.5 + p-queue: 7.3.0 + term-size: 3.0.2 + webpack-bundle-analyzer: 4.6.1 + webpack-stats-plugin: 1.1.0 + packages/create-next-app: specifiers: '@types/async-retry': 1.4.2 @@ -6689,6 +6713,16 @@ packages: defer-to-connect: 2.0.0 dev: true + /@szmarczak/http-timer/5.0.1: + resolution: + { + integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==, + } + engines: { node: '>=14.16' } + dependencies: + defer-to-connect: 2.0.1 + dev: false + /@taskr/clear/1.1.0: resolution: { @@ -8300,7 +8334,6 @@ packages: } engines: { node: '>=0.4.0' } hasBin: true - dev: true /add-stream/1.0.0: resolution: @@ -8371,7 +8404,6 @@ packages: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: true /ajv-keywords/3.5.2_ajv@6.12.6: resolution: @@ -8756,7 +8788,10 @@ packages: dev: true /array-flatten/1.1.1: - resolution: { integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= } + resolution: + { + integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==, + } dev: true /array-ify/1.0.0: @@ -8910,6 +8945,13 @@ packages: } dev: true + /asciichart/1.5.25: + resolution: + { + integrity: sha512-PNxzXIPPOtWq8T7bgzBtk9cI2lgS4SJZthUHEiQ1aoIc3lNzGfUvIvo9LiAnq26TACo9t1/4qP6KTGAUbzX9Xg==, + } + dev: false + /asn1.js/4.10.1: resolution: { @@ -10599,7 +10641,6 @@ packages: integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==, } engines: { node: '>=6' } - dev: true /cli-boxes/2.2.1: resolution: @@ -10674,6 +10715,17 @@ packages: string-width: 1.0.2 dev: true + /cli-truncate/2.1.0: + resolution: + { + integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==, + } + engines: { node: '>=8' } + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: false + /cli-width/2.2.0: resolution: { @@ -10881,6 +10933,13 @@ packages: } dev: true + /colorette/2.0.19: + resolution: + { + integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==, + } + dev: false + /colors/1.4.0: resolution: { @@ -10921,7 +10980,6 @@ packages: { integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==, } - dev: true /commander/2.20.3: resolution: @@ -10951,7 +11009,6 @@ packages: integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==, } engines: { node: '>= 10' } - dev: true /comment-json/3.0.3: resolution: @@ -11297,7 +11354,10 @@ packages: safe-buffer: 5.1.2 /cookie-signature/1.0.6: - resolution: { integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw= } + resolution: + { + integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==, + } dev: true /cookie/0.4.0: @@ -12400,6 +12460,14 @@ packages: engines: { node: '>=10' } dev: true + /defer-to-connect/2.0.1: + resolution: + { + integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==, + } + engines: { node: '>=10' } + dev: false + /define-lazy-prop/2.0.0: resolution: { @@ -12953,7 +13021,13 @@ packages: integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==, } engines: { node: '>=10' } - dev: true + + /downsample-lttb/0.0.1: + resolution: + { + integrity: sha512-Olebo5gyh44OAXTd2BKdcbN5VaZOIKFzoeo9JUFwxDlGt6Sd8fUo6SKaLcafy8aP2UrsKmWDpsscsFEghMjeZA==, + } + dev: false /duplexer/0.1.1: resolution: @@ -13973,7 +14047,6 @@ packages: { integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, } - dev: true /events/3.3.0: resolution: @@ -14842,7 +14915,10 @@ packages: dev: true /forwarded/0.1.2: - resolution: { integrity: sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= } + resolution: + { + integrity: sha512-Ua9xNhH0b8pwE3yRbFfXJvfdWF0UHNCdeyb2sbi9Ul/M+r3PTdrz7Cv4SCfZRMjmzEM9PhraqfZFbGTIg3OMyA==, + } engines: { node: '>= 0.6' } dev: true @@ -19311,6 +19387,28 @@ packages: - zenObservable dev: true + /listr2/5.0.5: + resolution: + { + integrity: sha512-DpBel6fczu7oQKTXMekeprc0o3XDgGMkD7JNYyX+X0xbwK+xgrx9dcyKoXKqpLSUvAWfmoePS7kavniOcq3r4w==, + } + engines: { node: ^14.13.1 || >=16.0.0 } + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.5.7 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: false + /load-json-file/1.1.0: resolution: { integrity: sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= } engines: { node: '>=0.10.0' } @@ -19667,6 +19765,19 @@ packages: wrap-ansi: 3.0.1 dev: true + /log-update/4.0.0: + resolution: + { + integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==, + } + engines: { node: '>=10' } + dependencies: + ansi-escapes: 4.3.0 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: false + /long/4.0.0: resolution: { @@ -20168,7 +20279,10 @@ packages: dev: true /merge-descriptors/1.0.1: - resolution: { integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= } + resolution: + { + integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==, + } dev: true /merge-stream/2.0.0: @@ -21985,7 +22099,6 @@ packages: engines: { node: '>=10' } dependencies: aggregate-error: 3.0.1 - dev: true /p-pipe/3.1.0: resolution: @@ -22006,6 +22119,17 @@ packages: p-timeout: 3.2.0 dev: true + /p-queue/7.3.0: + resolution: + { + integrity: sha512-5fP+yVQ0qp0rEfZoDTlP2c3RYBgxvRsw30qO+VtPPc95lyvSG+x6USSh1TuLB4n96IO6I8/oXQGsTgtna4q2nQ==, + } + engines: { node: '>=12' } + dependencies: + eventemitter3: 4.0.7 + p-timeout: 5.1.0 + dev: false + /p-reduce/2.1.0: resolution: { @@ -22044,6 +22168,14 @@ packages: p-finally: 1.0.0 dev: true + /p-timeout/5.1.0: + resolution: + { + integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==, + } + engines: { node: '>=12' } + dev: false + /p-try/1.0.0: resolution: { integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= } engines: { node: '>=4' } @@ -25611,6 +25743,13 @@ packages: engines: { node: '>=4' } dev: true + /rfdc/1.3.0: + resolution: + { + integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==, + } + dev: false + /rgb-regex/1.0.1: resolution: { @@ -25810,6 +25949,15 @@ packages: tslib: 2.4.0 dev: true + /rxjs/7.5.7: + resolution: + { + integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==, + } + dependencies: + tslib: 2.4.0 + dev: false + /sade/1.7.4: resolution: { @@ -26333,6 +26481,18 @@ packages: engines: { node: '>=0.10.0' } dev: true + /slice-ansi/3.0.0: + resolution: + { + integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==, + } + engines: { node: '>=8' } + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: false + /slice-ansi/4.0.0: resolution: { @@ -27516,6 +27676,14 @@ packages: engines: { node: '>=8' } dev: true + /term-size/3.0.2: + resolution: + { + integrity: sha512-YfE8KwjrumviCxmeOS1r1hAwqUcd/AnhrG/Pol/Gry91EyUCS+jQH0qFUZOUkpGQ5rXsKMK6S6kjne53ytYS/w==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + dev: false + /terminal-link/2.1.1: resolution: { @@ -29010,7 +29178,10 @@ packages: dev: true /utils-merge/1.0.1: - resolution: { integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= } + resolution: + { + integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==, + } engines: { node: '>= 0.4.0' } dev: true @@ -29326,6 +29497,28 @@ packages: - bufferutil - utf-8-validate + /webpack-bundle-analyzer/4.6.1: + resolution: + { + integrity: sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==, + } + engines: { node: '>= 10.13.0' } + hasBin: true + dependencies: + acorn: 8.8.0 + acorn-walk: 8.0.0 + chalk: 4.1.2 + commander: 7.2.0 + gzip-size: 6.0.0 + lodash: 4.17.21 + opener: 1.5.2 + sirv: 1.0.10 + ws: 7.5.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /webpack-sources/1.4.3: resolution: { @@ -29344,6 +29537,13 @@ packages: engines: { node: '>=10.13.0' } dev: true + /webpack-stats-plugin/1.1.0: + resolution: + { + integrity: sha512-D0meHk1WYryUbuCnWJuomJFAYvqs0rxv/JFu1XJT1YYpczdgnP1/vz+u/5Z31jrTxT6dJSxCg+TuKTgjhoZS6g==, + } + dev: false + /webpack/5.74.0: resolution: { @@ -29614,6 +29814,18 @@ packages: strip-ansi: 5.2.0 dev: true + /wrap-ansi/6.2.0: + resolution: + { + integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==, + } + engines: { node: '>=8' } + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + /wrap-ansi/7.0.0: resolution: { @@ -29624,7 +29836,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrappy/1.0.2: resolution: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 18ec407efc..dfc7a1ef0a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,3 @@ packages: - 'packages/*' + - 'bench/vercel/'