diff --git a/.github/workflows/codspeed.yaml b/.github/workflows/codspeed.yaml new file mode 100644 index 00000000..89708401 --- /dev/null +++ b/.github/workflows/codspeed.yaml @@ -0,0 +1,43 @@ +name: "Benchmarks" +on: + push: + branches: + - "main" + pull_request: + types: ["opened", "reopened", "synchronize"] + branches: + - "main" + workflow_dispatch: {} +permissions: {} +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true +jobs: + benchmarks: + name: "Run benchmarks" + runs-on: "ubuntu-latest" + timeout-minutes: 30 + permissions: + contents: "read" + id-token: "write" + steps: + - name: "Checkout" + uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6.0.2 + with: + ref: "${{ github.ref }}" + fetch-depth: 1 + persist-credentials: false + - name: "Setup Node.js" + uses: "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020" # v4.4.0 + with: + node-version: "22" + - name: "Install pnpm" + run: "corepack enable && corepack prepare" + - name: "Install dependencies" + run: "pnpm install --frozen-lockfile --ignore-scripts" + - name: "Run benchmarks" + uses: "CodSpeedHQ/action@3194d9a39c4d46684cb44bf7207fc56626aad8fd" # v4.15.1 + with: + mode: "simulation" + run: "pnpm exec vitest bench --config vitest.bench.config.mts --run" + working-directory: "packages/node" diff --git a/README.md b/README.md index e519b1d5..8d717ca9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![CI status][status-badge]][status-dashboard] [![Test coverage][coverage-badge]][coverage-dashboard] [![Supply-chain score][socket-badge]][socket-dashboard] +[![CodSpeed][codspeed-badge]][codspeed-dashboard] [github-badge]: https://img.shields.io/github/stars/vadimpiven/node_reqwest?style=flat&logo=github [github-repo]: https://github.com/vadimpiven/node_reqwest @@ -20,6 +21,8 @@ [coverage-dashboard]: https://app.codecov.io/gh/vadimpiven/node_reqwest/tree/main [socket-badge]: https://badge.socket.dev/npm/package/node-reqwest [socket-dashboard]: https://socket.dev/npm/package/node-reqwest +[codspeed-badge]: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json +[codspeed-dashboard]: https://codspeed.io/vadimpiven/node_reqwest?utm_source=badge [![Open in GitHub Codespaces][codespace-badge]][codespace-action] diff --git a/packages/node/export/agent.ts b/packages/node/export/agent.ts index ff0351a7..b76f716c 100644 --- a/packages/node/export/agent.ts +++ b/packages/node/export/agent.ts @@ -1,78 +1,8 @@ -import type Stream from "node:stream"; -import { Dispatcher, type FormData, Response } from "undici"; +import { Dispatcher } from "undici"; import { Addon } from "./addon.ts"; import type { AgentCreationOptions, AgentDispatchOptions, AgentInstance } from "./addon-def.ts"; import type { Agent as AgentDef, AgentOptions } from "./agent-def.ts"; - -function normalizePem(pem?: string | Buffer | (string | Buffer)[]): string[] { - if (!pem) { - return []; - } - - if (Array.isArray(pem)) { - return pem.flatMap(normalizePem); - } - - return [Buffer.isBuffer(pem) ? pem.toString() : pem]; -} - -type HeaderValue = string | string[] | number | undefined; - -function normalizeHeaders( - headers?: Record | Iterable<[string, HeaderValue]> | string[] | null, -): Record { - if (!headers) { - return {}; - } - - const result: Record = {}; - const add = (key: string, value: HeaderValue): void => { - if (value === undefined || value === null) { - return; - } - const k = key.toLowerCase(); - const v = Array.isArray(value) ? value.join(", ") : String(value); - if (!v) { - return; - } - const existing = result[k]; - result[k] = existing ? `${existing}, ${v}` : v; - }; - - if (Array.isArray(headers)) { - for (let i = 0; i < headers.length; i += 2) { - const key = headers[i]; - if (key !== undefined) { - add(key, headers[i + 1]); - } - } - } else if (Symbol.iterator in headers) { - for (const [key, value] of headers) { - add(key, value); - } - } else { - for (const [key, value] of Object.entries(headers)) { - add(key, value); - } - } - - return result; -} - -function normalizeBody( - body?: string | Buffer | Uint8Array | FormData | Stream.Readable | null, -): ReadableStreamBYOBReader | null { - if (!body) { - return null; - } - - const response = new Response(body); - if (!response.body) { - return null; - } - - return response.body.getReader({ mode: "byob" }); -} +import { normalizeBody, normalizeHeaders, normalizePem } from "./normalize.ts"; class AgentImpl extends Dispatcher { #agent: AgentInstance; diff --git a/packages/node/export/normalize.ts b/packages/node/export/normalize.ts new file mode 100644 index 00000000..47506966 --- /dev/null +++ b/packages/node/export/normalize.ts @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +import type Stream from "node:stream"; +import { type FormData, Response } from "undici"; + +/** + * Normalizes PEM certificate input into an array of strings. + */ +export function normalizePem(pem?: string | Buffer | (string | Buffer)[]): string[] { + if (!pem) { + return []; + } + + if (Array.isArray(pem)) { + return pem.flatMap(normalizePem); + } + + return [Buffer.isBuffer(pem) ? pem.toString() : pem]; +} + +export type HeaderValue = string | string[] | number | undefined; + +/** + * Normalizes various header formats into a flat record of lowercase key-value pairs. + */ +export function normalizeHeaders( + headers?: Record | Iterable<[string, HeaderValue]> | string[] | null, +): Record { + if (!headers) { + return {}; + } + + const result: Record = {}; + const add = (key: string, value: HeaderValue): void => { + if (value === undefined || value === null) { + return; + } + const k = key.toLowerCase(); + const v = Array.isArray(value) ? value.join(", ") : String(value); + if (!v) { + return; + } + const existing = result[k]; + result[k] = existing ? `${existing}, ${v}` : v; + }; + + if (Array.isArray(headers)) { + for (let i = 0; i < headers.length; i += 2) { + const key = headers[i]; + if (key !== undefined) { + add(key, headers[i + 1]); + } + } + } else if (Symbol.iterator in headers) { + for (const [key, value] of headers) { + add(key, value); + } + } else { + for (const [key, value] of Object.entries(headers)) { + add(key, value); + } + } + + return result; +} + +/** + * Normalizes various body types into a ReadableStreamBYOBReader. + */ +export function normalizeBody( + body?: string | Buffer | Uint8Array | FormData | Stream.Readable | null, +): ReadableStreamBYOBReader | null { + if (!body) { + return null; + } + + const response = new Response(body); + if (!response.body) { + return null; + } + + return response.body.getReader({ mode: "byob" }); +} diff --git a/packages/node/package.json b/packages/node/package.json index c3ee7a95..da74e684 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -66,13 +66,15 @@ "ci-test:rust": "pnpm run test:nextest -r --locked && pnpm run test:rustdoc -r --locked", "ci-test": "pnpm run test:ts && pnpm run ci-test:rust", "pack-addon": "slsa pack dist/node_reqwest-v{version}-{platform}-{arch}.node.gz", - "postinstall": "slsa wget" + "postinstall": "slsa wget", + "bench": "vitest bench --config vitest.bench.config.mts" }, "dependencies": { "node-addon-slsa": "1.0.0" }, "devDependencies": { "@codecov/vite-plugin": "catalog:", + "@codspeed/vitest-plugin": "^5.4.0", "@playwright/test": "catalog:", "@types/node": "catalog:", "@vitest/coverage-istanbul": "catalog:", diff --git a/packages/node/tests/bench/normalize-body.bench.ts b/packages/node/tests/bench/normalize-body.bench.ts new file mode 100644 index 00000000..d6f8ec10 --- /dev/null +++ b/packages/node/tests/bench/normalize-body.bench.ts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +import { bench, describe } from "vitest"; +import { normalizeBody } from "../../export/normalize.ts"; + +describe("normalizeBody", () => { + bench("null input", () => { + normalizeBody(null); + }); + + bench("string body", () => { + normalizeBody('{"key":"value"}'); + }); + + bench("Buffer body", () => { + normalizeBody(Buffer.from("request body content")); + }); + + bench("Uint8Array body", () => { + normalizeBody(new Uint8Array([72, 101, 108, 108, 111])); + }); + + bench("large string body", () => { + normalizeBody(JSON.stringify({ data: "x".repeat(1024) })); + }); +}); diff --git a/packages/node/tests/bench/normalize-headers.bench.ts b/packages/node/tests/bench/normalize-headers.bench.ts new file mode 100644 index 00000000..ed6881a7 --- /dev/null +++ b/packages/node/tests/bench/normalize-headers.bench.ts @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +import { bench, describe } from "vitest"; +import { normalizeHeaders } from "../../export/normalize.ts"; + +describe("normalizeHeaders", () => { + bench("empty input", () => { + normalizeHeaders(); + }); + + bench("object with string values", () => { + normalizeHeaders({ + "Content-Type": "application/json", + Accept: "text/html", + Authorization: "Bearer token123", + }); + }); + + bench("object with array values", () => { + normalizeHeaders({ + "Accept-Encoding": ["gzip", "deflate", "br"], + "Cache-Control": ["no-cache", "no-store"], + "X-Custom": ["value1", "value2", "value3"], + }); + }); + + bench("object with many headers", () => { + normalizeHeaders({ + "Content-Type": "application/json", + Accept: "text/html", + Authorization: "Bearer token123", + "Accept-Encoding": "gzip", + "Cache-Control": "no-cache", + "X-Request-Id": "abc-123", + "X-Forwarded-For": "127.0.0.1", + "X-Forwarded-Proto": "https", + "User-Agent": "node-reqwest/1.0", + Cookie: "session=abc; theme=dark", + }); + }); + + bench("flat string array pairs", () => { + normalizeHeaders([ + "Content-Type", + "application/json", + "Accept", + "text/html", + "Authorization", + "Bearer token123", + ]); + }); + + bench("Map iterable", () => { + const headers = new Map([ + ["Content-Type", "application/json"], + ["Accept", "text/html"], + ["Authorization", "Bearer token123"], + ]); + normalizeHeaders(headers); + }); + + bench("object with mixed value types", () => { + normalizeHeaders({ + "Content-Type": "application/json", + "Content-Length": 42, + "X-Optional": undefined, + Accept: ["text/html", "application/json"], + }); + }); +}); diff --git a/packages/node/tests/bench/normalize-pem.bench.ts b/packages/node/tests/bench/normalize-pem.bench.ts new file mode 100644 index 00000000..73e823a8 --- /dev/null +++ b/packages/node/tests/bench/normalize-pem.bench.ts @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +import { bench, describe } from "vitest"; +import { normalizePem } from "../../export/normalize.ts"; + +const samplePem = `-----BEGIN CERTIFICATE----- +MIICpDCCAYwCCQDU+PQ4F6a9WjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls +b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD +VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7 +-----END CERTIFICATE-----`; + +const samplePemBuffer = Buffer.from(samplePem); + +describe("normalizePem", () => { + bench("undefined input", () => { + normalizePem(); + }); + + bench("single string", () => { + normalizePem(samplePem); + }); + + bench("single Buffer", () => { + normalizePem(samplePemBuffer); + }); + + bench("array of strings", () => { + normalizePem([samplePem, samplePem, samplePem]); + }); + + bench("array of Buffers", () => { + normalizePem([samplePemBuffer, samplePemBuffer, samplePemBuffer]); + }); + + bench("mixed array", () => { + normalizePem([samplePem, samplePemBuffer, samplePem, samplePemBuffer]); + }); +}); diff --git a/packages/node/vitest.bench.config.mts b/packages/node/vitest.bench.config.mts new file mode 100644 index 00000000..9ccbedf0 --- /dev/null +++ b/packages/node/vitest.bench.config.mts @@ -0,0 +1,11 @@ +import codspeed from "@codspeed/vitest-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [codspeed()], + test: { + benchmark: { + include: ["tests/bench/**/*.bench.ts"], + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 01c6fa0a..9ba18684 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,9 @@ importers: '@codecov/vite-plugin': specifier: 'catalog:' version: 2.0.1(vite@8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0)) + '@codspeed/vitest-plugin': + specifier: ^5.4.0 + version: 5.4.0(tinybench@2.9.0)(vite@8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0))(vitest@4.1.6) '@playwright/test': specifier: 'catalog:' version: 1.60.0 @@ -249,6 +252,16 @@ packages: peerDependencies: vite: 4.x || 5.x || 6.x + '@codspeed/core@5.4.0': + resolution: {integrity: sha512-SwGjXDixN/zX1awBR95LzS0KxIs931qwf7Hbk7BRWv1jAdlMYf9o9GlSnWER4zGBHz941BvzFQJ1O2RIofW3cg==} + + '@codspeed/vitest-plugin@5.4.0': + resolution: {integrity: sha512-Xa9HaZHUjYXn1T39bTipV5hmguk1vIuDZs3Gc5OYA8X4ohftYbKfyoFtBqVFfB/ii/p1ihuwt+tltraKMcRDsA==} + peerDependencies: + tinybench: '>=2.9.0' + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vitest: ^3.2 || ^4 + '@electron/get@5.0.0': resolution: {integrity: sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==} engines: {node: '>=22.12.0'} @@ -1176,6 +1189,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1213,6 +1230,12 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.16.1: + resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + b4a@1.8.1: resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} peerDependencies: @@ -1312,6 +1335,10 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + caniuse-lite@1.0.30001792: resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} @@ -1346,6 +1373,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -1407,6 +1438,10 @@ packages: deep-equal@1.0.1: resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -1450,6 +1485,10 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -1490,6 +1529,10 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} @@ -1497,6 +1540,14 @@ packages: es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} @@ -1568,13 +1619,30 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + flexsearch@0.8.212: resolution: {integrity: sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@4.0.3: resolution: {integrity: sha512-yeXZaNbCBGaT9giTpLPBdtedzjwhlJBUoL/R4BVQU5mn0TQXOHwVIl1Q2DMuBIdNno4ktA1abZ7dQFVxD6uHxw==} engines: {node: '>=16'} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -1607,6 +1675,14 @@ packages: resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -1619,6 +1695,10 @@ packages: resolution: {integrity: sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==} engines: {node: '>=20'} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1626,6 +1706,14 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.3: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} @@ -1648,6 +1736,10 @@ packages: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -1850,6 +1942,10 @@ packages: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} + 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@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -1894,6 +1990,10 @@ packages: resolution: {integrity: sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==} engines: {node: '>=20'} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -2039,6 +2139,10 @@ packages: engines: {node: '>=22.12.0'} hasBin: true + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + node-releases@2.0.44: resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} @@ -2079,6 +2183,14 @@ packages: oxlint-tsgolint: optional: true + 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-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -2098,6 +2210,10 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + 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-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -2142,6 +2258,10 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -2581,6 +2701,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -2732,6 +2856,26 @@ snapshots: unplugin: 1.16.1 vite: 8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0) + '@codspeed/core@5.4.0': + dependencies: + axios: 1.16.1 + find-up: 6.3.0 + form-data: 4.0.5 + node-gyp-build: 4.8.4 + transitivePeerDependencies: + - debug + - supports-color + + '@codspeed/vitest-plugin@5.4.0(tinybench@2.9.0)(vite@8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0))(vitest@4.1.6)': + dependencies: + '@codspeed/core': 5.4.0 + tinybench: 2.9.0 + vite: 8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0) + vitest: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@25.7.0)(@vitest/coverage-istanbul@4.1.6)(vite@8.0.13(@types/node@25.7.0)(esbuild@0.27.3)(yaml@2.9.0)) + transitivePeerDependencies: + - debug + - supports-color + '@electron/get@5.0.0': dependencies: debug: 4.4.3 @@ -3440,6 +3584,12 @@ snapshots: acorn@8.16.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ajv-draft-04@1.0.0(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -3473,6 +3623,18 @@ snapshots: assertion-error@2.0.1: {} + asynckit@0.4.0: {} + + axios@1.16.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + b4a@1.8.1: {} balanced-match@4.0.4: {} @@ -3548,6 +3710,11 @@ snapshots: buffer-crc32@0.2.13: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + caniuse-lite@1.0.30001792: {} chai@6.2.2: {} @@ -3592,6 +3759,10 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@14.0.3: {} commander@8.3.0: {} @@ -3637,6 +3808,8 @@ snapshots: deep-equal@1.0.1: {} + delayed-stream@1.0.0: {} + delegates@1.0.0: {} depd@1.1.2: {} @@ -3674,6 +3847,12 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + ee-first@1.1.1: {} eight-colors@1.3.3: {} @@ -3707,11 +3886,23 @@ snapshots: env-paths@3.0.0: {} - es-errors@1.3.0: - optional: true + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} es-module-lexer@2.1.0: {} + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + esbuild@0.27.3: optionalDependencies: '@esbuild/aix-ppc64': 0.27.3 @@ -3818,12 +4009,27 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + flexsearch@0.8.212: {} + follow-redirects@1.16.0: {} + foreground-child@4.0.3: dependencies: signal-exit: 4.1.0 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + fresh@0.5.2: {} fs-extra@11.3.5: @@ -3846,13 +4052,30 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: - optional: true + function-bind@1.1.2: {} gensync@1.0.0-beta.2: {} get-east-asian-width@1.6.0: {} + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-stream@5.2.0: dependencies: pump: 3.0.4 @@ -3870,14 +4093,21 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.4.0 + gopd@1.2.0: {} + graceful-fs@4.2.11: {} has-flag@4.0.0: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.3: dependencies: function-bind: 1.1.2 - optional: true html-escaper@2.0.2: {} @@ -3909,6 +4139,13 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + husky@9.1.7: {} iconv-lite@0.6.3: @@ -4084,6 +4321,10 @@ snapshots: pkg-types: 2.3.1 quansync: 0.2.11 + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + lodash@4.17.23: optional: true @@ -4155,6 +4396,8 @@ snapshots: transitivePeerDependencies: - supports-color + math-intrinsics@1.1.0: {} + mdurl@2.0.0: {} media-typer@1.1.0: {} @@ -4402,6 +4645,8 @@ snapshots: node-addon-slsa@1.0.0: {} + node-gyp-build@4.8.4: {} + node-releases@2.0.44: {} nodemailer@8.0.7: {} @@ -4466,6 +4711,14 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.64.0 '@oxlint/binding-win32-x64-msvc': 1.64.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -4493,6 +4746,8 @@ snapshots: path-browserify@1.0.1: {} + path-exists@5.0.0: {} + path-parse@1.0.7: optional: true @@ -4534,6 +4789,8 @@ snapshots: progress@2.0.3: {} + proxy-from-env@2.1.0: {} + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -4926,4 +5183,6 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + yocto-queue@1.2.2: {} + zod@3.25.76: {}