0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

chore: codspeed benchmark (#12347)

This commit is contained in:
Emanuele Stoppa 2024-11-06 12:33:14 +00:00 committed by GitHub
parent 222f71894c
commit ed5a9f1b75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 527 additions and 42 deletions

View file

@ -0,0 +1,50 @@
name: Continuous benchmark
on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- main
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
FORCE_COLOR: true
CODSPEED_TOKEN: ${{ secrets.CODSPEED_TOKEN }}
CODSPEED: true
jobs:
codspeed:
if: ${{ github.repository_owner == 'withastro' }}
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Setup PNPM
uses: pnpm/action-setup@v3
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build
- name: Run the benchmarks
uses: CodSpeedHQ/action@b587655f756aab640e742fec141261bc6f0a569d # v3.0.1
timeout-minutes: 30
with:
run: pnpm benchmark codspeed

View file

@ -14,7 +14,7 @@ export const astroBin = path.resolve(astroPkgPath, '../astro.js');
export function calculateStat(numbers) {
const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length;
const stdev = Math.sqrt(
numbers.map((x) => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / numbers.length
numbers.map((x) => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / numbers.length,
);
const max = Math.max(...numbers);
return { avg, stdev, max };

View file

@ -1,6 +1,6 @@
import { fileURLToPath } from 'node:url';
import { exec } from 'tinyexec';
import { markdownTable } from 'markdown-table';
import { exec } from 'tinyexec';
import { astroBin, calculateStat } from './_util.js';
/** Default project to run for this benchmark if not specified */
@ -8,9 +8,8 @@ export const defaultProject = 'render-default';
/**
* @param {URL} projectDir
* @param {URL} outputFile
*/
export async function run(projectDir, outputFile) {
export async function run(projectDir) {
const root = fileURLToPath(projectDir);
console.log('Benchmarking `astro --help`...');
@ -28,7 +27,7 @@ export async function run(projectDir, outputFile) {
printResult({
'astro --help': helpStat,
'astro info': infoStat,
})
}),
);
console.log('='.repeat(10));
}
@ -69,6 +68,6 @@ function printResult(result) {
],
{
align: ['l', 'r', 'r', 'r'],
}
},
);
}

View file

@ -0,0 +1,50 @@
import path from 'node:path';
import { withCodSpeed } from '@codspeed/tinybench-plugin';
import { Bench } from 'tinybench';
import { exec } from 'tinyexec';
import { renderPages } from '../make-project/render-default.js';
import { astroBin } from './_util.js';
export async function run({ memory: _memory, render, stress: _stress }) {
const options = {
iterations: 10,
};
const bench = process.env.CODSPEED ? withCodSpeed(new Bench(options)) : new Bench(options);
let app;
bench.add(
'Rendering',
async () => {
console.info('Start task.');
const result = {};
for (const fileName of renderPages) {
const pathname = '/' + fileName.slice(0, -path.extname(fileName).length);
const request = new Request(new URL(pathname, 'http://exmpale.com'));
const response = await app.render(request);
const html = await response.text();
if (!result[pathname]) result[pathname] = [];
result[pathname].push(html);
}
console.info('Finish task.');
return result;
},
{
async beforeAll() {
// build for rendering
await exec(astroBin, ['build'], {
nodeOptions: {
cwd: render.root,
stdio: 'inherit',
},
});
const entry = new URL('./dist/server/entry.mjs', `file://${render.root}`);
const { manifest, createApp } = await import(entry);
app = createApp(manifest);
app.manifest = manifest;
},
},
);
await bench.run();
console.table(bench.table());
}

View file

@ -56,6 +56,6 @@ function printResult(output) {
],
{
align: ['l', 'r', 'r', 'r'],
}
},
);
}

View file

@ -1,12 +1,12 @@
import { exec } from 'tinyexec';
import { markdownTable } from 'markdown-table';
import fs from 'node:fs/promises';
import http from 'node:http';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { markdownTable } from 'markdown-table';
import { waitUntilBusy } from 'port-authority';
import { calculateStat, astroBin } from './_util.js';
import { exec } from 'tinyexec';
import { renderPages } from '../make-project/render-default.js';
import { astroBin, calculateStat } from './_util.js';
const port = 4322;
@ -60,14 +60,14 @@ export async function run(projectDir, outputFile) {
console.log('Done!');
}
async function benchmarkRenderTime() {
export async function benchmarkRenderTime(portToListen = port) {
/** @type {Record<string, number[]>} */
const result = {};
for (const fileName of renderPages) {
// Render each file 100 times and push to an array
for (let i = 0; i < 100; i++) {
const pathname = '/' + fileName.slice(0, -path.extname(fileName).length);
const renderTime = await fetchRenderTime(`http://localhost:${port}${pathname}`);
const renderTime = await fetchRenderTime(`http://localhost:${portToListen}${pathname}`);
if (!result[pathname]) result[pathname] = [];
result[pathname].push(renderTime);
}
@ -97,7 +97,7 @@ function printResult(result) {
],
{
align: ['l', 'r', 'r', 'r'],
}
},
);
}

View file

@ -1,10 +1,10 @@
import autocannon from 'autocannon';
import { exec } from 'tinyexec';
import { markdownTable } from 'markdown-table';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import autocannon from 'autocannon';
import { markdownTable } from 'markdown-table';
import { waitUntilBusy } from 'port-authority';
import pb from 'pretty-bytes';
import { exec } from 'tinyexec';
import { astroBin } from './_util.js';
const port = 4321;
@ -28,9 +28,11 @@ export async function run(projectDir, outputFile) {
});
console.log('Previewing...');
const previewProcess = execaCommand(`${astroBin} preview --port ${port}`, {
const previewProcess = await exec(astroBin, ['preview', '--port', port], {
nodeOptions: {
cwd: root,
stdio: 'inherit',
},
});
console.log('Waiting for server ready...');
@ -59,7 +61,7 @@ export async function run(projectDir, outputFile) {
/**
* @returns {Promise<import('autocannon').Result>}
*/
async function benchmarkCannon() {
export async function benchmarkCannon() {
return new Promise((resolve, reject) => {
const instance = autocannon(
{
@ -76,7 +78,7 @@ async function benchmarkCannon() {
instance.stop();
resolve(result);
}
}
},
);
autocannon.track(instance, { renderResultsTable: false });
});
@ -95,7 +97,7 @@ function printResult(output) {
],
{
align: ['l', 'r', 'r', 'r'],
}
},
);
const reqAndBytesTable = markdownTable(
@ -106,7 +108,7 @@ function printResult(output) {
],
{
align: ['l', 'r', 'r', 'r', 'r'],
}
},
);
return `${latencyTable}\n\n${reqAndBytesTable}`;

View file

@ -1,7 +1,7 @@
import mri from 'mri';
import fs from 'node:fs/promises';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import {fileURLToPath, pathToFileURL} from 'node:url';
const args = mri(process.argv.slice(2));
@ -14,6 +14,7 @@ Command
memory Run build memory and speed test
render Run rendering speed test
server-stress Run server stress test
codspeed Run codspeed test
cli-startup Run CLI startup speed test
Options
@ -29,6 +30,7 @@ const benchmarks = {
render: () => import('./bench/render.js'),
'server-stress': () => import('./bench/server-stress.js'),
'cli-startup': () => import('./bench/cli-startup.js'),
codspeed: () => import('./bench/codspeed.js')
};
if (commandName && !(commandName in benchmarks)) {
@ -37,12 +39,26 @@ if (commandName && !(commandName in benchmarks)) {
}
if (commandName) {
if (commandName === 'codspeed') {
const render = await makeProject('render-bench');
const rootRender = fileURLToPath(render);
const bench = benchmarks[commandName];
const benchMod = await bench();
const payload = {
render: {
root: rootRender,
output: await getOutputFile('render')
},
};
await benchMod.run(payload);
} else {
// Run single benchmark
const bench = benchmarks[commandName];
const benchMod = await bench();
const projectDir = await makeProject(args.project || benchMod.defaultProject);
const outputFile = await getOutputFile(commandName);
await benchMod.run(projectDir, outputFile);
}
} else {
// Run all benchmarks
for (const name in benchmarks) {
@ -54,7 +70,7 @@ if (commandName) {
}
}
async function makeProject(name) {
export async function makeProject(name) {
console.log('Making project:', name);
const projectDir = new URL(`./projects/${name}/`, import.meta.url);
@ -78,6 +94,5 @@ async function getOutputFile(benchmarkName) {
// Prepare output file directory
await fs.mkdir(new URL('./', file), { recursive: true });
return file;
}

View file

@ -0,0 +1,132 @@
import fs from 'node:fs/promises';
import { loremIpsumHtml, loremIpsumMd } from './_util.js';
// Map of files to be generated and tested for rendering.
// Ideally each content should be similar for comparison.
const renderFiles = {
'components/ListItem.astro': `\
---
const { className, item, attrs } = Astro.props;
const nested = item !== 0;
---
<li class={className}>
<a
href={item}
aria-current={item === 0}
class:list={[{ large: !nested }, className]}
{...attrs}
>
<span>{item}</span>
</a>
</li>
`,
'components/Sublist.astro': `\
---
import ListItem from '../components/ListItem.astro';
const { items } = Astro.props;
const className = "text-red-500";
const style = { color: "red" };
---
<ul style={style}>
{items.map((item) => (
<ListItem className={className} item={item} attrs={{}} />
))}
</ul>
`,
'pages/astro.astro': `\
---
const className = "text-red-500";
const style = { color: "red" };
const items = Array.from({ length: 10000 }, (_, i) => ({i}));
---
<html>
<head>
<title>My Site</title>
</head>
<body>
<h1 class={className + ' text-lg'}>List</h1>
<ul style={style}>
{items.map((item) => (
<li class={className}>
<a
href={item.i}
aria-current={item.i === 0}
class:list={[{ large: item.i === 0 }, className]}
{...({})}
>
<span>{item.i}</span>
</a>
</li>
))}
</ul>
${Array.from({ length: 1000 })
.map(() => `<p>${loremIpsumHtml}</p>`)
.join('\n')}
</body>
</html>`,
'pages/md.md': `\
# List
${Array.from({ length: 1000 }, (_, i) => i)
.map((v) => `- ${v}`)
.join('\n')}
${Array.from({ length: 1000 })
.map(() => loremIpsumMd)
.join('\n\n')}
`,
'pages/mdx.mdx': `\
export const className = "text-red-500";
export const style = { color: "red" };
export const items = Array.from({ length: 1000 }, (_, i) => i);
# List
<ul style={style}>
{items.map((item) => (
<li class={className}>{item}</li>
))}
</ul>
${Array.from({ length: 1000 })
.map(() => loremIpsumMd)
.join('\n\n')}
`,
};
export const renderPages = [];
for (const file of Object.keys(renderFiles)) {
if (file.startsWith('pages/')) {
renderPages.push(file.replace('pages/', ''));
}
}
/**
* @param {URL} projectDir
*/
export async function run(projectDir) {
await fs.rm(projectDir, { recursive: true, force: true });
await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true });
await fs.mkdir(new URL('./src/components', projectDir), { recursive: true });
await Promise.all(
Object.entries(renderFiles).map(([name, content]) => {
return fs.writeFile(new URL(`./src/${name}`, projectDir), content, 'utf-8');
})
);
await fs.writeFile(
new URL('./astro.config.js', projectDir),
`\
import { defineConfig } from 'astro/config';
import adapter from '@benchmark/adapter';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [mdx()],
output: 'server',
adapter: adapter(),
});`,
'utf-8'
);
}

View file

@ -10,6 +10,7 @@
"@astrojs/mdx": "workspace:*",
"@astrojs/node": "^8.3.4",
"@benchmark/timer": "workspace:*",
"@benchmark/adapter": "workspace:*",
"astro": "workspace:*",
"autocannon": "^7.15.0",
"markdown-table": "^3.0.4",
@ -18,5 +19,9 @@
"pretty-bytes": "^6.1.1",
"sharp": "^0.33.3",
"tinyexec": "^0.3.1"
},
"devDependencies": {
"@codspeed/tinybench-plugin": "^3.1.1",
"tinybench": "^2.9.0"
}
}

View file

@ -0,0 +1,3 @@
# @benchmark/timer
Like `@astrojs/node`, but returns the rendered time in milliseconds for the page instead of the page content itself. This is used for internal benchmarks only.

View file

@ -0,0 +1,35 @@
{
"name": "@benchmark/adapter",
"description": "Bench adapter",
"private": true,
"version": "0.0.0",
"type": "module",
"types": "./dist/index.d.ts",
"author": "withastro",
"license": "MIT",
"keywords": [
"withastro",
"astro-adapter"
],
"exports": {
".": "./dist/index.js",
"./server.js": "./dist/server.js",
"./package.json": "./package.json"
},
"scripts": {
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
"server-destroy": "^1.0.1"
},
"peerDependencies": {
"astro": "workspace:*"
},
"devDependencies": {
"@types/server-destroy": "^1.0.4",
"astro": "workspace:*",
"astro-scripts": "workspace:*"
}
}

View file

@ -0,0 +1,32 @@
import type { AstroAdapter, AstroIntegration } from 'astro';
export default function createIntegration(): AstroIntegration {
return {
name: '@benchmark/timer',
hooks: {
'astro:config:setup': ({ updateConfig }) => {
updateConfig({
vite: {
ssr: {
noExternal: ['@benchmark/timer'],
},
},
});
},
'astro:config:done': ({ setAdapter }) => {
setAdapter({
name: '@benchmark/adapter',
serverEntrypoint: '@benchmark/adapter/server.js',
exports: ['manifest', 'createApp'],
supportedAstroFeatures: {
serverOutput: 'stable',
envGetSecret: 'experimental',
staticOutput: 'stable',
hybridOutput: 'stable',
i18nDomains: 'stable',
},
});
},
},
};
}

View file

@ -0,0 +1,34 @@
import * as fs from 'node:fs';
import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import { applyPolyfills } from 'astro/app/node';
applyPolyfills();
class MyApp extends App {
#manifest: SSRManifest | undefined;
#streaming: boolean;
constructor(manifest: SSRManifest, streaming = false) {
super(manifest, streaming);
this.#manifest = manifest;
this.#streaming = streaming;
}
async render(request: Request) {
const url = new URL(request.url);
if (this.#manifest?.assets.has(url.pathname)) {
const filePath = new URL('../../client/' + this.removeBase(url.pathname), import.meta.url);
const data = await fs.promises.readFile(filePath);
return new Response(data);
}
return super.render(request);
}
}
export function createExports(manifest: SSRManifest) {
return {
manifest,
createApp: (streaming: boolean) => new MyApp(manifest, streaming),
};
}

View file

@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"compilerOptions": {
"outDir": "./dist"
}
}

View file

@ -6,7 +6,9 @@ export function getAdapter(): AstroAdapter {
serverEntrypoint: '@benchmark/timer/server.js',
previewEntrypoint: '@benchmark/timer/preview.js',
exports: ['handler'],
supportedAstroFeatures: {},
supportedAstroFeatures: {
serverOutput: 'stable',
},
};
}

View file

@ -13,6 +13,6 @@ export function createExports(manifest: SSRManifest) {
const end = performance.now();
res.write(end - start + '');
res.end();
},
}
};
}

View file

@ -9,20 +9,16 @@
"**/_temp-fixtures/**",
"**/vendor/**",
"**/.vercel/**",
"benchmark/projects/",
"benchmark/results/",
],
"include": ["test/**", "e2e/**", "packages/**", "/scripts/**"],
"include": ["test/**", "e2e/**", "packages/**", "/scripts/**", "benchmark/bench"],
},
"formatter": {
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100,
"ignore": [
"benchmark/projects/",
"benchmark/results/",
".changeset",
"pnpm-lock.yaml",
"*.astro",
],
"ignore": [".changeset", "pnpm-lock.yaml", "*.astro"],
},
"organizeImports": {
"enabled": true,

View file

@ -66,6 +66,9 @@ importers:
'@astrojs/node':
specifier: ^8.3.4
version: 8.3.4(astro@packages+astro)
'@benchmark/adapter':
specifier: workspace:*
version: link:packages/adapter
'@benchmark/timer':
specifier: workspace:*
version: link:packages/timer
@ -93,6 +96,29 @@ importers:
tinyexec:
specifier: ^0.3.1
version: 0.3.1
devDependencies:
'@codspeed/tinybench-plugin':
specifier: ^3.1.1
version: 3.1.1(tinybench@2.9.0)
tinybench:
specifier: ^2.9.0
version: 2.9.0
benchmark/packages/adapter:
dependencies:
server-destroy:
specifier: ^1.0.1
version: 1.0.1
devDependencies:
'@types/server-destroy':
specifier: ^1.0.4
version: 1.0.4
astro:
specifier: workspace:*
version: link:../../../packages/astro
astro-scripts:
specifier: workspace:*
version: link:../../../scripts
benchmark/packages/timer:
dependencies:
@ -5935,6 +5961,14 @@ packages:
bundledDependencies:
- is-unicode-supported
'@codspeed/core@3.1.1':
resolution: {integrity: sha512-ONhERVDAtkm0nc+FYPivDozoMOlNUP2BWRBFDJYATGA18Iap5Kd2mZ1/Lwz54RB5+g+3YDOpsvotHa4hd3Q+7Q==}
'@codspeed/tinybench-plugin@3.1.1':
resolution: {integrity: sha512-LVF4End0kDU9V7CzuwAcmngSPJNnpduPnr+csOKvcG++FsYwfUuBJ1rvLPtv6yTkvxpUmUEsj6VA7/AEIBGZVw==}
peerDependencies:
tinybench: ^2.3.0
'@colors/colors@1.5.0':
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'}
@ -7399,6 +7433,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
axios@1.7.7:
resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
@ -8212,6 +8249,10 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
find-up@6.3.0:
resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
find-yarn-workspace-root2@1.2.16:
resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==}
@ -8226,6 +8267,15 @@ packages:
resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
engines: {node: '>=8'}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
foreground-child@3.3.0:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
@ -8751,6 +8801,10 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
locate-path@7.2.0:
resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
lodash.chunk@4.2.0:
resolution: {integrity: sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==}
@ -9174,6 +9228,10 @@ packages:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-gyp-build@4.8.2:
resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==}
hasBin: true
node-html-parser@6.1.13:
resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
@ -9276,6 +9334,10 @@ packages:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
p-limit@4.0.0:
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
p-limit@6.1.0:
resolution: {integrity: sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg==}
engines: {node: '>=18'}
@ -9288,6 +9350,10 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
p-locate@6.0.0:
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
p-map@2.1.0:
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
engines: {node: '>=6'}
@ -9352,6 +9418,10 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
path-exists@5.0.0:
resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@ -9687,6 +9757,9 @@ packages:
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
pseudomap@1.0.2:
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
@ -11501,6 +11574,23 @@ snapshots:
picocolors: 1.1.0
sisteransi: 1.0.5
'@codspeed/core@3.1.1':
dependencies:
axios: 1.7.7
find-up: 6.3.0
form-data: 4.0.0
node-gyp-build: 4.8.2
transitivePeerDependencies:
- debug
'@codspeed/tinybench-plugin@3.1.1(tinybench@2.9.0)':
dependencies:
'@codspeed/core': 3.1.1
stack-trace: 1.0.0-pre2
tinybench: 2.9.0
transitivePeerDependencies:
- debug
'@colors/colors@1.5.0':
optional: true
@ -13033,6 +13123,14 @@ snapshots:
postcss: 8.4.47
postcss-value-parser: 4.2.0
axios@1.7.7:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
axobject-query@4.1.0: {}
babel-plugin-jsx-dom-expressions@0.38.5(@babel/core@7.26.0):
@ -13771,6 +13869,11 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
find-up@6.3.0:
dependencies:
locate-path: 7.2.0
path-exists: 5.0.0
find-yarn-workspace-root2@1.2.16:
dependencies:
micromatch: 4.0.8
@ -13785,6 +13888,8 @@ snapshots:
flattie@1.1.1: {}
follow-redirects@1.15.9: {}
foreground-child@3.3.0:
dependencies:
cross-spawn: 7.0.3
@ -14418,6 +14523,10 @@ snapshots:
dependencies:
p-locate: 5.0.0
locate-path@7.2.0:
dependencies:
p-locate: 6.0.0
lodash.chunk@4.2.0: {}
lodash.clonedeep@4.5.0: {}
@ -15091,6 +15200,8 @@ snapshots:
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
node-gyp-build@4.8.2: {}
node-html-parser@6.1.13:
dependencies:
css-select: 5.1.0
@ -15201,6 +15312,10 @@ snapshots:
dependencies:
yocto-queue: 0.1.0
p-limit@4.0.0:
dependencies:
yocto-queue: 1.1.1
p-limit@6.1.0:
dependencies:
yocto-queue: 1.1.1
@ -15213,6 +15328,10 @@ snapshots:
dependencies:
p-limit: 3.1.0
p-locate@6.0.0:
dependencies:
p-limit: 4.0.0
p-map@2.1.0: {}
p-queue@8.0.1:
@ -15282,6 +15401,8 @@ snapshots:
path-exists@4.0.0: {}
path-exists@5.0.0: {}
path-key@3.1.1: {}
path-key@4.0.0: {}
@ -15631,6 +15752,8 @@ snapshots:
property-information@6.5.0: {}
proxy-from-env@1.1.0: {}
pseudomap@1.0.2: {}
psl@1.9.0: {}