Skip to content

Refactored cli - #4

Open
BurnedChris wants to merge 22 commits into
mainfrom
refactored-cli
Open

BurnedChris wants to merge 22 commits into
mainfrom
refactored-cli

Conversation

@BurnedChris

@BurnedChris BurnedChris commented Oct 30, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • New interactive CLI with benchmark, results, scores, save and db commands plus a consolidated runner and benchmarking package for end-to-end benchmark execution and reporting.
  • Documentation

    • Added METHODOLOGY and multiple READMEs explaining usage, scoring, CLI workflows, and package guides.
  • Chores

    • Normalized benchmark metadata, updated package manifests and Node engine constraints, adjusted ignores, and introduced shared utilities.
  • Style

    • Added Ultracite formatting/linting rules.

✏️ Tip: You can customize this high-level summary in your review settings.

burnedchris added 4 commits October 29, 2025 19:10
…consentio/benchmark' package for core benchmarking logic, replace '@cookiebench/cli' with 'cookiebench' in various benchmark configurations, and enhance package.json scripts for improved functionality. Remove deprecated CLI files and streamline project structure.
…duce interactive multi-select mode for benchmark execution, add scores command to view existing results, and integrate Perfume.js for enhanced performance metrics. Update logger utility for better CLI output and refactor commands to utilize the new logging system. Update package dependencies and documentation accordingly.
…esults to the database, and implement interactive multi-select mode for benchmark execution and results viewing. Introduce admin access checks for restricted commands, improve logging, and update documentation to reflect new features and usage instructions.
…ects. Refactor TypeScript settings, enhance package.json scripts, and improve logging utilities. Introduce new constants for better code maintainability and streamline project structure. Update documentation to reflect recent changes and ensure consistency in coding standards.
@vercel

ghost commented Oct 30, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
benchmarks-with-cookie-yes Error Error Dec 5, 2025 0:14am

@coderabbitai

ghost commented Oct 30, 2025

Copy link
Copy Markdown

Walkthrough

New shared, benchmark, and runner packages plus a new cookiebench CLI; legacy CLI and many in-repo collectors/aggregators removed. Benchmarks normalized (bundleType → "iife"), TypeScript configs and manifests added/updated, docs and methodology introduced, and root tooling/ignore rules adjusted.

Changes

Cohort / File(s) Summary
New shared package
packages/shared/*, packages/shared/package.json, packages/shared/rslib.config.ts, packages/shared/tsconfig.json, packages/shared/README.md
Adds @consentio/shared: constants, config reader, conversion/format helpers, getPackageManager, and barrel exports.
New benchmark package
packages/benchmark/src/*, packages/benchmark/package.json, packages/benchmark/rslib.config.ts, packages/benchmark/tsconfig.json, packages/benchmark/README.md
Adds @consentio/benchmark: collectors (CookieBannerCollector, NetworkMonitor, ResourceTimingCollector, PerfumeCollector), bundle strategy, constants, types, and barrel export.
New runner package
packages/runner/src/*, packages/runner/package.json, packages/runner/rslib.config.ts, packages/runner/tsconfig.json, packages/runner/README.md
Adds @consentio/runner: BenchmarkRunner, PerformanceAggregator, server build/serve helpers, statistics utilities, types, and utility exports.
New cookiebench CLI
packages/cookiebench-cli/src/*, packages/cookiebench-cli/package.json, packages/cookiebench-cli/rslib.config.ts, packages/cookiebench-cli/tsconfig.json, packages/cookiebench-cli/README.md, packages/cookiebench-cli/base.json
New interactive CLI: commands (benchmark, results, scores, save, db), CLI logger, scoring, constants, auth util, and re-exports of shared utilities.
Removed legacy CLI
packages/cli/...
Deleted old CLI implementation (commands, collectors, aggregators, server helpers, types, utils, tsconfig).
Collectors & aggregation migration
packages/runner/src/*, packages/benchmark/src/*
Reimplemented orchestration and collectors in new packages and exposed public APIs/types for collectors and aggregator.
CLI command implementations
packages/cookiebench-cli/src/commands/*
Added command implementations for benchmark, results, scores, save, and db with interactive flows and persistence.
CLI utils & logger
packages/cookiebench-cli/src/utils/*
Added CLI logger factory, constants, scoring helpers, readConfig barrel, and re-exports from @consentio/shared.
Statistics & runner utils
packages/runner/src/statistics.ts, packages/runner/src/utils.ts
New statistical helpers (trimmed mean, CV, percentiles) and runner utilities (formatTime, getPackageManager, readConfig, ONE_SECOND).
Docs & methodology
METHODOLOGY.md, README.md, package READMEs
Added methodology doc, updated root README, and added READMEs for new packages and CLI.
Benchmarks normalization
benchmarks/*/config.json, benchmarks/*/package.json, benchmarks/*/next-env.d.ts, benchmarks/*/tsconfig.json
Renamed many benchmarks (removed "with-" prefix), normalized bundleType values ("iffe" → "iife"), added Node engine constraints, removed legacy benchmark scripts/dev deps, and added/updated next-env/tsconfig where needed.
Root tooling & config
.cursor/rules/ultracite.mdc, .gitignore, turbo.json, tsconfig.json, package.json
Added Ultracite lint rules, updated .gitignore, removed outdated turbo tasks, enabled strictNullChecks and formatting updates, and migrated root scripts to cookiebench workspace packages.
Schema update
packages/benchmark-schema/schema.json
Normalized formatting and renamed enum value "iffe" → "iife".
Various small edits
multiple benchmarks/**/app/layout.tsx, next-env.d.ts, package.json tweaks
Minor formatting, attribute ordering, type-augmentation imports, and metadata/value tweaks across many benchmark projects.

Sequence Diagram(s)

%%{init: {"theme":"base","themeVariables":{"actorBorder":"#1f6feb","actorBackground":"#f0f7ff","signal":"#2d9cdb"}}}%%
sequenceDiagram
    actor CLI as cookiebench-cli
    participant Runner as `@consentio/runner`
    participant Collectors as `@consentio/benchmark`
    participant Browser as Playwright
    participant Aggregator as PerformanceAggregator

    CLI->>Runner: benchmarkCommand(appPath?, iterations?)
    Runner->>Browser: launch browser, create context/page
    Runner->>Collectors: initialize collectors (CookieBanner, Network, Resource, Perfume)
    Runner->>Browser: navigate(url)
    Note right of Browser: injected init scripts, Perfume, observers, route handlers
    par detection & monitoring
        Collectors->>Browser: setupDetection / setupMonitoring (initScript, page.route)
        Browser-->>Collectors: window.__* metrics & intercepted responses
    and collection
        Runner->>Browser: wait for element / networkidle / traces
        Runner->>Collectors: collectMetrics()
        Collectors-->>Runner: CookieBannerData, NetworkMetrics, ResourceTimingData, PerfumeMetrics
    end
    Runner->>Aggregator: aggregateMetrics(collected data)
    Aggregator-->>Runner: BenchmarkDetails
    Runner->>Runner: repeat iterations, calculateAverages
    Runner-->>CLI: write results.json / return BenchmarkResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review areas:
    • Cross-package public types and re-exports for consistency and circular-import risks.
    • BenchmarkRunner lifecycle: browser launch, trace capture, retries, and cleanup.
    • PerformanceAggregator: TTI calculation, trimmed-mean/statistics, merging heterogeneous metric shapes.
    • Collector implementations: in-page injection, PerformanceObserver usage, Playwright route handling, and serialization of metrics.
    • CLI flows: prompt handling, admin gating (isAdmin), results persistence (saveCommand), and error paths.
    • Workspace manifests and turbo changes: script/dep migrations and CI graph impact.

Poem

🐰
I hopped through folders, bits and bites,
I stacked new packages into nights.
Collectors sniff the cookie tune,
Runners race beneath the moon.
A crunchy bench — carrot-code delights! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Refactored cli' is too vague and does not convey the substantial scope of changes. The PR involves major restructuring across multiple packages including removal of CLI modules, creation of new benchmark and runner packages, and significant architectural changes. Replace with a more descriptive title that captures the primary intent, such as 'Extract benchmark logic into separate packages and restructure CLI' or 'Modularize benchmark orchestration into @consentio/benchmark and @consentio/runner packages'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactored-cli

Comment @coderabbitai help to get the list of available commands and usage tips.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 41

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 671143c and 3f15183.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (135)
  • .cursor/rules/ultracite.mdc (1 hunks)
  • .gitignore (1 hunks)
  • benchmarks/baseline/app/layout.tsx (1 hunks)
  • benchmarks/baseline/config.json (1 hunks)
  • benchmarks/baseline/next-env.d.ts (1 hunks)
  • benchmarks/baseline/next.config.ts (1 hunks)
  • benchmarks/baseline/package.json (1 hunks)
  • benchmarks/baseline/tsconfig.json (1 hunks)
  • benchmarks/with-c15t-nextjs/app/layout.tsx (3 hunks)
  • benchmarks/with-c15t-nextjs/config.json (1 hunks)
  • benchmarks/with-c15t-nextjs/next-env.d.ts (1 hunks)
  • benchmarks/with-c15t-nextjs/next.config.ts (1 hunks)
  • benchmarks/with-c15t-nextjs/package.json (1 hunks)
  • benchmarks/with-c15t-nextjs/tsconfig.json (1 hunks)
  • benchmarks/with-c15t-react/app/layout.tsx (3 hunks)
  • benchmarks/with-c15t-react/config.json (1 hunks)
  • benchmarks/with-c15t-react/next.config.ts (1 hunks)
  • benchmarks/with-c15t-react/package.json (1 hunks)
  • benchmarks/with-c15t-react/tsconfig.json (1 hunks)
  • benchmarks/with-cookie-control/app/layout.tsx (1 hunks)
  • benchmarks/with-cookie-control/config.json (1 hunks)
  • benchmarks/with-cookie-control/next.config.ts (1 hunks)
  • benchmarks/with-cookie-control/package.json (1 hunks)
  • benchmarks/with-cookie-control/tsconfig.json (1 hunks)
  • benchmarks/with-cookie-yes/app/layout.tsx (1 hunks)
  • benchmarks/with-cookie-yes/config.json (1 hunks)
  • benchmarks/with-cookie-yes/next.config.ts (1 hunks)
  • benchmarks/with-cookie-yes/package.json (1 hunks)
  • benchmarks/with-cookie-yes/tsconfig.json (1 hunks)
  • benchmarks/with-didomi/config.json (1 hunks)
  • benchmarks/with-didomi/next.config.ts (1 hunks)
  • benchmarks/with-didomi/package.json (1 hunks)
  • benchmarks/with-didomi/tsconfig.json (1 hunks)
  • benchmarks/with-enzuzo/app/layout.tsx (2 hunks)
  • benchmarks/with-enzuzo/config.json (1 hunks)
  • benchmarks/with-enzuzo/next.config.ts (1 hunks)
  • benchmarks/with-enzuzo/package.json (1 hunks)
  • benchmarks/with-enzuzo/tsconfig.json (1 hunks)
  • benchmarks/with-iubenda/app/layout.tsx (1 hunks)
  • benchmarks/with-iubenda/config.json (1 hunks)
  • benchmarks/with-iubenda/next.config.ts (1 hunks)
  • benchmarks/with-iubenda/package.json (1 hunks)
  • benchmarks/with-iubenda/tsconfig.json (1 hunks)
  • benchmarks/with-ketch/app/layout.tsx (1 hunks)
  • benchmarks/with-ketch/config.json (1 hunks)
  • benchmarks/with-ketch/next.config.ts (1 hunks)
  • benchmarks/with-ketch/package.json (1 hunks)
  • benchmarks/with-ketch/tsconfig.json (1 hunks)
  • benchmarks/with-onetrust/app/layout.tsx (2 hunks)
  • benchmarks/with-onetrust/config.json (1 hunks)
  • benchmarks/with-onetrust/next.config.ts (1 hunks)
  • benchmarks/with-onetrust/package.json (1 hunks)
  • benchmarks/with-onetrust/tsconfig.json (1 hunks)
  • benchmarks/with-osano/app/layout.tsx (1 hunks)
  • benchmarks/with-osano/config.json (1 hunks)
  • benchmarks/with-osano/next.config.ts (1 hunks)
  • benchmarks/with-osano/package.json (1 hunks)
  • benchmarks/with-osano/tsconfig.json (1 hunks)
  • benchmarks/with-usercentrics/app/layout.tsx (1 hunks)
  • benchmarks/with-usercentrics/config.json (1 hunks)
  • benchmarks/with-usercentrics/next.config.ts (1 hunks)
  • benchmarks/with-usercentrics/package.json (1 hunks)
  • benchmarks/with-usercentrics/tsconfig.json (1 hunks)
  • biome.jsonc (2 hunks)
  • package.json (1 hunks)
  • packages/benchmark/README.md (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/benchmark/rslib.config.ts (1 hunks)
  • packages/benchmark/src/bundle-strategy.ts (1 hunks)
  • packages/benchmark/src/constants.ts (1 hunks)
  • packages/benchmark/src/cookie-banner-collector.ts (1 hunks)
  • packages/benchmark/src/index.ts (1 hunks)
  • packages/benchmark/src/network-monitor.ts (1 hunks)
  • packages/benchmark/src/perfume-collector.ts (1 hunks)
  • packages/benchmark/src/resource-timing-collector.ts (1 hunks)
  • packages/benchmark/src/types.ts (1 hunks)
  • packages/benchmark/tsconfig.json (1 hunks)
  • packages/cli/base.json (0 hunks)
  • packages/cli/package.json (0 hunks)
  • packages/cli/src/commands/benchmark/benchmark-runner.ts (0 hunks)
  • packages/cli/src/commands/benchmark/bundle-strategy.ts (0 hunks)
  • packages/cli/src/commands/benchmark/constants.ts (0 hunks)
  • packages/cli/src/commands/benchmark/cookie-banner-detector.ts (0 hunks)
  • packages/cli/src/commands/benchmark/index.ts (0 hunks)
  • packages/cli/src/commands/benchmark/metrics-calculator.ts (0 hunks)
  • packages/cli/src/commands/benchmark/network-monitor.ts (0 hunks)
  • packages/cli/src/commands/benchmark/resource-collector.ts (0 hunks)
  • packages/cli/src/commands/benchmark/types.ts (0 hunks)
  • packages/cli/src/commands/db.ts (0 hunks)
  • packages/cli/src/commands/results.ts (0 hunks)
  • packages/cli/src/index.ts (0 hunks)
  • packages/cli/src/lib/README.md (0 hunks)
  • packages/cli/src/lib/benchmark-runner.ts (0 hunks)
  • packages/cli/src/lib/collectors/cookie-banner-collector.ts (0 hunks)
  • packages/cli/src/lib/collectors/index.ts (0 hunks)
  • packages/cli/src/lib/collectors/network-monitor.ts (0 hunks)
  • packages/cli/src/lib/collectors/resource-timing-collector.ts (0 hunks)
  • packages/cli/src/lib/metrics/index.ts (0 hunks)
  • packages/cli/src/lib/metrics/performance-aggregator.ts (0 hunks)
  • packages/cli/src/lib/performance-enhanced.ts (0 hunks)
  • packages/cli/src/lib/performance.ts (0 hunks)
  • packages/cli/src/lib/server.ts (0 hunks)
  • packages/cli/src/types/index.ts (0 hunks)
  • packages/cli/src/utils/index.ts (0 hunks)
  • packages/cli/tsconfig.json (0 hunks)
  • packages/cookiebench-cli/README.md (1 hunks)
  • packages/cookiebench-cli/base.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/rslib.config.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/db.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/results.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/save.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/cookiebench-cli/src/components/intro.ts (1 hunks)
  • packages/cookiebench-cli/src/index.ts (1 hunks)
  • packages/cookiebench-cli/src/types/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/auth.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/constants.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/logger.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/scoring.ts (27 hunks)
  • packages/cookiebench-cli/tsconfig.json (1 hunks)
  • packages/runner/README.md (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/runner/rslib.config.ts (1 hunks)
  • packages/runner/src/benchmark-runner.ts (1 hunks)
  • packages/runner/src/index.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/server.ts (1 hunks)
  • packages/runner/src/types.ts (1 hunks)
  • packages/runner/src/utils.ts (1 hunks)
  • packages/runner/tsconfig.json (1 hunks)
  • tsconfig.json (2 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (28)
  • packages/cli/src/lib/metrics/index.ts
  • packages/cli/src/lib/server.ts
  • packages/cli/src/commands/benchmark/bundle-strategy.ts
  • packages/cli/src/index.ts
  • packages/cli/src/utils/index.ts
  • packages/cli/src/lib/performance-enhanced.ts
  • packages/cli/src/commands/benchmark/network-monitor.ts
  • packages/cli/src/lib/benchmark-runner.ts
  • packages/cli/src/commands/db.ts
  • packages/cli/src/lib/collectors/index.ts
  • packages/cli/src/lib/metrics/performance-aggregator.ts
  • packages/cli/src/commands/benchmark/index.ts
  • packages/cli/src/lib/collectors/network-monitor.ts
  • packages/cli/src/commands/benchmark/metrics-calculator.ts
  • packages/cli/src/lib/collectors/cookie-banner-collector.ts
  • packages/cli/src/lib/performance.ts
  • packages/cli/src/commands/benchmark/benchmark-runner.ts
  • packages/cli/src/commands/benchmark/resource-collector.ts
  • packages/cli/src/commands/benchmark/types.ts
  • packages/cli/src/commands/benchmark/constants.ts
  • packages/cli/tsconfig.json
  • packages/cli/src/lib/collectors/resource-timing-collector.ts
  • packages/cli/src/lib/README.md
  • packages/cli/base.json
  • packages/cli/src/types/index.ts
  • packages/cli/package.json
  • packages/cli/src/commands/benchmark/cookie-banner-detector.ts
  • packages/cli/src/commands/results.ts
🧰 Additional context used
🧬 Code graph analysis (25)
benchmarks/with-onetrust/app/layout.tsx (5)
benchmarks/baseline/app/layout.tsx (1)
  • metadata (10-12)
benchmarks/with-c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-c15t-react/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-ketch/app/layout.tsx (1)
  • metadata (4-6)
packages/cookiebench-cli/src/index.ts (9)
packages/cookiebench-cli/src/utils/logger.ts (3)
  • logger (152-152)
  • CliLogger (8-8)
  • createCliLogger (102-149)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/cookiebench-cli/src/utils/constants.ts (1)
  • HALF_SECOND (2-2)
packages/cookiebench-cli/src/components/intro.ts (1)
  • displayIntro (11-93)
packages/cookiebench-cli/src/commands/benchmark.ts (1)
  • benchmarkCommand (317-501)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (884-1114)
packages/cookiebench-cli/src/commands/scores.ts (1)
  • scoresCommand (93-183)
packages/cookiebench-cli/src/commands/save.ts (1)
  • saveCommand (228-391)
packages/cookiebench-cli/src/commands/db.ts (1)
  • dbCommand (46-125)
packages/benchmark/src/constants.ts (1)
packages/benchmark/src/index.ts (2)
  • BENCHMARK_CONSTANTS (5-5)
  • BUNDLE_TYPES (5-5)
benchmarks/with-iubenda/app/layout.tsx (5)
benchmarks/baseline/app/layout.tsx (1)
  • metadata (10-12)
benchmarks/with-c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-c15t-react/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-ketch/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-onetrust/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-ketch/app/layout.tsx (5)
benchmarks/baseline/app/layout.tsx (1)
  • metadata (10-12)
benchmarks/with-c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-c15t-react/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-onetrust/app/layout.tsx (1)
  • metadata (4-6)
packages/benchmark/src/perfume-collector.ts (1)
packages/benchmark/src/types.ts (2)
  • WindowWithPerfumeMetrics (261-277)
  • PerfumeMetrics (228-259)
benchmarks/baseline/app/layout.tsx (5)
benchmarks/with-c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-c15t-react/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-ketch/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-onetrust/app/layout.tsx (1)
  • metadata (4-6)
packages/benchmark/src/resource-timing-collector.ts (2)
packages/benchmark/src/types.ts (1)
  • ResourceTimingData (126-211)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/benchmark/src/bundle-strategy.ts (2)
packages/benchmark/src/types.ts (2)
  • Config (13-56)
  • BundleStrategy (119-123)
packages/benchmark/src/constants.ts (1)
  • BUNDLE_TYPES (15-20)
packages/cookiebench-cli/src/commands/scores.ts (6)
packages/cookiebench-cli/src/commands/results.ts (1)
  • RawBenchmarkDetail (39-179)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/utils/constants.ts (2)
  • HALF_SECOND (2-2)
  • PERCENTAGE_DIVISOR (6-6)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1402-1441)
  • calculateScores (1029-1398)
packages/cookiebench-cli/src/commands/db.ts (3)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/runner/src/utils.ts (1)
  • ONE_SECOND (5-5)
packages/cookiebench-cli/src/utils/index.ts (2)
packages/runner/src/utils.ts (4)
  • readConfig (6-16)
  • formatTime (18-23)
  • ONE_SECOND (5-5)
  • getPackageManager (25-57)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/commands/save.ts (6)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (181-215)
  • RawBenchmarkDetail (39-179)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/cookiebench-cli/src/utils/constants.ts (2)
  • HALF_SECOND (2-2)
  • PERCENTAGE_DIVISOR (6-6)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1029-1398)
benchmarks/with-c15t-react/app/layout.tsx (5)
benchmarks/baseline/app/layout.tsx (1)
  • metadata (10-12)
benchmarks/with-c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/with-iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-ketch/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-onetrust/app/layout.tsx (1)
  • metadata (4-6)
packages/runner/src/benchmark-runner.ts (2)
packages/runner/src/types.ts (3)
  • Config (6-6)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/runner/src/performance-aggregator.ts (1)
  • PerformanceAggregator (29-372)
packages/benchmark/src/network-monitor.ts (2)
packages/benchmark/src/types.ts (3)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • Config (13-56)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/cookiebench-cli/src/commands/results.ts (6)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/types/index.ts (2)
  • Config (5-5)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/constants.ts (18)
  • KILOBYTE (9-9)
  • SCORE_THRESHOLD_POOR (13-13)
  • SCORE_THRESHOLD_FAIR (14-14)
  • CLS_DECIMAL_PLACES (10-10)
  • PERCENTAGE_DIVISOR (6-6)
  • CLS_THRESHOLD_GOOD (17-17)
  • CLS_THRESHOLD_FAIR (18-18)
  • COL_WIDTH_NAME (21-21)
  • COL_WIDTH_CHART_PADDING (27-27)
  • MAX_FILENAME_LENGTH (30-30)
  • TRUNCATED_FILENAME_LENGTH (31-31)
  • MIN_DURATION_THRESHOLD (34-34)
  • COL_WIDTH_TYPE (22-22)
  • COL_WIDTH_SOURCE (23-23)
  • COL_WIDTH_SIZE (24-24)
  • COL_WIDTH_DURATION (25-25)
  • COL_WIDTH_TAGS (26-26)
  • DEFAULT_DOM_SIZE (4-4)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1029-1398)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/benchmark/src/cookie-banner-collector.ts (3)
packages/benchmark/src/types.ts (5)
  • Config (13-56)
  • CookieBannerMetrics (78-91)
  • WindowWithCookieMetrics (65-76)
  • LayoutShiftEntry (59-62)
  • CookieBannerData (93-101)
packages/benchmark/src/bundle-strategy.ts (1)
  • determineBundleStrategy (4-21)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/runner/src/types.ts (2)
packages/cookiebench-cli/src/types/index.ts (3)
  • ServerInfo (6-6)
  • BenchmarkDetails (3-3)
  • BenchmarkResult (4-4)
packages/runner/src/index.ts (3)
  • ServerInfo (21-21)
  • BenchmarkDetails (10-10)
  • BenchmarkResult (11-11)
packages/runner/src/performance-aggregator.ts (2)
packages/benchmark/src/types.ts (8)
  • CoreWebVitals (214-225)
  • CookieBannerData (93-101)
  • CookieBannerMetrics (78-91)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • ResourceTimingData (126-211)
  • Config (13-56)
  • PerfumeMetrics (228-259)
packages/runner/src/types.ts (10)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/cookiebench-cli/src/commands/benchmark.ts (7)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/constants.ts (6)
  • DEFAULT_THIRD_PARTY_DOMAINS (5-5)
  • PERCENTAGE_DIVISOR (6-6)
  • DEFAULT_DOM_SIZE (4-4)
  • HALF_SECOND (2-2)
  • DEFAULT_ITERATIONS (3-3)
  • SEPARATOR_WIDTH (7-7)
packages/runner/src/benchmark-runner.ts (2)
  • runSingleBenchmark (37-123)
  • BenchmarkRunner (15-187)
packages/runner/src/utils.ts (1)
  • readConfig (6-16)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1029-1398)
  • printScores (1402-1441)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (884-1114)
packages/runner/src/server.ts (2)
packages/runner/src/types.ts (1)
  • ServerInfo (18-21)
packages/runner/src/utils.ts (2)
  • getPackageManager (25-57)
  • ONE_SECOND (5-5)
packages/runner/src/utils.ts (2)
packages/cookiebench-cli/src/utils/index.ts (3)
  • readConfig (10-19)
  • formatTime (21-26)
  • getPackageManager (28-60)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/utils/scoring.ts (2)
packages/runner/src/utils.ts (1)
  • ONE_SECOND (5-5)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/components/intro.ts (1)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
🪛 LanguageTool
packages/cookiebench-cli/README.md

[uncategorized] ~373-~373: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ice overhead 4. Transparency (15%): Open source status, documentation, licensing 5. **U...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
packages/benchmark/README.md

99-99: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (74)
turbo.json (1)

1-28: Workspace-level task definitions sufficiently cover all new packages.

Verification confirms that @consentio/benchmark, @consentio/runner, and cookiebench-cli all define npm scripts (build, check-types, lint, fmt, dev) that align with the workspace-level turbo.json tasks. All build outputs target dist/ (covered by turbo.json), and static-only packages (benchmark-schema, typescript-config) have no scripts. The simplified configuration is appropriate and requires no custom package-level overrides.

benchmarks/with-cookie-yes/app/layout.tsx (1)

1-2: LGTM! Formatting standardization applied.

The quote style changes (single → double quotes) are purely cosmetic and align with the PR's broader formatting standardization effort across benchmark configurations.

Also applies to: 5-5

benchmarks/with-ketch/next.config.ts (1)

1-1: LGTM! Quote style normalized.

Import statement formatting updated to use double quotes, consistent with the formatting standardization across all benchmark configurations in this PR.

benchmarks/with-c15t-react/next.config.ts (1)

1-1: LGTM! Quote style normalized.

Import statement formatting updated to use double quotes, consistent with the formatting standardization across all benchmark configurations in this PR.

benchmarks/with-iubenda/next.config.ts (1)

1-1: LGTM! Quote style normalized.

Import statement formatting updated to use double quotes, consistent with the formatting standardization across all benchmark configurations in this PR.

benchmarks/with-enzuzo/next.config.ts (1)

1-1: LGTM! Quote style normalized.

Import statement formatting updated to use double quotes, consistent with the formatting standardization across all benchmark configurations in this PR.

benchmarks/with-enzuzo/config.json (1)

1-40: LGTM! JSON formatting normalized.

The changes are purely cosmetic indentation and whitespace adjustments with no semantic modifications to configuration values. This aligns with the PR's formatting standardization effort.

benchmarks/with-didomi/next.config.ts (1)

1-1: LGTM! Quote style normalized.

Import statement formatting updated to use double quotes, consistent with the formatting standardization across all benchmark configurations in this PR.

benchmarks/with-usercentrics/config.json (1)

1-40: LGTM! JSON formatting normalized.

The changes are purely cosmetic indentation and whitespace adjustments with no semantic modifications to configuration values. This aligns with the PR's formatting standardization effort.

benchmarks/with-c15t-react/config.json (1)

2-47: Schema reference requires package validation.

All JSON benchmark configs now reference ./node_modules/@cookiebench/benchmark-schema/schema.json. Verify that the @cookiebench/benchmark-schema package is properly declared as a dependency and available to all benchmark packages during development and build time.

benchmarks/with-didomi/config.json (1)

2-48: Formatting consistency maintained.

Indentation and whitespace standardized; configuration semantics unchanged.

benchmarks/baseline/config.json (1)

2-29: Formatting consistency maintained.

Indentation and whitespace standardized; configuration semantics unchanged.

benchmarks/with-osano/config.json (1)

2-39: Verify bundleType value consistency.

Line 16 specifies "bundleType": "iffe". Confirm this is the intended spelling—likely should be "iife" (Immediately Invoked Function Expression). Verify against the schema definition to ensure consistency with other bundle type values.

benchmarks/with-onetrust/next.config.ts (1)

1-1: Quote style normalization applied.

Import quote changed from single to double quotes, consistent with project-wide formatting updates.

benchmarks/with-ketch/config.json (1)

2-38: Verify bundleType value consistency.

Line 19 specifies "bundleType": "iffe", same as with-osano config. Confirm this spelling is intentional—expected value is typically "iife" (Immediately Invoked Function Expression). Verify against schema definition.

benchmarks/with-onetrust/app/layout.tsx (1)

1-2: Quote style normalization applied.

Import quotes and string literals updated from single to double quotes, consistent with project-wide formatting updates. No runtime behavior changes.

Also applies to: 23-23

packages/cookiebench-cli/rslib.config.ts (2)

1-24: Verify figlet exclusion rationale.

The exclude list includes "figlet" (line 8). Confirm this is intentional—whether figlet is an optional dependency, only imported conditionally in CLI commands, or should be externalized. If figlet is a significant dependency, consider documenting this decision in comments or a CHANGELOG.


3-24: RSLib configuration properly aligned with CLI requirements.

Configuration mirrors packages/benchmark pattern: ESM format for modern Node.js, declarations enabled, bundle mode active, and clean dist output. Target set to "node" correctly identifies this as a CLI package.

benchmarks/baseline/next-env.d.ts (1)

3-3: Verify manual modification of auto-generated file.

Line 5 states "This file should not be edited," yet this import is being added manually. Ensure this change aligns with Next.js best practices for route type augmentation, or confirm if this should be auto-generated by Next.js tooling instead.

benchmarks/baseline/next.config.ts (1)

1-1: LGTM!

Formatting change standardizes quote style across benchmark configurations.

benchmarks/with-cookie-yes/config.json (1)

1-44: LGTM!

Formatting standardization improves consistency across benchmark configurations without changing any semantic content.

benchmarks/with-c15t-react/tsconfig.json (1)

2-10: LGTM!

Formatting standardization without functional changes.

packages/benchmark/tsconfig.json (1)

1-15: TypeScript version requirement verified—no issues found.

The packages/benchmark/package.json specifies TypeScript ^5.9.3, which satisfies the minimum TypeScript 5.0+ requirement for moduleResolution: "bundler". The configuration is correct.

benchmarks/with-enzuzo/app/layout.tsx (1)

1-19: LGTM! Formatting standardization.

The changes consistently update string literals from single to double quotes across imports and metadata, aligning with the project-wide formatting standards applied throughout this PR.

benchmarks/with-ketch/app/layout.tsx (1)

1-5: LGTM! Formatting standardization.

Quote style updates align with the broader formatting standardization across benchmark files in this PR.

benchmarks/with-onetrust/config.json (1)

1-46: LGTM! Formatting normalization.

The changes are purely formatting and whitespace adjustments with no semantic modifications to the configuration.

benchmarks/baseline/app/layout.tsx (1)

1-11: LGTM! Formatting standardization.

Quote style updates are consistent with the formatting standardization across all benchmark layout files in this PR.

tsconfig.json (2)

4-8: LGTM! Improved readability.

Reformatting the lib, include, and exclude arrays to multi-line format improves readability with no semantic changes.

Also applies to: 23-29


21-21: Manual TypeScript compilation verification required.

The sandbox environment lacks TypeScript installation to execute the tsc --noEmit verification. The codebase contains 30 TypeScript source files and already has strictNullChecks: true enabled in tsconfig.json with TypeScript 5.9.3 as a dependency.

To verify no type errors are introduced by this compiler option change, run npm install followed by npx tsc --noEmit in your local development environment or CI pipeline. This is essential before merging, as strictNullChecks is a significant compiler setting that enforces stricter null/undefined type checking.

benchmarks/with-c15t-react/app/layout.tsx (1)

1-31: LGTM! Formatting standardization.

String literal updates from single to double quotes align with the project-wide formatting standards.

packages/runner/rslib.config.ts (1)

1-14: LGTM! Standard build configuration.

The rslib configuration is appropriate for a Node.js library package:

  • ESM format for modern module compatibility
  • ES2021 syntax for current JavaScript features
  • Type declaration generation enabled
  • Node target correctly specified

This aligns with similar configurations across the monorepo (e.g., packages/benchmark/rslib.config.ts).

packages/benchmark/rslib.config.ts (1)

1-14: LGTM!

The RSlib configuration is appropriate for a library package with ESM output targeting Node.js. The TypeScript declaration generation (dts: true) ensures proper type support for consumers of this package.

.cursor/rules/ultracite.mdc (1)

1-5: Verify the alwaysApply: false setting.

The ultracite rules are configured with alwaysApply: false, meaning they won't be automatically enforced. Is this intentional, or should these rules be always applied for consistent code quality across the project?

If these rules should be enforced by default, apply this diff:

 ---
 description: Ultracite Rules - AI-Ready Formatter and Linter
 globs: "**/*.{ts,tsx,js,jsx,json,jsonc,html,vue,svelte,astro,css,yaml,yml,graphql,gql,md,mdx,grit}"
-alwaysApply: false
+alwaysApply: true
 ---
benchmarks/with-iubenda/app/layout.tsx (1)

1-6: LGTM!

The formatting changes standardize quote styles across the codebase. No functional changes detected.

packages/cookiebench-cli/base.json (1)

9-9: Verify the need for DOM libraries in a CLI package.

The CLI configuration includes "DOM" and "DOM.Iterable" in the lib array, which are typically used for browser environments. CLI tools usually only need Node.js types. Is there browser-related code in this package, or can these be removed?

If DOM types aren't needed, apply this diff:

-    "lib": ["es2022", "DOM", "DOM.Iterable"],
+    "lib": ["es2022"],

Otherwise, please clarify why DOM types are required for the CLI package.

packages/runner/tsconfig.json (1)

1-15: LGTM!

The TypeScript configuration is well-structured for a library package with modern module resolution and declaration generation enabled.

packages/cookiebench-cli/README.md (1)

1-395: Excellent documentation!

The README is comprehensive, well-organized, and provides clear examples for all commands. The distinction between admin-only and public commands is particularly helpful, and the interactive mode documentation with examples greatly improves usability.

biome.jsonc (1)

1-21: LGTM: Clean configuration simplification

The migration to ultracite presets and local schema reference is well-structured. The simplified rules section indicates that most configuration is now handled by the extended presets, which improves maintainability.

package.json (2)

5-12: LGTM: Clean script reorganization

The new scripts align well with the CLI refactoring. The migration from @cookiebench/cli to cookiebench and the addition of check-types and db commands improve the developer experience.


14-28: All specified dependency versions are current.

The verification confirms that each dependency is at its latest published version:

  • @playwright/test, typescript, turbo, and @biomejs/biome all match their latest npm versions
  • No outdated or stale versions present

The code requires no changes.

benchmarks/with-cookie-yes/package.json (1)

1-25: LGTM: Clean Next.js 16 and React 19 upgrade

The dependency updates and script simplification align well with the PR's refactoring goals. The explicit port specification (--port 3001) is useful for running multiple benchmarks in parallel during development.

packages/benchmark/package.json (1)

1-33: LGTM: Well-structured package manifest

The package configuration follows modern Node.js/TypeScript best practices with clear ESM exports, type definitions, and appropriate tooling. The dependency choices (Playwright for browser automation, perfume.js for metrics) align well with the benchmark package's purpose.

packages/runner/package.json (1)

1-33: LGTM: Consistent package structure

The runner package follows the same structure as the benchmark package, maintaining consistency across the monorepo. The workspace dependency on @consentio/benchmark is correctly specified.

packages/cookiebench-cli/src/components/intro.ts (1)

11-40: LGTM: Good error handling for figlet generation

The async figlet generation with proper error handling and fallback ensures the CLI remains functional even if the ASCII art generation fails.

benchmarks/with-ketch/package.json (1)

1-24: LGTM: Consistent benchmark package upgrade

The updates mirror the pattern used in other benchmark packages, maintaining consistency across the monorepo. Note that this package uses the default Next.js dev port (3000), while with-cookie-yes explicitly uses port 3001.

packages/benchmark/src/bundle-strategy.ts (1)

4-21: Edge-case handling is correct; no issues found

The function properly handles the mentioned edge cases:

  • undefined bundleType: Both isIIFE and isBundled correctly evaluate to false (all equality and Array.isArray checks fail)
  • empty array []: Both flags correctly evaluate to false (includes() returns false for empty arrays)

The caller in cookie-banner-collector.ts gracefully handles this by defaulting bundleStrategy to "Unknown" when both flags are false. The defensive programming pattern (optional chaining despite techStack being required in the type) is intentional and safe.

packages/benchmark/src/constants.ts (1)

1-13: LGTM! Well-structured benchmark constants.

The timing and polling constants are appropriately defined with clear comments and reasonable values. Using as const ensures type safety and immutability.

benchmarks/with-onetrust/package.json (1)

2-24: LGTM! Standard Next.js 16 upgrade.

The package.json updates align with the PR's migration to Next.js 16, React 19.2, and simplified tooling. The port assignment (3006) is noted for conflict checking.

benchmarks/with-osano/app/layout.tsx (1)

1-21: LGTM! Formatting standardization.

The changes only update quote style from single to double quotes, aligning with the project's formatting standards. No functional changes.

packages/cookiebench-cli/src/utils/logger.ts (1)

102-149: LGTM! Well-structured CLI logger implementation.

The logger properly extends the base logger with CLI-specific methods (message, note, step, outro, clear) and integrates well with clack prompts. The design allows for flexible log level handling while maintaining type safety.

packages/benchmark/src/cookie-banner-collector.ts (1)

25-58: LGTM! Clean initialization logic.

The bundle strategy detection and metrics initialization are well-structured. The debug logging provides good visibility into the detected configuration.

packages/benchmark/src/types.ts (1)

1-277: LGTM! Comprehensive type definitions.

The type definitions are well-structured, thoroughly documented, and provide strong type safety for the benchmark module. The separation of concerns (config types, performance types, network types, etc.) makes the types easy to understand and maintain.

benchmarks/with-c15t-nextjs/next.config.ts (1)

1-7: LGTM! Formatting standardization.

Quote style updated for consistency across the codebase.

benchmarks/with-onetrust/tsconfig.json (1)

1-11: LGTM! Formatting standardization.

Indentation and whitespace formatting updated for consistency.

benchmarks/baseline/tsconfig.json (1)

1-11: LGTM! Formatting standardization.

Indentation and whitespace formatting updated for consistency.

benchmarks/with-iubenda/tsconfig.json (1)

1-11: LGTM! Formatting standardization.

Indentation and whitespace formatting updated for consistency.

benchmarks/with-usercentrics/tsconfig.json (1)

1-11: LGTM! Formatting standardization.

Indentation and whitespace formatting updated for consistency.

benchmarks/baseline/package.json (1)

12-25: Verification complete: all package versions are stable and secure.

The installed versions match the latest available releases and are not affected by the reported security advisories. The vulnerabilities listed in the advisory database target older major versions (Next.js < 16, React < 19, React DOM < 19), which are not in use. No action required.

benchmarks/with-c15t-nextjs/config.json (1)

2-47: Formatting update is clean.

The JSON reformatting maintains structural and semantic integrity. All keys and values remain unchanged.

benchmarks/with-cookie-yes/tsconfig.json (1)

2-10: TypeScript configuration formatting is consistent.

Indentation normalized without altering extends, compilerOptions, or paths configuration.

benchmarks/with-osano/tsconfig.json (1)

2-10: TypeScript configuration formatting applied consistently.

Standard Next.js configuration structure maintained across benchmarks.

benchmarks/with-usercentrics/next.config.ts (1)

1-1: Import quote normalization applied.

Double-quote import style now aligns with standardized formatting across benchmark configs.

benchmarks/with-enzuzo/tsconfig.json (1)

2-10: TypeScript configuration formatting standardized.

Indentation adjustments applied without altering configuration semantics.

benchmarks/with-c15t-nextjs/tsconfig.json (1)

2-10: TypeScript configuration formatting consistent with broader repository updates.

Formatting standardization applied without altering configuration values.

benchmarks/with-cookie-control/next.config.ts (1)

1-1: Import quote normalization applied.

Double-quote import style standardized across benchmark Next.js configurations.

benchmarks/with-cookie-control/config.json (1)

2-39: Configuration JSON formatting standardized.

Indentation and spacing updated with all keys and values preserved intact.

benchmarks/with-usercentrics/package.json (1)

1-25: LGTM! Clean dependency upgrades and script simplification.

The upgrade to Next.js 16, React 19.2, and TypeScript 5.9 with simplified scripts and proper workspace dependencies looks good.

packages/runner/README.md (1)

1-150: Excellent documentation for the new runner package.

The README provides comprehensive coverage of the runner's features, clear usage examples, and a helpful configuration schema. This will make the package easy to adopt and use.

packages/cookiebench-cli/src/types/index.ts (1)

1-7: LGTM! Clean type re-exports.

Re-exporting types from the runner package provides a convenient interface for CLI consumers.

packages/cookiebench-cli/src/index.ts (3)

1-24: LGTM! Clean initialization and admin gating.

The logger initialization, admin check, and cancellation handler are well-implemented. The use of environment-based log level configuration is a good practice.


26-75: Well-structured command routing with proper admin gating.

The direct command execution path correctly gates admin commands and provides helpful error messages with available commands.


76-129: Interactive mode provides good UX.

The interactive prompt mode with conditional admin options provides a clear user experience.

packages/runner/src/index.ts (1)

3-24: Public surface looks coherent.
Exports align with the reorganized runner modules, keeping consumption straightforward.

packages/benchmark/src/index.ts (1)

3-26: Benchmark barrel looks consistent.
The export surface neatly mirrors the new module layout and keeps consumers on a single import path.

Comment thread .gitignore Outdated
Comment thread benchmarks/with-cookie-control/package.json
Comment thread benchmarks/with-didomi/package.json Outdated
Comment thread benchmarks/with-enzuzo/package.json
Comment thread benchmarks/with-iubenda/config.json Outdated
Comment thread packages/cookiebench-cli/src/utils/logger.ts
Comment thread packages/runner/src/server.ts
Comment thread packages/runner/src/utils.ts
Comment thread packages/shared/src/utils/package-manager.ts
Comment thread packages/shared/src/utils/package-manager.ts

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (30)
benchmarks/baseline/next-env.d.ts (1)

3-3: Same issue: Manual modification of auto-generated file.

This file has the same issue as benchmarks/with-c15t-nextjs/next-env.d.ts. The manual import of a build-time artifact contradicts the file's explicit guidance not to edit it and may cause TypeScript errors before the first build.

#!/bin/bash
# Description: Check Next.js config and verify the pattern across all benchmark directories

echo "=== Checking Next.js config for typed routes configuration ==="
fd -e js -e ts -e mjs 'next.config' benchmarks/baseline/ --exec cat {}

echo -e "\n=== Searching for all next-env.d.ts files with this import pattern ==="
rg -n "import.*\.next/types/routes" benchmarks/
.gitignore (1)

46-139: Duplicate: Remove unnecessary .gitignore entries.

This issue was already flagged in a previous review. The 94 entries for c15t/cli/ and c15t/logger/ reference non-existent directories and should be removed.

benchmarks/with-iubenda/package.json (1)

6-6: Port conflict still unresolved.

As noted in the previous review, port 3001 conflicts with multiple other benchmark packages (with-c15t-nextjs, with-cookie-control, with-cookie-yes, with-enzuzo, with-usercentrics), preventing simultaneous execution during benchmark comparisons.

benchmarks/with-osano/package.json (1)

6-6: Port conflict with benchmarks/with-onetrust remains unresolved.

As flagged in the previous review, both with-osano and with-onetrust use port 3006, preventing simultaneous benchmark execution.

packages/cookiebench-cli/src/types/index.ts (1)

12-34: Align grade/status casing.

The top-level grade uses Title Case strings, but status values stay lowercase. This mismatch forces consumers to normalize casing in order to compare them. Please pick one convention (e.g., Title Case everywhere) and update both unions so they stay consistent.

packages/cookiebench-cli/package.json (1)

21-30: Avoid shipping on an alpha of @clack/prompts.

^1.0.0-alpha.0 is still pre-release and has churn risks. Please stick to the latest stable (currently 0.11.0) until v1 is officially released, or document why alpha stability is acceptable for production use.

packages/benchmark/src/constants.ts (1)

15-19: Fix the IIFE typo.

BUNDLE_TYPES.IIFE should be "iife"—the current "iffe" value will cause bundle strategy checks to fail. Correct the string so downstream comparisons work.

 export const BUNDLE_TYPES = {
-	IIFE: "iffe",
+	IIFE: "iife",
 	ESM: "esm",
 	CJS: "cjs",
 	BUNDLED: "bundled",
 } as const;
benchmarks/with-enzuzo/package.json (1)

6-6: Port conflict remains unresolved.

As previously noted, both with-enzuzo and with-iubenda use port 3001, preventing simultaneous execution during benchmark comparisons.

benchmarks/with-cookie-control/package.json (1)

12-14: Node.js version requirement remains unaddressed.

As previously noted, Next.js 16.0.1 and React 19.2.0 require Node >= 20.9.0. Without an explicit engines field, environments on older Node versions will fail to build or run.

packages/cookiebench-cli/README.md (1)

99-113: Terminal compatibility note for checkbox glyphs.

As previously noted, the checkbox glyphs (◼) used in the interactive example may not render consistently across all terminals.

packages/cookiebench-cli/src/utils/auth.ts (1)

5-10: Case-sensitive admin check will reject uppercase values.

The current implementation only accepts lowercase strings ("true", "1", "yes"), but environment variables are commonly set as uppercase literals (e.g., CONSENT_ADMIN=TRUE). This will prevent legitimate admin users from accessing admin commands.

packages/runner/src/utils.ts (3)

6-16: Inconsistent error handling across packages.

The readConfig function logs errors to console (line 13), while the identical function in packages/cookiebench-cli/src/utils/index.ts silently returns null with the expectation that the caller will log. This inconsistency was previously identified and should be resolved by aligning both implementations.


25-57: Code duplication and complex nesting require refactoring.

This function duplicates logic from packages/cookiebench-cli/src/utils/index.ts (lines 28-60) and uses deeply nested try-catch blocks (3 levels). The duplication and nesting issues were previously identified and should be addressed by consolidating into a single shared implementation with flattened structure.


55-56: Remove unreachable return statement.

Line 56 is unreachable because line 51 always returns in the final catch block. This dead code was previously identified and should be removed.

packages/cookiebench-cli/src/index.ts (1)

114-128: Consider adding default case for defensive programming.

The biome-ignore directive on line 114 bypasses the linter's requirement for a default case. While the select() prompt (lines 105-108) constrains possible values, adding a default case was previously suggested as defensive programming practice and would eliminate the need for the ignore directive.

packages/cookiebench-cli/src/utils/index.ts (1)

10-60: Deduplicate CLI utilities with the runner package

readConfig, formatTime, and getPackageManager are copied from @consentio/runner/src/utils.ts, but the copies have already drifted (no configPath support, different logging/error semantics). This duplication guarantees the two surfaces will diverge further and forces us to patch bugs twice. Please re-export the runner implementations instead of maintaining a fork.

-import { readFileSync } from "node:fs";
-import { join } from "node:path";
-import type { Config } from "../types";
-import { ONE_SECOND } from "./constants";
+import type { Config } from "../types";
+import {
+	formatTime as runnerFormatTime,
+	getPackageManager as runnerGetPackageManager,
+	readConfig as runnerReadConfig,
+} from "@consentio/runner";
+import { ONE_SECOND } from "./constants";
 
 export * from "./constants";
 
-export function readConfig(): Config | null {
-	try {
-		const configPath = join(process.cwd(), "config.json");
-		const configContent = readFileSync(configPath, "utf-8");
-		return JSON.parse(configContent) as Config;
-	} catch {
-		return null;
-	}
-}
-
-export function formatTime(ms: number): string {
-	if (ms < ONE_SECOND) {
-		return `${ms.toFixed(0)}ms`;
-	}
-	return `${(ms / ONE_SECOND).toFixed(2)}s`;
-}
-
-export async function getPackageManager(): Promise<{
-	command: string;
-	args: string[];
-}> {
-	// ...
-}
+export const readConfig = (configPath?: string): Config | null =>
+	(runnerReadConfig(configPath) as Config | null);
+export const formatTime = runnerFormatTime;
+export const getPackageManager = runnerGetPackageManager;

This keeps the CLI aligned with the runner’s behavior and avoids another maintenance hot spot.

packages/runner/src/server.ts (1)

55-70: Ensure we clean up the Next.js process when startup fails.

If all health checks fail we throw, but the spawned Next.js process keeps running and keeps the port bound. We need to terminate it (and wait briefly for exit) before propagating the error to avoid leaking processes.

-	throw new Error("Server failed to start");
+	const startupError = new Error("Server failed to start");
+	if (!serverProcess.killed) {
+		await new Promise<void>((resolve) => {
+			const timeout = setTimeout(resolve, ONE_SECOND * 2);
+			serverProcess.once("exit", () => {
+				clearTimeout(timeout);
+				resolve();
+			});
+			serverProcess.kill("SIGTERM");
+		});
+	}
+	throw startupError;
packages/benchmark/src/resource-timing-collector.ts (2)

88-111: Fix third-party detection to avoid substring false positives.

Using entry.name.includes(window.location.hostname) misclassifies hosts like https://notexample.com as first-party because the hostname happens to contain ours. Parse the URL and compare the hostname instead (fall back to origin/relative checks on failures) before using it in all bundled/third-party filters and resource flags.

+			const isFirstParty = (entry: PerformanceResourceTiming) => {
+				try {
+					const entryUrl = new URL(entry.name, window.location.origin);
+					return entryUrl.hostname === window.location.hostname;
+				} catch {
+					return (
+						entry.name.startsWith(window.location.origin) ||
+						entry.name.startsWith("/")
+					);
+				}
+			};
...
-					bundled: calculateSize(
-						scriptEntries.filter((e) =>
-							e.name.includes(window.location.hostname)
-						)
-					),
-					thirdParty: calculateSize(
-						scriptEntries.filter(
-							(e) => !e.name.includes(window.location.hostname)
-						)
-					),
+					bundled: calculateSize(
+						scriptEntries.filter((entry) => isFirstParty(entry))
+					),
+					thirdParty: calculateSize(
+						scriptEntries.filter((entry) => !isFirstParty(entry))
+					),
...
-						thirdParty: calculateSize(
-							scriptEntries.filter(
-								(e) => !e.name.includes(window.location.hostname)
-							)
-						),
+						thirdParty: calculateSize(
+							scriptEntries.filter((entry) => !isFirstParty(entry))
+						),
...
-						isThirdParty: !entry.name.includes(window.location.hostname),
+						isThirdParty: !isFirstParty(entry),

Also applies to: 119-162


59-66: Report the actual document language instead of hardcoding "en".

Every non-English benchmark is currently labeled English, skewing downstream analysis. Read the <html lang> attribute, fall back to navigator.language / navigator.languages, and only then default to "en".

-			return {
+			const docLang =
+				(document.documentElement.getAttribute("lang") || "").trim();
+			const language =
+				docLang ||
+				navigator.language ||
+				(Array.isArray(navigator.languages) && navigator.languages.length > 0
+					? navigator.languages[0]
+					: "en");
+
+			return {
...
-				language: "en",
+				language,

Also applies to: 164-166

packages/cookiebench-cli/src/commands/db.ts (1)

30-44: Make drizzle-kit invocation path-safe.

This issue was flagged in a previous review. Building the shell command with cd ${DB_PACKAGE_PATH} breaks when the repository path contains spaces (e.g., C:\Users\Jane Doe\...). Use the cwd option instead to make the invocation portable across all platforms.

Apply this diff:

 function runDrizzleCommand(logger: CliLogger, command: string): void {
 	try {
 		logger.step(`Running: ${color.cyan(`drizzle-kit ${command}`)}`);
-		execSync(`cd ${DB_PACKAGE_PATH} && pnpm drizzle-kit ${command}`, {
+		execSync(`pnpm drizzle-kit ${command}`, {
 			stdio: "inherit",
 			encoding: "utf-8",
+			cwd: DB_PACKAGE_PATH,
 		});
 	} catch (error) {
packages/benchmark/src/perfume-collector.ts (4)

15-70: Security and reliability concerns with CDN script injection.

This issue was flagged in a previous review. Several concerns with loading Perfume.js from a CDN without Subresource Integrity (SRI) protection, hardcoded version, silent failure on load errors, and external dependency on unpkg.com availability. Consider bundling Perfume.js with the benchmark package or adding SRI hash validation.


78-78: Fixed timeout may not work for all network conditions.

This issue was flagged in a previous review. The hardcoded PERFUME_METRICS_WAIT timeout might be insufficient for slow networks or heavily loaded pages. Consider making the timeout configurable or implementing a more sophisticated waiting strategy.


89-104: Using deprecated Performance Timing API.

This issue was flagged in a previous review. The performance.timing API is deprecated and will be removed from browsers. Replace with the Performance Timeline API using performance.getEntriesByType('navigation')[0] cast to PerformanceNavigationTiming.


107-119: Handle experimental API more defensively.

This issue was flagged in a previous review. The navigator.connection API is experimental and not supported in all browsers (notably Safari). Add more defensive checks and default values to prevent failures in unsupported environments.

packages/cookiebench-cli/src/commands/save.ts (2)

467-472: Count third-party requests across all asset types.

This issue was flagged in a previous review. The thirdPartyRequests calculation only inspects scripts, missing any third-party CSS, images, fonts, or other resources. This underreports network impact and leads to incorrect scores.

Apply this diff:

 			thirdPartyRequests:
 				appResults.reduce(
 					(a, b) =>
-						a + b.resources.scripts.filter((s) => s.isThirdParty).length,
+						a +
+						b.resources.scripts.filter((r) => r.isThirdParty).length +
+						b.resources.styles.filter((r) => r.isThirdParty).length +
+						b.resources.images.filter((r) => r.isThirdParty).length +
+						b.resources.fonts.filter((r) => r.isThirdParty).length +
+						b.resources.other.filter((r) => r.isThirdParty).length,
 					0
 				) / appResults.length,

544-550: Populate average payload metrics instead of zeroing them.

This issue was flagged in a previous review. scriptLoadTime and scriptSize are set to zero, and resourceCount only counts scripts. This ships inaccurate data to the API. Calculate proper averages from the raw measurements in appResults.

packages/benchmark/src/network-monitor.ts (1)

33-56: Fix third-party detection logic.

This issue was flagged in a previous review. Line 34 sets isThirdParty = !url.includes(new URL(url).hostname), but a URL always contains its own hostname, so this predicate is always false. This causes all requests to be classified as first-party, breaking network impact metrics and downstream scoring.

packages/benchmark/src/cookie-banner-collector.ts (2)

144-146: Consider logging selector errors for debugging.

This issue was flagged in a previous review. The empty catch block silently ignores selector errors. Consider logging these errors at debug level to aid troubleshooting invalid selectors in configurations.


214-217: Add detection check for hydration time consistency.

This issue was flagged in a previous review. The bannerHydrationTime is calculated whenever bannerInteractive > 0, but doesn't verify that the banner was actually detected. For consistency with other metrics, check metrics.detected as well.

packages/cookiebench-cli/src/commands/results.ts (1)

1044-1049: Fix third-party request counting.

This issue was flagged in a previous review. thirdPartyRequests only counts script resources, ignoring any third-party styles, images, fonts, or other assets. This underreports network impact and leads to incorrect scores.

Apply this diff:

 			thirdPartyRequests:
 				appResults.reduce(
 					(a, b) =>
-						a + b.resources.scripts.filter((s) => s.isThirdParty).length,
+						a +
+						b.resources.scripts.filter((r) => r.isThirdParty).length +
+						b.resources.styles.filter((r) => r.isThirdParty).length +
+						b.resources.images.filter((r) => r.isThirdParty).length +
+						b.resources.fonts.filter((r) => r.isThirdParty).length +
+						b.resources.other.filter((r) => r.isThirdParty).length,
 					0
 				) / appResults.length,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ab0a042 and 507a1f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (94)
  • .cursor/rules/ultracite.mdc (1 hunks)
  • .gitignore (1 hunks)
  • benchmarks/baseline/next-env.d.ts (1 hunks)
  • benchmarks/baseline/package.json (2 hunks)
  • benchmarks/with-c15t-nextjs/next-env.d.ts (1 hunks)
  • benchmarks/with-c15t-nextjs/package.json (1 hunks)
  • benchmarks/with-c15t-react/package.json (1 hunks)
  • benchmarks/with-cookie-control/app/layout.tsx (1 hunks)
  • benchmarks/with-cookie-control/config.json (1 hunks)
  • benchmarks/with-cookie-control/next.config.ts (1 hunks)
  • benchmarks/with-cookie-control/package.json (1 hunks)
  • benchmarks/with-cookie-control/tsconfig.json (1 hunks)
  • benchmarks/with-cookie-yes/package.json (1 hunks)
  • benchmarks/with-didomi/package.json (0 hunks)
  • benchmarks/with-enzuzo/app/layout.tsx (2 hunks)
  • benchmarks/with-enzuzo/config.json (1 hunks)
  • benchmarks/with-enzuzo/next.config.ts (1 hunks)
  • benchmarks/with-enzuzo/package.json (1 hunks)
  • benchmarks/with-enzuzo/tsconfig.json (1 hunks)
  • benchmarks/with-iubenda/package.json (1 hunks)
  • benchmarks/with-ketch/package.json (0 hunks)
  • benchmarks/with-onetrust/package.json (1 hunks)
  • benchmarks/with-osano/package.json (1 hunks)
  • benchmarks/with-usercentrics/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/benchmark/README.md (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/benchmark/rslib.config.ts (1 hunks)
  • packages/benchmark/src/bundle-strategy.ts (1 hunks)
  • packages/benchmark/src/constants.ts (1 hunks)
  • packages/benchmark/src/cookie-banner-collector.ts (1 hunks)
  • packages/benchmark/src/index.ts (1 hunks)
  • packages/benchmark/src/network-monitor.ts (1 hunks)
  • packages/benchmark/src/perfume-collector.ts (1 hunks)
  • packages/benchmark/src/resource-timing-collector.ts (1 hunks)
  • packages/benchmark/src/types.ts (1 hunks)
  • packages/benchmark/tsconfig.json (1 hunks)
  • packages/cli/base.json (0 hunks)
  • packages/cli/src/commands/benchmark/benchmark-runner.ts (0 hunks)
  • packages/cli/src/commands/benchmark/bundle-strategy.ts (0 hunks)
  • packages/cli/src/commands/benchmark/constants.ts (0 hunks)
  • packages/cli/src/commands/benchmark/cookie-banner-detector.ts (0 hunks)
  • packages/cli/src/commands/benchmark/index.ts (0 hunks)
  • packages/cli/src/commands/benchmark/metrics-calculator.ts (0 hunks)
  • packages/cli/src/commands/benchmark/network-monitor.ts (0 hunks)
  • packages/cli/src/commands/benchmark/resource-collector.ts (0 hunks)
  • packages/cli/src/commands/benchmark/types.ts (0 hunks)
  • packages/cli/src/commands/db.ts (0 hunks)
  • packages/cli/src/commands/results.ts (0 hunks)
  • packages/cli/src/index.ts (0 hunks)
  • packages/cli/src/lib/README.md (0 hunks)
  • packages/cli/src/lib/benchmark-runner.ts (0 hunks)
  • packages/cli/src/lib/collectors/cookie-banner-collector.ts (0 hunks)
  • packages/cli/src/lib/collectors/index.ts (0 hunks)
  • packages/cli/src/lib/collectors/network-monitor.ts (0 hunks)
  • packages/cli/src/lib/collectors/resource-timing-collector.ts (0 hunks)
  • packages/cli/src/lib/metrics/index.ts (0 hunks)
  • packages/cli/src/lib/metrics/performance-aggregator.ts (0 hunks)
  • packages/cli/src/lib/performance-enhanced.ts (0 hunks)
  • packages/cli/src/lib/performance.ts (0 hunks)
  • packages/cli/src/lib/server.ts (0 hunks)
  • packages/cli/src/types/index.ts (0 hunks)
  • packages/cli/src/utils/index.ts (0 hunks)
  • packages/cli/tsconfig.json (0 hunks)
  • packages/cookiebench-cli/README.md (1 hunks)
  • packages/cookiebench-cli/base.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/rslib.config.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/db.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/results.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/save.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/cookiebench-cli/src/components/intro.ts (1 hunks)
  • packages/cookiebench-cli/src/index.ts (1 hunks)
  • packages/cookiebench-cli/src/types/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/auth.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/constants.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/logger.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/scoring.ts (27 hunks)
  • packages/cookiebench-cli/tsconfig.json (1 hunks)
  • packages/runner/README.md (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/runner/rslib.config.ts (1 hunks)
  • packages/runner/src/benchmark-runner.ts (1 hunks)
  • packages/runner/src/index.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/server.ts (1 hunks)
  • packages/runner/src/types.ts (1 hunks)
  • packages/runner/src/utils.ts (1 hunks)
  • packages/runner/tsconfig.json (1 hunks)
  • tsconfig.json (2 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (29)
  • benchmarks/with-ketch/package.json
  • packages/cli/src/index.ts
  • packages/cli/src/lib/server.ts
  • packages/cli/src/lib/README.md
  • packages/cli/src/commands/benchmark/constants.ts
  • packages/cli/src/lib/metrics/index.ts
  • packages/cli/src/lib/collectors/index.ts
  • benchmarks/with-didomi/package.json
  • packages/cli/src/commands/benchmark/benchmark-runner.ts
  • packages/cli/src/commands/benchmark/bundle-strategy.ts
  • packages/cli/src/commands/db.ts
  • packages/cli/base.json
  • packages/cli/src/lib/performance-enhanced.ts
  • packages/cli/src/lib/collectors/cookie-banner-collector.ts
  • packages/cli/src/commands/benchmark/index.ts
  • packages/cli/src/lib/metrics/performance-aggregator.ts
  • packages/cli/src/commands/benchmark/metrics-calculator.ts
  • packages/cli/src/utils/index.ts
  • packages/cli/src/commands/benchmark/resource-collector.ts
  • packages/cli/src/lib/benchmark-runner.ts
  • packages/cli/src/lib/performance.ts
  • packages/cli/src/commands/results.ts
  • packages/cli/src/lib/collectors/resource-timing-collector.ts
  • packages/cli/src/commands/benchmark/network-monitor.ts
  • packages/cli/src/lib/collectors/network-monitor.ts
  • packages/cli/src/commands/benchmark/types.ts
  • packages/cli/src/types/index.ts
  • packages/cli/tsconfig.json
  • packages/cli/src/commands/benchmark/cookie-banner-detector.ts
🧰 Additional context used
🧬 Code graph analysis (20)
packages/benchmark/src/bundle-strategy.ts (3)
packages/benchmark/src/types.ts (2)
  • Config (13-56)
  • BundleStrategy (119-123)
packages/benchmark/src/constants.ts (1)
  • BUNDLE_TYPES (15-20)
packages/cli/src/commands/benchmark/bundle-strategy.ts (1)
  • determineBundleStrategy (5-22)
packages/runner/src/server.ts (2)
packages/runner/src/types.ts (1)
  • ServerInfo (18-21)
packages/runner/src/utils.ts (2)
  • getPackageManager (25-57)
  • ONE_SECOND (5-5)
packages/cookiebench-cli/src/components/intro.ts (1)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/commands/scores.ts (6)
packages/cookiebench-cli/src/commands/results.ts (1)
  • RawBenchmarkDetail (39-179)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/utils/constants.ts (2)
  • HALF_SECOND (2-2)
  • PERCENTAGE_DIVISOR (6-6)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1402-1441)
  • calculateScores (1029-1398)
packages/benchmark/src/network-monitor.ts (2)
packages/benchmark/src/types.ts (3)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • Config (13-56)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/benchmark/src/constants.ts (1)
packages/benchmark/src/index.ts (2)
  • BENCHMARK_CONSTANTS (5-5)
  • BUNDLE_TYPES (5-5)
packages/cookiebench-cli/src/commands/save.ts (6)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (181-215)
  • RawBenchmarkDetail (39-179)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/cookiebench-cli/src/utils/constants.ts (2)
  • HALF_SECOND (2-2)
  • PERCENTAGE_DIVISOR (6-6)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1029-1398)
packages/cookiebench-cli/src/utils/index.ts (2)
packages/runner/src/utils.ts (4)
  • readConfig (6-16)
  • formatTime (18-23)
  • ONE_SECOND (5-5)
  • getPackageManager (25-57)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/runner/src/utils.ts (2)
packages/cookiebench-cli/src/utils/index.ts (3)
  • readConfig (10-19)
  • formatTime (21-26)
  • getPackageManager (28-60)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/index.ts (4)
packages/cookiebench-cli/src/utils/logger.ts (3)
  • logger (152-152)
  • CliLogger (8-8)
  • createCliLogger (102-149)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/cookiebench-cli/src/utils/constants.ts (1)
  • HALF_SECOND (2-2)
packages/cookiebench-cli/src/components/intro.ts (1)
  • displayIntro (11-93)
packages/benchmark/src/resource-timing-collector.ts (2)
packages/benchmark/src/types.ts (1)
  • ResourceTimingData (126-211)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/runner/src/benchmark-runner.ts (8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/runner/src/types.ts (3)
  • Config (6-6)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/cookie-banner-collector.ts (1)
  • CookieBannerCollector (13-242)
packages/benchmark/src/network-monitor.ts (1)
  • NetworkMonitor (6-123)
packages/benchmark/src/resource-timing-collector.ts (1)
  • ResourceTimingCollector (6-169)
packages/benchmark/src/perfume-collector.ts (1)
  • PerfumeCollector (6-142)
packages/runner/src/performance-aggregator.ts (1)
  • PerformanceAggregator (29-372)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/runner/src/performance-aggregator.ts (2)
packages/benchmark/src/types.ts (8)
  • CoreWebVitals (214-225)
  • CookieBannerData (93-101)
  • CookieBannerMetrics (78-91)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • ResourceTimingData (126-211)
  • Config (13-56)
  • PerfumeMetrics (228-259)
packages/runner/src/types.ts (10)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/cookiebench-cli/src/commands/db.ts (3)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/runner/src/utils.ts (1)
  • ONE_SECOND (5-5)
packages/runner/src/types.ts (2)
packages/cookiebench-cli/src/types/index.ts (3)
  • ServerInfo (6-6)
  • BenchmarkDetails (3-3)
  • BenchmarkResult (4-4)
packages/runner/src/index.ts (3)
  • ServerInfo (21-21)
  • BenchmarkDetails (10-10)
  • BenchmarkResult (11-11)
packages/benchmark/src/cookie-banner-collector.ts (3)
packages/benchmark/src/types.ts (5)
  • Config (13-56)
  • CookieBannerMetrics (78-91)
  • WindowWithCookieMetrics (65-76)
  • LayoutShiftEntry (59-62)
  • CookieBannerData (93-101)
packages/benchmark/src/bundle-strategy.ts (1)
  • determineBundleStrategy (4-21)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (1-13)
packages/cookiebench-cli/src/commands/results.ts (6)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/types/index.ts (2)
  • Config (5-5)
  • BenchmarkScores (10-38)
packages/runner/src/utils.ts (2)
  • formatTime (18-23)
  • ONE_SECOND (5-5)
packages/cookiebench-cli/src/utils/constants.ts (18)
  • KILOBYTE (9-9)
  • SCORE_THRESHOLD_POOR (13-13)
  • SCORE_THRESHOLD_FAIR (14-14)
  • CLS_DECIMAL_PLACES (10-10)
  • PERCENTAGE_DIVISOR (6-6)
  • CLS_THRESHOLD_GOOD (17-17)
  • CLS_THRESHOLD_FAIR (18-18)
  • COL_WIDTH_NAME (21-21)
  • COL_WIDTH_CHART_PADDING (27-27)
  • MAX_FILENAME_LENGTH (30-30)
  • TRUNCATED_FILENAME_LENGTH (31-31)
  • MIN_DURATION_THRESHOLD (34-34)
  • COL_WIDTH_TYPE (22-22)
  • COL_WIDTH_SOURCE (23-23)
  • COL_WIDTH_SIZE (24-24)
  • COL_WIDTH_DURATION (25-25)
  • COL_WIDTH_TAGS (26-26)
  • DEFAULT_DOM_SIZE (4-4)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1029-1398)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/cookiebench-cli/src/commands/benchmark.ts (8)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/constants.ts (6)
  • DEFAULT_THIRD_PARTY_DOMAINS (5-5)
  • PERCENTAGE_DIVISOR (6-6)
  • DEFAULT_DOM_SIZE (4-4)
  • HALF_SECOND (2-2)
  • DEFAULT_ITERATIONS (3-3)
  • SEPARATOR_WIDTH (7-7)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/runner/src/benchmark-runner.ts (2)
  • runSingleBenchmark (37-123)
  • BenchmarkRunner (15-187)
packages/runner/src/utils.ts (1)
  • readConfig (6-16)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1029-1398)
  • printScores (1402-1441)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (884-1114)
packages/benchmark/src/perfume-collector.ts (1)
packages/benchmark/src/types.ts (2)
  • WindowWithPerfumeMetrics (261-277)
  • PerfumeMetrics (228-259)
packages/cookiebench-cli/src/utils/scoring.ts (2)
packages/runner/src/utils.ts (1)
  • ONE_SECOND (5-5)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
🪛 LanguageTool
packages/cookiebench-cli/README.md

[uncategorized] ~373-~373: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ice overhead 4. Transparency (15%): Open source status, documentation, licensing 5. **U...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
packages/cookiebench-cli/README.md

99-99: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


170-170: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


170-170: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


336-336: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🔇 Additional comments (46)
benchmarks/with-enzuzo/next.config.ts (1)

1-1: Stylistic import update aligns with broader refactoring.

This quote-style normalization is consistent with the formatting updates across multiple benchmark configs mentioned in the PR. The change has no functional impact.

benchmarks/with-enzuzo/config.json (1)

2-39: Formatting sync looks good.

Thanks for normalizing the indentation—keeping these configs tidy helps reduce future diff noise.

benchmarks/with-cookie-control/config.json (1)

2-39: Formatting refresh acknowledged.

Indentation is consistent now, which will keep future semantic diffs easy to spot.

packages/cookiebench-cli/rslib.config.ts (1)

1-23: Config aligns with the new bundling story.

ESM bundle output with a clean dist matches the rest of the toolchain—looks solid.

benchmarks/with-enzuzo/tsconfig.json (1)

2-10: No issues with the formatting pass.

Whitespace changes only; thanks for keeping the project configs consistent.

tsconfig.json (1)

4-8: LGTM! Good type safety improvements.

The addition of strictNullChecks: true enhances type safety across the project. The multi-line formatting of arrays improves readability and makes diffs clearer for future changes.

Also applies to: 20-21, 23-29

packages/benchmark/README.md (1)

1-113: LGTM! Comprehensive package documentation.

The README provides clear documentation of the benchmark package with well-structured sections covering installation, usage examples, API reference, and type definitions. The code examples are practical and demonstrate proper usage patterns.

packages/runner/rslib.config.ts (1)

1-14: LGTM! Clean library configuration.

The rslib configuration is properly set up for a Node.js library with ESM output, TypeScript declaration generation, and modern ES2021 syntax. This aligns well with the monorepo's restructuring goals.

benchmarks/with-usercentrics/package.json (1)

4-9: LGTM! Consistent with refactoring pattern.

The changes align with the CLI refactoring seen across all benchmark packages in this PR. The benchmark script removal and CLI dependency rename from @cookiebench/cli to cookiebench are consistent with the broader restructuring effort.

Also applies to: 22-22

turbo.json (1)

5-27: No build task configuration issues identified.

The renamed cookiebench package (previously @cookiebench/cli) contains a build script ("build": "rslib build"), and turbo's dependency pattern "dependsOn": ["^build"] will automatically discover it. The removal of task definitions from turbo.json is correct since they will be auto-discovered. The @cookiebench/www package has been removed entirely as part of the refactoring.

benchmarks/with-c15t-react/package.json (1)

4-9: I need to verify whether the removal of the benchmark script from the individual package breaks benchmark execution for that specific package, and check the broader benchmark workflow structure.

I need to check the turbo configuration and verify the cookiebench CLI setup to ensure benchmarks can be executed properly.

Based on my verification, the workflow has been properly restructured:

Root-level benchmark execution infrastructure is in place. Turbo scripts should only be written in the root package.json, as writing turbo commands into individual package.json files can lead to recursively calling turbo. The removal of the benchmark script from benchmarks/with-c15t-react/package.json is correct. Specific packages can be filtered using turbo build --filter, allowing benchmarks to execute through the root-level "benchmark": "pnpm exec cookiebench benchmark" script with the renamed cookiebench CLI dependency.

benchmarks/with-onetrust/package.json (1)

22-22: LGTM!

The dependency update from @cookiebench/cli to cookiebench aligns correctly with the PR's CLI refactoring objectives.

package.json (2)

5-12: LGTM!

The script updates correctly use pnpm exec cookiebench to invoke the renamed CLI, ensuring proper workspace resolution. The command structure is consistent across all three benchmark-related scripts.


17-21: LGTM!

The addition of workspace-scoped packages (@consentio/benchmark, @consentio/runner, and cookiebench) correctly supports the modularized architecture described in the PR objectives.

packages/benchmark/rslib.config.ts (1)

1-14: LGTM!

The rslib configuration is appropriate for a Node.js library package. ESM format with ES2021 syntax and TypeScript declarations will ensure proper module resolution and type safety for consumers.

benchmarks/with-iubenda/package.json (1)

22-22: LGTM!

Dependency migration from @cookiebench/cli to cookiebench is consistent with the broader refactoring.

benchmarks/with-osano/package.json (1)

22-22: LGTM!

The dependency update to cookiebench aligns with the CLI refactoring objectives.

benchmarks/with-c15t-nextjs/package.json (1)

24-24: LGTM!

The dependency migration to cookiebench is consistent with the broader CLI refactoring across all benchmark packages.

.cursor/rules/ultracite.mdc (1)

1-227: ****

The review comment misunderstands the configuration setup. The biome.jsonc already extends Ultracite rules ("extends": ["ultracite/core", "ultracite/next"]), so Biome and Ultracite work together, not in conflict. The .cursor/rules/ultracite.mdc file is a Cursor IDE editor configuration (alwaysApply: false is an editor-specific setting), separate from the Biome linter. These are complementary tools: Biome handles actual linting/formatting, while Cursor IDE rules provide editor hints. No duplication or consolidation needed.

Likely an incorrect or invalid review comment.

packages/cookiebench-cli/base.json (1)

9-9: The CLI base.json file is not actively used by the package and has no practical impact, but the concern about DOM types is valid.

The file packages/cookiebench-cli/base.json contains "DOM" and "DOM.Iterable" in its lib array, but this configuration is not used anywhere in the CLI package. The actual TypeScript configuration used by the CLI is tsconfig.json, which does not include a lib array and therefore does not include DOM types. Verification confirms:

  • The CLI's actual tsconfig.json (used by tsc --noEmit) has no "lib" compiler option
  • The base.json file is not referenced in rslib.config.ts, package.json, or any source code
  • There are no DOM API usages in the CLI codebase
  • The package has no browser automation dependencies and runs exclusively in Node.js

Your concern about DOM types being inappropriate for a Node.js CLI is technically correct, but since base.json is not being used by the CLI at all, it has zero impact on type checking or runtime behavior. This appears to be unused/dead configuration that should be investigated for removal or clarification of its intended purpose.

benchmarks/with-cookie-control/app/layout.tsx (1)

1-2: LGTM! Formatting changes align with project style.

The quote style changes are purely cosmetic and consistent with the broader formatting updates across the PR.

Also applies to: 5-5

benchmarks/with-cookie-control/next.config.ts (1)

1-1: LGTM! Formatting change aligns with project style.

The quote style change is purely cosmetic.

benchmarks/with-enzuzo/package.json (1)

22-22: LGTM! Dependency update aligns with CLI refactoring.

The change from @cookiebench/cli to cookiebench is consistent with the broader refactoring across all benchmark packages in this PR.

benchmarks/with-cookie-control/tsconfig.json (1)

2-10: LGTM! Formatting changes improve consistency.

The indentation and formatting adjustments have no functional impact.

benchmarks/with-enzuzo/app/layout.tsx (1)

1-2: LGTM! Formatting changes align with project style.

The quote style and indentation changes are purely cosmetic and consistent with the broader formatting updates across the PR.

Also applies to: 5-5, 18-18

benchmarks/with-cookie-control/package.json (1)

22-22: LGTM! Dependency update aligns with CLI refactoring.

The change from @cookiebench/cli to cookiebench is consistent with the broader refactoring across all benchmark packages in this PR.

packages/cookiebench-cli/README.md (1)

1-394: Excellent comprehensive documentation!

The README provides thorough coverage of installation, usage, commands, configuration, and development workflows. The structure is clear, examples are helpful, and the coverage of both interactive and direct modes is valuable.

benchmarks/with-cookie-yes/package.json (1)

22-22: LGTM! Dependency migration aligns with CLI refactoring.

The addition of the cookiebench workspace dependency is consistent with the PR's objective to rename the CLI package from @cookiebench/cli to cookiebench.

packages/runner/README.md (1)

1-150: Well-structured package documentation.

The README comprehensively documents the runner package with clear examples for basic usage, server management, and remote benchmarking scenarios. The API reference and configuration schema provide good developer guidance.

packages/runner/tsconfig.json (1)

1-15: LGTM! Standard TypeScript configuration.

The compiler options are appropriately configured for a modern library package with strict type checking, ES2020 target, and declaration generation enabled.

packages/benchmark/package.json (1)

1-33: LGTM! Well-configured package manifest.

The package manifest properly defines ESM exports, includes appropriate build tooling, and declares necessary dependencies for benchmark collection functionality.

packages/cookiebench-cli/src/index.ts (1)

1-135: Well-structured CLI entry point with proper command routing.

The implementation correctly:

  • Initializes logging with environment-driven log levels
  • Gates admin commands with proper access checks
  • Supports both direct command invocation and interactive mode
  • Handles cancellation and errors gracefully
  • Routes to appropriate command handlers
packages/benchmark/src/bundle-strategy.ts (1)

4-21: Duplication resolved – refactoring successfully completed.

The old bundle-strategy.ts file has been removed, and determineBundleStrategy is now exclusively maintained in the @consentio/benchmark package. The function is properly exported via the package's public API (index.ts) and used internally without any orphaned references. The migration is complete.

packages/runner/src/types.ts (1)

1-291: LGTM!

The type definitions are comprehensive and well-structured. The re-exports from @consentio/benchmark promote code reuse, and the BenchmarkDetails and BenchmarkResult types provide clear contracts for benchmark data.

packages/cookiebench-cli/src/utils/constants.ts (1)

1-34: LGTM!

The constant definitions are clear and well-organized. The values are appropriate for their use cases across the CLI (timing constants, UI dimensions, thresholds, etc.).

packages/runner/src/performance-aggregator.ts (6)

123-196: Verify the intentional duplication of cookieBanner and thirdParty structures.

The method builds cookieBanner structures in two places:

  • Lines 163: timing.cookieBanner (with render/interaction timing)
  • Lines 177-183: Root-level cookieBanner (with detection metadata)

Similarly, thirdParty appears in:

  • Lines 164-168: timing.thirdParty
  • Lines 184-194: Root-level thirdParty

While this may align with the BenchmarkDetails type definition, verify that this duplication is intentional and that both structures serve distinct purposes rather than being redundant.


38-49: TTI calculation uses domain-specific heuristic.

The TTI calculation uses Math.max of FCP, DOM complete timing, and banner interactive time plus a 1000ms buffer. This is a simplified heuristic specific to cookie banner benchmarking rather than the standard TTI metric from Lighthouse/web vitals.

Ensure this approach aligns with your benchmarking objectives, as it may not match standard TTI definitions used in broader web performance contexts.


201-212: LGTM!

The network impact calculation is straightforward and correctly aggregates size and duration from network requests.


342-372: LGTM!

The logging method provides comprehensive debug information with clear bundle strategy determination. Using logger.debug is appropriate for detailed performance metrics.


75-94: ****

The hardcoded values at lines 81-82 and 89-90 are intentional and correct. The DNS and connection timing data collected by ResourceTimingCollector is not included in the NetworkRequest type and therefore never flows to buildThirdPartyMetrics. The function only receives aggregated impact metrics (totalImpact and totalDownloadTime), not individual timing breakdowns. Defaulting these fields to 0 is the appropriate design given the available data.

Likely an incorrect or invalid review comment.


54-70: Interaction timing values create zero-duration measurement; clarify intended semantics.

Lines 61-62 map both interactionStart and interactionEnd to the same value (bannerInteractiveTime), resulting in zero interaction duration. The source data provides bannerHydrationTime (available in CookieBannerData), which is currently unused and could represent the actual interaction window if that's the intended semantics.

Verify whether:

  1. Zero interaction duration is intentional (measuring only "time to interactive" moment)
  2. interactionEnd should use a different calculation, such as bannerInteractiveTime + bannerHydrationTime, if distinct interaction phases need to be tracked
  3. The semantic meaning of interactionStart and interactionEnd fields aligns with current implementation
packages/cookiebench-cli/src/utils/scoring.ts (5)

19-42: LGTM! Well-structured type enhancements.

The additions to MetricsData properly integrate Perfume.js metrics with appropriate optional markers. The timeToFirstByte, interactionToNextPaint, and networkInformation fields are correctly typed and align with the performance-aggregator integration.


369-421: LGTM! TTFB and INP scoring logic is well-implemented.

The new TTFB and INP scoring additions enhance the performance assessment with modern web vitals. The thresholds align with industry best practices, and the neutral score (5 points) for INP when no interactions are detected is a reasonable fallback.


879-909: LGTM! Enhanced insights provide valuable context.

The new insights for network quality, TTFB, and INP enrich the scoring output with actionable feedback. The network information context (lines 879-885) is particularly useful for understanding test conditions, and the TTFB/INP thresholds align with the scoring logic.


946-956: LGTM! Actionable recommendations for TTFB and INP.

The new recommendations provide concrete guidance for improving TTFB (server optimization, CDN, caching) and INP (JavaScript execution, event handler performance). The thresholds (>200ms) align with the "Good" threshold from the scoring logic.


1029-1071: LGTM! Backward-compatible API extension.

The calculateScores function signature properly extends to accept Perfume.js metrics (timeToFirstByte, interactionToNextPaint) and network information. The new parameters are optional, maintaining backward compatibility, and are correctly integrated into the metrics data structure.

Also applies to: 1205-1208

@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify manual modification of auto-generated file.

This file explicitly states it should not be edited (line 5-6), yet a manual import has been added. Additionally, the import references .next/types/routes.d.ts, which is a build-time artifact that may not exist before the first build, potentially causing TypeScript errors.

Next.js typed routes are typically configured via next.config.js using experimental.typedRoutes: true (or typedRoutes: true in Next.js 15+), and the types are automatically included without manual imports.

Please confirm whether this manual import is necessary or if Next.js configuration should be used instead.


🏁 Script executed:

#!/bin/bash
# Description: Check if typed routes are properly configured in next.config.js
# and verify if this manual import is necessary

# Check for typed routes configuration in Next.js config files
echo "=== Checking Next.js config for typed routes configuration ==="
fd -e js -e ts -e mjs 'next.config' benchmarks/with-c15t-nextjs/ --exec cat {}

echo -e "\n=== Checking if .next/types/routes.d.ts exists (build artifact) ==="
if [ -f "benchmarks/with-c15t-nextjs/.next/types/routes.d.ts" ]; then
  echo "File exists"
else
  echo "File does not exist (expected - it's a build artifact)"
fi

Length of output: 318



---

🌐 Web query:

```web
Next.js typed routes import next-env.d.ts manual configuration

💡 Result:

Short answer — don’t edit Next’s generated next-env.d.ts; instead make sure it’s included and add your own declarations. Steps & minimal configs:

  1. Ensure Next’s generated file is included in tsconfig.json (Next will regenerate it on dev/build). Example include array:
    {
    "include": [
    "next-env.d.ts",
    ".next/types//*.ts", // required when using typedRoutes / generated types
    "
    /.ts",
    "**/
    .tsx"
    ],
    "exclude": ["node_modules"]
    }
    (Next docs recommend exactly this pattern). [1][2]

  2. If you need custom global declarations, create a separate d.ts (e.g. src/types.d.ts or global.d.ts) — do NOT modify next-env.d.ts (it’s overwritten). Add that filename to tsconfig.json include. Example:

    • src/global.d.ts
      declare namespace MyApp { /* ... */ }
    • tsconfig.json include: ["src/global.d.ts", "next-env.d.ts", ".next/types//*.ts", "/.ts", "**/.tsx"]. [2]
  3. If you need to “manually” provide the next types (e.g. you didn’t use create-next-app), you can add a next-env.d.ts containing the standard reference and keep it under version control:
    ///
    ///
    But be aware Next will regenerate/overwrite this file during dev/build — prefer creating your own d.ts for custom types. [2]

  4. For typed routes (typedRoutes: true in next.config), include the generated .next/types/**/*.ts so TypeScript picks up .next’s route/type definitions. Run next dev / next build (or next typegen) to generate those types. [1]

Sources:

  • Next.js TypeScript docs (includes guidance about next-env.d.ts, including .next types, and creating separate declaration files). [1][2]


---

🏁 Script executed:

```shell
#!/bin/bash
# Check tsconfig.json and verify the full context

echo "=== Checking tsconfig.json in benchmarks/with-c15t-nextjs/ ==="
cat benchmarks/with-c15t-nextjs/tsconfig.json

echo -e "\n=== Checking the full content of next-env.d.ts ==="
cat benchmarks/with-c15t-nextjs/next-env.d.ts

Length of output: 614


Remove manual import from next-env.d.ts; configure typed routes in next.config.js instead.

The file explicitly states it should not be edited. The manual import on line 3 (import "./.next/types/routes.d.ts") violates this guidance and references a build artifact that doesn't exist pre-build, causing TypeScript compilation errors before the first build.

If you need typed routes, configure typedRoutes: true in next.config.js and let Next.js auto-generate the types. The tsconfig.json already includes the correct path (.next/types/**/*.ts), so no manual import is needed.

Action:

  1. Remove the import statement from line 3
  2. If typed routes are needed for this benchmark, add typedRoutes: true to next.config.js and run next build or next typegen
🤖 Prompt for AI Agents
In benchmarks/with-c15t-nextjs/next-env.d.ts around line 3, remove the manual
import `import "./.next/types/routes.d.ts"` because it references a build
artifact and causes TS errors pre-build; instead, delete that line and if typed
routes are required enable `typedRoutes: true` in next.config.js and run `next
build` or `next typegen` so Next.js auto-generates the `.next/types` files.

Comment thread packages/benchmark/src/network-monitor.ts Outdated
Comment on lines +3 to +11
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reconsider moduleResolution: "bundler" for this package.

bundler is great for app builds, but for a library emitting declarations it can leak extensionless specifiers into .d.ts files. Those specifiers break consumers compiling with Node-compatible resolution (node16/nodenext) because TypeScript won’t rewrite them to .js, and Node rejects the extensionless import at runtime. The TypeScript docs explicitly recommend sticking to the Node modes (or bundling the declaration output) when you plan to publish library code.(runebook.dev)

A straightforward fix is to switch to the Node-aligned setting:

-    "moduleResolution": "bundler",
+    "moduleResolution": "nodenext",

If you truly need bundler, please ensure the declarations are post-processed so that every relative specifier has the correct .js (or .mjs) suffix before publishing.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
🤖 Prompt for AI Agents
In packages/benchmark/tsconfig.json around lines 3 to 11, moduleResolution is
set to "bundler", which can emit extensionless specifiers into .d.ts files that
break consumers using Node-compatible resolution; change moduleResolution to a
Node-aligned mode such as "node16" or "nodenext" (and adjust related settings
like module if needed) so TypeScript rewrites imports correctly for published
declarations, or if you must keep "bundler" add a post-processing step that
rewrites all relative specifiers in emitted .d.ts files to include the
appropriate .js/.mjs extensions before publishing.

Comment thread packages/cookiebench-cli/package.json Outdated
- ✅ Summary report at the end

**Example:**
```

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Minor markdown formatting issues.

Static analysis tools flagged some markdown formatting concerns:

  • Fenced code blocks at lines 99, 170, and 336 should be surrounded by blank lines and have language specifiers
  • Line 373: "Open source" could be hyphenated as "open-source" when used as a compound adjective

These are minor style issues that don't affect functionality.

Also applies to: 170-170, 336-336

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

99-99: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In packages/cookiebench-cli/README.md around lines 99, 170, 336 and line 373:
the fenced code blocks at lines 99, 170, and 336 need blank lines before and
after the triple-backticks and should include a language specifier (e.g.,
```bash or ```js) to satisfy markdown linters; edit those blocks to add a blank
line above and below each fence and append the appropriate language token after
the opening backticks. Also update line 373 by changing the phrase "Open source"
to the hyphenated compound adjective "open-source".

Comment thread packages/cookiebench-cli/src/utils/logger.ts
Comment on lines +1 to +14
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding declaration file generation.

While this CLI package configuration is functional, it lacks "declaration": true for generating TypeScript declaration files. Although CLIs typically don't require declarations, adding this option would improve consistency with other packages in the monorepo (e.g., packages/runner/rslib.config.ts has dts: true) and enable better IDE support if other packages import utilities from the CLI.

Apply this diff if declaration files are desired:

     "strict": true,
     "skipLibCheck": true,
+    "declaration": true,
     "outDir": "dist",
     "rootDir": "src"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
🤖 Prompt for AI Agents
packages/cookiebench-cli/tsconfig.json lines 1-14: the TS config doesn’t enable
declaration file generation; to align with monorepo conventions and improve
IDE/consumer support, set compilerOptions.declaration to true (and optionally
add declarationDir, e.g., "dist/types" or similar) so .d.ts files are emitted to
your build output; ensure declaration generation does not conflict with existing
outDir/rootDir settings.

Comment on lines +52 to +110
// Setup monitoring and detection
await this.networkMonitor.setupMonitoring(page);
await this.cookieBannerCollector.setupDetection(page);
await this.perfumeCollector.setupPerfume(page);

// Navigate to the page
this.logger.debug(`Navigating to: ${url}`);
await page.goto(url, { waitUntil: "networkidle" });

// Wait for the specified element
await this.waitForElement(page);

// Wait for network to be idle
this.logger.debug("Waiting for network idle...");
await page.waitForLoadState("networkidle");

// Collect core web vitals from playwright-performance-metrics (primary source)
this.logger.debug("Collecting core web vitals...");
const coreWebVitals = await collector.collectMetrics(page, {
timeout: BENCHMARK_CONSTANTS.METRICS_TIMEOUT,
retryTimeout: BENCHMARK_CONSTANTS.METRICS_RETRY_TIMEOUT,
});

this.logger.debug("Core web vitals collected:", {
fcp: coreWebVitals.paint?.firstContentfulPaint,
lcp: coreWebVitals.largestContentfulPaint,
cls: coreWebVitals.cumulativeLayoutShift,
tbt: coreWebVitals.totalBlockingTime,
});

// Collect Perfume.js metrics (supplementary - TTFB, navigation timing, network info)
this.logger.debug("Collecting Perfume.js supplementary metrics...");
const perfumeMetrics = await this.perfumeCollector.collectMetrics(page);
this.logger.debug("Perfume.js metrics:", perfumeMetrics);

// Collect cookie banner specific metrics
const cookieBannerData =
await this.cookieBannerCollector.collectMetrics(page);
this.logger.debug("Cookie banner metrics:", cookieBannerData);

// Collect detailed resource timing data
const resourceMetrics = await this.resourceTimingCollector.collect(page);

// Get network metrics
const networkRequests = this.networkMonitor.getNetworkRequests();
const networkMetrics = this.networkMonitor.getMetrics();

// Aggregate all metrics
const finalMetrics = this.performanceAggregator.aggregateMetrics({
coreWebVitals,
cookieBannerData,
cookieBannerMetrics,
networkRequests,
networkMetrics,
resourceMetrics,
config: this.config,
perfumeMetrics,
});

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Third-party/network metrics never populate

NetworkMonitor’s third-party detection currently runs !url.includes(new URL(url).hostname), which is always false; it also leaves duration at 0. As a result, networkMonitor.getMetrics() and networkMonitor.getNetworkRequests() feed only zeroes into aggregateMetrics, so every run reports zero third-party impact and zero download time. That breaks the headline scoring story of this refactor.

Please pass the page’s hostname into setupMonitoring and update NetworkMonitor to compare new URL(request.url()).hostname against that host and to record timing (e.g., via response.timing()). For example, this file should provide the host:

-		await this.networkMonitor.setupMonitoring(page);
+		const targetHost = new URL(url).hostname;
+		await this.networkMonitor.setupMonitoring(page, targetHost);

and NetworkMonitor should use that hostname when classifying requests and populate duration. Without these fixes, the CLI reports misleading metrics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Setup monitoring and detection
await this.networkMonitor.setupMonitoring(page);
await this.cookieBannerCollector.setupDetection(page);
await this.perfumeCollector.setupPerfume(page);
// Navigate to the page
this.logger.debug(`Navigating to: ${url}`);
await page.goto(url, { waitUntil: "networkidle" });
// Wait for the specified element
await this.waitForElement(page);
// Wait for network to be idle
this.logger.debug("Waiting for network idle...");
await page.waitForLoadState("networkidle");
// Collect core web vitals from playwright-performance-metrics (primary source)
this.logger.debug("Collecting core web vitals...");
const coreWebVitals = await collector.collectMetrics(page, {
timeout: BENCHMARK_CONSTANTS.METRICS_TIMEOUT,
retryTimeout: BENCHMARK_CONSTANTS.METRICS_RETRY_TIMEOUT,
});
this.logger.debug("Core web vitals collected:", {
fcp: coreWebVitals.paint?.firstContentfulPaint,
lcp: coreWebVitals.largestContentfulPaint,
cls: coreWebVitals.cumulativeLayoutShift,
tbt: coreWebVitals.totalBlockingTime,
});
// Collect Perfume.js metrics (supplementary - TTFB, navigation timing, network info)
this.logger.debug("Collecting Perfume.js supplementary metrics...");
const perfumeMetrics = await this.perfumeCollector.collectMetrics(page);
this.logger.debug("Perfume.js metrics:", perfumeMetrics);
// Collect cookie banner specific metrics
const cookieBannerData =
await this.cookieBannerCollector.collectMetrics(page);
this.logger.debug("Cookie banner metrics:", cookieBannerData);
// Collect detailed resource timing data
const resourceMetrics = await this.resourceTimingCollector.collect(page);
// Get network metrics
const networkRequests = this.networkMonitor.getNetworkRequests();
const networkMetrics = this.networkMonitor.getMetrics();
// Aggregate all metrics
const finalMetrics = this.performanceAggregator.aggregateMetrics({
coreWebVitals,
cookieBannerData,
cookieBannerMetrics,
networkRequests,
networkMetrics,
resourceMetrics,
config: this.config,
perfumeMetrics,
});
// Setup monitoring and detection
const targetHost = new URL(url).hostname;
await this.networkMonitor.setupMonitoring(page, targetHost);
await this.cookieBannerCollector.setupDetection(page);
await this.perfumeCollector.setupPerfume(page);
// Navigate to the page
this.logger.debug(`Navigating to: ${url}`);
await page.goto(url, { waitUntil: "networkidle" });
// Wait for the specified element
await this.waitForElement(page);
// Wait for network to be idle
this.logger.debug("Waiting for network idle...");
await page.waitForLoadState("networkidle");
// Collect core web vitals from playwright-performance-metrics (primary source)
this.logger.debug("Collecting core web vitals...");
const coreWebVitals = await collector.collectMetrics(page, {
timeout: BENCHMARK_CONSTANTS.METRICS_TIMEOUT,
retryTimeout: BENCHMARK_CONSTANTS.METRICS_RETRY_TIMEOUT,
});
this.logger.debug("Core web vitals collected:", {
fcp: coreWebVitals.paint?.firstContentfulPaint,
lcp: coreWebVitals.largestContentfulPaint,
cls: coreWebVitals.cumulativeLayoutShift,
tbt: coreWebVitals.totalBlockingTime,
});
// Collect Perfume.js metrics (supplementary - TTFB, navigation timing, network info)
this.logger.debug("Collecting Perfume.js supplementary metrics...");
const perfumeMetrics = await this.perfumeCollector.collectMetrics(page);
this.logger.debug("Perfume.js metrics:", perfumeMetrics);
// Collect cookie banner specific metrics
const cookieBannerData =
await this.cookieBannerCollector.collectMetrics(page);
this.logger.debug("Cookie banner metrics:", cookieBannerData);
// Collect detailed resource timing data
const resourceMetrics = await this.resourceTimingCollector.collect(page);
// Get network metrics
const networkRequests = this.networkMonitor.getNetworkRequests();
const networkMetrics = this.networkMonitor.getMetrics();
// Aggregate all metrics
const finalMetrics = this.performanceAggregator.aggregateMetrics({
coreWebVitals,
cookieBannerData,
cookieBannerMetrics,
networkRequests,
networkMetrics,
resourceMetrics,
config: this.config,
perfumeMetrics,
});
🤖 Prompt for AI Agents
In packages/runner/src/benchmark-runner.ts around lines 52 to 110, the
NetworkMonitor is currently set up without the page hostname so its third-party
detection `!url.includes(new URL(url).hostname)` always fails and durations stay
zero; update the call to this.networkMonitor.setupMonitoring(page) to pass the
page's hostname (e.g., new URL(url).hostname or await page.url() hostname) so
NetworkMonitor has the origin to compare against, and then update NetworkMonitor
to classify requests by comparing new URL(request.url()).hostname !== host and
to compute/record duration from the response timing (use response.timing() or
available timing fields to set a numeric duration), ensuring getMetrics() and
getNetworkRequests() return proper third-party flags and non-zero durations for
aggregation.

Comment thread packages/runner/src/performance-aggregator.ts
Comment thread packages/runner/src/performance-aggregator.ts
…le benchmarks. Corrected typos in README, standardized bundle type naming from 'iffe' to 'iife', and added Node.js engine requirements in package.json files. Enhanced constants and utility functions by importing shared values for better maintainability.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

♻️ Duplicate comments (23)
benchmarks/with-usercentrics/config.json (1)

20-20: Correct bundleType typo.

Consistent with bundle type corrections across all benchmark configurations.

benchmarks/with-iubenda/config.json (1)

25-25: Fixed bundleType typo.

The typo correction from "iffe" to "iife" addresses the issue previously flagged in the review.

benchmarks/with-cookie-control/config.json (1)

20-20: Correct bundleType value.

The bundleType has been corrected to "iife" as part of the systematic bundle type standardization across all benchmark configurations.

benchmarks/with-enzuzo/config.json (1)

20-20: Correct bundleType typo.

Aligns with bundle type standardization across benchmark configurations.

benchmarks/with-cookie-yes/config.json (1)

24-24: Correct bundleType value.

Consistent with bundle type corrections applied across all benchmark configurations.

packages/cookiebench-cli/src/commands/save.ts (2)

467-472: Critical issue: Third-party request count is incomplete.

The thirdPartyRequests metric only counts script resources, excluding third-party CSS, images, fonts, and other resource types. This results in significantly understated third-party request counts being persisted to the API.

Apply this diff to count all third-party resources:

 			thirdPartyRequests:
 				appResults.reduce(
 					(a, b) =>
-						a + b.resources.scripts.filter((s) => s.isThirdParty).length,
+						a +
+						b.resources.scripts.filter((r) => r.isThirdParty).length +
+						b.resources.styles.filter((r) => r.isThirdParty).length +
+						b.resources.images.filter((r) => r.isThirdParty).length +
+						b.resources.fonts.filter((r) => r.isThirdParty).length +
+						b.resources.other.filter((r) => r.isThirdParty).length,
 					0
 				) / appResults.length,

544-550: Critical issue: Multiple metrics are zeroed or incomplete.

Several averaged metrics are hardcoded to 0 or only count scripts, shipping inaccurate data to the API:

  • scriptLoadTime is hardcoded to 0 instead of averaging actual script load durations
  • scriptSize is hardcoded to 0 instead of averaging actual script sizes
  • resourceCount only counts scripts, excluding styles, images, fonts, and other resources

Apply this diff to populate these metrics correctly:

-		scriptLoadTime: 0,
+		scriptLoadTime:
+			appResults.reduce(
+				(total, result) =>
+					total +
+					result.resources.scripts.reduce(
+						(sum, script) => sum + script.duration,
+						0
+					),
+				0
+			) / appResults.length,
 		totalSize:
 			appResults.reduce((a, b) => a + b.size.total, 0) / appResults.length,
-		scriptSize: 0,
+		scriptSize:
+			appResults.reduce(
+				(total, result) => total + result.size.scripts.total,
+				0
+			) / appResults.length,
 		resourceCount:
-			appResults.reduce((a, b) => a + b.resources.scripts.length, 0) /
-			appResults.length,
+			appResults.reduce(
+				(total, result) =>
+					total +
+					result.resources.scripts.length +
+					result.resources.styles.length +
+					result.resources.images.length +
+					result.resources.fonts.length +
+					result.resources.other.length,
+				0
+			) / appResults.length,
benchmarks/with-enzuzo/package.json (1)

6-6: Port conflict already flagged.

The port conflict on 3001 with benchmarks/with-iubenda was already identified in previous review comments.

benchmarks/with-didomi/package.json (1)

2-2: Package name fix already flagged.

The correction from "with-dodomi" to "with-didomi" was already identified in previous review comments.

benchmarks/with-osano/package.json (1)

6-6: Port conflict already flagged.

The port conflict on 3006 with benchmarks/with-onetrust was already identified in previous review comments.

benchmarks/with-iubenda/package.json (1)

6-6: Port 3001 conflict already flagged.

The dev server port conflict on port 3001 was already identified in a previous review.

packages/shared/src/utils/package-manager.ts (2)

5-37: Refactor nested try-catch and remove code duplication.

The function has deeply nested try-catch blocks (3 levels) and duplicates execSync imports. This pattern was already flagged in a previous review.

Apply this refactor to flatten the logic and improve readability:

 export async function getPackageManager(): Promise<{
 	command: string;
 	args: string[];
 }> {
+	const { execSync } = await import("node:child_process");
+	const managers = [
+		{ name: "npm", command: "npm", args: ["run"] },
+		{ name: "yarn", command: "yarn", args: [] },
+		{ name: "pnpm", command: "pnpm", args: [] },
+	];
+
+	for (const manager of managers) {
+		try {
+			execSync(`${manager.name} -v`, { encoding: "utf-8" });
+			return { command: manager.command, args: manager.args };
+		} catch {
+			// Try next manager
+		}
+	}
+
+	// Default to npm if no package manager is found
+	return { command: "npm", args: ["run"] };
-	try {
-		const { execSync } = await import("node:child_process");
-		const output = execSync("npm -v", { encoding: "utf-8" });
-		if (output) {
-			return { command: "npm", args: ["run"] };
-		}
-	} catch {
-		try {
-			const { execSync } = await import("node:child_process");
-			const output = execSync("yarn -v", { encoding: "utf-8" });
-			if (output) {
-				return { command: "yarn", args: [] };
-			}
-		} catch {
-			try {
-				const { execSync } = await import("node:child_process");
-				const output = execSync("pnpm -v", { encoding: "utf-8" });
-				if (output) {
-					return { command: "pnpm", args: [] };
-				}
-			} catch {
-				// Default to npm if no package manager is found
-				return { command: "npm", args: ["run"] };
-			}
-		}
-	}
-	// Default to npm if no package manager is found
-	return { command: "npm", args: ["run"] };
 }

35-36: Remove unreachable code.

Line 36 is unreachable because line 31 always returns in the final catch block. This was already flagged in a previous review.

 			} catch {
 				// Default to npm if no package manager is found
 				return { command: "npm", args: ["run"] };
 			}
 		}
 	}
-	// Default to npm if no package manager is found
-	return { command: "npm", args: ["run"] };
 }
packages/cookiebench-cli/src/index.ts (1)

114-128: Consider adding default case for defensive programming.

As noted in previous reviews, adding a default case to this switch statement would be defensive programming and eliminate the need for the biome-ignore directive. While the select prompt constrains valid commands, an explicit default provides clarity and guards against future refactoring issues.

Apply this diff:

 		// biome-ignore lint/style/useDefaultSwitchClause: this is a CLI tool
 		switch (selectedCommand) {
 			case "benchmark":
 				await benchmarkCommand(logger);
 				break;
 			case "results":
 				await resultsCommand(logger);
 				break;
 			case "save":
 				await saveCommand(logger);
 				break;
 			case "db":
 				await dbCommand(logger);
 				break;
+			default:
+				logger.error(`Unexpected command: ${selectedCommand}`);
+				process.exit(1);
 		}
packages/cookiebench-cli/package.json (3)

9-11: Shebang configuration still needed for CLI executable.

As noted in previous reviews, the bin entry requires a shebang in the built output. Configure rslib to inject it via BannerPlugin in rslib.config.ts, or add a postbuild script to ensure the file is executable.


22-22: Replace alpha dependency with stable release.

As noted in previous reviews, @clack/prompts@1.0.0-alpha.0 should be replaced with the latest stable version 0.11.0 to avoid potential breaking changes and stability issues.

Apply this diff:

-    "@clack/prompts": "^1.0.0-alpha.0",
+    "@clack/prompts": "^0.11.0",

26-26: Move @types/figlet to devDependencies.

As noted in previous reviews, TypeScript type definitions should be in devDependencies to avoid bloating runtime installs.

Apply this diff:

   "dependencies": {
     "@c15t/logger": "^1.0.0",
     "@clack/prompts": "^1.0.0-alpha.0",
     "@consentio/benchmark": "workspace:*",
     "@consentio/runner": "workspace:*",
     "@consentio/shared": "workspace:*",
-    "@types/figlet": "^1.7.0",
     "cli-table3": "^0.6.3",
     "dotenv": "^17.2.3",
     "figlet": "^1.9.3",
     "picocolors": "^1.0.0",
     "pretty-ms": "^9.3.0"
   },
   "devDependencies": {
     "@rsdoctor/rspack-plugin": "^1.3.6",
     "@rslib/core": "^0.16.1",
     "@types/node": "^24.9.2",
+    "@types/figlet": "^1.7.0",
     "typescript": "^5.9.3"
   }
packages/runner/src/server.ts (1)

55-71: Terminate the Next.js process when startup fails.

If the health check never returns 200, the code throws an error but leaves the spawned server alive, leaking a process and port. Kill the process before surfacing the error.

Apply this diff to fix:

 	while (retries < maxRetries) {
 		try {
 			const response = await fetch(url);
 			if (response.ok) {
 				logger.success("Server is ready!");
 				return { serverProcess, url };
 			}
 		} catch {
 			// Ignore error and retry
 		}
 
 		await new Promise((resolve) => setTimeout(resolve, ONE_SECOND));
 		retries += 1;
 	}
 
+	if (!serverProcess.killed) {
+		serverProcess.kill();
+	}
 	throw new Error("Server failed to start");
packages/cookiebench-cli/src/commands/scores.ts (1)

208-226: Guard against empty result sets before averaging.

If a results file exists but results is empty (e.g., a failed/aborted run wrote the file), every average below divides by 0, producing NaN scores. Bail out early with a clear error before calling calculateScores.

Apply this diff:

 	const appResults = result.results;
+	if (!Array.isArray(appResults) || appResults.length === 0) {
+		logger.error(`No iterations recorded for ${appName}; cannot compute scores.`);
+		return;
+	}
 	const config = await loadConfigForApp(logger, appName);
packages/cookiebench-cli/src/commands/db.ts (1)

30-44: Make drizzle-kit invocation path-safe.

Building the shell command with cd ${DB_PACKAGE_PATH} breaks whenever the repo path has spaces (e.g., C:\Users\Jane Doe\...). Using cwd keeps the invocation portable across Windows and macOS.

Apply this diff:

 function runDrizzleCommand(logger: CliLogger, command: string): void {
 	try {
 		logger.step(`Running: ${color.cyan(`drizzle-kit ${command}`)}`);
-		execSync(`cd ${DB_PACKAGE_PATH} && pnpm drizzle-kit ${command}`, {
+		execSync(`pnpm drizzle-kit ${command}`, {
 			stdio: "inherit",
 			encoding: "utf-8",
+			cwd: DB_PACKAGE_PATH,
 		});
 	} catch (error) {
packages/runner/src/performance-aggregator.ts (2)

104-108: Redundant fallback in division operation.

Line 106 uses (totalBlockingTime || 1) as the denominator, but this fallback is unnecessary since the ternary on line 105 already ensures the division only occurs when totalBlockingTime > 0.

Apply this diff to simplify:

 const percentageFromCookies =
   totalBlockingTime > 0
-    ? (cookieBannerEstimate / (totalBlockingTime || 1)) *
+    ? (cookieBannerEstimate / totalBlockingTime) *
         PERCENTAGE_MULTIPLIER
     : 0;

214-334: Consider distinguishing "not measured" from zero.

Several metrics default to 0 when not calculated (e.g., speedIndex at line 238, domSize at line 259, and various third-party metrics at lines 289-321). This conflates "not measured" with "measured as zero." Consider using null or marking unavailable metrics explicitly to distinguish these cases.

packages/cookiebench-cli/src/commands/results.ts (1)

1042-1047: Fix third-party request counting.

thirdPartyRequests only counts script resources, so any third-party styles, images, fonts, or "other" assets are ignored. This underreports the network impact category and leads to incorrect scores for apps that load third-party resources outside of JavaScript.

Apply this diff:

 				thirdPartyRequests:
 					appResults.reduce(
 						(a, b) =>
-							a + b.resources.scripts.filter((s) => s.isThirdParty).length,
+							a +
+							b.resources.scripts.filter((r) => r.isThirdParty).length +
+							b.resources.styles.filter((r) => r.isThirdParty).length +
+							b.resources.images.filter((r) => r.isThirdParty).length +
+							b.resources.fonts.filter((r) => r.isThirdParty).length +
+							b.resources.other.filter((r) => r.isThirdParty).length,
 						0
 					) / appResults.length,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 507a1f3 and 5b9ee80.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (53)
  • .gitignore (1 hunks)
  • README.md (2 hunks)
  • benchmarks/baseline/package.json (2 hunks)
  • benchmarks/with-c15t-nextjs/package.json (1 hunks)
  • benchmarks/with-c15t-react/package.json (1 hunks)
  • benchmarks/with-cookie-control/app/layout.tsx (3 hunks)
  • benchmarks/with-cookie-control/config.json (1 hunks)
  • benchmarks/with-cookie-control/package.json (1 hunks)
  • benchmarks/with-cookie-yes/config.json (1 hunks)
  • benchmarks/with-cookie-yes/package.json (1 hunks)
  • benchmarks/with-didomi/app/layout.tsx (3 hunks)
  • benchmarks/with-didomi/package.json (2 hunks)
  • benchmarks/with-enzuzo/config.json (1 hunks)
  • benchmarks/with-enzuzo/package.json (1 hunks)
  • benchmarks/with-iubenda/app/layout.tsx (2 hunks)
  • benchmarks/with-iubenda/config.json (1 hunks)
  • benchmarks/with-iubenda/package.json (1 hunks)
  • benchmarks/with-ketch/config.json (1 hunks)
  • benchmarks/with-ketch/package.json (1 hunks)
  • benchmarks/with-onetrust/app/layout.tsx (2 hunks)
  • benchmarks/with-onetrust/config.json (1 hunks)
  • benchmarks/with-onetrust/package.json (1 hunks)
  • benchmarks/with-osano/config.json (1 hunks)
  • benchmarks/with-osano/package.json (1 hunks)
  • benchmarks/with-usercentrics/config.json (1 hunks)
  • benchmarks/with-usercentrics/package.json (1 hunks)
  • packages/benchmark-schema/schema.json (7 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/benchmark/src/constants.ts (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/db.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/results.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/save.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/cookiebench-cli/src/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/constants.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/index.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/scoring.ts (26 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/runner/src/index.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/server.ts (1 hunks)
  • packages/shared/README.md (1 hunks)
  • packages/shared/package.json (1 hunks)
  • packages/shared/rslib.config.ts (1 hunks)
  • packages/shared/src/constants.ts (1 hunks)
  • packages/shared/src/index.ts (1 hunks)
  • packages/shared/src/utils/config.ts (1 hunks)
  • packages/shared/src/utils/conversion.ts (1 hunks)
  • packages/shared/src/utils/package-manager.ts (1 hunks)
  • packages/shared/src/utils/time.ts (1 hunks)
  • packages/shared/tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (17)
packages/shared/src/utils/time.ts (2)
packages/shared/src/index.ts (2)
  • formatTime (22-22)
  • ONE_SECOND (8-8)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/shared/src/utils/package-manager.ts (1)
packages/shared/src/index.ts (1)
  • getPackageManager (21-21)
packages/shared/src/utils/config.ts (2)
packages/shared/src/index.ts (2)
  • BaseConfig (13-13)
  • readConfig (13-13)
packages/cookiebench-cli/src/utils/index.ts (1)
  • readConfig (34-36)
packages/cookiebench-cli/src/commands/db.ts (3)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/cookiebench-cli/src/commands/benchmark.ts (7)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/constants.ts (4)
  • DEFAULT_THIRD_PARTY_DOMAINS (4-4)
  • DEFAULT_DOM_SIZE (3-3)
  • DEFAULT_ITERATIONS (2-2)
  • SEPARATOR_WIDTH (5-5)
packages/shared/src/constants.ts (2)
  • PERCENTAGE_DIVISOR (11-11)
  • HALF_SECOND (3-3)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1010-1379)
  • printScores (1383-1422)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (882-1112)
packages/benchmark/src/constants.ts (2)
packages/benchmark/src/index.ts (2)
  • BENCHMARK_CONSTANTS (5-5)
  • BUNDLE_TYPES (5-5)
packages/shared/src/constants.ts (2)
  • ONE_SECOND (2-2)
  • TTI_BUFFER_MS (14-14)
packages/shared/src/constants.ts (2)
packages/cookiebench-cli/src/utils/index.ts (5)
  • ONE_SECOND (22-22)
  • HALF_SECOND (20-20)
  • KILOBYTE (21-21)
  • PERCENTAGE_MULTIPLIER (24-24)
  • PERCENTAGE_DIVISOR (23-23)
packages/shared/src/index.ts (7)
  • ONE_SECOND (8-8)
  • HALF_SECOND (6-6)
  • BYTES_TO_KB (5-5)
  • KILOBYTE (7-7)
  • PERCENTAGE_MULTIPLIER (10-10)
  • PERCENTAGE_DIVISOR (9-9)
  • TTI_BUFFER_MS (11-11)
packages/cookiebench-cli/src/commands/save.ts (6)
packages/cookiebench-cli/src/commands/results.ts (1)
  • RawBenchmarkDetail (37-177)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/shared/src/constants.ts (2)
  • HALF_SECOND (3-3)
  • PERCENTAGE_DIVISOR (11-11)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1010-1379)
packages/cookiebench-cli/src/utils/index.ts (1)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/cookiebench-cli/src/index.ts (9)
packages/cookiebench-cli/src/utils/logger.ts (3)
  • logger (152-152)
  • CliLogger (8-8)
  • createCliLogger (102-149)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/shared/src/constants.ts (1)
  • HALF_SECOND (3-3)
packages/cookiebench-cli/src/components/intro.ts (1)
  • displayIntro (11-93)
packages/cookiebench-cli/src/commands/benchmark.ts (1)
  • benchmarkCommand (317-501)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (882-1112)
packages/cookiebench-cli/src/commands/scores.ts (1)
  • scoresCommand (93-183)
packages/cookiebench-cli/src/commands/save.ts (1)
  • saveCommand (228-391)
packages/cookiebench-cli/src/commands/db.ts (1)
  • dbCommand (46-125)
packages/runner/src/server.ts (3)
packages/runner/src/types.ts (1)
  • ServerInfo (18-21)
packages/shared/src/utils/package-manager.ts (1)
  • getPackageManager (5-37)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/cookiebench-cli/src/commands/results.ts (6)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/cookiebench-cli/src/types/index.ts (2)
  • Config (5-5)
  • BenchmarkScores (10-38)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/constants.ts (3)
  • KILOBYTE (7-7)
  • PERCENTAGE_DIVISOR (11-11)
  • ONE_SECOND (2-2)
packages/cookiebench-cli/src/utils/constants.ts (16)
  • SCORE_THRESHOLD_POOR (10-10)
  • SCORE_THRESHOLD_FAIR (11-11)
  • CLS_DECIMAL_PLACES (7-7)
  • CLS_THRESHOLD_GOOD (14-14)
  • CLS_THRESHOLD_FAIR (15-15)
  • COL_WIDTH_NAME (18-18)
  • COL_WIDTH_CHART_PADDING (24-24)
  • MAX_FILENAME_LENGTH (27-27)
  • TRUNCATED_FILENAME_LENGTH (28-28)
  • MIN_DURATION_THRESHOLD (31-31)
  • COL_WIDTH_TYPE (19-19)
  • COL_WIDTH_SOURCE (20-20)
  • COL_WIDTH_SIZE (21-21)
  • COL_WIDTH_DURATION (22-22)
  • COL_WIDTH_TAGS (23-23)
  • DEFAULT_DOM_SIZE (3-3)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1010-1379)
packages/runner/src/performance-aggregator.ts (3)
packages/benchmark/src/types.ts (7)
  • CoreWebVitals (214-225)
  • CookieBannerData (93-101)
  • CookieBannerMetrics (78-91)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • ResourceTimingData (126-211)
  • PerfumeMetrics (228-259)
packages/runner/src/types.ts (9)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/shared/src/utils/conversion.ts (2)
packages/shared/src/index.ts (7)
  • bytesToKB (16-16)
  • BYTES_TO_KB (5-5)
  • formatBytes (18-18)
  • KILOBYTE (7-7)
  • decimalToPercentage (17-17)
  • PERCENTAGE_MULTIPLIER (10-10)
  • percentageToDecimal (19-19)
packages/shared/src/constants.ts (3)
  • BYTES_TO_KB (6-6)
  • KILOBYTE (7-7)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/cookiebench-cli/src/commands/scores.ts (6)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (179-213)
  • RawBenchmarkDetail (37-177)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (152-152)
  • CliLogger (8-8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/constants.ts (2)
  • HALF_SECOND (3-3)
  • PERCENTAGE_DIVISOR (11-11)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1383-1422)
  • calculateScores (1010-1379)
benchmarks/with-cookie-control/app/layout.tsx (2)
benchmarks/with-iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/with-onetrust/app/layout.tsx (1)
  • metadata (4-6)
packages/cookiebench-cli/src/utils/scoring.ts (3)
packages/shared/src/utils/time.ts (1)
  • formatTime (8-13)
packages/shared/src/utils/conversion.ts (1)
  • formatBytes (17-25)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
🪛 ast-grep (0.39.6)
benchmarks/with-iubenda/app/layout.tsx

[warning] 18-18: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

benchmarks/with-cookie-control/app/layout.tsx

[warning] 22-22: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

benchmarks/with-onetrust/app/layout.tsx

[warning] 23-23: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 LanguageTool
README.md

[grammar] ~7-~7: Ensure spelling is correct
Context: ...rics when loading web applications with diiferent cookie consent solutions. It helps deve...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
packages/shared/README.md

22-22: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


27-27: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


31-31: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


37-37: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


40-40: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


77-77: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


78-78: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


81-81: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


82-82: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


85-85: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


86-86: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


87-87: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🔇 Additional comments (32)
benchmarks/with-cookie-control/app/layout.tsx (2)

1-2: Formatting consistency with double quotes approved.

The quote changes align with the other layout components (iubenda, onetrust) and improve consistency across the benchmark apps.

Also applies to: 5-5


22-23: Security acknowledgment with biome-ignore comments is appropriate.

The hardcoded script configuration is not user-provided or dynamic, so using dangerouslySetInnerHTML here is reasonable and the biome-ignore comments properly document this decision. However, if this script configuration ever becomes dynamic or user-influenced in the future, this should be revisited and the content should be sanitized before injection.

Verify that the injected script content remains static and hardcoded (not user-provided or dynamically generated from external sources).

Also applies to: 51-52

benchmarks/with-onetrust/app/layout.tsx (2)

16-21: LGTM: attribute reordering is cosmetic.

The reordering of async and src attributes has no runtime impact. This appears to be an auto-formatter adjustment.


22-25: LGTM: biome-ignore is appropriate for static content.

The biome-ignore comment is valid here. The content being set ("function OptanonWrapper() {}") is a hardcoded function declaration required by OneTrust and poses no XSS risk. The static analysis warning is a false positive in this context.

packages/shared/tsconfig.json (2)

4-5: Module strategy is consistent across all packages — no compatibility issues.

Verification confirms that all consuming packages (benchmark, runner, cookiebench-cli) have identical module configurations to the shared package: "module": "ESNext" with "moduleResolution": "bundler". No compatibility concerns exist.


1-14: No alignment issues found—configuration is consistent across all packages.

The shared package's TypeScript configuration is properly aligned with packages/benchmark, packages/runner, and packages/cookiebench-cli. All packages consistently use the same module resolution strategy (bundler), output target (ES2020/ESNext), and directory structure (src/dist). The configurations are complementary and compatible across the monorepo.

packages/shared/rslib.config.ts (1)

1-19: No issues found. Configuration is appropriate for the project.

Verification confirms all compatibility concerns are satisfied:

  • ESM-only format: All consuming packages (@consentio/runner, @consentio/benchmark, cookiebench) are ESM-based with "type": "module". No CommonJS consumers detected.
  • ES2021 syntax: Project requires Node.js >=18, which exceeds the ES2021 minimum of 14+.

The configuration is well-suited for this codebase and follows modern best practices.

benchmarks/with-iubenda/app/layout.tsx (1)

18-84: These changes appear unrelated to the PR objectives.

This PR is titled "Refactored cli" and focuses on reorganizing benchmark CLI architecture, but the changes in this file (adding a linter suppression comment and reordering a script attribute) don't appear to relate to CLI refactoring. Were these changes included intentionally, or should they be part of a separate cleanup PR?

benchmarks/with-onetrust/config.json (1)

22-22: Correct typo in bundleType value.

The value has been corrected from "iffe" to "iife" (Immediately Invoked Function Expression). This aligns with the bundle type corrections applied across multiple benchmark configurations.

README.md (1)

96-96: Update bundleType documentation.

The documentation now correctly reflects the bundleType values ("iife", "cjs", "esm") following the standardized bundle type corrections across benchmark configurations.

.gitignore (1)

45-45: No issues found — addition of .pnpm-store is correct and past problematic entries have been resolved.

The verification confirms that:

  • .pnpm-store is appropriately added to ignore pnpm's package store directory
  • The past review's concerns about c15t/cli/ and c15t/logger/ entries have been fully resolved—no such entries exist in the current .gitignore
  • The file is clean and follows standard conventions
benchmarks/with-ketch/config.json (1)

19-19: LGTM! Typo correction aligns with schema and constants.

The bundleType correction from "iffe" to "iife" properly matches the BUNDLE_TYPES.IIFE constant and JSON schema enum update, ensuring consistent bundle type detection across the benchmark suite.

packages/shared/src/utils/config.ts (1)

14-26: LGTM! Synchronous file read is appropriate for CLI config loading.

The generic config reader implementation is well-suited for CLI startup configuration loading. The synchronous readFileSync is acceptable in this context, as config files are typically small and loaded once during application initialization.

packages/benchmark-schema/schema.json (1)

121-121: LGTM! Schema enum corrected to match constants.

The bundleType enum correction from "iffe" to "iife" ensures schema validation aligns with the BUNDLE_TYPES constant definitions and configuration files across the codebase.

packages/shared/src/utils/conversion.ts (1)

8-43: LGTM! Clean conversion utilities with proper constants.

All conversion functions are implemented correctly with appropriate use of imported constants. The formatBytes function follows standard logarithmic scaling for human-readable size formatting.

packages/shared/src/constants.ts (1)

1-14: LGTM! Constants provide clear semantic distinction.

The constants are well-organized and appropriately named. While BYTES_TO_KB and KILOBYTE share the same value (1024), they serve distinct semantic purposes in conversion contexts, improving code readability.

packages/benchmark/src/constants.ts (1)

22-27: LGTM! Previous typo has been corrected.

The BUNDLE_TYPES constant now correctly defines IIFE as "iife". This resolves the critical typo that was flagged in previous review comments and ensures proper bundle type detection throughout the benchmark suite.

benchmarks/with-osano/config.json (1)

16-16: LGTM! Typo correction aligns with schema and constants.

The bundleType correction from "iffe" to "iife" properly matches the BUNDLE_TYPES.IIFE constant and JSON schema enum update, ensuring consistent bundle type detection across the benchmark suite.

benchmarks/with-didomi/package.json (1)

17-23: Verify missing cookiebench devDependency.

This package is missing the cookiebench workspace devDependency that was added to other benchmarks. Confirm this is intentional.

packages/shared/src/utils/time.ts (1)

1-13: LGTM!

The formatTime utility is well-implemented with clear logic, proper JSDoc, and correct formatting for both millisecond and second ranges.

benchmarks/with-didomi/app/layout.tsx (1)

32-34: Good defensive programming.

The null check prevents potential runtime errors if onConsentChanged is called before onDidomiReady sets the didomiObject state.

packages/shared/package.json (1)

1-29: LGTM!

The package manifest follows best practices with proper ESM configuration, TypeScript setup, and standard build tooling.

benchmarks/with-ketch/package.json (1)

16-22: Confirm whether cookiebench devDependency omission is intentional.

Verification confirms that with-ketch (and also with-didomi) lack the direct "cookiebench": "workspace:*" devDependency, while 10 other benchmarks include it. Both packages use the scoped @cookiebench/* packages but not the direct dependency. Clarify whether this omission is intentional or an oversight.

packages/shared/src/index.ts (1)

1-22: LGTM!

The barrel export file is well-organized with clear sections for constants and utilities. The biome ignore directive is appropriate for this use case.

packages/cookiebench-cli/src/utils/index.ts (1)

1-36: LGTM! Duplication eliminated via shared utilities.

This implementation correctly addresses previous code duplication concerns by importing shared utilities from @consentio/shared and providing thin wrappers where CLI-specific behavior is needed. The readConfig function delegates to readConfigShared<Config>, maintaining type safety while reusing the core implementation.

packages/benchmark/package.json (1)

1-34: LGTM! Well-structured package manifest.

The package configuration follows best practices with proper exports mapping, consistent build tooling, and appropriate dependency declarations. The setup aligns with the broader refactoring to extract benchmark functionality into a dedicated package.

packages/runner/package.json (1)

1-34: LGTM! Consistent package structure.

The runner package manifest follows the same well-organized structure as the benchmark package, with proper workspace dependencies and build configuration. The dependency chain (runner → benchmark → shared) is clear and appropriate.

packages/runner/src/index.ts (1)

1-22: LGTM! Clean barrel export structure.

The index provides a well-organized public API surface with clear grouping (runner, aggregation, server, types) and appropriate use of type-only exports. The structure facilitates clean imports for package consumers.

packages/cookiebench-cli/src/utils/constants.ts (1)

1-31: LGTM!

The CLI-specific constants are well-organized and provide clear defaults for formatting, thresholds, and layout. The naming is consistent and self-documenting.

packages/cookiebench-cli/src/utils/scoring.ts (1)

1-1422: Well-structured scoring implementation.

The scoring logic is comprehensive and well-organized with clear helper functions for each category (performance, bundle, network, transparency, UX). The addition of Perfume.js metrics (TTFB, INP) and network information enhances the scoring accuracy. The code properly handles edge cases with fallbacks and validation.

packages/cookiebench-cli/src/commands/benchmark.ts (2)

194-312: Solid benchmark execution flow with proper cleanup.

The runSingleBenchmark function properly handles both local and remote benchmarking scenarios, uses a finally block to ensure server cleanup, and includes comprehensive error handling. The metric calculation and score computation are well-integrated.


317-501: Well-designed multi-benchmark orchestration.

The command properly handles both single-app and multi-select flows, provides clear user feedback with progress indicators, and integrates the results panel seamlessly. The iteration override feature is well-implemented with per-config defaults.

Comment thread benchmarks/baseline/package.json Outdated
Comment thread benchmarks/with-c15t-nextjs/package.json Outdated
Comment thread benchmarks/c15t-react/package.json Outdated
Comment thread benchmarks/with-cookie-control/package.json Outdated
Comment thread benchmarks/with-cookie-yes/package.json Outdated
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment thread packages/shared/README.md
Comment thread packages/shared/README.md
Comment on lines +76 to +87
✅ **@consentio/benchmark** - Fully integrated
- Uses shared constants for time and size conversions
- Imports directly via `@consentio/shared`

✅ **@consentio/runner** - Fully integrated
- Re-exports shared utilities with proper Config typing
- Provides typed wrappers: `formatTime`, `getPackageManager`, `readConfig`

✅ **cookiebench CLI** - Fully integrated
- Re-exports shared utilities and constants via `utils/index.ts`
- Provides typed `readConfig` wrapper for CLI Config type
- All duplicate implementations removed

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown list indentation.

The nested list items under "Integration Status" have incorrect indentation (2 spaces instead of 0), which may cause rendering issues.

Apply this diff to correct the indentation:

 ## Integration Status
 
 ✅ **@consentio/benchmark** - Fully integrated
-  - Uses shared constants for time and size conversions
-  - Imports directly via `@consentio/shared`
+- Uses shared constants for time and size conversions
+- Imports directly via `@consentio/shared`
 
 ✅ **@consentio/runner** - Fully integrated
-  - Re-exports shared utilities with proper Config typing
-  - Provides typed wrappers: `formatTime`, `getPackageManager`, `readConfig`
+- Re-exports shared utilities with proper Config typing
+- Provides typed wrappers: `formatTime`, `getPackageManager`, `readConfig`
 
 ✅ **cookiebench CLI** - Fully integrated  
-  - Re-exports shared utilities and constants via `utils/index.ts`
-  - Provides typed `readConfig` wrapper for CLI Config type
-  - All duplicate implementations removed
+- Re-exports shared utilities and constants via `utils/index.ts`
+- Provides typed `readConfig` wrapper for CLI Config type
+- All duplicate implementations removed
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

77-77: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


78-78: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


81-81: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


82-82: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


85-85: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


86-86: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


87-87: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In packages/shared/README.md around lines 76 to 87, the nested list items under
"Integration Status" are indented with two spaces which breaks markdown
rendering; remove the two-space indentation so each list item begins at the left
margin (no leading spaces) and keep the sub-bullets properly nested under their
parent entries, ensuring consistent use of a single space after the dash and
preserving the existing text.

Comment on lines +1 to +14
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add declaration: true to emit TypeScript declaration files for consumer packages.

As a shared utilities package, emitting .d.ts files enables better type safety and IDE support for consuming packages (benchmark, runner, cookiebench-cli). This is essential for a monorepo where internal packages depend on shared types.

Apply this diff to enable TypeScript declaration emission:

  {
    "compilerOptions": {
      "target": "ES2020",
      "module": "ESNext",
      "moduleResolution": "bundler",
      "esModuleInterop": true,
      "strict": true,
      "skipLibCheck": true,
      "outDir": "dist",
      "rootDir": "src"
+     "declaration": true,
+     "declarationMap": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
  }

Additionally, consider adding sourceMap: true for development debugging.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
🤖 Prompt for AI Agents
In packages/shared/tsconfig.json lines 1-14, the tsconfig is missing the setting
to emit TypeScript declaration files and optional source maps; update
compilerOptions to include "declaration": true to produce .d.ts files for
consumers, and optionally add "sourceMap": true for development debugging (add
both under compilerOptions and keep outDir/rootDir as-is so declarations are
emitted into dist alongside compiled JS).

Comment thread README.md Outdated
…g the first-party hostname from configuration or provided URL. Update performance metrics to handle null values for unmeasured data. Enhance logging utility to handle circular references. Restore '@types/figlet' dependency in package.json files for consistency across projects.
…e" to "engines" for consistency in Node.js version specification.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
packages/cookiebench-cli/package.json (2)

10-10: Shebang configuration still required for CLI executability.

The bin entry points to dist/index.mjs, but rslib/Rspack won't auto-preserve the shebang line needed to make the CLI executable as a command-line tool. This issue was flagged in the previous review and still needs resolution.

Add a postbuild script to ensure the output is executable:

  "scripts": {
    "build": "rslib build",
+   "postbuild": "chmod +x ./dist/index.mjs",
    "check-types": "tsc --noEmit",

Alternatively, configure rslib to inject the shebang via BannerPlugin in rslib.config.ts (not shown here).


22-22: Upgrade @clack/prompts from alpha to stable release.

The dependency is pinned to 1.0.0-alpha.0, but a stable version 0.11.0 is available. Alpha versions risk breaking changes and stability issues in production. This issue was flagged in the previous review.

Update to the stable release:

-   "@clack/prompts": "^1.0.0-alpha.0",
+   "@clack/prompts": "^0.11.0",
packages/cookiebench-cli/src/utils/logger.ts (2)

8-8: Unused export remains: remove validLogLevels or add usage.

This was flagged in a previous review and remains unresolved. The validLogLevels constant is exported but not consumed by any module in the codebase.

Based on learnings.


132-134: Unresolved: message method still drops variadic args.

This issue was flagged in the previous review and remains unfixed. The CliExtensions type at line 13 declares message: (message: string, ...args: unknown[]) => void, but the implementation only accepts (message: string) and drops any additional arguments, causing silent data loss.

Apply the suggested fix from the previous review:

-	extendedLogger.message = (message: string) => {
-		log.message(message);
+	extendedLogger.message = (message: string, ...args: unknown[]) => {
+		if (args.length === 0) {
+			log.message(message);
+		} else {
+			log.message(`${message}${formatArgs(args)}`);
+		}
 	};

Based on learnings.

packages/runner/src/performance-aggregator.ts (1)

228-266: Average Perfume metrics over actual samples

Right now we coerce missing FID/INP readings to 0 and divide by results.length, so a series like [120, null, 80] reports 66.7 instead of the correct 100. Also, firstPaint || null drops real 0 values. Use avgDefined with ?? to average over the defined samples and keep zeros intact.

-		const firstPaintValues = results.map((r) => r.timing.firstPaint || null);
-		const cookieBannerFirstPaint = avgDefined(firstPaintValues);
+		const firstPaintValues = results.map((r) => r.timing.firstPaint ?? null);
+		const cookieBannerFirstPaint = avgDefined(firstPaintValues);
+		const firstInputDelayAvg = avgDefined(
+			results.map((curr) => curr.timing.firstInputDelay ?? null)
+		);
+		const interactionToNextPaintAvg = avgDefined(
+			results.map((curr) => curr.timing.interactionToNextPaint ?? null)
+		);
@@
-			firstInputDelay:
-				results.reduce(
-					(acc, curr) => acc + (curr.timing.firstInputDelay || 0),
-					0
-				) / results.length,
-			interactionToNextPaint:
-				results.reduce(
-					(acc, curr) => acc + (curr.timing.interactionToNextPaint || 0),
-					0
-				) / results.length,
+			firstInputDelay: firstInputDelayAvg ?? 0,
+			interactionToNextPaint: interactionToNextPaintAvg ?? 0,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5b9ee80 and c79d556.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • packages/benchmark/src/network-monitor.ts (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/src/utils/logger.ts (1 hunks)
  • packages/runner/src/benchmark-runner.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/server.ts (1 hunks)
  • packages/runner/src/types.ts (1 hunks)
  • packages/shared/src/utils/package-manager.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
packages/benchmark/src/network-monitor.ts (2)
packages/benchmark/src/types.ts (3)
  • Config (13-56)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (8-20)
packages/shared/src/utils/package-manager.ts (1)
packages/shared/src/index.ts (1)
  • getPackageManager (21-21)
packages/runner/src/server.ts (3)
packages/runner/src/types.ts (1)
  • ServerInfo (18-21)
packages/shared/src/utils/package-manager.ts (1)
  • getPackageManager (5-38)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/runner/src/performance-aggregator.ts (3)
packages/runner/src/types.ts (10)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/types.ts (8)
  • CoreWebVitals (214-225)
  • CookieBannerData (93-101)
  • CookieBannerMetrics (78-91)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • ResourceTimingData (126-211)
  • Config (13-56)
  • PerfumeMetrics (228-259)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/runner/src/benchmark-runner.ts (8)
packages/runner/src/types.ts (3)
  • Config (6-6)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/benchmark/src/cookie-banner-collector.ts (1)
  • CookieBannerCollector (13-242)
packages/benchmark/src/network-monitor.ts (1)
  • NetworkMonitor (6-135)
packages/benchmark/src/resource-timing-collector.ts (1)
  • ResourceTimingCollector (6-169)
packages/benchmark/src/perfume-collector.ts (1)
  • PerfumeCollector (6-142)
packages/runner/src/performance-aggregator.ts (1)
  • PerformanceAggregator (26-382)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (8-20)
packages/runner/src/types.ts (2)
packages/runner/src/index.ts (3)
  • ServerInfo (21-21)
  • BenchmarkDetails (10-10)
  • BenchmarkResult (11-11)
packages/cookiebench-cli/src/types/index.ts (3)
  • ServerInfo (6-6)
  • BenchmarkDetails (3-3)
  • BenchmarkResult (4-4)
🔇 Additional comments (10)
packages/cookiebench-cli/package.json (3)

26-26: Verify cli-table3 downgrade compatibility.

The version was downgraded from ^0.6.5 to ^0.6.3. Confirm this change is intentional and that no features or fixes from 0.6.4–0.6.5 are required by the CLI.


33-33: Good: @types/figlet moved to devDependencies.

Type stubs belong in devDependencies since they're only needed during development and build time. This fixes the earlier trivial issue.


23-25: Workspace packages and CLI utilities look appropriate.

The addition of workspace dependencies (@consentio/benchmark, @consentio/runner, @consentio/shared) and CLI formatting utilities (figlet, picocolors, pretty-ms) align well with the CLI refactoring goals described in the PR.

Also applies to: 28-30

packages/cookiebench-cli/src/utils/logger.ts (1)

20-34: LGTM: Circular reference handling implemented.

The formatArgs function now properly handles circular references and serialization errors with a try/catch block and util.inspect fallback, addressing the issue raised in the previous review.

packages/runner/src/types.ts (6)

1-1: LGTM!

The import of ChildProcess is correctly used in the ServerInfo type definition.


3-15: LGTM!

Re-exporting types from the benchmark package is a good pattern for providing a unified API surface.


17-21: LGTM!

The ServerInfo type is straightforward and correctly typed for managing server processes.


158-271: LGTM!

The BenchmarkResult type structure is comprehensive and well-designed, with appropriate handling of optional and nullable fields. The scores object provides detailed performance analysis with clear categorization.


273-291: Consider whether internal types should be exported.

The EnhancedCookieBannerTiming and ThirdPartyMetrics types are used in the exported BenchmarkDetails type but are not themselves exported. While this works, it may limit external code that needs to construct or manipulate these nested structures independently.

If these are strictly internal implementation details, the current approach is fine. Otherwise, consider exporting them for better composability.


24-156: Confirm the duplication is real and document the architectural intent.

Your observation is correct. Verification reveals:

  1. thirdParty fields are identically duplicated:

    • timing.thirdParty and root-level thirdParty both receive the same object from buildThirdPartyMetrics() with identical structure (lines 160–164 vs. 180–189)
  2. cookieBanner fields represent different abstractions:

    • timing.cookieBanner contains 10 fields (detailed: renderStart, renderEnd, interactionStart, interactionEnd, layoutShift, detected, selector, serviceName, visibilityTime, viewportCoverage)
    • Root-level cookieBanner contains 5 fields (summary: detected, selector, serviceName, visibilityTime, viewportCoverage)
    • Both derive from the same source but serve different purposes

The codebase uses both representations inconsistently—some modules access timing.cookieBanner, others access root-level cookieBanner; similarly for thirdParty. This creates maintenance risk and API confusion.

Recommended resolution:

  • For thirdParty: Consolidate into a single field (likely at root-level, since it's already used there)
  • For cookieBanner: Either document the summary/detailed distinction with JSDoc or consolidate if the subset is unnecessary
  • Update all consumers to use a consistent field path to avoid confusion

Comment thread packages/runner/src/performance-aggregator.ts Outdated
baseline: boolean;
techStack: {
bundler: string;
bundleType: string | string[];

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider normalizing bundleType to always use an array.

The bundleType: string | string[] union requires consumers to handle both single values and arrays. For consistency and simpler usage, consider always using string[].

Apply this diff to normalize the type:

-		bundleType: string | string[];
+		bundleType: string[];

If a single bundle type is common, consumers can still provide a single-element array: bundleType: ["esm"].

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bundleType: string | string[];
bundleType: string[];
🤖 Prompt for AI Agents
In packages/runner/src/types.ts around line 163, the union type bundleType:
string | string[] should be normalized to bundleType: string[]; update the type
declaration to string[] and then update all call sites, default values and
destructurings across the codebase to expect an array (wrap single string usages
into single-element arrays, update tests and any runtime validation), and ensure
exported typings and any docs are updated accordingly.

…erformance metrics collection. Removed redundant network idle wait and context creation, ensuring a more efficient benchmarking process.
…ance metrics handling and streamline monitoring setup. Update types to ensure consistent data types and default values for unmeasured metrics. Introduce utility functions for better configuration management and package manager detection.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
packages/runner/src/server.ts (1)

55-71: Kill the server process before throwing on startup failure.

When the health check exhausts all retries at line 70, the error is thrown without terminating the spawned serverProcess, leaving it running and leaking the port. This issue was previously flagged but remains unresolved in the current code.

Apply this fix:

 	while (retries < maxRetries) {
 		try {
 			const response = await fetch(url);
 			if (response.ok) {
 				logger.success("Server is ready!");
 				return { serverProcess, url };
 			}
 		} catch {
 			// Ignore error and retry
 		}
 
 		await new Promise((resolve) => setTimeout(resolve, ONE_SECOND));
 		retries += 1;
 	}
 
+	// Kill the spawned process before throwing
+	if (!serverProcess.killed) {
+		serverProcess.kill();
+	}
 	throw new Error("Server failed to start");
 }
packages/runner/src/benchmark-runner.ts (1)

52-59: Pass target hostname to network monitor for third-party detection.

The NetworkMonitor.setupMonitoring call at line 53 does not receive the page's hostname, which is required to classify requests as first-party vs third-party. Without this, the third-party metrics (line 96-97) will not populate correctly, breaking the headline benchmarking functionality. This critical issue was flagged in previous reviews and remains unresolved.

Apply this fix:

 	// Initialize collectors
 	const collector = new PerformanceMetricsCollector();
 	const cookieBannerMetrics = this.cookieBannerCollector.initializeMetrics();
 
 	// Setup monitoring and detection
-	await this.networkMonitor.setupMonitoring(page);
+	await this.networkMonitor.setupMonitoring(page, url);
 	await this.cookieBannerCollector.setupDetection(page);
 	await this.perfumeCollector.setupPerfume(page);
 
 	// Navigate to the page
 	this.logger.debug(`Navigating to: ${url}`);
 	await page.goto(url, { waitUntil: "networkidle" });
packages/runner/src/performance-aggregator.ts (3)

107-111: Remove redundant fallback in division.

Line 109 uses (totalBlockingTime || 1) as a fallback even though line 108 already ensures totalBlockingTime > 0 via the ternary guard. The || 1 is redundant. This issue was flagged in previous reviews but remains unresolved.

Apply this diff:

 	const percentageFromCookies =
 		totalBlockingTime > 0
-			? (cookieBannerEstimate / (totalBlockingTime || 1)) *
+			? (cookieBannerEstimate / totalBlockingTime) *
 				PERCENTAGE_MULTIPLIER
 			: 0;

150-162: Use nullish coalescing to preserve legitimate zero values.

Lines 150-152 use logical OR (||) for fallbacks, which incorrectly treats valid zero-valued metrics (e.g., timeToFirstByte: 0, firstInputDelay: 0) as falsy and replaces them with defaults. This makes zero measurements indistinguishable from missing data. Line 153 has the same issue with navigationTiming. Use nullish coalescing (??) instead. This issue was flagged in previous reviews but remains unresolved.

Apply this fix:

-			timeToFirstByte: perfumeMetrics?.timeToFirstByte || 0,
-			firstInputDelay: perfumeMetrics?.firstInputDelay || null,
-			interactionToNextPaint: perfumeMetrics?.interactionToNextPaint || null,
-			navigationTiming: perfumeMetrics?.navigationTiming || {
+			timeToFirstByte: perfumeMetrics?.timeToFirstByte ?? 0,
+			firstInputDelay: perfumeMetrics?.firstInputDelay ?? null,
+			interactionToNextPaint: perfumeMetrics?.interactionToNextPaint ?? null,
+			navigationTiming: perfumeMetrics?.navigationTiming ?? {
 				timeToFirstByte: 0,
 				domInteractive: 0,
 				domContentLoadedEventStart: 0,
 				domContentLoadedEventEnd: 0,
 				domComplete: 0,
 				loadEventStart: 0,
 				loadEventEnd: 0,
 			},
-			networkInformation: perfumeMetrics?.networkInformation || undefined,
+			networkInformation: perfumeMetrics?.networkInformation ?? undefined,

241-323: Distinguish unmeasured metrics from zero measurements.

Multiple metrics default to 0 when not calculated:

  • Line 241: speedIndex: 0
  • Line 262: domSize: 0
  • Lines 292-311: Various third-party counters
  • Line 323: cookieBannerTiming.firstPaint: 0

Defaulting to 0 conflates "not measured" with "measured as zero," which can mislead consumers of this data. Consider using null for unimplemented metrics or computing actual averages where data exists (e.g., firstPaint from results[].timing.firstPaint). This issue was flagged in previous reviews but remains unresolved.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a12ccfb and 224646c.

📒 Files selected for processing (7)
  • packages/runner/package.json (1 hunks)
  • packages/runner/src/benchmark-runner.ts (1 hunks)
  • packages/runner/src/index.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/server.ts (1 hunks)
  • packages/runner/src/types.ts (1 hunks)
  • packages/runner/src/utils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
packages/runner/src/server.ts (2)
packages/runner/src/types.ts (1)
  • ServerInfo (18-21)
packages/runner/src/utils.ts (1)
  • getPackageManager (25-57)
packages/runner/src/performance-aggregator.ts (3)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/runner/src/types.ts (10)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/types.ts (8)
  • CoreWebVitals (214-225)
  • CookieBannerData (93-101)
  • CookieBannerMetrics (78-91)
  • NetworkRequest (104-111)
  • NetworkMetrics (113-116)
  • ResourceTimingData (126-211)
  • Config (13-56)
  • PerfumeMetrics (228-259)
packages/runner/src/utils.ts (2)
packages/cookiebench-cli/src/utils/index.ts (1)
  • readConfig (34-36)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/runner/src/benchmark-runner.ts (8)
packages/runner/src/types.ts (3)
  • Config (6-6)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/benchmark/src/cookie-banner-collector.ts (1)
  • CookieBannerCollector (13-242)
packages/benchmark/src/network-monitor.ts (1)
  • NetworkMonitor (6-135)
packages/benchmark/src/resource-timing-collector.ts (1)
  • ResourceTimingCollector (6-169)
packages/benchmark/src/perfume-collector.ts (1)
  • PerfumeCollector (6-142)
packages/runner/src/performance-aggregator.ts (1)
  • PerformanceAggregator (29-372)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (8-20)
packages/runner/src/types.ts (2)
packages/runner/src/index.ts (3)
  • ServerInfo (21-21)
  • BenchmarkDetails (10-10)
  • BenchmarkResult (11-11)
packages/cookiebench-cli/src/types/index.ts (3)
  • ServerInfo (6-6)
  • BenchmarkDetails (3-3)
  • BenchmarkResult (4-4)
🔇 Additional comments (2)
packages/runner/package.json (1)

1-33: LGTM!

The package manifest is well-structured with appropriate module configuration, exports map, and dependencies for the runner orchestration layer.

packages/runner/src/index.ts (1)

1-24: LGTM!

Clean barrel file providing a well-organized public API surface for the runner package.

Comment thread packages/runner/src/performance-aggregator.ts Outdated
Comment on lines +25 to +57
export async function getPackageManager(): Promise<{
command: string;
args: string[];
}> {
try {
const { execSync } = await import("node:child_process");
const output = execSync("npm -v", { encoding: "utf-8" });
if (output) {
return { command: "npm", args: ["run"] };
}
} catch {
try {
const { execSync } = await import("node:child_process");
const output = execSync("yarn -v", { encoding: "utf-8" });
if (output) {
return { command: "yarn", args: [] };
}
} catch {
try {
const { execSync } = await import("node:child_process");
const output = execSync("pnpm -v", { encoding: "utf-8" });
if (output) {
return { command: "pnpm", args: [] };
}
} catch {
// Default to npm if no package manager is found
return { command: "npm", args: ["run"] };
}
}
}
// Default to npm if no package manager is found
return { command: "npm", args: ["run"] };
}

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Optimize package manager detection.

The function has several issues:

  1. Redundant dynamic imports: Lines 30, 37, and 44 each dynamically import child_process separately, which is inefficient.
  2. Unreachable code: Lines 55-56 return a default value, but this code is unreachable because lines 50-51 already return the same default in the innermost catch block.
  3. Silent error swallowing: Empty catch blocks at lines 35, 42, and 49 hide failures without logging.

Apply this refactor to import once and remove dead code:

 export async function getPackageManager(): Promise<{
 	command: string;
 	args: string[];
 }> {
+	const { execSync } = await import("node:child_process");
+	
 	try {
-		const { execSync } = await import("node:child_process");
 		const output = execSync("npm -v", { encoding: "utf-8" });
 		if (output) {
 			return { command: "npm", args: ["run"] };
 		}
 	} catch {
 		try {
-			const { execSync } = await import("node:child_process");
 			const output = execSync("yarn -v", { encoding: "utf-8" });
 			if (output) {
 				return { command: "yarn", args: [] };
 			}
 		} catch {
 			try {
-				const { execSync } = await import("node:child_process");
 				const output = execSync("pnpm -v", { encoding: "utf-8" });
 				if (output) {
 					return { command: "pnpm", args: [] };
 				}
 			} catch {
-				// Default to npm if no package manager is found
-				return { command: "npm", args: ["run"] };
+				// All checks failed, fall through to default
 			}
 		}
 	}
 	// Default to npm if no package manager is found
 	return { command: "npm", args: ["run"] };
 }
🤖 Prompt for AI Agents
In packages/runner/src/utils.ts around lines 25-57, replace the repeated dynamic
imports and nested try/catches by importing child_process.execSync once at the
start of the function, then sequentially attempt "npm -v", "yarn -v", and "pnpm
-v" each in its own small try/catch that does not swallow errors (log them with
console.debug or console.error), remove the redundant return in the innermost
catch and keep a single default return at the end of the function; this reduces
repeated imports, eliminates unreachable code, and surfaces failures instead of
silently swallowing them.

…ity files to improve code organization and maintainability. Add new lines for better readability and consistency across the project.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
packages/shared/tsconfig.json (1)

2-11: Add declaration: true and declarationMap: true to emit TypeScript declaration files.

This issue was flagged in the previous review but remains unresolved. As a shared utilities package in a monorepo, declaration files are essential for type safety and IDE support in consuming packages (benchmark, runner, cookiebench-cli).

Apply this diff to enable TypeScript declaration emission:

  {
    "compilerOptions": {
      "target": "ES2020",
      "module": "ESNext",
      "moduleResolution": "bundler",
      "esModuleInterop": true,
      "strict": true,
      "skipLibCheck": true,
      "outDir": "dist",
      "rootDir": "src",
+     "declaration": true,
+     "declarationMap": true,
+     "sourceMap": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
  }

This ensures .d.ts files and source maps are emitted to the dist directory alongside compiled JavaScript, enabling proper type resolution across the monorepo.

packages/shared/src/utils/package-manager.ts (1)

12-37: Apply the suggested refactoring to flatten nested try-catch blocks and eliminate repeated imports.

The current code contains deeply nested error handling (3 levels) that imports execSync three separate times and performs redundant output checks. The suggested refactoring uses a single import with loop-based iteration to improve readability, performance, and maintainability:

 export async function getPackageManager(): Promise<
 	| {
 			command: string;
 			args: string[];
 	  }
 	| undefined
 > {
+	const { execSync } = await import("node:child_process");
+	const managers = [
+		{ name: "npm", command: "npm", args: ["run"] },
+		{ name: "yarn", command: "yarn", args: [] },
+		{ name: "pnpm", command: "pnpm", args: [] },
+	];
+
+	for (const manager of managers) {
+		try {
+			execSync(`${manager.name} -v`, { encoding: "utf-8" });
+			return { command: manager.command, args: manager.args };
+		} catch {
+			// Try next manager
+		}
+	}
+
+	// Default to npm if no package manager is found
+	return { command: "npm", args: ["run"] };
-	try {
-		const { execSync } = await import("node:child_process");
-		const output = execSync("npm -v", { encoding: "utf-8" });
-		if (output) {
-			return { command: "npm", args: ["run"] };
-		}
-	} catch {
-		try {
-			const { execSync } = await import("node:child_process");
-			const output = execSync("yarn -v", { encoding: "utf-8" });
-			if (output) {
-				return { command: "yarn", args: [] };
-			}
-		} catch {
-			try {
-				const { execSync } = await import("node:child_process");
-				const output = execSync("pnpm -v", { encoding: "utf-8" });
-				if (output) {
-					return { command: "pnpm", args: [] };
-				}
-			} catch {
-				// Default to npm if no package manager is found
-				return { command: "npm", args: ["run"] };
-			}
-		}
-	}
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 224646c and 9542204.

📒 Files selected for processing (7)
  • packages/shared/package.json (1 hunks)
  • packages/shared/rslib.config.ts (1 hunks)
  • packages/shared/src/constants.ts (1 hunks)
  • packages/shared/src/utils/config.ts (1 hunks)
  • packages/shared/src/utils/package-manager.ts (1 hunks)
  • packages/shared/src/utils/time.ts (1 hunks)
  • packages/shared/tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/shared/src/utils/config.ts (3)
packages/shared/src/index.ts (2)
  • BaseConfig (13-13)
  • readConfig (13-13)
packages/runner/src/index.ts (1)
  • readConfig (24-24)
packages/cookiebench-cli/src/utils/index.ts (1)
  • readConfig (34-36)
packages/shared/src/utils/time.ts (4)
packages/runner/src/index.ts (1)
  • formatTime (24-24)
packages/shared/src/index.ts (2)
  • formatTime (22-22)
  • ONE_SECOND (8-8)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/cookiebench-cli/src/utils/index.ts (1)
  • ONE_SECOND (22-22)
packages/shared/src/constants.ts (2)
packages/cookiebench-cli/src/utils/index.ts (5)
  • ONE_SECOND (22-22)
  • HALF_SECOND (20-20)
  • KILOBYTE (21-21)
  • PERCENTAGE_MULTIPLIER (24-24)
  • PERCENTAGE_DIVISOR (23-23)
packages/shared/src/index.ts (7)
  • ONE_SECOND (8-8)
  • HALF_SECOND (6-6)
  • BYTES_TO_KB (5-5)
  • KILOBYTE (7-7)
  • PERCENTAGE_MULTIPLIER (10-10)
  • PERCENTAGE_DIVISOR (9-9)
  • TTI_BUFFER_MS (11-11)
packages/shared/src/utils/package-manager.ts (2)
packages/runner/src/index.ts (1)
  • getPackageManager (24-24)
packages/shared/src/index.ts (1)
  • getPackageManager (21-21)
🔇 Additional comments (4)
packages/shared/rslib.config.ts (1)

1-19: No issues found. Configuration is compatible with monorepo Node.js requirements.

The rslib configuration is appropriate for the shared package. Verification confirms all packages in the monorepo require Node.js >=18 (or higher), and ES2021 syntax has full support in Node.js 16+, so there are no compatibility concerns. The ESM-only format, TypeScript declarations, and Node target are well-suited for a modern shared package.

packages/shared/src/utils/time.ts (1)

1-13: LGTM! Clean and well-documented time formatting utility.

The implementation correctly formats milliseconds with no decimals and seconds with two decimals, using the shared constant appropriately.

packages/shared/src/utils/config.ts (1)

1-26: LGTM! Appropriate generic config reader.

The synchronous file read is acceptable for config files, and the null-returning error handling is clearly documented. The generic type parameter provides flexibility for consumers to specify their own config shapes.

packages/shared/package.json (1)

1-29: Package manifest structure looks good.

The exports, scripts, and devDependencies are properly configured for an ESM TypeScript library with workspace-based development dependencies.

Comment thread packages/shared/package.json
Comment on lines +6 to +7
export const BYTES_TO_KB = 1024;
export const KILOBYTE = 1024;

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider consolidating duplicate constants.

BYTES_TO_KB and KILOBYTE both have the value 1024. Having two names for the same constant can be confusing. Consider using a single well-named constant unless these are intended to represent semantically different concepts.

If they're meant to be the same, consolidate to one:

 // Size constants (bytes to kilobytes)
-export const BYTES_TO_KB = 1024;
-export const KILOBYTE = 1024;
+export const KILOBYTE = 1024;
+export const BYTES_TO_KB = KILOBYTE; // Alias for clarity in conversion contexts
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const BYTES_TO_KB = 1024;
export const KILOBYTE = 1024;
export const KILOBYTE = 1024;
export const BYTES_TO_KB = KILOBYTE; // Alias for clarity in conversion contexts
🤖 Prompt for AI Agents
In packages/shared/src/constants.ts around lines 6 to 7, there are duplicate
constants BYTES_TO_KB and KILOBYTE both set to 1024; consolidate them by keeping
a single well-named constant (e.g., KILOBYTE or BYTES_TO_KB) and replace all
usages of the removed name across the codebase with the chosen name, then remove
the redundant declaration and run tests/lint to ensure no references remain.

Comment on lines +10 to +11
export const PERCENTAGE_MULTIPLIER = 100;
export const PERCENTAGE_DIVISOR = 100;

ghost Oct 30, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Evaluate the need for identical percentage constants.

PERCENTAGE_MULTIPLIER and PERCENTAGE_DIVISOR both equal 100. If multiplication and division operations conceptually differ in your domain, document why they share the same value. Otherwise, a single constant would reduce confusion.

🤖 Prompt for AI Agents
In packages/shared/src/constants.ts around lines 10 to 11, PERCENTAGE_MULTIPLIER
and PERCENTAGE_DIVISOR are both set to 100 which is confusing; either collapse
them into a single exported constant (e.g., PERCENTAGE_SCALE or PERCENTAGE_BASE
= 100) and update all imports/usages to that single name, or keep both but add a
short comment above the declarations explaining the conceptual difference and
why they share the same value; ensure you update any tests/consumers to use the
new name or keep backward-compatible re-exports if removing one constant.

…n opacity, improving UX metrics. Update types to include new visibility metrics and refine benchmark runner with robust error handling and retry logic. Introduce statistical utilities for performance analysis, ensuring better data handling and stability checks in performance aggregation.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (7)
packages/cookiebench-cli/src/commands/results.ts (1)

1050-1055: Fix third-party request counting to include all resource types.

The previous review comment on this code segment is still valid. thirdPartyRequests currently only counts script resources, ignoring third-party styles, images, fonts, and "other" assets. This underreports the network impact and leads to incorrect scores.

Apply the diff from the previous review:

-				thirdPartyRequests:
-					appResults.reduce(
-						(a, b) =>
-							a + b.resources.scripts.filter((s) => s.isThirdParty).length,
-						0
-					) / appResults.length,
+				thirdPartyRequests:
+					appResults.reduce(
+						(a, b) =>
+							a +
+							b.resources.scripts.filter((r) => r.isThirdParty).length +
+							b.resources.styles.filter((r) => r.isThirdParty).length +
+							b.resources.images.filter((r) => r.isThirdParty).length +
+							b.resources.fonts.filter((r) => r.isThirdParty).length +
+							b.resources.other.filter((r) => r.isThirdParty).length,
+						0
+					) / appResults.length,
packages/cookiebench-cli/src/commands/scores.ts (1)

211-318: Guard against empty benchmark data before averaging.

When result.results is [] (e.g., aborted run wrote an empty array), every reduction below divides by 0, producing NaN scores and breaking the CLI output. Bail out before calling calculateScores so we never average an empty set.

 	logger.debug("Calculating scores from raw benchmark data");
 
 	const appResults = result.results;
+	if (!Array.isArray(appResults) || appResults.length === 0) {
+		logger.error(`No iterations recorded for ${appName}; cannot compute scores.`);
+		return;
+	}
 	const config = await loadConfigForApp(logger, appName);
packages/runner/src/benchmark-runner.ts (1)

115-117: Pass the benchmark URL into NetworkMonitor.

Right now setupMonitoring only receives the Page, so inside NetworkMonitor the first-party hostname defaults to page.url(), which is still about:blank before navigation. Every request is therefore marked third-party and the aggregated third-party metrics in PerformanceAggregator are garbage. Hand the actual benchmark URL through so classification stays correct.

Apply this diff:

-		await this.networkMonitor.setupMonitoring(page);
+		await this.networkMonitor.setupMonitoring(page, url);
packages/runner/src/performance-aggregator.ts (4)

21-22: Constants still duplicated from shared package.

Despite the previous review marking this as addressed, TTI_BUFFER_MS and PERCENTAGE_MULTIPLIER remain defined here but already exist in packages/shared/src/constants.ts.

Based on relevant snippets.


118-122: Redundant fallback still present.

Line 120 uses (totalBlockingTime || 1) but the ternary on line 119 already ensures division only occurs when totalBlockingTime > 0, making the || 1 fallback unnecessary.


162-163: Zero-value metrics still incorrectly converted to null.

Using || null converts legitimate 0ms FID/INP values to null. A First Input Delay or Interaction to Next Paint of 0ms is a valid measurement indicating no delay, not missing data.


290-347: Unimplemented metrics still default to zero.

Multiple metrics default to 0 when not tracked:

  • Line 290: speedIndex
  • Line 301: domSize
  • Lines 309-328: All third-party detail metrics
  • Lines 329-338: thirdPartyTiming sub-fields
  • Line 340: cookieBannerTiming.firstPaint

Defaulting to 0 conflates "not measured" with an actual zero value. While pragmatic for unimplemented features, consider using null or explicitly marking these as unavailable to distinguish missing data from zero measurements.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9542204 and dff5e2c.

📒 Files selected for processing (9)
  • packages/benchmark/src/cookie-banner-collector.ts (1 hunks)
  • packages/benchmark/src/types.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/results.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/runner/src/benchmark-runner.ts (1 hunks)
  • packages/runner/src/index.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/statistics.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
packages/benchmark/src/types.ts (4)
packages/runner/src/index.ts (9)
  • CookieBannerConfig (14-14)
  • Config (13-13)
  • CookieBannerMetrics (16-16)
  • CookieBannerData (15-15)
  • NetworkRequest (19-19)
  • NetworkMetrics (18-18)
  • BundleStrategy (12-12)
  • ResourceTimingData (20-20)
  • CoreWebVitals (17-17)
packages/runner/src/types.ts (10)
  • CookieBannerConfig (7-7)
  • Config (6-6)
  • CookieBannerMetrics (9-9)
  • CookieBannerData (8-8)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • BundleStrategy (5-5)
  • ResourceTimingData (14-14)
  • CoreWebVitals (10-10)
  • PerfumeMetrics (13-13)
packages/benchmark/src/index.ts (13)
  • CookieBannerConfig (15-15)
  • Config (14-14)
  • LayoutShiftEntry (19-19)
  • WindowWithCookieMetrics (24-24)
  • CookieBannerMetrics (17-17)
  • CookieBannerData (16-16)
  • NetworkRequest (21-21)
  • NetworkMetrics (20-20)
  • BundleStrategy (13-13)
  • ResourceTimingData (23-23)
  • CoreWebVitals (18-18)
  • PerfumeMetrics (22-22)
  • WindowWithPerfumeMetrics (25-25)
packages/cookiebench-cli/src/types/index.ts (1)
  • Config (5-5)
packages/runner/src/statistics.ts (1)
packages/runner/src/index.ts (4)
  • calculateStatistics (26-26)
  • calculateTrimmedMean (27-27)
  • calculateCoefficientOfVariation (25-25)
  • isStable (28-28)
packages/cookiebench-cli/src/commands/scores.ts (6)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (179-213)
  • RawBenchmarkDetail (37-177)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (162-162)
  • CliLogger (9-9)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/constants.ts (2)
  • HALF_SECOND (3-3)
  • PERCENTAGE_DIVISOR (11-11)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1383-1422)
  • calculateScores (1010-1379)
packages/benchmark/src/cookie-banner-collector.ts (3)
packages/benchmark/src/types.ts (5)
  • Config (13-56)
  • CookieBannerMetrics (79-92)
  • WindowWithCookieMetrics (65-77)
  • LayoutShiftEntry (59-62)
  • CookieBannerData (94-103)
packages/benchmark/src/bundle-strategy.ts (1)
  • determineBundleStrategy (4-21)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (8-20)
packages/cookiebench-cli/src/commands/benchmark.ts (10)
packages/runner/src/index.ts (6)
  • BenchmarkResult (11-11)
  • readConfig (31-31)
  • ServerInfo (21-21)
  • buildAndServeNextApp (7-7)
  • BenchmarkRunner (3-3)
  • cleanupServer (7-7)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/constants.ts (4)
  • DEFAULT_THIRD_PARTY_DOMAINS (4-4)
  • DEFAULT_DOM_SIZE (3-3)
  • DEFAULT_ITERATIONS (2-2)
  • SEPARATOR_WIDTH (5-5)
packages/shared/src/constants.ts (2)
  • PERCENTAGE_DIVISOR (11-11)
  • HALF_SECOND (3-3)
packages/cookiebench-cli/src/utils/index.ts (3)
  • PERCENTAGE_DIVISOR (23-23)
  • readConfig (34-36)
  • HALF_SECOND (20-20)
packages/runner/src/benchmark-runner.ts (2)
  • runSingleBenchmark (91-194)
  • BenchmarkRunner (25-432)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1010-1379)
  • printScores (1383-1422)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (890-1120)
packages/runner/src/benchmark-runner.ts (8)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/runner/src/types.ts (3)
  • Config (6-6)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/benchmark/src/cookie-banner-collector.ts (1)
  • CookieBannerCollector (17-310)
packages/benchmark/src/network-monitor.ts (1)
  • NetworkMonitor (6-135)
packages/benchmark/src/resource-timing-collector.ts (1)
  • ResourceTimingCollector (6-169)
packages/benchmark/src/perfume-collector.ts (1)
  • PerfumeCollector (6-142)
packages/runner/src/performance-aggregator.ts (1)
  • PerformanceAggregator (38-446)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (8-20)
packages/cookiebench-cli/src/commands/results.ts (6)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (162-162)
  • CliLogger (9-9)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/cookiebench-cli/src/types/index.ts (2)
  • Config (5-5)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/constants.ts (16)
  • SCORE_THRESHOLD_POOR (10-10)
  • SCORE_THRESHOLD_FAIR (11-11)
  • CLS_DECIMAL_PLACES (7-7)
  • CLS_THRESHOLD_GOOD (14-14)
  • CLS_THRESHOLD_FAIR (15-15)
  • COL_WIDTH_NAME (18-18)
  • COL_WIDTH_CHART_PADDING (24-24)
  • MAX_FILENAME_LENGTH (27-27)
  • TRUNCATED_FILENAME_LENGTH (28-28)
  • MIN_DURATION_THRESHOLD (31-31)
  • COL_WIDTH_TYPE (19-19)
  • COL_WIDTH_SOURCE (20-20)
  • COL_WIDTH_SIZE (21-21)
  • COL_WIDTH_DURATION (22-22)
  • COL_WIDTH_TAGS (23-23)
  • DEFAULT_DOM_SIZE (3-3)
packages/cookiebench-cli/src/utils/scoring.ts (1)
  • calculateScores (1010-1379)
packages/cookiebench-cli/src/utils/auth.ts (1)
  • isAdminUser (5-11)
packages/runner/src/performance-aggregator.ts (4)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/benchmark/src/types.ts (8)
  • CoreWebVitals (216-227)
  • CookieBannerData (94-103)
  • CookieBannerMetrics (79-92)
  • NetworkRequest (106-113)
  • NetworkMetrics (115-118)
  • ResourceTimingData (128-213)
  • Config (13-56)
  • PerfumeMetrics (230-261)
packages/runner/src/types.ts (10)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/runner/src/statistics.ts (4)
  • isStable (132-138)
  • calculateCoefficientOfVariation (112-127)
  • calculateTrimmedMean (84-107)
  • calculateStatistics (11-52)
🔇 Additional comments (6)
packages/cookiebench-cli/src/commands/results.ts (2)

36-213: Well-structured type definitions.

The RawBenchmarkDetail and BenchmarkOutput types are comprehensive and properly model the benchmark data structure with appropriate optional fields.


1091-1091: Document why networkInformation uses only the first result.

The networkInformation is passed from only the first result. If this is intentional (because network conditions are assumed consistent across iterations), add a comment explaining the rationale. Otherwise, consider selecting the most representative value or aggregating connection quality metrics.

packages/cookiebench-cli/src/commands/benchmark.ts (3)

103-157: Excellent handling of banner detection consistency.

The implementation properly validates banner detection across all iterations and provides appropriate warnings when detection is inconsistent. The null/zero timing checks and coverage calculations are thorough.


298-303: Good use of finally block for cleanup.

The server cleanup is correctly placed in a finally block, ensuring the local server is terminated even if benchmark execution fails.


384-387: Misleading comment about "most common iteration count".

The comment claims to find the "most common iteration count" but the code simply takes the first value from the Map. Either fix the logic to find the actual mode or update the comment.

Apply this diff to fix the comment:

-	// Find the most common iteration count or first one
+	// Use the first benchmark's iteration count as default
 	const defaultIterations =
 		benchmarkConfigs.size > 0
 			? Array.from(benchmarkConfigs.values())[0]
 			: DEFAULT_ITERATIONS;

Or if you want the actual most common value:

-	// Find the most common iteration count or first one
+	// Find the most common iteration count
 	const defaultIterations =
-		benchmarkConfigs.size > 0
-			? Array.from(benchmarkConfigs.values())[0]
-			: DEFAULT_ITERATIONS;
+		benchmarkConfigs.size > 0
+			? Array.from(benchmarkConfigs.values())
+					.sort(
+						(a, b) =>
+							benchmarkConfigs
+								.values()
+								.toArray()
+								.filter((v) => v === b).length -
+							benchmarkConfigs
+								.values()
+								.toArray()
+								.filter((v) => v === a).length
+					)[0]
+			: DEFAULT_ITERATIONS;

Likely an incorrect or invalid review comment.

packages/runner/src/performance-aggregator.ts (1)

354-445: LGTM: Statistical summary and logging methods.

The getStatisticalSummary, logResults, and logStatisticalSummary methods are well-structured. Statistical calculations delegate appropriately to the imported functions, logging provides useful debug and info output, and stability indicators give clear feedback on metric variability.

Comment thread packages/cookiebench-cli/src/commands/benchmark.ts Outdated
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts Outdated
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment on lines +342 to +346
if (results[data.app]) {
logger.warn(
`Duplicate app name "${data.app}" found in ${file}. Previous results will be overwritten.`
);
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider preventing duplicate overwrites.

When duplicate app names are detected, the current implementation warns but proceeds to overwrite the previous results. This silently discards data, which could mask configuration errors or unexpected file structures.

Consider either:

  1. Throwing an error to halt execution and force resolution
  2. Merging results arrays instead of replacing them
  3. Using file paths as unique keys to preserve all data
🤖 Prompt for AI Agents
In packages/cookiebench-cli/src/commands/results.ts around lines 342 to 346, the
code only warns on duplicate app names and overwrites prior results; instead
preserve data by merging results arrays: detect if results[data.app] exists and
is an array, then concat/merge the incoming data results into the existing array
(and log an info message about merging), ensuring you don't lose previous
entries; also add a unit test or guard to validate the merged structure.
Optionally, if you prefer fail-fast behavior, replace the warning with throwing
an Error to halt execution or switch the key to a unique identifier (e.g.,
include source file path) to avoid collisions—implement one of these fixes and
remove the silent overwrite.

Comment on lines +421 to +888
function printDetailedResults(
appName: string,
results: RawBenchmarkDetail[],
scores: BenchmarkScores,
baseline?: RawBenchmarkDetail[]
) {
console.log(
`\n${color.bold(color.cyan(`━━━ ${appName.toUpperCase()} ━━━`))}`
);

// ━━━ Score Display ━━━
const score = Math.round(scores.totalScore);
let scoreColor = color.green;
let scoreBgColor = color.bgGreen;

if (score < SCORE_THRESHOLD_POOR) {
scoreColor = color.red;
scoreBgColor = color.bgRed;
} else if (score < SCORE_THRESHOLD_FAIR) {
scoreColor = color.yellow;
scoreBgColor = color.bgYellow;
}

console.log(`\n${color.bold("🎯 Overall Score")}`);
console.log(
scoreColor(` ${score}/100`) +
" " +
scoreBgColor(color.black(` ${scores.grade} `))
);

// ━━━ Key Insights ━━━
if (scores.insights && scores.insights.length > 0) {
console.log(`\n${color.bold("💡 Key Insights")}`);
for (const insight of scores.insights) {
console.log(`${color.blue(" •")} ${color.dim(insight)}`);
}
}

// Calculate averages
const avgBannerVisibility =
results.reduce((a, b) => a + b.timing.cookieBanner.visibilityTime, 0) /
results.length;
const avgViewportCoverage =
results.reduce((a, b) => a + b.timing.cookieBanner.viewportCoverage, 0) /
results.length;
const avgNetworkImpact =
results.reduce((a, b) => a + b.size.thirdParty, 0) / results.length;
const _bannerDetected = results.some((r) => r.timing.cookieBanner.detected);
const isBundled = results[0]?.size.thirdParty === 0;

const avgFCP =
results.reduce((a, b) => a + b.timing.firstContentfulPaint, 0) /
results.length;
const avgLCP =
results.reduce((a, b) => a + b.timing.largestContentfulPaint, 0) /
results.length;
const avgTTI =
results.reduce((a, b) => a + b.timing.timeToInteractive, 0) /
results.length;
const avgCLS =
results.reduce((a, b) => a + b.timing.cumulativeLayoutShift, 0) /
results.length;
const avgTBT =
results.reduce((a, b) => a + b.timing.mainThreadBlocking.total, 0) /
results.length;

const totalSize =
results.reduce((a, b) => a + b.size.total, 0) / results.length;
const jsSize =
results.reduce((a, b) => a + b.size.scripts.total, 0) / results.length;
const cssSize =
results.reduce((a, b) => a + b.size.styles, 0) / results.length;
const imageSize =
results.reduce((a, b) => a + b.size.images, 0) / results.length;
const fontSize =
results.reduce((a, b) => a + b.size.fonts, 0) / results.length;
const otherSize =
results.reduce((a, b) => a + b.size.other, 0) / results.length;

const jsFiles =
results.reduce((a, b) => a + b.resources.scripts.length, 0) /
results.length;
const cssFiles =
results.reduce((a, b) => a + b.resources.styles.length, 0) / results.length;
const imageFiles =
results.reduce((a, b) => a + b.resources.images.length, 0) / results.length;
const fontFiles =
results.reduce((a, b) => a + b.resources.fonts.length, 0) / results.length;
const otherFiles =
results.reduce((a, b) => a + b.resources.other.length, 0) / results.length;

// Calculate deltas if baseline exists
let bannerDelta = "";
if (baseline && appName !== "baseline") {
const baselineAvgBanner =
baseline.reduce((a, b) => a + b.timing.cookieBanner.visibilityTime, 0) /
baseline.length;
const delta = avgBannerVisibility - baselineAvgBanner;
bannerDelta = ` ${delta > 0 ? "+" : ""}${formatTime(delta)}`;
}

// ━━━ Cookie Banner Impact ━━━
console.log(`\n${color.bold("🍪 Cookie Banner Impact")}`);
const bannerTable = new Table({
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
style: { "padding-left": 2, "padding-right": 2, border: ["grey"] },
});

bannerTable.push(
[
{ content: "Banner Visibility", colSpan: 1 },
{ content: "Viewport Coverage", colSpan: 1 },
{ content: "Network Impact", colSpan: 1 },
{ content: "Bundle Strategy", colSpan: 1 },
],
[
`${color.bold(formatTime(avgBannerVisibility))}\n${color.dim(bannerDelta || "baseline")}`,
`${color.bold(`${avgViewportCoverage.toFixed(1)}%`)}\n${color.dim("Screen real estate")}`,
`${color.bold(formatBytes(avgNetworkImpact * KILOBYTE))}\n${color.dim(isBundled ? "Bundled (no network)" : "External requests")}`,
`${color.bold(isBundled ? "Bundled" : "External")}\n${color.dim(isBundled ? "Included in main bundle" : "Loaded from CDN")}`,
]
);

console.log(bannerTable.toString());

// ━━━ Core Web Vitals ━━━
console.log(`\n${color.bold("⚡ Core Web Vitals")}`);
const vitalsTable = new Table({
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
style: { "padding-left": 2, "padding-right": 2, border: ["grey"] },
});

vitalsTable.push(
[
{ content: "First Contentful Paint", colSpan: 1 },
{ content: "Largest Contentful Paint", colSpan: 1 },
{ content: "Time to Interactive", colSpan: 1 },
{ content: "Cumulative Layout Shift", colSpan: 1 },
],
[
`${color.bold(formatTime(avgFCP))}\n${getPerformanceRating("fcp", avgFCP)}`,
`${color.bold(formatTime(avgLCP))}\n${getPerformanceRating("lcp", avgLCP)}`,
`${color.bold(formatTime(avgTTI))}\n${getPerformanceRating("tti", avgTTI)}`,
`${color.bold(avgCLS.toFixed(CLS_DECIMAL_PLACES))}\n${getPerformanceRating("cls", avgCLS)}`,
]
);

console.log(vitalsTable.toString());

// ━━━ Resource Breakdown ━━━
console.log(`\n${color.bold("📦 Resource Breakdown")}`);

const totalFiles = jsFiles + cssFiles + imageFiles + fontFiles + otherFiles;
const jsPercentage =
totalSize > 0 ? (jsSize / totalSize) * PERCENTAGE_DIVISOR : 0;
const cssPercentage =
totalSize > 0 ? (cssSize / totalSize) * PERCENTAGE_DIVISOR : 0;
const imagePercentage =
totalSize > 0 ? (imageSize / totalSize) * PERCENTAGE_DIVISOR : 0;
const fontPercentage =
totalSize > 0 ? (fontSize / totalSize) * PERCENTAGE_DIVISOR : 0;
const otherPercentage =
totalSize > 0 ? (otherSize / totalSize) * PERCENTAGE_DIVISOR : 0;

const resourceTable = new Table({
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
style: { "padding-left": 2, "padding-right": 2, border: ["grey"] },
});

resourceTable.push(
[
{ content: "Type", colSpan: 1 },
{ content: "Size", colSpan: 1 },
{ content: "Files", colSpan: 1 },
{ content: "% of Total", colSpan: 1 },
],
[
color.cyan("JavaScript"),
formatBytes(jsSize * KILOBYTE),
Math.round(jsFiles).toString(),
`${jsPercentage.toFixed(1)}%`,
],
[
color.cyan("CSS"),
formatBytes(cssSize * KILOBYTE),
Math.round(cssFiles).toString(),
`${cssPercentage.toFixed(1)}%`,
],
[
color.cyan("Images"),
formatBytes(imageSize * KILOBYTE),
Math.round(imageFiles).toString(),
`${imagePercentage.toFixed(1)}%`,
],
[
color.cyan("Fonts"),
formatBytes(fontSize * KILOBYTE),
Math.round(fontFiles).toString(),
`${fontPercentage.toFixed(1)}%`,
],
[
color.cyan("Other"),
formatBytes(otherSize * KILOBYTE),
Math.round(otherFiles).toString(),
`${otherPercentage.toFixed(1)}%`,
],
[
color.bold("Total"),
color.bold(formatBytes(totalSize * KILOBYTE)),
color.bold(Math.round(totalFiles).toString()),
color.bold("100%"),
]
);

console.log(resourceTable.toString());

// ━━━ Performance Impact Summary ━━━
console.log(`\n${color.bold("📊 Performance Impact Summary")}`);
const summaryTable = new Table({
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
style: { "padding-left": 2, "padding-right": 2, border: ["grey"] },
});

let layoutStability = "Poor";
if (avgCLS === 0) {
layoutStability = "Perfect";
} else if (avgCLS < CLS_THRESHOLD_GOOD) {
layoutStability = "Good";
} else if (avgCLS < CLS_THRESHOLD_FAIR) {
layoutStability = "Fair";
}

summaryTable.push(
["Loading Strategy", color.bold(isBundled ? "Bundled" : "External")],
["Render Performance", color.bold(formatTime(avgBannerVisibility))],
["Network Overhead", color.bold(formatBytes(avgNetworkImpact * KILOBYTE))],
["Main Thread Impact", color.bold(formatTime(avgTBT))],
["Layout Stability", color.bold(layoutStability)],
["User Disruption", color.bold(`${avgViewportCoverage.toFixed(1)}%`)]
);

console.log(summaryTable.toString());

// ━━━ Network Chart (Waterfall) ━━━
console.log(`\n${color.bold("🌐 Network Chart")}`);

// Get first iteration's resources for waterfall
const firstResult = results[0];
if (firstResult?.resources) {
const allResources = [
...firstResult.resources.scripts.map((r) => ({ ...r, type: "script" })),
...firstResult.resources.styles.map((r) => ({ ...r, type: "style" })),
...firstResult.resources.images.map((r) => ({ ...r, type: "image" })),
...firstResult.resources.fonts.map((r) => ({ ...r, type: "font" })),
...firstResult.resources.other.map((r) => ({ ...r, type: "other" })),
].sort((a, b) => a.startTime - b.startTime);

// Take top 10 resources for waterfall
const topResources = allResources.slice(0, 10);

if (topResources.length > 0) {
const maxEndTime = Math.max(
...topResources.map((r) => r.startTime + r.duration)
);
const chartWidth = 60; // Width of the waterfall bars

const waterfallTable = new Table({
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
colWidths: [COL_WIDTH_NAME, chartWidth + COL_WIDTH_CHART_PADDING],
style: { "padding-left": 1, "padding-right": 1, border: ["grey"] },
wordWrap: true,
});

waterfallTable.push([
color.dim("Resource"),
color.dim(
"Timeline (0ms ───────────────────────────► " +
formatTime(maxEndTime) +
")"
),
]);

for (const resource of topResources) {
const fileName = resource.name.split("/").pop() || resource.name;
const shortName =
fileName.length > MAX_FILENAME_LENGTH
? `${fileName.substring(0, TRUNCATED_FILENAME_LENGTH)}...`
: fileName;

const startPos = Math.floor(
(resource.startTime / maxEndTime) * chartWidth
);
const barLength = Math.max(
1,
Math.floor((resource.duration / maxEndTime) * chartWidth)
);

const emptyBefore = " ".repeat(startPos);
const bar = "█".repeat(barLength);
const durationLabel =
resource.duration > maxEndTime * MIN_DURATION_THRESHOLD
? formatTime(resource.duration)
: "";

let barColor = color.blue;
if (resource.isThirdParty) {
barColor = color.yellow;
}
if (resource.isCookieService) {
barColor = color.red;
}

waterfallTable.push([
color.dim(shortName),
`${emptyBefore + barColor(bar)} ${color.dim(durationLabel)}`,
]);
}

console.log(waterfallTable.toString());
}
}

// ━━━ Resource Details ━━━
console.log(`\n${color.bold("📋 Resource Details")}`);

// Aggregate resource data across all results
const aggregatedResources: Array<{
name: string;
type: string;
source: string;
size: number;
duration: number;
tags: string[];
}> = [];

// Use first result for resource list (assuming resources are consistent)
const sampleResult = results[0];
if (sampleResult?.resources) {
const allSampleResources = [
...sampleResult.resources.scripts.map((r) => ({
...r,
type: "JavaScript",
})),
...sampleResult.resources.styles.map((r) => ({ ...r, type: "CSS" })),
...sampleResult.resources.images.map((r) => ({ ...r, type: "Image" })),
...sampleResult.resources.fonts.map((r) => ({ ...r, type: "Font" })),
...sampleResult.resources.other.map((r) => ({ ...r, type: "Other" })),
];

// Calculate averages for each resource
for (const sampleResource of allSampleResources) {
const resourceName = sampleResource.name;

// Find this resource in all results and average the values
const avgSize =
results.reduce((sum, result) => {
const allResources = [
...result.resources.scripts,
...result.resources.styles,
...result.resources.images,
...result.resources.fonts,
...result.resources.other,
];
const found = allResources.find((r) => r.name === resourceName);
return sum + (found ? found.size : 0);
}, 0) / results.length;

const avgDuration =
results.reduce((sum, result) => {
const allResources = [
...result.resources.scripts,
...result.resources.styles,
...result.resources.images,
...result.resources.fonts,
...result.resources.other,
];
const found = allResources.find((r) => r.name === resourceName);
return sum + (found ? found.duration : 0);
}, 0) / results.length;

let source = "Bundled";
if (sampleResource.isThirdParty) {
source = sampleResource.isCookieService
? "Cookie Service"
: "Third-Party";
}

const tags: string[] = [];
if (!sampleResource.isThirdParty) {
tags.push("bundled");
}
if (sampleResource.isThirdParty) {
tags.push("third-party");
}
if (sampleResource.isCookieService) {
tags.push("cookie-service");
}
if ("isDynamic" in sampleResource && sampleResource.isDynamic) {
tags.push("dynamic");
}

// Add core/other categorization for bundled scripts
if (
!sampleResource.isThirdParty &&
sampleResource.type === "JavaScript"
) {
tags.push("core");
}

aggregatedResources.push({
name: resourceName,
type: sampleResource.type,
source,
size: avgSize,
duration: avgDuration,
tags,
});
}
}

// Sort by size (descending) and take top 10
const topResources = aggregatedResources
.sort((a, b) => b.size - a.size)
.slice(0, 10);

if (topResources.length > 0) {
const detailsTable = new Table({
head: ["Resource Name", "Type", "Source", "Size", "Duration", "Tags"],
colWidths: [
COL_WIDTH_NAME,
COL_WIDTH_TYPE,
COL_WIDTH_SOURCE,
COL_WIDTH_SIZE,
COL_WIDTH_DURATION,
COL_WIDTH_TAGS,
],
style: { head: ["cyan"], border: ["grey"] },
wordWrap: true,
});

for (const resource of topResources) {
const fileName = resource.name.split("/").pop() || resource.name;
const shortName =
fileName.length > MAX_FILENAME_LENGTH
? `${fileName.substring(0, TRUNCATED_FILENAME_LENGTH)}...`
: fileName;

let sourceColor = color.green;
if (resource.source === "Third-Party") {
sourceColor = color.yellow;
}
if (resource.source === "Cookie Service") {
sourceColor = color.red;
}

detailsTable.push([
shortName,
resource.type,
sourceColor(resource.source),
formatBytes(resource.size * KILOBYTE),
color.blue(formatTime(resource.duration)),
resource.tags.join(", "),
]);
}

console.log(detailsTable.toString());
}
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider extracting display sections into separate functions.

The printDetailedResults function spans 467 lines and handles multiple distinct display sections (score, insights, banner impact, web vitals, resources, etc.). This violates the Single Responsibility Principle and makes the function difficult to test and maintain.

Consider extracting each display section into its own function (e.g., printScoreSection, printWebVitalsSection, printResourceBreakdown, etc.).

🤖 Prompt for AI Agents
In packages/cookiebench-cli/src/commands/results.ts around lines 421 to 888, the
printDetailedResults function is too large and mixes multiple display
responsibilities; extract each major display section into its own function
(e.g., printScoreSection(appName, scores), printInsightsSection(scores),
printBannerImpactSection(avgBannerVisibility, avgViewportCoverage,
avgNetworkImpact, isBundled, bannerDelta), printWebVitalsSection(avgFCP, avgLCP,
avgTTI, avgCLS), printResourceBreakdownSection(totalSize, jsSize, cssSize,
imageSize, fontSize, otherSize, jsFiles, cssFiles, imageFiles, fontFiles,
otherFiles), printSummarySection(...), printWaterfallSection(firstResult), and
printResourceDetailsSection(aggregatedResources)). Move all console/table
rendering logic into these helpers, keep metric calculations in
printDetailedResults (or extract a computeMetrics(results, baseline) helper that
returns the averages/flags used by the printers), pass only the minimal data
each printer needs, and keep existing output formatting intact; update
imports/exports as needed and add small unit tests for the new functions to
verify they render the same strings/tables as before.

const avgNetworkImpact =
results.reduce((a, b) => a + b.size.thirdParty, 0) / results.length;
const _bannerDetected = results.some((r) => r.timing.cookieBanner.detected);
const isBundled = results[0]?.size.thirdParty === 0;

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Verify bundled status across all results.

The bundled status is determined from only the first result. If results are inconsistent across iterations, this could give incorrect information.

Consider checking all results for consistency or using a threshold:

-const isBundled = results[0]?.size.thirdParty === 0;
+const isBundled = results.every((r) => r.size.thirdParty === 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isBundled = results[0]?.size.thirdParty === 0;
const isBundled = results.every((r) => r.size.thirdParty === 0);
🤖 Prompt for AI Agents
In packages/cookiebench-cli/src/commands/results.ts around line 469, the bundled
status is computed only from results[0], which can be wrong if iterations
differ; update the logic to examine all results instead (e.g., set isBundled =
results.every(r => r?.size.thirdParty === 0) or use a threshold/majority like
count of results with thirdParty===0 divided by results.length) and emit a
warning or note when results are inconsistent so callers know a consensus was
not reached.

appResults.reduce((a, b) => a + (b.timing.timeToFirstByte || 0), 0) /
appResults.length,
interactionToNextPaint:
appResults[0]?.timing.interactionToNextPaint || null,

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider averaging interactionToNextPaint across all results.

The interactionToNextPaint metric uses only the first result instead of averaging across all iterations. While INP may not be present in all results (hence the || null), if multiple results have values, they should be averaged for consistency with other metrics.

Consider averaging when values are available:

-				interactionToNextPaint:
-					appResults[0]?.timing.interactionToNextPaint || null,
+				interactionToNextPaint: (() => {
+					const values = appResults
+						.map((r) => r.timing.interactionToNextPaint)
+						.filter((v): v is number => v !== null && v !== undefined);
+					return values.length > 0
+						? values.reduce((a, b) => a + b, 0) / values.length
+						: null;
+				})(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
appResults[0]?.timing.interactionToNextPaint || null,
interactionToNextPaint: (() => {
const values = appResults
.map((r) => r.timing.interactionToNextPaint)
.filter((v): v is number => v !== null && v !== undefined);
return values.length > 0
? values.reduce((a, b) => a + b, 0) / values.length
: null;
})(),
🤖 Prompt for AI Agents
In packages/cookiebench-cli/src/commands/results.ts around line 1021, the code
currently selects interactionToNextPaint from only the first result; change this
to compute the average across all results that have a defined non-null
interactionToNextPaint value and return null if none exist. Iterate over
appResults, collect numeric interactionToNextPaint values, sum and divide by the
count to produce the mean (preserving numeric type), and replace the
single-index access with that averaged value so it matches how other metrics are
aggregated.

thirdPartySize:
appResults.reduce((a, b) => a + b.size.thirdParty, 0) /
appResults.length,
thirdPartyDomains: 5,

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded thirdPartyDomains with calculated value.

The thirdPartyDomains metric is hardcoded to 5, which produces incorrect scoring for network impact. This value should be calculated from the actual benchmark data by counting unique third-party domains.

Calculate the actual count of unique third-party domains:

-				thirdPartyDomains: 5,
+				thirdPartyDomains: new Set(
+					appResults.flatMap((result) =>
+						[
+							...result.resources.scripts,
+							...result.resources.styles,
+							...result.resources.images,
+							...result.resources.fonts,
+							...result.resources.other,
+						]
+							.filter((r) => r.isThirdParty)
+							.map((r) => {
+								try {
+									return new URL(r.name).hostname;
+								} catch {
+									return r.name.split('/')[2] || r.name;
+								}
+							})
+					)
+				).size,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
thirdPartyDomains: 5,
thirdPartyDomains: new Set(
appResults.flatMap((result) =>
[
...result.resources.scripts,
...result.resources.styles,
...result.resources.images,
...result.resources.fonts,
...result.resources.other,
]
.filter((r) => r.isThirdParty)
.map((r) => {
try {
return new URL(r.name).hostname;
} catch {
return r.name.split('/')[2] || r.name;
}
})
)
).size,

Comment thread packages/runner/src/performance-aggregator.ts
Comment on lines +102 to +104
if (trimmed.length === 0) {
return sorted[Math.floor(sorted.length / 2)];
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix trimmed-mean fallback for even-length datasets.

When trimming drops every element (e.g., trimPercent ≥ 50 on an even-sized sample), sorted[Math.floor(sorted.length / 2)] returns only the upper middle value, inflating the mean instead of falling back to the true median. Reuse your existing getMedian helper here.

Apply this diff:

-	if (trimmed.length === 0) {
-		return sorted[Math.floor(sorted.length / 2)];
-	}
+	if (trimmed.length === 0) {
+		return getMedian(sorted);
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (trimmed.length === 0) {
return sorted[Math.floor(sorted.length / 2)];
}
if (trimmed.length === 0) {
return getMedian(sorted);
}
🤖 Prompt for AI Agents
In packages/runner/src/statistics.ts around lines 102 to 104, the trimmed-mean
fallback uses sorted[Math.floor(sorted.length / 2)] which returns the upper
middle for even-length arrays and inflates the result; replace that direct index
access with a call to the existing getMedian(sorted) helper so the fallback
returns the true median for both even and odd lengths.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

♻️ Duplicate comments (7)
packages/shared/package.json (1)

21-21: Empty dependencies object can be omitted.

The empty "dependencies" object is unnecessary and should be removed to keep the manifest clean.

-  "dependencies": {},
   "devDependencies": {
packages/shared/tsconfig.json (1)

1-14: Add declaration: true to emit TypeScript declaration files.

As a shared utilities package consumed by other packages in the monorepo (benchmark, runner, cookiebench-cli), emitting .d.ts files is essential for type safety and IDE support.

Apply this diff:

  {
    "compilerOptions": {
      "target": "ES2020",
      "module": "ESNext",
      "moduleResolution": "bundler",
      "esModuleInterop": true,
      "strict": true,
      "skipLibCheck": true,
      "outDir": "dist",
-     "rootDir": "src"
+     "rootDir": "src",
+     "declaration": true,
+     "declarationMap": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
  }
packages/shared/src/constants.ts (2)

6-7: Consolidate duplicate constants with identical values.

BYTES_TO_KB and KILOBYTE both equal 1024. Having two names for the same constant creates confusion about which to use.

Consider consolidating:

 // Size constants (bytes to kilobytes)
-export const BYTES_TO_KB = 1024;
-export const KILOBYTE = 1024;
+export const KILOBYTE = 1024;
+export const BYTES_TO_KB = KILOBYTE; // Alias for conversion contexts

Or pick one name and update all usages across the codebase.


10-11: Clarify or consolidate percentage constants.

PERCENTAGE_MULTIPLIER and PERCENTAGE_DIVISOR both equal 100. If they represent conceptually different operations (e.g., converting to/from percentage), document why they share the same value. Otherwise, use a single constant.

Either consolidate to a single name:

 // Percentage constants
-export const PERCENTAGE_MULTIPLIER = 100;
-export const PERCENTAGE_DIVISOR = 100;
+export const PERCENTAGE_BASE = 100;

Or add a comment explaining the semantic distinction between multiplication and division contexts.

packages/cookiebench-cli/src/commands/scores.ts (1)

211-318: Guard against empty result sets before averaging.

If appResults is empty (e.g., a failed run wrote an empty results array), every average computed on lines 231-317 divides by 0, producing NaN scores. This issue was previously flagged and remains unaddressed.

Bail out early with a clear error before calling calculateScores:

 	const appResults = result.results;
+	if (!Array.isArray(appResults) || appResults.length === 0) {
+		logger.error(`No iterations recorded for ${appName}; cannot compute scores.`);
+		return;
+	}
+
 	const config = await loadConfigForApp(logger, appName);
packages/runner/src/performance-aggregator.ts (2)

133-137: Redundant fallback still present despite prior review.

The division on line 135 uses (totalBlockingTime || 1) as the denominator, but this fallback is unnecessary since line 134's ternary already ensures the division only occurs when totalBlockingTime > 0. A past review marked this as addressed in commit c79d556, but the redundant fallback remains in the code.

Apply this diff to remove the redundant fallback:

 const percentageFromCookies =
   totalBlockingTime > 0
-    ? (cookieBannerEstimate / (totalBlockingTime || 1)) *
+    ? (cookieBannerEstimate / totalBlockingTime) *
       PERCENTAGE_MULTIPLIER
     : 0;

179-179: Inconsistent nullish coalescing for navigationTiming.

Line 179 uses logical OR (||) for the navigationTiming fallback, but a past review requested changing this to nullish coalescing (??) to avoid converting falsy but valid values. While lines 176-178 correctly adopted ??, this line was missed.

Apply this diff for consistency:

-      navigationTiming: perfumeMetrics?.navigationTiming || {
+      navigationTiming: perfumeMetrics?.navigationTiming ?? {
         timeToFirstByte: 0,
         domInteractive: 0,
         domContentLoadedEventStart: 0,
         domContentLoadedEventEnd: 0,
         domComplete: 0,
         loadEventStart: 0,
         loadEventEnd: 0,
       },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cd8c8a0 and ce8b88a.

📒 Files selected for processing (13)
  • METHODOLOGY.md (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/cookiebench-cli/src/utils/index.ts (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/shared/package.json (1 hunks)
  • packages/shared/rslib.config.ts (1 hunks)
  • packages/shared/src/constants.ts (1 hunks)
  • packages/shared/src/utils/config.ts (1 hunks)
  • packages/shared/src/utils/package-manager.ts (1 hunks)
  • packages/shared/src/utils/time.ts (1 hunks)
  • packages/shared/tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
packages/shared/src/utils/time.ts (4)
packages/cookiebench-cli/src/utils/index.ts (2)
  • formatTime (9-9)
  • ONE_SECOND (13-13)
packages/runner/src/index.ts (1)
  • formatTime (31-31)
packages/shared/src/index.ts (2)
  • formatTime (22-22)
  • ONE_SECOND (8-8)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/cookiebench-cli/src/commands/scores.ts (5)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (179-213)
  • RawBenchmarkDetail (37-177)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/constants.ts (2)
  • HALF_SECOND (3-3)
  • PERCENTAGE_DIVISOR (11-11)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1383-1422)
  • calculateScores (1010-1379)
packages/shared/src/constants.ts (2)
packages/cookiebench-cli/src/utils/index.ts (5)
  • ONE_SECOND (13-13)
  • HALF_SECOND (11-11)
  • KILOBYTE (12-12)
  • PERCENTAGE_MULTIPLIER (15-15)
  • PERCENTAGE_DIVISOR (14-14)
packages/shared/src/index.ts (7)
  • ONE_SECOND (8-8)
  • HALF_SECOND (6-6)
  • BYTES_TO_KB (5-5)
  • KILOBYTE (7-7)
  • PERCENTAGE_MULTIPLIER (10-10)
  • PERCENTAGE_DIVISOR (9-9)
  • TTI_BUFFER_MS (11-11)
packages/cookiebench-cli/src/utils/index.ts (3)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/utils.ts (1)
  • readConfig (6-16)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/utils/config.ts (1)
packages/cookiebench-cli/src/utils/index.ts (1)
  • readConfig (21-23)
packages/shared/src/utils/package-manager.ts (3)
packages/cookiebench-cli/src/utils/index.ts (1)
  • getPackageManager (10-10)
packages/runner/src/index.ts (1)
  • getPackageManager (31-31)
packages/shared/src/index.ts (1)
  • getPackageManager (21-21)
packages/cookiebench-cli/src/commands/benchmark.ts (11)
packages/runner/src/index.ts (6)
  • BenchmarkResult (11-11)
  • readConfig (31-31)
  • ServerInfo (21-21)
  • buildAndServeNextApp (7-7)
  • BenchmarkRunner (3-3)
  • cleanupServer (7-7)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (162-162)
  • CliLogger (9-9)
packages/cookiebench-cli/src/utils/index.ts (3)
  • PERCENTAGE_DIVISOR (14-14)
  • readConfig (21-23)
  • HALF_SECOND (11-11)
packages/shared/src/constants.ts (2)
  • PERCENTAGE_DIVISOR (11-11)
  • HALF_SECOND (3-3)
packages/cookiebench-cli/src/utils/constants.ts (3)
  • DEFAULT_DOM_SIZE (3-3)
  • DEFAULT_ITERATIONS (2-2)
  • SEPARATOR_WIDTH (5-5)
packages/runner/src/benchmark-runner.ts (1)
  • BenchmarkRunner (30-474)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1010-1379)
  • printScores (1383-1422)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (890-1120)
packages/runner/src/performance-aggregator.ts (4)
packages/benchmark/src/types.ts (7)
  • CoreWebVitals (216-227)
  • CookieBannerData (94-103)
  • CookieBannerMetrics (79-92)
  • NetworkRequest (106-113)
  • NetworkMetrics (115-118)
  • ResourceTimingData (128-213)
  • PerfumeMetrics (230-261)
packages/runner/src/types.ts (9)
  • CoreWebVitals (10-10)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-156)
  • BenchmarkResult (158-271)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/runner/src/statistics.ts (4)
  • isStable (132-138)
  • calculateCoefficientOfVariation (112-127)
  • calculateTrimmedMean (84-107)
  • calculateStatistics (11-52)
🪛 LanguageTool
METHODOLOGY.md

[grammar] ~79-~79: Ensure spelling is correct
Context: ...aintains speed - Fast 3G (1.6 Mbps, 562ms latency): More realistic, significantly...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (27)
packages/shared/package.json (1)

1-29: Package manifest structure looks solid.

The exports configuration, entry points, and build tooling are well-configured for a TypeScript library in this monorepo. Once you remove the empty dependencies object (line 21), this is ready to go.

packages/shared/rslib.config.ts (1)

1-19: LGTM! Standard rslib configuration.

The configuration follows best practices for a shared package in a Node.js monorepo, with appropriate settings for ESM output, TypeScript declarations, and ES2021 syntax.

packages/runner/package.json (4)

1-13: Solid package structure with dual exports for maximum compatibility.

The use of both the modern exports field (with types) and legacy main/module/types fields is best practice for universal packages. This ensures ESM and CJS consumers, as well as TypeScript tooling, can properly resolve the package.


22-22: Verify the strict version pinning on @c15t/logger.

The @c15t/logger dependency is pinned to an exact version (1.0.0) without flexibility modifiers (^ or ~). Confirm whether this strict pinning is intentional for stability or if it should allow minor/patch updates for security fixes and improvements.


29-32: Verify dev dependency versions align with workspace conventions.

Confirm that @rslib/core, typescript, and other dev tools are version-consistent with those used in other workspace packages to avoid tooling conflicts.


25-26: I'll search for the latest versions of both packages to verify stability and compatibility.

I need to search for the correct package name to verify the version:

Let me search more specifically for the npm package:

The package playwright-performance-metrics@^1.2.2 does not appear to exist on npm and will fail installation.

@playwright/test is currently at version 1.56.1, which is the latest stable release. However, playwright-performance-metrics is documented on GitHub but doesn't appear as a published npm package. The npm registry search returns no results for this exact package name.

Verify that:

  • The package name is correct (similar packages on npm include playwright-performance at v2.0.5, playwright-lighthouse, and others)
  • If playwright-performance-metrics is intentional, check whether it's a private package or needs to be installed from the GitHub repository directly
  • The version constraint ^1.2.2 aligns with what's actually available
packages/shared/src/utils/config.ts (1)

20-20: Verify JSON structure matches the expected type.

The type cast as T provides no runtime validation that the parsed JSON matches the expected config schema. Invalid configs will only cause errors downstream.

Consider adding runtime validation using a schema validator (e.g., Zod, io-ts) or document that consumers must validate configs themselves. For critical config properties, at least verify their presence:

const parsed = JSON.parse(configContent);
if (!parsed || typeof parsed !== 'object') {
	throw new Error('Config must be a valid object');
}
return parsed as T;
packages/cookiebench-cli/src/utils/index.ts (1)

1-23: Excellent refactoring to eliminate duplication.

The delegation to @consentio/shared eliminates the code duplication previously flagged in past reviews. This centralized approach ensures consistent behavior across packages and simplifies maintenance.

METHODOLOGY.md (1)

1-289: LGTM! Comprehensive and well-structured methodology documentation.

The methodology document is thorough and clearly explains:

  • The distinction between Banner Render Time and Banner Visibility Time
  • Opacity threshold (0.5) for user-perceived visibility
  • Measurement baseline from navigationStart
  • Network conditions and their impact
  • Primary and secondary metrics with scoring methodology
  • Reproducibility requirements and limitations

The documentation aligns with industry standards (Core Web Vitals, Lighthouse, WebPageTest, W3C) and maintains transparency about measurement approaches and limitations.

packages/cookiebench-cli/src/commands/benchmark.ts (5)

35-40: LGTM! Empty array handling implemented correctly.

The function now properly guards against division by zero by returning 0 for empty arrays, preventing NaN propagation through metric calculations.


214-226: LGTM! DOM size now uses actual data with safe fallback.

The function correctly extracts DOM size from benchmark results using optional chaining and provides a sensible default when data is unavailable.


248-383: LGTM! Robust implementation with proper error handling and cleanup.

The function demonstrates good practices:

  • Validates results before processing (lines 301-304)
  • Supports both remote and local benchmarking
  • Ensures server cleanup in finally block
  • Returns boolean status instead of calling process.exit()
  • Creates necessary directories with error handling
  • Enables trace saving for debugging

388-572: LGTM! Well-structured interactive command with excellent UX.

The main command function demonstrates strong implementation:

  • Proper error handling without process.exit() (lines 396, 415)
  • Clear user prompts and progress indicators
  • Configurable iterations with per-benchmark defaults
  • Sequential execution with failure tracking
  • Optional results panel via dynamic import
  • Clean cancellation handling throughout

The interactive flow provides a professional CLI experience.


101-121: The review comment is incorrect—resource.name is the correct property.

The codebase defines resource objects with a name property containing the URL string (from the PerformanceResourceTiming API), not a url property. Line 113 of benchmark.ts correctly uses resource.name to extract the hostname. No url property exists on resource objects anywhere in the codebase.

Likely an incorrect or invalid review comment.

packages/cookiebench-cli/src/commands/scores.ts (1)

218-226: No action needed—the JSON.stringify calls are correct.

The AppData type definition confirms all relevant fields (company, techStack, source, tags) must be strings or null, not objects. The JSON.stringify calls are necessary and properly convert object values to match the expected type. No changes required.

packages/runner/src/performance-aggregator.ts (12)

1-19: LGTM: Clean imports with proper constant reuse.

The imports correctly leverage shared constants (line 12), addressing the code duplication concern from previous reviews.


21-24: LGTM: Well-defined local configuration constants.


25-34: LGTM: Comprehensive parameter type definition.


36-41: LGTM: Clean dependency injection.


42-56: LGTM: Sound TTI calculation logic.

The use of Math.max with appropriate fallbacks and buffer addition correctly implements the Time to Interactive metric.


58-96: LGTM: Well-documented UX-focused visibility metrics.

The visibility time fallback chain correctly prioritizes user-perceived timing over technical render time, with excellent documentation explaining the rationale.


149-175: LGTM: Comprehensive metric aggregation.

The aggregateMetrics method effectively combines all performance data sources into a unified BenchmarkDetails structure. The visibility time handling in lines 207-210 correctly implements the user-perceived timing approach.

Also applies to: 189-225


227-241: LGTM: Clean network impact calculation.


243-367: LGTM: Robust statistical aggregation with outlier handling.

The use of trimmed mean (10% trim) and coefficient of variation checks provides resilient averaging. The variability warnings (lines 287-301) offer valuable observability into measurement stability.


369-389: LGTM: Clean statistical summary extraction.


391-423: LGTM: Comprehensive structured logging.

The debug output captures all essential performance metrics and configuration details for effective troubleshooting.


425-463: LGTM: Informative statistical summary output.

The method provides clear, actionable insights into measurement stability with both quantitative statistics and qualitative indicators.

Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment on lines +37 to +39
} catch {
// Directory doesn't exist or can't be read
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider logging suppressed errors in debug mode.

The empty catch block silently swallows all errors, which may hide actionable issues like permission problems or I/O failures that differ from a non-existent directory.

Apply this diff to log errors in debug mode:

-	} catch {
-		// Directory doesn't exist or can't be read
+	} catch (error) {
+		// Directory doesn't exist or can't be read - this is expected in some cases
+		// Optionally log at debug level if a logger is available
 	}

Note: Since logger is not available in this helper, you could either pass it as a parameter or accept that some errors remain hidden.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/cookiebench-cli/src/commands/scores.ts around lines 37 to 39, the
empty catch block is swallowing all errors; change it to catch the error (catch
(err)) and, in debug mode, log the error details instead of ignoring them—either
accept a logger parameter and call logger.debug("reading scores directory
failed", err) or, if adding logger is undesirable, call console.debug with the
error; keep the original behavior for non-debug runs (silently ignore
non-actionable cases like missing directory).

Comment thread packages/cookiebench-cli/src/commands/scores.ts Outdated
Comment thread packages/cookiebench-cli/src/commands/scores.ts Outdated
Comment thread packages/cookiebench-cli/src/commands/scores.ts
Comment thread packages/cookiebench-cli/src/commands/scores.ts Outdated
Comment thread packages/runner/src/performance-aggregator.ts Outdated
Comment on lines +14 to +26
export function readConfig<T extends BaseConfig = BaseConfig>(
configPath?: string
): T | null {
try {
const path = configPath || join(process.cwd(), "config.json");
const configContent = readFileSync(path, "utf-8");
return JSON.parse(configContent) as T;
} catch (error) {
// biome-ignore lint/suspicious/noConsole: console error is needed for debugging
console.error("Failed to read config.json:", error);
return null;
}
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider using async file I/O to avoid blocking the event loop.

readFileSync blocks the Node.js event loop during config reads. For large config files or startup performance, an async alternative would be preferable.

Consider this async alternative:

import { readFile } from "node:fs/promises";

export async function readConfig<T extends BaseConfig = BaseConfig>(
	configPath?: string
): Promise<T | null> {
	try {
		const path = configPath || join(process.cwd(), "config.json");
		const configContent = await readFile(path, "utf-8");
		return JSON.parse(configContent) as T;
	} catch (error) {
		console.error("Failed to read config.json:", error);
		return null;
	}
}

If synchronous behavior is required for CLI startup simplicity, document this decision in a comment.

🤖 Prompt for AI Agents
In packages/shared/src/utils/config.ts around lines 14 to 26, the function
currently uses readFileSync which blocks the event loop; change it to use the
async fs/promises API (import readFile from "node:fs/promises"), update the
function signature to async and return Promise<T | null>, replace readFileSync
call with await readFile(path, "utf-8"), and keep the same try/catch and error
logging; also update all call sites to await the new async function (or if
synchronous startup behavior is required, add a clear comment explaining why
readFileSync is intentionally used and keep a synchronous wrapper only for those
specific CLI paths).

Comment thread packages/shared/src/utils/package-manager.ts Outdated
Comment thread packages/shared/src/utils/time.ts
…benchmarks. Enhance Iubenda layout script comment for clarity. Refactor bundle strategy and cookie banner collector logic for improved accuracy in performance metrics. Update PerfumeCollector to utilize navigation timing API and handle network information more robustly. Introduce first-party resource detection in ResourceTimingCollector and improve language detection logic.
…ror handling. Update performance metrics to handle optional properties more robustly, ensuring consistent data retrieval and preventing potential runtime errors.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ce8b88a and b835336.

📒 Files selected for processing (7)
  • benchmarks/with-enzuzo/package.json (2 hunks)
  • benchmarks/with-iubenda/app/layout.tsx (2 hunks)
  • benchmarks/with-osano/package.json (2 hunks)
  • packages/benchmark/src/bundle-strategy.ts (1 hunks)
  • packages/benchmark/src/cookie-banner-collector.ts (1 hunks)
  • packages/benchmark/src/perfume-collector.ts (1 hunks)
  • packages/benchmark/src/resource-timing-collector.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
packages/benchmark/src/resource-timing-collector.ts (2)
packages/benchmark/src/types.ts (1)
  • ResourceTimingData (128-213)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (17-74)
packages/benchmark/src/bundle-strategy.ts (2)
packages/benchmark/src/types.ts (2)
  • Config (13-56)
  • BundleStrategy (121-125)
packages/benchmark/src/constants.ts (1)
  • BUNDLE_TYPES (76-81)
packages/benchmark/src/perfume-collector.ts (1)
packages/benchmark/src/types.ts (2)
  • WindowWithPerfumeMetrics (263-279)
  • PerfumeMetrics (230-261)
packages/benchmark/src/cookie-banner-collector.ts (3)
packages/benchmark/src/types.ts (5)
  • Config (13-56)
  • CookieBannerMetrics (79-92)
  • WindowWithCookieMetrics (65-77)
  • LayoutShiftEntry (59-62)
  • CookieBannerData (94-103)
packages/benchmark/src/bundle-strategy.ts (1)
  • determineBundleStrategy (4-24)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (17-74)
🪛 ast-grep (0.39.6)
benchmarks/with-iubenda/app/layout.tsx

[warning] 18-18: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🔇 Additional comments (16)
benchmarks/with-iubenda/app/layout.tsx (1)

18-85: Comment improves suppression justification and addresses previous feedback.

The biome-ignore comment on Line 18 has been properly updated with specific, accurate justification: the Iubenda configuration is hardcoded and contains no user input, making the use of dangerouslySetInnerHTML safe. This directly addresses the previous review's concern about grammar and justification.

The script tag attribute reordering on Line 84 is a minor cosmetic change with no functional impact.

benchmarks/with-osano/package.json (1)

6-6: ✅ Previous issues resolved: port conflict, package migration, and engines field.

Mirrors the improvements in with-enzuzo:

  • Line 6: Port updated from 3006 → 3007 (resolves conflict with with-onetrust).
  • Line 22: Package migrated from @cookiebench/clicookiebench.
  • Lines 25–27: engines field correctly named with Node constraint.

Verify port 3007 does not conflict with other benchmarks (see script in with-enzuzo review for comprehensive port audit).

Also applies to: 22-22, 25-27

benchmarks/with-enzuzo/package.json (1)

6-6: ✅ All previous issues resolved and port conflict verified.

The changes correctly address earlier flagged issues:

  • Line 6: Port updated from 3001 → 3002 (resolves conflict; 3002 is unique across benchmark packages).
  • Line 22: Package migrated from @cookiebench/clicookiebench.
  • Lines 25–27: engines field correctly named (plural) with proper Node constraint.
packages/benchmark/src/bundle-strategy.ts (1)

1-1: The IIFE typo fix is complete and verified across the codebase.

The constant BUNDLE_TYPES.IIFE is correctly spelled as "iife" in constants.ts (line 77), no instances of the misspelling "iffe" remain, and all benchmark configuration files consistently use the correct spelling.

packages/benchmark/src/resource-timing-collector.ts (4)

1-11: LGTM! Clean dependency injection.

The imports are appropriate and the constructor properly injects the logger dependency for use throughout the class.


27-39: Excellent fix! Third-party detection now accurate.

The isFirstParty helper properly addresses the previous review comment by using URL parsing and hostname comparison instead of substring matching. The fallback handling for relative URLs and malformed URLs is also correct.


173-180: Excellent fix! Language detection now accurate.

The language detection properly addresses the previous review comment by reading the actual document language with appropriate fallbacks to navigator.language, navigator.languages[0], and finally "en".


81-96: Verify placeholder timing values.

Several timing fields are hardcoded to 0 (loadStart, executeStart, executeEnd). Ensure these are intentional placeholders or confirm whether the Resource Timing API provides data to populate them (e.g., responseStart, responseEnd could map to load times).

packages/benchmark/src/cookie-banner-collector.ts (3)

1-23: LGTM: Clear documentation of opacity threshold.

The imports are appropriate and the OPACITY_VISIBILITY_THRESHOLD constant is well-documented with a clear rationale for the 50% threshold to account for CSS animations.


37-70: LGTM: Proper initialization with bundle strategy detection.

The method correctly determines the bundle strategy, logs it for debugging, and returns a properly initialized metrics object.


275-366: LGTM: Comprehensive metrics collection with proper fallback logic.

The metrics collection correctly computes render time, visibility time with fallback, interactive time, layout shift impact, and viewport coverage. The viewport intersection calculation properly handles partial visibility.

Note: The bannerHydrationTime calculation at lines 316-319 has already been flagged in a previous review for missing the metrics.detected check.

packages/benchmark/src/perfume-collector.ts (5)

10-15: LGTM!

Clean constructor with proper dependency injection.


86-158: LGTM!

Robust script loading with multiple fallback strategies for different monorepo layouts. The createRequire fallback provides good coverage, and the warning ensures visibility when all attempts fail.


163-176: LGTM!

The metrics collection waits for Perfume.js to populate data and logs appropriately. The timeout constant was previously flagged as potentially configurable, but using a constant is acceptable for now.


177-195: LGTM! Previous deprecation issue resolved.

Now correctly uses the modern Performance Timeline API (PerformanceNavigationTiming) instead of the deprecated performance.timing. The null check properly handles cases where navigation entries aren't available.


198-226: LGTM! Previous experimental API concerns resolved.

Excellent defensive programming: checks for navigator availability, wraps in try/catch, includes vendor prefixes, and provides sensible defaults. The optional return type properly matches the PerfumeMetrics interface.

Comment thread packages/benchmark/src/bundle-strategy.ts
Comment thread packages/benchmark/src/cookie-banner-collector.ts
Comment thread packages/benchmark/src/cookie-banner-collector.ts Outdated
Comment on lines +195 to +201
if (
isVisible &&
(bannerMetrics.bannerVisibleTime === 0 ||
now < bannerMetrics.bannerVisibleTime)
) {
bannerMetrics.bannerVisibleTime = now;
}

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Visibility time update condition may be unnecessary.

The condition now < bannerMetrics.bannerVisibleTime allows updating to an earlier time, but given the linear polling nature of detection, bannerVisibleTime should only be set once. The bannerVisibleTime === 0 check alone should be sufficient.

Consider simplifying:

-if (
-	isVisible &&
-	(bannerMetrics.bannerVisibleTime === 0 ||
-		now < bannerMetrics.bannerVisibleTime)
-) {
+if (isVisible && bannerMetrics.bannerVisibleTime === 0) {
 	bannerMetrics.bannerVisibleTime = now;
 }

If the "earlier time" logic is intentionally defensive against edge cases, consider adding a comment explaining the scenario it protects against.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
isVisible &&
(bannerMetrics.bannerVisibleTime === 0 ||
now < bannerMetrics.bannerVisibleTime)
) {
bannerMetrics.bannerVisibleTime = now;
}
if (isVisible && bannerMetrics.bannerVisibleTime === 0) {
bannerMetrics.bannerVisibleTime = now;
}
🤖 Prompt for AI Agents
In packages/benchmark/src/cookie-banner-collector.ts around lines 195 to 201,
the visibility-time update currently allows replacing bannerVisibleTime with an
earlier timestamp via the now < bannerMetrics.bannerVisibleTime clause; simplify
by only setting bannerMetrics.bannerVisibleTime when it is still 0 (i.e., if
(isVisible && bannerMetrics.bannerVisibleTime === 0)
bannerMetrics.bannerVisibleTime = now), or if you intend to keep the
earlier-time guard, add a concise comment explaining the specific edge case it
defends against so future readers understand why an earlier timestamp would be
preferred.

Comment thread packages/benchmark/src/perfume-collector.ts Outdated
Comment thread packages/benchmark/src/perfume-collector.ts
Comment thread packages/benchmark/src/resource-timing-collector.ts
Comment thread packages/benchmark/src/resource-timing-collector.ts

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b835336 and 42cabde.

📒 Files selected for processing (1)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/cookiebench-cli/src/commands/benchmark.ts (8)
packages/runner/src/types.ts (2)
  • BenchmarkResult (158-271)
  • ServerInfo (18-21)
packages/cookiebench-cli/src/utils/index.ts (3)
  • PERCENTAGE_DIVISOR (14-14)
  • readConfig (21-23)
  • HALF_SECOND (11-11)
packages/shared/src/constants.ts (2)
  • PERCENTAGE_DIVISOR (11-11)
  • HALF_SECOND (3-3)
packages/cookiebench-cli/src/utils/constants.ts (3)
  • DEFAULT_DOM_SIZE (3-3)
  • DEFAULT_ITERATIONS (2-2)
  • SEPARATOR_WIDTH (5-5)
packages/runner/src/benchmark-runner.ts (2)
  • runSingleBenchmark (96-199)
  • BenchmarkRunner (30-474)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1010-1379)
  • printScores (1383-1422)
🔇 Additional comments (10)
packages/cookiebench-cli/src/commands/benchmark.ts (10)

1-30: LGTM - Clean import structure.

All imports are properly organized and appear to be used throughout the file. The separation between node built-ins, third-party packages, and local utilities is clear.


35-40: LGTM - Proper empty array handling.

The guard at line 36 correctly prevents NaN from division by zero when the array is empty.


234-246: LGTM - Robust directory scanning.

The function properly filters hidden directories and handles errors gracefully by returning an empty array, which is appropriate for the benchmark discovery flow.


304-307: LGTM - Proper result validation.

The validation ensures result.details exists and contains at least one iteration before attempting metric calculations, preventing division by zero errors in calculateAverage.


372-377: LGTM - Proper resource cleanup.

The finally block ensures server cleanup only when a local server was started, preventing errors when using remote benchmarking.


391-402: LGTM - Proper error handling for direct benchmark execution.

The function now throws an error instead of calling process.exit(), allowing the CLI entry point to handle the exit code gracefully. This also makes the code more testable.


413-419: LGTM - Proper error handling for missing benchmarks.

The function throws an error instead of calling process.exit(), following the same pattern as the previous fix and allowing proper error propagation.


473-504: LGTM - Robust iteration input validation.

The validation logic properly handles empty input (to use config defaults) and validates numeric input, with clear error messages. The parsing with parseInt(value, 10) correctly specifies the radix.


517-547: LGTM - Resilient sequential execution.

The benchmark execution loop properly:

  • Tracks success/failure for each benchmark
  • Continues execution even if individual benchmarks fail (good for batch runs)
  • Provides clear progress indicators with [i/total] format
  • Adds visual separation between benchmarks

101-121: ****

The review comment incorrectly identifies a bug. The resource objects in all arrays (scripts, styles, images, fonts, other) have a name: string property—not a url property. This name property contains the resource URL (sourced from PerformanceResourceTiming.name). The code on line 111 is using the correct property name and will successfully extract hostnames from third-party URLs.

Likely an incorrect or invalid review comment.

Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
Comment thread packages/cookiebench-cli/src/commands/benchmark.ts
burnedchris added 2 commits October 31, 2025 09:31
…ing in CookieBannerCollector and improving resource timing calculations. Update bundle strategy to include bundled types and ensure robust error handling in benchmark commands. Add type safety for layout shift entries and streamline data retrieval in various collectors.
… README.md to remove 'with-' prefix from benchmark names. This change enhances consistency and simplifies command usage for running benchmarks.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
benchmarks/cookie-control/app/layout.tsx (1)

22-53: Remove redundant biome-ignore comment.

The static analysis warning about dangerouslySetInnerHTML is a false positive—the injected content is a hardcoded Cookie Control configuration with no user input, making this usage safe.

However, the biome-ignore comment on line 51 is redundant and misplaced. The comment on line 22 already suppresses the lint rule for the dangerouslySetInnerHTML prop. The second comment before the type attribute serves no purpose since the lint rule doesn't apply to that attribute.

Apply this diff to remove the redundant comment:

 					}}
-					// biome-ignore lint/security/noDangerouslySetInnerHtml: its okay to set inner html
 					type="text/javascript"
♻️ Duplicate comments (4)
packages/shared/src/utils/package-manager.ts (1)

9-34: Flatten detection loop and import once.

We’re still carrying the triple-nested try/catch pyramid and re-importing node:child_process on every branch, exactly the duplication that was flagged earlier. Please flatten the control flow by importing once and iterating the candidates—this keeps the logic readable and avoids redundant dynamic imports.

 export async function getPackageManager(): Promise<{
 	command: string;
 	args: string[];
 }> {
-	try {
-		const { execSync } = await import("node:child_process");
-		const output = execSync("npm -v", { encoding: "utf-8" });
-		if (output) {
-			return { command: "npm", args: ["run"] };
-		}
-	} catch {
-		try {
-			const { execSync } = await import("node:child_process");
-			const output = execSync("yarn -v", { encoding: "utf-8" });
-			if (output) {
-				return { command: "yarn", args: [] };
-			}
-		} catch {
-			try {
-				const { execSync } = await import("node:child_process");
-				const output = execSync("pnpm -v", { encoding: "utf-8" });
-				if (output) {
-					return { command: "pnpm", args: [] };
-				}
-			} catch {
-				// Default to npm if no package manager is found
-				return { command: "npm", args: ["run"] };
-			}
-		}
-	}
-	// Fallback if all checks succeed but output is falsy (shouldn't happen in practice)
-	return { command: "npm", args: ["run"] };
+	const { execSync } = await import("node:child_process");
+	const candidates = [
+		{ check: "npm -v", result: { command: "npm", args: ["run"] } },
+		{ check: "yarn -v", result: { command: "yarn", args: [] } },
+		{ check: "pnpm -v", result: { command: "pnpm", args: [] } },
+	];
+
+	for (const candidate of candidates) {
+		try {
+			const output = execSync(candidate.check, { encoding: "utf-8" });
+			if (output) {
+				return candidate.result;
+			}
+		} catch {
+			// try next candidate
+		}
+	}
+
+	return { command: "npm", args: ["run"] };
 }
benchmarks/cookie-control/next-env.d.ts (1)

3-3: Duplicate concern: Manual edit of auto-generated file.

This file exhibits the same pattern as benchmarks/c15t-react/next-env.d.ts and benchmarks/iubenda/next-env.d.ts, where route types are being manually imported into an auto-generated Next.js environment file. Please refer to the verification steps outlined in the review of those files.

packages/cookiebench-cli/src/commands/scores.ts (1)

209-256: Guard against empty result sets before averaging.

If a results.json exists but results is empty, every average below divides by 0, producing NaN scores. Bail out early so we don’t feed invalid data into calculateScores.

-	const appResults = result.results;
+	const appResults = result.results;
+	if (!Array.isArray(appResults) || appResults.length === 0) {
+		logger.error(`No iterations recorded for ${appName}; cannot compute scores.`);
+		return;
+	}
packages/benchmark/src/perfume-collector.ts (1)

248-251: Fix TTFB fallback to keep legitimate zero measurements.

|| treats a 0 ms TTFB reading as falsy and replaces it with the navigation timing fallback, overstating ultra-fast responses. Use ?? so only missing metrics fall back.

 				timeToFirstByte:
-					rawMetrics.TTFB?.value || navigationTiming?.timeToFirstByte || 0,
+					rawMetrics.TTFB?.value ??
+					navigationTiming?.timeToFirstByte ??
+					0,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 42cabde and a509bd5.

⛔ Files ignored due to path filters (12)
  • benchmarks/c15t-nextjs/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/c15t-react/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/cookie-control/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/cookie-yes/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/didomi/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/enzuzo/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/iubenda/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/ketch/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/onetrust/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/osano/app/favicon.ico is excluded by !**/*.ico
  • benchmarks/usercentrics/app/favicon.ico is excluded by !**/*.ico
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (60)
  • benchmarks/c15t-nextjs/config.json (1 hunks)
  • benchmarks/c15t-nextjs/next-env.d.ts (1 hunks)
  • benchmarks/c15t-nextjs/package.json (2 hunks)
  • benchmarks/c15t-react/config.json (1 hunks)
  • benchmarks/c15t-react/next-env.d.ts (1 hunks)
  • benchmarks/c15t-react/package.json (2 hunks)
  • benchmarks/cookie-control/app/layout.tsx (3 hunks)
  • benchmarks/cookie-control/config.json (1 hunks)
  • benchmarks/cookie-control/next-env.d.ts (1 hunks)
  • benchmarks/cookie-control/package.json (2 hunks)
  • benchmarks/cookie-yes/config.json (2 hunks)
  • benchmarks/cookie-yes/next-env.d.ts (1 hunks)
  • benchmarks/cookie-yes/package.json (2 hunks)
  • benchmarks/didomi/app/layout.tsx (3 hunks)
  • benchmarks/didomi/config.json (1 hunks)
  • benchmarks/didomi/next-env.d.ts (1 hunks)
  • benchmarks/didomi/package.json (2 hunks)
  • benchmarks/enzuzo/app/layout.tsx (2 hunks)
  • benchmarks/enzuzo/config.json (1 hunks)
  • benchmarks/enzuzo/next-env.d.ts (1 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/app/layout.tsx (2 hunks)
  • benchmarks/iubenda/config.json (2 hunks)
  • benchmarks/iubenda/next-env.d.ts (1 hunks)
  • benchmarks/iubenda/package.json (2 hunks)
  • benchmarks/ketch/config.json (2 hunks)
  • benchmarks/ketch/next-env.d.ts (1 hunks)
  • benchmarks/ketch/package.json (2 hunks)
  • benchmarks/onetrust/app/layout.tsx (2 hunks)
  • benchmarks/onetrust/config.json (2 hunks)
  • benchmarks/onetrust/next-env.d.ts (1 hunks)
  • benchmarks/onetrust/package.json (2 hunks)
  • benchmarks/osano/config.json (2 hunks)
  • benchmarks/osano/next-env.d.ts (1 hunks)
  • benchmarks/osano/next.config.ts (1 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/osano/tsconfig.json (1 hunks)
  • benchmarks/usercentrics/config.json (2 hunks)
  • benchmarks/usercentrics/next-env.d.ts (1 hunks)
  • benchmarks/usercentrics/next.config.ts (1 hunks)
  • benchmarks/usercentrics/package.json (2 hunks)
  • benchmarks/usercentrics/tsconfig.json (1 hunks)
  • benchmarks/with-enzuzo/next-env.d.ts (0 hunks)
  • benchmarks/with-iubenda/next-env.d.ts (0 hunks)
  • benchmarks/with-ketch/next-env.d.ts (0 hunks)
  • benchmarks/with-onetrust/next-env.d.ts (0 hunks)
  • benchmarks/with-osano/next-env.d.ts (0 hunks)
  • benchmarks/with-usercentrics/next-env.d.ts (0 hunks)
  • packages/benchmark/src/bundle-strategy.ts (1 hunks)
  • packages/benchmark/src/cookie-banner-collector.ts (1 hunks)
  • packages/benchmark/src/perfume-collector.ts (1 hunks)
  • packages/benchmark/src/resource-timing-collector.ts (1 hunks)
  • packages/benchmark/src/types.ts (1 hunks)
  • packages/cookiebench-cli/README.md (1 hunks)
  • packages/cookiebench-cli/src/commands/benchmark.ts (1 hunks)
  • packages/cookiebench-cli/src/commands/scores.ts (1 hunks)
  • packages/runner/src/performance-aggregator.ts (1 hunks)
  • packages/runner/src/types.ts (1 hunks)
  • packages/shared/src/utils/package-manager.ts (1 hunks)
  • packages/shared/src/utils/time.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • benchmarks/with-usercentrics/next-env.d.ts
  • benchmarks/with-onetrust/next-env.d.ts
  • benchmarks/with-osano/next-env.d.ts
  • benchmarks/with-enzuzo/next-env.d.ts
  • benchmarks/with-ketch/next-env.d.ts
  • benchmarks/with-iubenda/next-env.d.ts
🧰 Additional context used
🧬 Code graph analysis (11)
packages/shared/src/utils/package-manager.ts (3)
packages/cookiebench-cli/src/utils/index.ts (1)
  • getPackageManager (10-10)
packages/runner/src/index.ts (1)
  • getPackageManager (31-31)
packages/shared/src/index.ts (1)
  • getPackageManager (21-21)
packages/benchmark/src/resource-timing-collector.ts (2)
packages/benchmark/src/types.ts (1)
  • ResourceTimingData (129-214)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (17-74)
packages/benchmark/src/perfume-collector.ts (1)
packages/benchmark/src/types.ts (2)
  • WindowWithPerfumeMetrics (264-280)
  • PerfumeMetrics (231-262)
packages/cookiebench-cli/src/commands/scores.ts (7)
packages/cookiebench-cli/src/commands/results.ts (2)
  • BenchmarkOutput (179-213)
  • RawBenchmarkDetail (37-177)
packages/cookiebench-cli/src/types/index.ts (1)
  • BenchmarkScores (10-38)
packages/cookiebench-cli/src/utils/logger.ts (2)
  • logger (162-162)
  • CliLogger (9-9)
packages/benchmark/src/types.ts (1)
  • Config (13-56)
packages/shared/src/constants.ts (2)
  • HALF_SECOND (3-3)
  • PERCENTAGE_DIVISOR (11-11)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • printScores (1383-1422)
  • calculateScores (1010-1379)
packages/cookiebench-cli/src/utils/constants.ts (1)
  • DEFAULT_DOM_SIZE (3-3)
packages/shared/src/utils/time.ts (4)
packages/cookiebench-cli/src/utils/index.ts (2)
  • formatTime (9-9)
  • ONE_SECOND (13-13)
packages/runner/src/index.ts (1)
  • formatTime (31-31)
packages/shared/src/index.ts (2)
  • formatTime (22-22)
  • ONE_SECOND (8-8)
packages/shared/src/constants.ts (1)
  • ONE_SECOND (2-2)
packages/benchmark/src/cookie-banner-collector.ts (3)
packages/benchmark/src/types.ts (5)
  • Config (13-56)
  • CookieBannerMetrics (80-93)
  • WindowWithCookieMetrics (65-78)
  • LayoutShiftEntry (59-62)
  • CookieBannerData (95-104)
packages/benchmark/src/bundle-strategy.ts (1)
  • determineBundleStrategy (4-25)
packages/benchmark/src/constants.ts (1)
  • BENCHMARK_CONSTANTS (17-74)
packages/runner/src/performance-aggregator.ts (4)
packages/benchmark/src/types.ts (7)
  • CookieBannerData (95-104)
  • CookieBannerMetrics (80-93)
  • NetworkRequest (107-114)
  • NetworkMetrics (116-119)
  • ResourceTimingData (129-214)
  • Config (13-56)
  • PerfumeMetrics (231-262)
packages/runner/src/types.ts (9)
  • CookieBannerData (8-8)
  • CookieBannerMetrics (9-9)
  • NetworkRequest (12-12)
  • NetworkMetrics (11-11)
  • ResourceTimingData (14-14)
  • Config (6-6)
  • PerfumeMetrics (13-13)
  • BenchmarkDetails (24-159)
  • BenchmarkResult (161-274)
packages/shared/src/constants.ts (2)
  • TTI_BUFFER_MS (14-14)
  • PERCENTAGE_MULTIPLIER (10-10)
packages/runner/src/statistics.ts (4)
  • isStable (132-138)
  • calculateCoefficientOfVariation (112-127)
  • calculateTrimmedMean (84-107)
  • calculateStatistics (11-52)
benchmarks/enzuzo/app/layout.tsx (9)
benchmarks/cookie-control/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/c15t-react/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/iubenda/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/cookie-yes/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/c15t-nextjs/app/layout.tsx (1)
  • metadata (16-18)
benchmarks/onetrust/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/osano/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/ketch/app/layout.tsx (1)
  • metadata (4-6)
benchmarks/usercentrics/app/layout.tsx (1)
  • metadata (4-6)
packages/benchmark/src/bundle-strategy.ts (2)
packages/benchmark/src/types.ts (2)
  • Config (13-56)
  • BundleStrategy (122-126)
packages/benchmark/src/constants.ts (1)
  • BUNDLE_TYPES (76-81)
packages/runner/src/types.ts (2)
packages/runner/src/index.ts (3)
  • ServerInfo (21-21)
  • BenchmarkDetails (10-10)
  • BenchmarkResult (11-11)
packages/cookiebench-cli/src/types/index.ts (3)
  • ServerInfo (6-6)
  • BenchmarkDetails (3-3)
  • BenchmarkResult (4-4)
packages/cookiebench-cli/src/commands/benchmark.ts (6)
packages/runner/src/types.ts (2)
  • BenchmarkResult (161-274)
  • ServerInfo (18-21)
packages/runner/src/benchmark-runner.ts (2)
  • runSingleBenchmark (96-199)
  • BenchmarkRunner (30-474)
packages/shared/src/utils/config.ts (1)
  • readConfig (14-26)
packages/runner/src/server.ts (2)
  • buildAndServeNextApp (6-71)
  • cleanupServer (73-77)
packages/cookiebench-cli/src/utils/scoring.ts (2)
  • calculateScores (1010-1379)
  • printScores (1383-1422)
packages/cookiebench-cli/src/commands/results.ts (1)
  • resultsCommand (890-1120)
🪛 ast-grep (0.39.6)
benchmarks/iubenda/app/layout.tsx

[warning] 18-18: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

benchmarks/onetrust/app/layout.tsx

[warning] 23-23: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

benchmarks/cookie-control/app/layout.tsx

[warning] 22-22: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 LanguageTool
packages/cookiebench-cli/README.md

[uncategorized] ~373-~373: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ice overhead 4. Transparency (15%): Open source status, documentation, licensing 5. **U...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🪛 markdownlint-cli2 (0.18.1)
packages/cookiebench-cli/README.md

99-99: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


170-170: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


170-170: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


336-336: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🔇 Additional comments (70)
benchmarks/cookie-yes/package.json (3)

2-2: Naming consistency with refactoring objectives.

The package name change from "with-cookie-yes" to "cookie-yes" aligns with the consolidation theme and shorter naming convention in the PR. This change appears consistent with other benchmark project renames mentioned in the PR context.


22-22: Verify dependency replacement preserves functionality.

The change from @cookiebench/cli to cookiebench aligns with the new centralized CLI tooling described in the PR. However, confirm that this replacement provides equivalent or enhanced functionality and that any build scripts or workflows dependent on the old CLI remain compatible.


25-27: Node engine constraint aligns with workspace standards.

The addition of engines: { "node": ">=20.9.0" } is consistent with other benchmark packages in the PR. Verify that this minimum version is compatible with all declared dependencies, particularly next@16.0.1, react@^19.2.0, and typescript@^5.9.3.

benchmarks/osano/next.config.ts (1)

1-1: LGTM — consistent quote style update.

This formatting adjustment aligns the import statement with double-quote conventions, likely as part of the broader linting/formatting standardization introduced in this PR (new .cursor/rules/ultracite.mdc). No functional changes.

benchmarks/cookie-yes/config.json (4)

3-3: Project name updated to match naming convention.

The name has been changed from with-cookie-yes to cookie-yes, aligning with the broader refactor. This change is consistent with the PR objectives.


8-8: Remote URL updated to reflect the new project name.

The URL has been updated from benchmarks-with-cookie-yes.vercel.app to benchmarks-cookie-yes.vercel.app, maintaining consistency with the name change.

Please verify that the new Vercel deployment URL is accessible and correctly configured to serve the benchmark.


24-24: Typo correction: "iffe" → "iife".

The bundle type has been corrected from "iffe" (a typo) to "iife" (Immediately Invoked Function Expression), which is the correct term.


4-4: Verify the ID field consistency.

The id field remains "cookie-yes-banner" while the name field has changed to "cookie-yes". Confirm that this ID is not used elsewhere in the codebase in ways that would create inconsistencies, and that the differing naming conventions are intentional.

benchmarks/didomi/package.json (3)

2-2: Package name alignment consistent with project renaming convention.

The name field has been updated to "didomi", removing the "with-" prefix and aligning with the standardized naming convention across the benchmarks suite as part of this refactoring.


4-10: Scripts section appropriately updated for new tooling.

The removal of the public "benchmark" script aligns with the refactoring to use the new cookiebench-cli tooling instead of individual package scripts. The retained scripts (build, dev, fmt, lint, start) are appropriate for a Next.js benchmark project.


25-27: ✅ Engines constraint properly specified (resolves previous critical issue).

The "engines" field is correctly specified using the plural form (not the singular "engine"), which ensures that package managers will properly enforce the Node.js runtime constraint. This resolves the critical issue flagged in previous reviews and aligns with the standardized engine requirements across the benchmarks suite.

benchmarks/didomi/config.json (1)

3-3: Name standardization is consistent and verified across all benchmarks.

Verification confirms:

  • No remaining references to the old "with-didomi" name in the codebase
  • All other benchmark configs consistently use the same naming pattern without the "with-" prefix (e.g., "iubenda", "onetrust", "usercentrics", "osano", "ketch", "cookie-control", "enzuzo", "c15t-react", "baseline", "cookie-yes")
  • The change aligns with the standardized naming convention for the benchmark suite
benchmarks/enzuzo/app/layout.tsx (1)

1-2: LGTM! Formatting improvements for consistency.

The quote style changes and indentation adjustments improve consistency with other benchmark layout files across the codebase.

Also applies to: 5-5, 18-18

benchmarks/cookie-yes/next-env.d.ts (1)

3-3: LGTM! Typed routes import added correctly.

This import enables Next.js's typed routes feature, which provides type-safe routing paths. The addition aligns with Next.js 15.5's stable typed routes support and is consistent with the pattern applied across other benchmarks in this PR.

Based on library documentation

benchmarks/enzuzo/package.json (1)

25-27: ✅ Critical issue resolved: engines field now correctly named.

The previous critical issue flagging "engine" (singular) has been properly corrected to "engines" (plural) on the package.json spec. Package managers will now correctly read the Node.js version constraint.

benchmarks/enzuzo/config.json (2)

7-7: Verify the cookie banner selector against Enzuzo's current implementation.

The selector ".enzuzo-cookiebanner-container" (line 7) controls which DOM element is measured during benchmarking. If Enzuzo's banner HTML structure changes or if this selector is incorrect, measurements will fail or return invalid results.

Please confirm that this selector correctly targets Enzuzo's cookie banner container in their current implementation. You may want to test this locally or verify against Enzuzo's documentation/source.


2-2: Verify schema reference resolves correctly.

The schema reference on line 2 points to ./node_modules/@cookiebench/benchmark-schema/schema.json. Ensure this package is properly installed as a dependency and that the schema file exists at this path during both development and CI/CD environments.

benchmarks/cookie-control/package.json (3)

2-2: Package rename improves clarity.

Renaming from "with-enzuzo" to "cookie-control" better reflects the benchmark's purpose and aligns with the new naming convention applied across benchmarks.

Also applies to: 2-2


22-22: Verify cookiebench workspace dependency replaces CLI functionality.

The change from @cookiebench/cli to cookiebench is part of the CLI refactoring. Ensure the new dependency provides all tooling previously available via the benchmark script that was removed.

Also applies to: 22-22


25-27: Node engine constraint added consistently.

The engines constraint node: >=20.9.0 aligns with the Next.js 16.0.1 and React 19.2.0 requirements and should be compatible across the benchmark suite. Verify this minimum version is enforced in CI/deployment.

benchmarks/cookie-control/config.json (1)

1-40: New benchmark configuration file is well-structured.

The configuration correctly captures Cookie Control's metadata, cookie banner detection specifics (proprietary selector .ccc-module--slideout, Civic Computing service host), and appropriate tech stack details (IIFE bundleType for proprietary delivery, no TypeScript, no frameworks). The schema reference ensures consistency with the benchmark-schema contract.

Verify the schema at ./node_modules/@cookiebench/benchmark-schema/schema.json exists and validates this configuration during build/test.

benchmarks/c15t-nextjs/package.json (3)

2-2: Package rename follows consistent convention.

Renaming from "with-c15t-nextjs" to "c15t-nextjs" matches the pattern applied to other benchmarks and improves naming clarity.

Also applies to: 2-2


24-24: Consistent CLI dependency refactoring.

The change to cookiebench workspace dependency mirrors the pattern in other benchmarks and aligns with the PR's CLI consolidation objective. Ensure the new package provides equivalent functionality.

Also applies to: 24-24


27-29: Engine constraint applied consistently.

Node >=20.9.0 matches the requirement applied across the benchmark suite and is compatible with the Next.js and React versions pinned in this package.

packages/shared/src/utils/time.ts (1)

8-20: LGTM! Previous feedback fully addressed.

The implementation correctly handles all edge cases suggested in the previous review (non-finite values, negative numbers) and provides appropriate formatting for both milliseconds and seconds. The validation logic is clean and the rounding behavior is suitable for display purposes.

benchmarks/iubenda/app/layout.tsx (2)

18-18: Lint suppression is appropriate.

The biome-ignore comment correctly justifies the use of dangerouslySetInnerHTML since the content is entirely hardcoded configuration with no user input. The static analysis warning is a false positive in this case.


82-85: Question: Is this attribute reordering intentional?

The src attribute was moved to appear after charSet. While this has no functional impact, it adds noise to the diff. Was this change required by the new Ultracite Rules configuration mentioned in the PR, or was it unintentional?

benchmarks/cookie-control/app/layout.tsx (1)

1-5: LGTM: Formatting consistency improvements.

The quote style changes align with the broader refactoring effort for consistent conventions across the codebase.

benchmarks/c15t-nextjs/config.json (1)

3-3: The name change is correct and safe.

Verification confirms:

  • No references to the old name "with-c15t-nextjs" exist in the codebase
  • Naming convention "c15t-nextjs" is consistent with all other benchmarks (c15t-react, baseline, usercentrics, etc.)
  • The cookiebench CLI and turbo.json use generic task configurations that don't depend on specific benchmark names
  • This aligns with the PR's normalization goals
benchmarks/onetrust/package.json (1)

25-27: Node version constraint is consistent across all benchmark packages—no action required.

The verification confirms that all 12 benchmark packages in the workspace already enforce engines.node >= 20.9.0 uniformly, including onetrust. This consistency is maintained across the entire workspace, and the constraint aligns with the dependencies used (React, TypeScript, and related types).

benchmarks/iubenda/package.json (2)

2-2: Name consistency and engine constraints look good.

The rename from "with-cookie-yes" to "iubenda" aligns with the PR's consistent naming objective, and the Node engine constraint (>=20.9.0) is appropriately aligned with the broader benchmark infrastructure updates.

Also applies to: 25-27


4-9: No changes needed; benchmark refactoring is correct.

The PR properly refactors benchmark automation. The benchmarks/iubenda package.json correctly:

  • Removes its local "benchmark" script (now delegated to root)
  • Swaps @cookiebench/cli for cookiebench in devDependencies
  • The cookiebench package (packages/cookiebench-cli) confirms it exports CLI functionality via its bin entry: "cookiebench": "dist/index.mjs"
  • Root package.json retains the "benchmark" script invoking "pnpm exec cookiebench benchmark", ensuring CI/automation workflows continue to function
benchmarks/c15t-react/config.json (1)

3-3: Consistent naming normalization.

The benchmark name is being normalized from "with-c15t-react" to "c15t-react", aligning with the PR's broader standardization effort.

benchmarks/c15t-react/package.json (4)

2-2: Package name normalized consistently.

The package name matches the config.json update, maintaining consistency across the benchmark configuration.


27-29: Node.js engine constraint correctly specified.

The engines field now uses the correct plural form (fixing the previous engine typo). The Node.js version constraint (>=20.9.0) is appropriate for this project.


24-24: Dependency migration is valid.

The cookiebench workspace reference is correct. The package exists at packages/cookiebench-cli/ with the name cookiebench and provides CLI functionality via the bin entry "cookiebench": "dist/index.mjs". The pnpm workspace configuration properly includes this package.


4-9: Review comment is based on incorrect assumptions about the change.

The benchmarks/c15t-react/package.json file is a new file being added to the repository, not a modified file. The git diff shows new file mode 100644, and this is the first commit touching this file. There was no previous version to remove any script from.

The architecture is correct: individual benchmark packages like c15t-react do not define their own benchmark scripts. Instead, benchmark execution is managed centrally via the root package.json using the cookiebench CLI (which is already available as a workspace dependency in this file's devDependencies). This is consistent across all other benchmarks in the repository.

No action is required.

Likely an incorrect or invalid review comment.

benchmarks/onetrust/app/layout.tsx (1)

16-21: No issues found — async attribute is correct per OneTrust best practices.

The async attribute on otSDKStub.js is the recommended pattern according to OneTrust documentation. The synchronous OptanonWrapper callback defined immediately after ensures proper initialization timing. The implementation is correct as-is.

benchmarks/didomi/app/layout.tsx (3)

32-34: LGTM!

The multi-line early return improves readability without changing functionality.


58-59: LGTM!

Prop reordering has no functional impact in JSX.


61-63: LGTM!

The event handlers are appropriately reintroduced with consistent console logging for demonstration purposes in this benchmark application.

benchmarks/iubenda/config.json (2)

3-3: LGTM: Package naming standardization.

The removal of the "with-" prefix aligns with the broader refactoring effort to standardize benchmark naming conventions across the repository.


25-25: Excellent: Typo correction for bundleType.

The change from "iffe" to "iife" (Immediately Invoked Function Expression) corrects an important typo. This ensures the configuration accurately reflects the bundle type being used.

benchmarks/osano/config.json (1)

3-3: LGTM: Naming standardization and typo correction.

Both changes align with the broader refactoring:

  • Removing the "with-" prefix standardizes benchmark naming
  • Correcting "iffe" → "iife" fixes the bundle type designation (IIFE = Immediately Invoked Function Expression)

These changes mirror the pattern applied consistently across other benchmark configurations in this PR.

Also applies to: 16-16

benchmarks/usercentrics/tsconfig.json (1)

1-11: LGTM: Standard TypeScript configuration.

The configuration appropriately extends the shared Next.js TypeScript config and sets up standard path aliases and compilation options. This aligns with best practices for Next.js projects.

benchmarks/usercentrics/next.config.ts (1)

1-1: LGTM: Quote style standardization.

The change from single to double quotes is a cosmetic formatting adjustment with no semantic impact, likely part of broader code style standardization across the repository.

benchmarks/usercentrics/package.json (3)

2-2: LGTM: Package naming standardization.

The package rename from "with-usercentrics" to "usercentrics" is consistent with the broader naming standardization effort across all benchmark packages in this PR.


22-22: LGTM: CLI tooling migration.

The replacement of @cookiebench/cli with cookiebench aligns with the PR's objective to replace the old CLI with a new cookiebench-cli package as part of the infrastructure reorganization.


25-27: Address undocumented Node.js engine constraint and clarify versioning strategy.

The benchmark packages consistently use >=20.9.0, but this requires verification:

  1. Documentation gap: Node.js version requirement is not documented in README or contribution guidelines.
  2. Misalignment with main repo: Main package.json specifies >=18, while benchmarks require >=20.9.0—clarify if this intentional separation is necessary or should be aligned.
  3. Justification unclear: The constraint is more restrictive than the main repo without documented rationale (Next 16.0.1 and React 19.2.0 don't inherently require 20.9.0).

Ensure the engine constraint is either: (a) documented in contribution guidelines with clear rationale, or (b) aligned with main repository requirements if the higher version is not strictly necessary.

benchmarks/iubenda/next-env.d.ts (1)

1-6: Remove incorrect import from auto-generated next-env.d.ts file.

When typedRoutes is enabled in Next.js configuration, it generates a .next/types/link.d.ts file—not routes.js. Since none of the benchmarks have typedRoutes configured, the import import "./.next/types/routes.js" references a non-existent file and should be removed from benchmarks/iubenda/next-env.d.ts (and any other affected files). This import violates the file's own directive stating it "should not be edited."

If typed routes are intended for these benchmarks, enable typedRoutes: true in each benchmark's next.config.ts instead of manually editing the generated file.

Likely an incorrect or invalid review comment.

benchmarks/c15t-nextjs/next-env.d.ts (1)

3-3: LGTM! Typed routes import added correctly.

This import enables Next.js 15's typed routes feature, which is stable as of version 15.5. The pattern is consistent across all benchmarks in this PR.

benchmarks/onetrust/config.json (2)

3-3: LGTM! Benchmark name normalized.

The name change from "with-onetrust" to "onetrust" aligns with the broader naming convention standardization across benchmarks in this PR.


22-22: LGTM! Typo corrected.

The correction from "iffe" to "iife" fixes the typo for the standard acronym (Immediately Invoked Function Expression).

benchmarks/osano/next-env.d.ts (1)

1-6: LGTM! Standard Next.js type declarations.

This file follows the standard Next.js pattern for TypeScript environment setup, including typed routes support. The content is consistent with other benchmarks in the PR.

benchmarks/ketch/config.json (2)

3-3: LGTM! Benchmark name normalized.

The name change from "with-ketch" to "ketch" aligns with the broader naming convention standardization across benchmarks in this PR.


19-19: LGTM! Typo corrected.

The correction from "iffe" to "iife" fixes the typo for the standard acronym (Immediately Invoked Function Expression).

benchmarks/didomi/next-env.d.ts (1)

1-6: LGTM! Standard Next.js type declarations.

This file follows the standard Next.js pattern for TypeScript environment setup, including typed routes support. The content is consistent with other benchmarks in the PR.

benchmarks/onetrust/next-env.d.ts (1)

1-6: LGTM! Standard Next.js type declarations.

This file follows the standard Next.js pattern for TypeScript environment setup, including typed routes support. The content is consistent with other benchmarks in the PR.

benchmarks/enzuzo/next-env.d.ts (1)

1-6: LGTM! Standard Next.js type declarations.

This file follows the standard Next.js pattern for TypeScript environment setup, including typed routes support. The content is consistent with other benchmarks in the PR.

benchmarks/ketch/package.json (2)

2-2: LGTM! Package name normalized.

The name change from "with-ketch" to "ketch" is consistent with the corresponding config.json change and aligns with the broader naming convention standardization across benchmarks in this PR.


24-26: LGTM! Node.js version constraint added correctly.

The engines field is correctly spelled (plural) and specifies a reasonable Node.js version constraint. The previous typo issue flagged in past reviews has been properly resolved.

benchmarks/usercentrics/config.json (2)

3-3: LGTM: Name simplification improves consistency.

The name change from "with-usercentrics" to "usercentrics" aligns with the PR's goal of standardizing benchmark naming conventions.


20-20: LGTM: Typo correction.

The change from "iffe" to "iife" correctly represents the bundle type (Immediately Invoked Function Expression).

benchmarks/osano/tsconfig.json (1)

1-11: LGTM: Standard Next.js TypeScript configuration.

The configuration appropriately extends the shared cookiebench TypeScript config and uses standard Next.js patterns for path resolution and file inclusion.

benchmarks/usercentrics/next-env.d.ts (1)

1-6: LGTM: Standard Next.js environment type declarations.

This file correctly establishes ambient TypeScript types for Next.js. The pattern is consistent with other benchmarks in this PR, which is good for maintainability.

benchmarks/osano/package.json (1)

1-28: LGTM: Package configuration is correct.

The package manifest is well-structured with appropriate dependencies and scripts. Note that the previous critical issue with "engine" vs "engines" has been addressed in commit a823f20, and the current configuration correctly uses "engines".

benchmarks/ketch/next-env.d.ts (1)

1-6: LGTM: Standard Next.js environment type declarations.

This file correctly establishes ambient TypeScript types for Next.js, maintaining consistency with other benchmark projects in this PR.

packages/benchmark/src/types.ts (1)

1-280: Comprehensive and well-structured type definitions.

The type definitions are thorough and properly organized. The Window augmentations (WindowWithCookieMetrics, WindowWithPerfumeMetrics) correctly extend the Window interface, and the LayoutShiftEntry interface properly extends PerformanceEntry. All types provide clear contracts for the benchmark infrastructure and align well with their usage in the collector implementations.

packages/benchmark/src/cookie-banner-collector.ts (2)

121-145: Excellent improvements to type safety and resource management.

The addition of the isLayoutShiftEntry type guard (lines 121-128) properly validates entry properties before type narrowing, and storing the observer reference in __cookieBannerMetrics.clsObserver (lines 143-145) enables proper cleanup later. These changes address previous feedback about type assertions and observer lifecycle management.


298-306: Proper observer cleanup prevents memory leaks.

The observer disconnection logic (lines 303-306) properly cleans up the PerformanceObserver after reading final metrics, addressing the previous concern about potential memory buildup in long-running pages. Reading the layout shift value before disconnecting ensures accurate final measurements.

Comment thread benchmarks/c15t-react/next-env.d.ts Outdated
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.js";

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify: Manual edit of auto-generated file.

This file includes a comment stating it "should not be edited" (Line 5-6), yet the route types import is being manually added. In Next.js 15, with the typedRoutes config enabled, this import should be auto-generated. If you're manually adding it without enabling typed routes in next.config.ts, this could lead to:

  1. Type mismatches if the routes file doesn't exist or is out of sync
  2. The import being overwritten when Next.js regenerates this file

Please verify:


🏁 Script executed:

#!/bin/bash
# Check if typedRoutes is enabled in the Next.js config
rg -n "typedRoutes|experimental.*typedRoutes" benchmarks/c15t-react/next.config.ts benchmarks/c15t-react/next.config.js benchmarks/c15t-react/next.config.mjs

Length of output: 154


🌐 Web query:

Does Next.js 15 require typedRoutes to be enabled in next.config for the .next/types/routes.js import to work?

💡 Result:

Yes. Next.js generates the .next/types route helpers only when the typed routes feature is enabled (and you’re using TypeScript). In Next.js 15 this is controlled by the typedRoutes config (previously experimental.typedRoutes); enable it (typedRoutes: true) so the .next/types/*.ts (or .js) route types are emitted. [1][2]

Sources:

  • Next.js docs — typedRoutes config. [1]
  • Next.js 15 release notes / TypeScript improvements (typed routes). [2]


---

🏁 Script executed:

```shell
#!/bin/bash
# Find the actual next.config file
fd "next\.config\." benchmarks/c15t-react/

Length of output: 36


🏁 Script executed:

#!/bin/bash
# Search for typedRoutes in the benchmarks directory
rg -n "typedRoutes" benchmarks/c15t-react/ -C 3

Length of output: 0


🏁 Script executed:

#!/bin/bash
# View the full next-env.d.ts file to see the comment and full context
cat benchmarks/c15t-react/next-env.d.ts

Length of output: 244


🏁 Script executed:

#!/bin/bash
# View the next.config.ts to confirm typedRoutes configuration
cat benchmarks/c15t-react/next.config.ts

Length of output: 131


Enable typedRoutes in next.config.ts or remove the manual import.

The next-env.d.ts file explicitly warns against manual editing, yet line 3 contains a manually added import to ./.next/types/routes.js. Next.js generates the .next/types route helpers only when the typed routes feature is enabled, and typedRoutes is not currently configured in next.config.ts (benchmarks/c15t-react/next.config.ts is empty). Without this configuration, the import will either fail at runtime or be overwritten when Next.js regenerates the file. Add typedRoutes: true to the nextConfig object in next.config.ts.

🤖 Prompt for AI Agents
In benchmarks/c15t-react/next-env.d.ts around line 3 there is a manual import of
"./.next/types/routes.js" which is unsafe because typed route helpers are only
generated when typedRoutes is enabled; update
benchmarks/c15t-react/next.config.ts to add typedRoutes: true to the exported
nextConfig object (or remove the manual import from next-env.d.ts) so Next.js
will generate .next/types/routes.js consistently; ensure the change is in the
nextConfig export and committed so the import will not fail or be overwritten.

@@ -1,3 +1,4 @@
/** biome-ignore-all lint/suspicious/noConsole: its okay to show it working */

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incorrect comment syntax for Biome suppression.

Biome file-level suppression comments must start with biome-ignore-all, but Biome uses // comment style for suppression comments, not JSDoc-style block comments. The current JSDoc-style comment (/** ... */) is non-standard and may not work as intended.

Apply this diff to use the correct comment syntax:

-/** biome-ignore-all lint/suspicious/noConsole: its okay to show it working */
+// biome-ignore-all lint/suspicious/noConsole: its okay to show it working
🤖 Prompt for AI Agents
In benchmarks/didomi/app/layout.tsx around line 1, the Biome suppression is
currently written as a JSDoc block comment (/** biome-ignore-all ... */) which
Biome does not recognize; replace it with a line comment using the correct
syntax: start the file with // biome-ignore-all lint/suspicious/noConsole: its
okay to show it working so the file-level suppression is applied properly.

src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"
/>
<script
// biome-ignore lint/security/noDangerouslySetInnerHtml: its okay to set inner html

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Improve the biome-ignore comment justification.

The comment reason "its okay to set inner html" is vague. Consider a more descriptive justification that explains why this usage is safe, such as: "Required OneTrust callback stub - hardcoded content with no user input".

Apply this diff to improve the comment:

-					// biome-ignore lint/security/noDangerouslySetInnerHtml: its okay to set inner html
+					// biome-ignore lint/security/noDangerouslySetInnerHtml: Required OneTrust callback stub - hardcoded content with no user input
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// biome-ignore lint/security/noDangerouslySetInnerHtml: its okay to set inner html
// biome-ignore lint/security/noDangerouslySetInnerHtml: Required OneTrust callback stub - hardcoded content with no user input
🧰 Tools
🪛 ast-grep (0.39.6)

[warning] 23-23: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🤖 Prompt for AI Agents
In benchmarks/onetrust/app/layout.tsx around line 23, the biome-ignore comment
justification is vague; replace the current "its okay to set inner html" with a
more descriptive reason such as "Required OneTrust callback stub - hardcoded
content with no user input" so the linter exception documents why
dangerouslySetInnerHTML is safe here and clarifies there is no user-supplied
content involved.

Comment thread benchmarks/onetrust/package.json Outdated
Comment on lines +99 to +113
```
? Select benchmarks to run:
◼ baseline
◼ c15t-nextjs
◼ cookieyes

● info Config iterations: baseline: 5, c15t-nextjs: 3, cookieyes: 5

? Number of iterations (press Enter to use config values):
› Default: 5

[Just press Enter to use config values, or type a number to override all]

? Show results panel after completion? › Yes
```

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix markdown fences to satisfy lint.

markdownlint still flags these fences because they lack surrounding blank lines and language identifiers. Add the empty lines before/after each block and declare an appropriate language so the docs build doesn’t fail.

-**Example:**
-```
+**Example:**
+
+```text
 ? Select benchmarks to run:
   ◼ baseline
   ◼ c15t-nextjs
   ◼ cookieyes
-```
+```

-**Interactive Multi-Select Mode:**
-```
+**Interactive Multi-Select Mode:**
+
+```text
 ? Select benchmarks to view (use space to toggle, all selected by default):
   ◼ baseline (benchmarks/baseline)
   ◼ c15t-nextjs (benchmarks/c15t-nextjs)
   ◼ cookieyes (benchmarks/cookieyes)
-```
+```

-**For Consent.io team members:**
-```
+**For Consent.io team members:**
+
+```bash
 # Enable admin mode
 export CONSENT_ADMIN=true
@@
 cookiebench save
 cookiebench db push
-```
+```

Also applies to: 170-178, 336-343

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

99-99: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In packages/cookiebench-cli/README.md around lines 99-113 (also apply same fixes
at 170-178 and 336-343): the fenced code blocks lack surrounding blank lines and
language identifiers causing markdownlint failures; add a blank line before and
after each triple-backtick fence and add an appropriate language tag (e.g., text
for interactive prompts, bash for shell snippets) to each opening fence so every
block is surrounded by empty lines and begins with ```text or ```bash as
applicable.

Comment on lines +373 to +374
4. **Transparency** (15%): Open source status, documentation, licensing
5. **User Experience** (10%): Banner timing, coverage, layout shifts

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hyphenate the compound adjective.

When “open source” modifies “status,” markdownlint/LanguageTool expect “open-source status.” Quick wording tweak keeps automated checks quiet.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~373-~373: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...ice overhead 4. Transparency (15%): Open source status, documentation, licensing 5. **U...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🤖 Prompt for AI Agents
In packages/cookiebench-cli/README.md around lines 373 to 374, the phrase "Open
source status" should be hyphenated as "Open-source status"; update the markdown
line so the compound adjective modifying "status" uses a hyphen ("Open-source
status") to satisfy markdownlint/LanguageTool and keep automated checks quiet.

Comment on lines +90 to +94
visibilityTime:
cookieBannerData?.bannerVisibilityTime ||
cookieBannerData?.bannerInteractiveTime ||
0,
viewportCoverage: cookieBannerData?.viewportCoverage || 0,

ghost Oct 31, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve zero visibility timings with nullish coalescing.

The intent is to fall back to bannerInteractiveTime only when bannerVisibilityTime is absent, but || also fires when the measured visibility is 0. A banner that becomes visible at 0 ms will therefore get overwritten by the (typically higher) interactive time, inflating the reported UX timing. Please switch these fallbacks to ?? so only null/undefined trigger the fallback.

 			visibilityTime:
-				cookieBannerData?.bannerVisibilityTime ||
-				cookieBannerData?.bannerInteractiveTime ||
+				cookieBannerData?.bannerVisibilityTime ??
+				cookieBannerData?.bannerInteractiveTime ??
 				0,
...
 				visibilityTime:
-					cookieBannerData?.bannerVisibilityTime ||
-					cookieBannerData?.bannerInteractiveTime ||
+					cookieBannerData?.bannerVisibilityTime ??
+					cookieBannerData?.bannerInteractiveTime ??
 					0,

Also applies to: 206-210

🤖 Prompt for AI Agents
In packages/runner/src/performance-aggregator.ts around lines 90-94 (and
similarly update lines 206-210), the visibilityTime and viewportCoverage
fallbacks use || which treats 0 as absent; replace those logical-OR fallbacks
with nullish coalescing (??) so only null/undefined trigger the fallback — e.g.,
use cookieBannerData?.bannerVisibilityTime ??
cookieBannerData?.bannerInteractiveTime ?? 0 and
cookieBannerData?.viewportCoverage ?? 0 (and mirror the same ?? changes at lines
206-210).

… benchmarks and update pnpm-lock.yaml accordingly. This cleanup enhances project maintainability and reduces unnecessary package bloat.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (3)
benchmarks/c15t-react/next-env.d.ts (1)

3-6: Duplicate: Manual import requires typedRoutes configuration.

This concern was already raised in a previous review. The manual import of ./.next/types/routes.d.ts on line 3 requires typedRoutes: true to be enabled in next.config.ts for Next.js to generate the route types file. Without this configuration, the import will either fail at compile time or be overwritten when Next.js regenerates next-env.d.ts.

Please ensure typedRoutes: true is added to the Next.js config for this benchmark.

benchmarks/cookie-control/next-env.d.ts (1)

3-6: Duplicate: Manual import requires typedRoutes configuration.

Same issue as in benchmarks/c15t-react/next-env.d.ts: the manual import on line 3 requires typedRoutes: true in next.config.ts. Verify this is configured for the cookie-control benchmark.

benchmarks/onetrust/next-env.d.ts (1)

3-6: Duplicate: Manual import requires typedRoutes configuration.

Same issue as other next-env.d.ts files in this PR: the manual import on line 3 requires typedRoutes: true in the onetrust benchmark's next.config.ts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a509bd5 and 674766e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • benchmarks/baseline/package.json (2 hunks)
  • benchmarks/c15t-nextjs/next-env.d.ts (1 hunks)
  • benchmarks/c15t-nextjs/package.json (2 hunks)
  • benchmarks/c15t-react/next-env.d.ts (1 hunks)
  • benchmarks/c15t-react/package.json (2 hunks)
  • benchmarks/cookie-control/next-env.d.ts (1 hunks)
  • benchmarks/cookie-control/package.json (2 hunks)
  • benchmarks/cookie-yes/next-env.d.ts (1 hunks)
  • benchmarks/cookie-yes/package.json (2 hunks)
  • benchmarks/didomi/next-env.d.ts (1 hunks)
  • benchmarks/enzuzo/next-env.d.ts (1 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/next-env.d.ts (1 hunks)
  • benchmarks/iubenda/package.json (2 hunks)
  • benchmarks/ketch/next-env.d.ts (1 hunks)
  • benchmarks/onetrust/next-env.d.ts (1 hunks)
  • benchmarks/onetrust/package.json (2 hunks)
  • benchmarks/osano/next-env.d.ts (1 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/usercentrics/next-env.d.ts (1 hunks)
  • benchmarks/usercentrics/package.json (1 hunks)
  • package.json (1 hunks)
🔇 Additional comments (25)
benchmarks/usercentrics/next-env.d.ts (1)

1-6: LGTM! Standard Next.js type declaration file.

This file correctly follows Next.js 15 conventions for TypeScript support. The triple-slash directives reference the appropriate Next.js types, and the import statement on line 3 will include the generated route types (created during next dev or next build). This aligns with the PR's broader effort to standardize Next.js configuration across benchmark packages.

benchmarks/onetrust/package.json (1)

12-12: Verify Next.js version 16.0.1 exists.

Same as benchmarks/osano/package.json: please verify that Next.js 16.0.1 is a valid, published version. As of recent releases, Next.js 15.x is the latest stable major version.

benchmarks/iubenda/package.json (1)

12-12: Verify Next.js version 16.0.1 exists.

Same issue across multiple benchmarks: verify that 16.0.1 is a valid Next.js version. Consider using a stable release version like 15.x if this version doesn't exist.

benchmarks/usercentrics/package.json (1)

12-12: Verify Next.js version 16.0.1 exists.

Same version verification issue as other benchmarks: ensure 16.0.1 is a valid, published Next.js version.

benchmarks/cookie-control/package.json (1)

12-12: Verify Next.js version 16.0.1 exists.

Same version verification needed as other benchmarks: confirm 16.0.1 is available or use a stable Next.js 15.x version.

benchmarks/osano/package.json (1)

12-12: No issues found — Next.js version 16.0.1 is valid and current.

Next.js 16.0.1 was published on October 28, 2025, making it the latest patch release and a valid dependency specification. The package.json entry is correct.

Likely an incorrect or invalid review comment.

benchmarks/c15t-nextjs/package.json (2)

2-2: LGTM: Package renamed for consistency.

The package name change from "with-c15t-nextjs" to "c15t-nextjs" aligns with the standardized naming convention across benchmark packages in this PR.


26-28: LGTM: Node.js version constraint added.

The engines field correctly enforces Node.js >=20.9.0, aligning with other benchmark packages in this refactor.

benchmarks/c15t-react/package.json (2)

2-2: LGTM: Package renamed for consistency.

The package name change from "with-c15t-react" to "c15t-react" follows the standardized naming pattern across benchmark packages.


25-27: LGTM: Node.js version constraint correctly specified.

The engines field is properly formatted (note the previous "engine" typo was already addressed in commit a823f20) and enforces Node.js >=20.9.0.

package.json (2)

5-12: LGTM: Scripts migrated to new cookiebench CLI.

The script commands have been successfully updated to use pnpm exec cookiebench instead of the deprecated benchmark-cli, aligning with the PR's refactoring objectives.


14-26: LGTM: Workspace dependencies updated.

The addition of @consentio/benchmark, @consentio/runner, and cookiebench as workspace dependencies, along with the removal of the deprecated @cookiebench/cli, properly reflects the new tooling architecture.

benchmarks/cookie-yes/package.json (2)

2-2: LGTM: Package renamed for consistency.

The package name change from "with-cookie-yes" to "cookie-yes" follows the standardized naming convention across benchmark packages.


24-26: LGTM: Node.js version constraint added.

The engines field correctly enforces Node.js >=20.9.0, consistent with other benchmark packages in this refactor.

benchmarks/baseline/package.json (2)

6-6: LGTM: Build script normalized.

The trailing space has been removed from the build script, improving consistency.


25-27: LGTM: Node.js version constraint correctly specified.

The engines field is properly formatted (the previous "engine" typo was already addressed in commit a823f20) and enforces Node.js >=20.9.0.

benchmarks/iubenda/next-env.d.ts (1)

1-6: LGTM: Standard Next.js TypeScript declarations.

This is the standard auto-generated Next.js environment declaration file that provides TypeScript type references for Next.js APIs and routes. The file correctly includes the necessary type references and route imports.

benchmarks/c15t-nextjs/next-env.d.ts (1)

3-3: LGTM: Route types import added.

The addition of the routes type import enhances TypeScript support for Next.js routing. This is a standard pattern consistent with other benchmark packages in this PR.

benchmarks/didomi/next-env.d.ts (1)

1-6: LGTM: Standard Next.js TypeScript declarations.

This is the standard auto-generated Next.js environment declaration file that provides TypeScript type references for Next.js APIs and routes. The file correctly includes all necessary declarations.

benchmarks/enzuzo/package.json (2)

24-26: Past issue addressed: engines field is now correctly spelled.

The previous typo of "engine" has been corrected to "engines" (plural). The field will now be properly recognized by package managers.


11-15: No issues found.

Next.js 16 was released October 21, 2025, making the specified version 16.0.1 valid and current. The dependencies are compatible and correctly specified.

benchmarks/osano/next-env.d.ts (1)

1-6: LGTM: Standard Next.js boilerplate.

This is the standard Next.js ambient type declaration file. The triple-slash references and route types import are correct and automatically managed by Next.js.

benchmarks/ketch/next-env.d.ts (1)

1-6: LGTM: Standard Next.js boilerplate.

This file matches the standard Next.js ambient type declaration format and is correctly configured.

benchmarks/enzuzo/next-env.d.ts (1)

1-6: LGTM: Standard Next.js boilerplate.

This ambient type declaration file is consistent with the other benchmark next-env.d.ts files in this PR and follows Next.js standards.

benchmarks/cookie-yes/next-env.d.ts (1)

3-3: No issues found. The typed routes import is part of intentional codebase setup.

The import import "./.next/types/routes.d.ts"; appears consistently across all 12 benchmark directories (not just cookie-yes), with identical tsconfig.json configuration including .next/types/**/*.ts in the TypeScript include array. This codebase-wide pattern indicates the import is either auto-generated by Next.js tooling or intentionally added as part of the build process, not a manual violation of the file's warning.

"scripts": {
"benchmark": "pnpm exec benchmark-cli benchmark",
"build": "next build",
"dev": "next dev --port 3001",

ghost Nov 4, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Port conflict: multiple benchmarks use port 3001.

This benchmark uses port 3001, which is also used by benchmarks/iubenda/package.json (line 6) and benchmarks/usercentrics/package.json (line 6). Ensure unique ports are assigned across all benchmarks to avoid conflicts during concurrent development.

🤖 Prompt for AI Agents
In benchmarks/cookie-control/package.json around line 6, the dev script uses
port 3001 which conflicts with other benchmarks; update the "dev" script to use
a unique, unused port (for example 3003) by changing the Next.js dev command to
--port <new_port>, and if any local docs, README or CI scripts reference port
3001 for this benchmark, update them to the new port as well.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (6)
package.json (1)

28-30: ⚠️ Node version mismatch persists: root allows >=18 but benchmarks require >=20.9.0.

This conflict was previously flagged (see commit history). All benchmark packages specify node >=20.9.0, but the root workspace still allows >=18, which could allow developers to install incompatible versions.

Apply this diff to align the root constraint with benchmark requirements:

   "engines": {
-    "node": ">=18"
+    "node": ">=20.9.0"
   }
benchmarks/usercentrics/package.json (1)

6-6: ⚠️ Unresolved port conflict from previous review.

Line 6 continues to use --port 3001, which was previously flagged as conflicting with another benchmark (iubenda). Additionally, benchmarks/c15t-nextjs also uses port 3001. This will cause failures if multiple benchmarks are run concurrently. Assign unique ports to each benchmark.

packages/cookiebench-cli/package.json (3)

20-30: Critical: Replace alpha dependency with stable release.

Line 22 pins @clack/prompts to 1.0.0-alpha.7, which is a pre-release version. This was flagged in a previous review but has not been addressed. Alpha versions can introduce breaking changes and stability issues in production CLI code.

The latest stable version is 0.11.0. Upgrade to the stable release.

Apply this diff:

   "dependencies": {
     "@c15t/logger": "^1.0.1",
-    "@clack/prompts": "1.0.0-alpha.7",
+    "@clack/prompts": "^0.11.0",
     "@consentio/benchmark": "workspace:*",

9-11: Add BannerPlugin to inject shebang in CLI entry point.

The rslib.config.ts is missing the BannerPlugin configuration needed to inject #!/usr/bin/env node into the CLI executable. Without this, the dist/index.mjs entry point referenced in package.json's bin field will not be executable.

Add a plugins array to your rslib configuration:

plugins: [
  new BannerPlugin({
    banners: ["#!/usr/bin/env node"],
  }),
]

Alternatively, add a postbuild script in package.json to chmod the output file executable.


9-11: Critical: Bin entry requires shebang injection in rslib config.

The rslib.config.ts file does not include a BannerPlugin to inject the required #!/usr/bin/env node shebang into dist/index.mjs. Without this, the CLI will not be executable when installed as a global or local bin.

Add the following to packages/cookiebench-cli/rslib.config.ts:

import { defineConfig, BannerPlugin } from "@rslib/core";

export default defineConfig({
  plugins: [
    new BannerPlugin({
      banners: {
        js: ["#!/usr/bin/env node"],
      },
    }),
  ],
  // ... rest of config
});

Alternatively, add a postbuild script to chmod the output file: "postbuild": "chmod +x dist/index.mjs".

benchmarks/cookie-control/package.json (1)

6-6: ⚠️ Port conflict persists: multiple benchmarks use port 3001.

The dev script still uses port 3001, which conflicts with other benchmarks in the repository. While this was previously flagged, it remains unresolved. Assign a unique, unused port to avoid conflicts during concurrent development.

Apply this diff to resolve the conflict:

-    "dev": "next dev --port 3001",
+    "dev": "next dev --port 3005",

Note: Verify that port 3005 (or your chosen port) is not already in use by other benchmarks in the repository.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 674766e and 02620a7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • benchmarks/baseline/package.json (1 hunks)
  • benchmarks/c15t-nextjs/package.json (2 hunks)
  • benchmarks/c15t-react/package.json (2 hunks)
  • benchmarks/cookie-control/package.json (2 hunks)
  • benchmarks/cookie-yes/package.json (2 hunks)
  • benchmarks/didomi/package.json (2 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/package.json (2 hunks)
  • benchmarks/ketch/package.json (2 hunks)
  • benchmarks/onetrust/package.json (2 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/usercentrics/package.json (2 hunks)
  • package.json (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/shared/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-07T16:17:07.589Z
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").

Applied to files:

  • benchmarks/onetrust/package.json
  • benchmarks/cookie-control/package.json
  • benchmarks/enzuzo/package.json
  • benchmarks/iubenda/package.json
  • benchmarks/ketch/package.json
  • benchmarks/c15t-nextjs/package.json
  • benchmarks/cookie-yes/package.json
  • package.json
  • packages/benchmark/package.json
  • benchmarks/baseline/package.json
  • benchmarks/usercentrics/package.json
  • packages/cookiebench-cli/package.json
🔇 Additional comments (18)
benchmarks/enzuzo/package.json (1)

1-27: LGTM!

Manifest is well-structured with correct field naming (engines plural), appropriate version constraints, and proper workspace dependency references.

benchmarks/osano/package.json (1)

1-27: LGTM!

Manifest is consistent with other benchmark packages, with correct field naming and appropriate dependency constraints.

packages/shared/package.json (1)

1-28: LGTM!

The manifest properly omits the unnecessary empty dependencies object (addressing prior review feedback) and correctly defines export maps for the ESM module. Build tooling and structure are appropriate for a shared library package.

benchmarks/onetrust/package.json (1)

1-27: LGTM!

The package updates are clean: name normalized, React and type definitions bumped consistently, unused cookiebench devDependency removed, and benchmark script cleaned up. The engines field is correctly named and set to node >=20.9.0.

packages/runner/package.json (1)

21-27: Verify external dependency version constraints are intentional.

The package uses caret constraints on @playwright/test@^1.57.0 and playwright-performance-metrics@^1.2.4. For @c15t/logger, the exact version 1.0.0 is pinned. Confirm:

  1. Whether playwright-performance-metrics is truly an external package (or should be workspace:* for internal monorepo dependency)
  2. Whether the exact pinning of @c15t/logger@1.0.0 is intentional (may prevent security updates)
benchmarks/iubenda/package.json (1)

1-27: LGTM!

Updates follow the established pattern: name normalized, React and type definitions bumped consistently, unused dependencies removed, and engines constraint properly added at node >=20.9.0.

benchmarks/cookie-yes/package.json (1)

1-27: LGTM!

Updates are consistent with other benchmark packages: name normalized, React and type definitions bumped uniformly, and engines constraint properly configured at node >=20.9.0.

benchmarks/didomi/package.json (1)

2-27: Consistent dependency and engine constraint updates.

Package name, React versions, type definitions, and engines field all follow the PR-wide pattern. Changes are aligned with Next.js 16 and React 19 requirements.

packages/benchmark/package.json (1)

1-38: New benchmark package structure looks sound.

The ESM module setup, export surface, and workspace integration align well with the CLI refactoring. Dependencies for performance monitoring (perfume.js, playwright-performance-metrics) and logging are appropriate for a benchmark runner.

benchmarks/ketch/package.json (1)

2-26: Consistent with PR-wide benchmark updates.

Name, React versions, type definitions, and engines constraint all follow the established pattern. No issues identified.

benchmarks/c15t-react/package.json (1)

2-26: Consistent update aligned with PR-wide pattern.

Name, React versions, types, and engines all properly updated. Changes follow the established benchmark modernization pattern.

benchmarks/usercentrics/package.json (1)

2-26: Otherwise consistent with PR pattern.

Name, React versions, types, and engines properly updated. Aside from the port conflict, changes follow the established pattern.

benchmarks/c15t-nextjs/package.json (1)

2-28: Consistent dependency and configuration updates.

Name, React versions, types, and engines all properly aligned. Changes follow the PR-wide modernization pattern.

benchmarks/baseline/package.json (1)

6-26: Clean, consistent update across all fields.

React versions, type definitions, engines constraint, and scripts all properly updated. Minor formatting improvement (trailing space removed from build script). Changes align with PR-wide modernization.

packages/cookiebench-cli/package.json (2)

1-39: Overall structure and dependencies align with CLI goals.

The ESM module setup, export surface, bin entry, and dev tooling are well-configured for a CLI package. Workspace integration with benchmark, runner, and shared packages is appropriate. Aside from the critical issues above (shebang injection and alpha dependency), the manifest is sound.


1-39: The review comment references Next.js 16.0.1 and React 19.2.1 compatibility, but packages/cookiebench-cli/package.json does not contain either Next.js or React as dependencies. This package is a CLI tool with dependencies like cli-table3, figlet, and picocolors. If compatibility concerns apply, they would be relevant to a different package (possibly one of the workspace packages like @consentio/benchmark or @consentio/runner). Additionally, the comment contains internal scratchpad notes and malformed verification syntax that should not appear in a review comment.

Likely an incorrect or invalid review comment.

benchmarks/cookie-control/package.json (2)

2-2: Approve project rename and engine constraint updates.

The project rename to cookie-control aligns with the benchmark's purpose. The added Node >=20.9.0 engine constraint is appropriate and consistent with the broader refactor moving to Node 20+. Dependency updates (React 19.2.1, TypeScript types) appear to be routine patch/minor version bumps.

Also applies to: 24-26


13-14: React 19.2.1 and react-dom 19.2.1 are compatible with Next.js 16.0.1. Next.js 16 is explicitly designed to support React 19.2, as documented in the official release notes and upgrade guidance. The TypeScript type definitions (@types/react ^19.2.7 and @types/react-dom ^19.2.3) are properly aligned with the React version used, ensuring no type mismatches.

"scripts": {
"benchmark": "pnpm exec benchmark-cli benchmark",
"build": "next build",
"dev": "next dev --port 3001",

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Port conflict: dev server uses port 3001.

Line 7 specifies --port 3001, which conflicts with benchmarks/usercentrics/package.json (also port 3001). Running both concurrently in development will cause port binding errors. Assign a unique port to this benchmark.

🤖 Prompt for AI Agents
In benchmarks/c15t-nextjs/package.json around line 7, the dev script sets the
Next dev server to port 3001 which conflicts with
benchmarks/usercentrics/package.json; change the port to a unique unused port
(for example 3002 or another agreed port) by updating the "dev" script to use
--port <unique-port> so both benchmarks can run concurrently without binding
errors.

Comment thread packages/benchmark/package.json Outdated
Comment on lines +21 to +26
"dependencies": {
"@c15t/logger": "1.0.0",
"@consentio/shared": "workspace:*",
"@playwright/test": "^1.57.0",
"perfume.js": "^9.4.0",
"playwright-performance-metrics": "^1.2.4"

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Exact version pin on @c15t/logger may limit security patches.

Line 22 pins @c15t/logger to 1.0.0 exactly, which means patch releases with bug fixes and security updates won't be picked up automatically. Consider using ^1.0.0 to allow patch and minor updates, unless a specific version is required for compatibility.

🤖 Prompt for AI Agents
In packages/benchmark/package.json around lines 21 to 26, the dependency for
@c15t/logger is pinned to "1.0.0"; change it to a caret range (e.g., ^1.0.0) to
allow patch/minor updates so security fixes are picked up automatically, then
update the lockfile (run npm/yarn install) and run the test/benchmark suite to
ensure no compatibility break; if a strict pin is required for compatibility,
add a comment in the package.json or repo docs explaining why and note the
process to manually update for security patches.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (9)
packages/cookiebench-cli/tsconfig.json (1)

1-15: [Duplicate] Missing declaration file generation.

This concern was raised in a previous review and remains unaddressed. The "declaration": true option is still missing and would improve consistency with other packages in the monorepo.

package.json (1)

28-30: Resolve Node.js engine version mismatch.

The root package.json specifies "node": ">=18", but all benchmark packages require "node": ">=20.9.0". This inconsistency could allow developers installing Node 18 or 19, satisfying the root constraint but failing benchmark requirements. Align the root engines to ">=20.9.0" to ensure consistency across the workspace.

   "engines": {
-    "node": ">=18"
+    "node": ">=20.9.0"
   }
packages/cookiebench-cli/package.json (1)

20-30: Replace alpha dependency with stable release.

@clack/prompts is pinned to version 1.0.0-alpha.7 (line 22), which carries stability and breaking-change risks. The latest stable version is 0.11.0. Upgrade to the stable release for production readiness.

   "dependencies": {
     "@c15t/logger": "^1.0.1",
-    "@clack/prompts": "1.0.0-alpha.7",
+    "@clack/prompts": "^0.11.0",
     "@consentio/benchmark": "workspace:*",
benchmarks/iubenda/package.json (1)

6-6: ⚠️ Port conflict: 3001 used by multiple benchmarks.

This benchmark (and benchmarks/cookie-control/package.json, benchmarks/usercentrics/package.json, benchmarks/cookie-yes/package.json, and benchmarks/c15t-nextjs/package.json) all use port 3001. Running multiple benchmarks concurrently will cause binding errors. Assign a unique port to this benchmark.

packages/benchmark/package.json (1)

22-22: Consider using caret range for @c15t/logger to allow security patches.

Line 22 pins @c15t/logger to exact version 1.0.0, which blocks automatic security patch updates. All other dependencies use caret ranges (e.g., ^1.0.0). Unless a specific version is required for compatibility, use "@c15t/logger": "^1.0.0" to allow patch and minor releases.

benchmarks/cookie-control/package.json (1)

6-6: Port conflict: 3001 already used by multiple benchmarks.

benchmarks/usercentrics/package.json (1)

6-6: Port conflict: 3001 already used by multiple benchmarks.

benchmarks/cookie-yes/package.json (1)

6-6: Port conflict: 3001 already used by multiple benchmarks.

benchmarks/c15t-nextjs/package.json (1)

7-7: Port conflict: 3001 already used by multiple benchmarks.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 02620a7 and 83a3559.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • benchmarks/baseline/package.json (1 hunks)
  • benchmarks/c15t-nextjs/package.json (2 hunks)
  • benchmarks/c15t-react/package.json (2 hunks)
  • benchmarks/cookie-control/package.json (2 hunks)
  • benchmarks/cookie-yes/package.json (2 hunks)
  • benchmarks/didomi/package.json (2 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/package.json (2 hunks)
  • benchmarks/ketch/package.json (2 hunks)
  • benchmarks/onetrust/package.json (2 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/usercentrics/package.json (2 hunks)
  • package.json (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/tsconfig.json (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/shared/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").
🔇 Additional comments (20)
benchmarks/osano/package.json (2)

1-27: Package structure looks good.

The manifest is well-structured with proper exports, scripts, and engine constraints. The "engines" field correctly specifies Node >=20.9.0.


12-14: > Likely an incorrect or invalid review comment.

benchmarks/enzuzo/package.json (1)

1-27: Package structure is consistent with similar benchmark packages.

The manifest follows the established pattern for benchmark packages. Approval is contingent on the Next.js 16 / React 19.2.1 compatibility verification performed on osano.

benchmarks/onetrust/package.json (1)

13-21: React and type package versions look good.

Updates to React 19.2.1 and type packages (24.10.1, 19.2.7, 19.2.3) follow the established pattern across benchmarks and are consistent with the Next.js 16 baseline.

packages/shared/package.json (1)

1-28: Well-structured utilities package manifest.

The ESM-focused configuration with clean exports and rslib tooling is appropriate. No runtime dependencies is correct for a shared utilities library.

package.json (1)

5-12: Script refactoring to cookiebench looks good.

Delegating benchmark tasks to the unified cookiebench CLI simplifies the root workflow. The turbo-based build/check/dev/fmt/lint commands are appropriately maintained.

packages/runner/package.json (1)

21-26: Runtime dependency placement of @playwright/test warrants verification.

Typically, @playwright/test is a dev-only dependency, but here it's listed in dependencies. Confirm this is intentional (i.e., the runner executes Playwright tests at runtime) rather than a miscategorization.

benchmarks/didomi/package.json (3)

2-2: Package renamed from with-didomi to didomi.

This normalization aligns the package name with the benchmark provider name, improving clarity. The change is safe as this is a private workspace package.


14-15: React dependency updates are safe.

Minor version bump from 19.2.0 to 19.2.1 is a patch release with no breaking changes expected.


20-26: Type packages and engine constraint properly updated.

Updates to @types packages and the addition of "engines": {"node": ">=20.9.0"} align with the benchmark modernization strategy across the repository.

benchmarks/iubenda/package.json (1)

13-14: React and TypeScript type definitions updated correctly.

The updates from React 19.2.0 to 19.2.1 and corresponding TypeScript type definitions are safe patch releases. No breaking changes expected for Next.js 16.

Also applies to: 19-21

benchmarks/cookie-control/package.json (1)

13-14: React and TypeScript type definitions updated correctly.

The updates from React 19.2.0 to 19.2.1 and corresponding TypeScript type definitions are safe patch releases.

Also applies to: 19-21

benchmarks/usercentrics/package.json (1)

13-14: React and TypeScript type definitions updated correctly.

The updates from React 19.2.0 to 19.2.1 and corresponding TypeScript type definitions are safe patch releases.

Also applies to: 19-21

benchmarks/cookie-yes/package.json (1)

13-14: React and TypeScript type definitions updated correctly.

The updates from React 19.2.0 to 19.2.1 and corresponding TypeScript type definitions are safe patch releases.

Also applies to: 19-21

benchmarks/c15t-nextjs/package.json (1)

15-16: React and TypeScript type definitions updated correctly.

The updates from React 19.2.0 to 19.2.1 and corresponding TypeScript type definitions are safe patch releases.

Also applies to: 21-23

benchmarks/c15t-react/package.json (1)

14-15: React dependencies, types, and Node engine constraint properly configured.

The React 19.2.1 updates, TypeScript type definitions, and correct engines field (plural) are all properly configured. Port 3003 is unique and avoids conflicts with other benchmarks.

Also applies to: 20-22, 25-27

benchmarks/ketch/package.json (1)

13-14: React dependencies, types, and Node engine constraint properly configured.

The React 19.2.1 updates, TypeScript type definitions, and correct engines field are all properly configured. The dev script uses the default Next.js port 3000 (no explicit port specified), which should not conflict with other benchmarks using 3001 or 3003.

Also applies to: 19-21, 24-26

benchmarks/baseline/package.json (3)

25-27: ✅ Critical issue resolved: "engines" field now correctly spelled.

The previous critical issue flagged the incorrect field name "engine" (singular). This has been corrected to "engines" (plural), which ensures npm/Yarn will recognize and enforce the Node.js version constraint.


6-10: Script cleanup aligns with PR objectives.

The removal of the "benchmark" script and whitespace normalization align with the PR's migration to the new cookiebench-cli tooling and away from the legacy CLI setup.


14-15: Dependency and type definition updates maintain alignment.

React/React-DOM patch bumps (19.2.0 → 19.2.1) and corresponding type definition updates keep runtime and types in sync. This is appropriate for dependency maintenance.

Also applies to: 20-23

Comment thread benchmarks/baseline/package.json Outdated

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
packages/cookiebench-cli/tsconfig.json (1)

1-15: Past comment: Consider declaration files (already flagged as nitpick).

A previous review suggested adding "declaration": true for consistency with other packages in the monorepo. While CLIs are typically not consumed as libraries and don't require declaration files, this remains a valid point for internal consistency if utilities might be imported. This is marked as a trivial nitpick and can be addressed separately if desired.

benchmarks/cookie-control/package.json (1)

6-6: ** Resolve port conflict: port 3001.**

The dev script uses port 3001, which conflicts with benchmarks/iubenda/package.json and benchmarks/usercentrics/package.json. Assign a unique port (e.g., 3003) to avoid binding failures during concurrent development.

packages/benchmark/package.json (1)

21-27: Inconsistent dependency versioning: use ^1.0.1 for @c15t/logger instead of exact pin.

Line 22 pins @c15t/logger to 1.0.1 exactly, while other production dependencies use semver ranges (e.g., ^1.57.0 for Playwright, ^9.4.0 for perfume.js). Exact pinning prevents automatic patch and minor updates with security fixes. Use a caret range ^1.0.1 unless a specific version is required for compatibility.

   "dependencies": {
-    "@c15t/logger": "1.0.1",
+    "@c15t/logger": "^1.0.1",
     "@consentio/shared": "workspace:*",
packages/cookiebench-cli/package.json (1)

20-22: Upgrade @clack/prompts to stable release; alpha versions pose stability risks.

The @clack/prompts dependency is pinned to 1.0.0-alpha.7 (line 22), which remains an alpha/pre-release version. While the version was updated from a previous alpha, the stable release should be preferred to avoid breaking changes and instability in production. A previous review flagged this issue but it was not resolved.

Consider upgrading to the latest stable version. If there's a specific reason to use the alpha, document it in a comment or ADR.

-    "@clack/prompts": "1.0.0-alpha.7",
+    "@clack/prompts": "0.11.0",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 83a3559 and aad9ccd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • benchmarks/baseline/package.json (1 hunks)
  • benchmarks/c15t-nextjs/package.json (1 hunks)
  • benchmarks/c15t-react/package.json (1 hunks)
  • benchmarks/cookie-control/package.json (1 hunks)
  • benchmarks/cookie-yes/package.json (1 hunks)
  • benchmarks/didomi/package.json (2 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/package.json (1 hunks)
  • benchmarks/ketch/package.json (1 hunks)
  • benchmarks/onetrust/package.json (1 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/usercentrics/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/tsconfig.json (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/shared/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").
📚 Learning: 2025-11-07T16:17:07.589Z
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").

Applied to files:

  • packages/benchmark/package.json
  • packages/cookiebench-cli/package.json
  • benchmarks/iubenda/package.json
  • benchmarks/cookie-control/package.json
  • package.json
  • benchmarks/cookie-yes/package.json
  • benchmarks/baseline/package.json
  • benchmarks/c15t-nextjs/package.json
  • packages/cookiebench-cli/tsconfig.json
🔇 Additional comments (12)
packages/cookiebench-cli/tsconfig.json (1)

1-15: Configuration looks solid for a modern CLI project.

The TypeScript configuration is well-aligned with current best practices:

  • ES2022 target with appropriate libraries (ES2022, DOM, DOM.Iterable)
  • Modern module resolution (bundler) and ESNext modules
  • Strict type checking enabled with sensible defaults
  • Clear directory structure (src → dist)

No issues identified.

benchmarks/enzuzo/package.json (1)

1-27: LGTM!

New benchmark package follows the established pattern with correct Node engine constraint and consistent dependency versions across the workspace.

benchmarks/osano/package.json (1)

1-27: LGTM!

New Osano benchmark package is well-structured with consistent dependency versions and proper Node engine constraint.

benchmarks/iubenda/package.json (1)

1-27: LGTM!

Iubenda benchmark package update follows the established pattern with normalized package name, consistent dependency version upgrades, and proper Node engine constraint.

benchmarks/didomi/package.json (1)

1-28: LGTM!

Didomi benchmark package update is consistent with the broader refactor pattern—normalized package name, aligned dependency versions, and proper Node engine constraint.

benchmarks/ketch/package.json (1)

1-27: LGTM!

Ketch benchmark package follows the refactor pattern with normalized package name, consistent dependency upgrades, and proper Node engine constraint. Uses default port 3000, avoiding conflicts.

benchmarks/c15t-react/package.json (1)

1-28: ✅ Approved. All dependency updates and engine constraints are correctly configured.

The package naming is consistent with the broader refactoring, dependency versions align with the baseline, and the engines field (correctly spelled) enforces Node.js >=20.9.0.

benchmarks/cookie-yes/package.json (1)

1-27: ✅ Approved. Package configuration is consistent with baseline updates.

Dependency versions, type package updates, and engine constraints are all correctly configured.

benchmarks/c15t-nextjs/package.json (1)

7-7: ⚠️ Verify port conflict resolution: dev script still uses port 3001.

A previous review flagged that port 3001 conflicts with benchmarks/usercentrics/package.json. The dev script (line 7) still specifies --port 3001. Confirm whether this conflict has been resolved elsewhere (e.g., usercentrics reassigned to a different port), or assign a unique port to this benchmark.

benchmarks/baseline/package.json (1)

1-28: ✅ Approved. Previously flagged critical issues have been resolved.

The engines field typo has been corrected (now "engines" not "engine"), and Next.js has been upgraded from 16.0.1 to 16.0.7, addressing the RSC deserialization security vulnerabilities.

benchmarks/onetrust/package.json (1)

1-27: ✅ Approved. Previously flagged unused dependency has been removed.

The cookiebench workspace dependency that was flagged as unused in the previous review has been removed. Only the schema and config workspace dependencies remain.

packages/shared/package.json (1)

1-28: ✅ Approved. Empty dependencies object has been removed.

The unnecessary empty dependencies field flagged in the previous review has been removed. The package is appropriately configured as a shared utilities library with no runtime dependencies.

"private": true,
"scripts": {
"build": "next build",
"dev": "next dev --port 3001",

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Resolve port conflict: port 3001 is duplicated.

The dev script uses port 3001, which is also used by benchmarks/iubenda/package.json and benchmarks/cookie-control/package.json. Running multiple benchmarks concurrently in development will cause port binding failures.

Assign a unique port (e.g., 3004) or use an environment variable (e.g., ${USERCENTRICS_PORT:-3004}) to allow flexible port assignment.

🤖 Prompt for AI Agents
In benchmarks/usercentrics/package.json around line 6, the "dev" script
hardcodes port 3001 which conflicts with other benchmark packages; update the
script to use a unique default port or an environment variable to allow
overrides (for example change it to use port 3004 or to reference
${USERCENTRICS_PORT:-3004}) so concurrent dev runs won't fail; modify the "dev"
entry accordingly and ensure documentation or run scripts mention the new env
var if used.

Comment thread package.json
Comment on lines +28 to +30
"engines": {
"node": ">=18"
}

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "package.json" | head -20

Repository: consentdotio/benchmarks

Length of output: 726


🏁 Script executed:

#!/bin/bash
# Extract engines constraints from all package.json files
for file in ./package.json ./benchmarks/*/package.json ./packages/*/package.json; do
  if [ -f "$file" ]; then
    engines=$(jq -r '.engines // empty' "$file" 2>/dev/null)
    if [ -n "$engines" ]; then
      echo "=== $file ==="
      echo "$engines"
    fi
  fi
done

Repository: consentdotio/benchmarks

Length of output: 932


🏁 Script executed:

cat -n ./package.json

Repository: consentdotio/benchmarks

Length of output: 1109


🏁 Script executed:

cat -n ./packages/cookiebench-cli/package.json

Repository: consentdotio/benchmarks

Length of output: 1329


Node.js version mismatch: root constraint incompatible with benchmark packages.

Root package.json specifies "node": ">=18", but all 12 benchmark packages require "node": ">=20.9.0". Developers installing with Node 18 or 19 will fail to meet benchmark requirements, causing runtime errors when executing benchmark scripts (e.g., pnpm benchmark).

Align the root engines constraint to ">=20.9.0" or explicitly document why the root supports a broader range.

🤖 Prompt for AI Agents
In package.json around lines 28 to 30, the root "engines" field lists "node":
">=18" which conflicts with the 12 benchmark packages that require "node":
">=20.9.0"; update the root constraint to ">=20.9.0" (or the same semver range
used by the benchmark packages) so all packages agree, and if you deliberately
want broader support instead, add a clear comment or documentation entry
explaining why the root allows older Node versions and how to run benchmarks
using Node >=20.9.0.

Comment on lines +21 to +27
"dependencies": {
"@c15t/logger": "1.0.1",
"@consentio/benchmark": "workspace:*",
"@consentio/shared": "workspace:*",
"@playwright/test": "^1.57.0",
"playwright-performance-metrics": "^1.2.4"
},

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Inconsistent dependency versioning: use ^1.0.1 for @c15t/logger instead of exact pin.

Line 22 pins @c15t/logger to 1.0.1 exactly, while other production dependencies use semver ranges (e.g., ^1.57.0 for Playwright). Exact pinning prevents automatic patch and minor updates with security fixes. Use a caret range ^1.0.1 for consistency and to allow security patches.

   "dependencies": {
-    "@c15t/logger": "1.0.1",
+    "@c15t/logger": "^1.0.1",
     "@consentio/benchmark": "workspace:*",
🤖 Prompt for AI Agents
In packages/runner/package.json around lines 21 to 27, the dependency
"@c15t/logger" is pinned to "1.0.1" while other deps use semver ranges; change
the version string to "^1.0.1" to allow minor/patch updates and maintain
consistency with the project's dependency versioning; update the package.json
entry, run npm/yarn install to refresh lockfile, and commit the updated
package.json and lockfile.

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (8)
packages/cookiebench-cli/tsconfig.json (1)

8-11: Consider adding declaration file generation for monorepo consistency.

This configuration lacks "declaration": true, which was previously flagged as misaligned with other packages in the monorepo (e.g., packages/runner generates .d.ts files). Although CLIs typically don't export public APIs, adding declarations would improve IDE support if other packages import utilities from this CLI.

Apply this diff if declaration files are desired:

     "strict": true,
     "skipLibCheck": true,
+    "declaration": true,
     "outDir": "dist",
packages/cookiebench-cli/package.json (1)

22-22: Replace alpha dependency with stable release.

Line 22 pins @clack/prompts to 1.0.0-alpha.7 (still an alpha version). According to previous review, a stable release 0.11.0 is available. Upgrade to the stable version to avoid potential breaking changes and stability issues in production.

What is the latest stable version of @clack/prompts npm package?
packages/runner/package.json (1)

21-27: Inconsistent exact pin on @c15t/logger prevents security patches.

Line 22 pins @c15t/logger to 1.0.1 exactly, while other dependencies use semver ranges. Exact pinning prevents automatic patch updates with security fixes. Use ^1.0.1 for consistency and to allow minor/patch updates.

benchmarks/usercentrics/package.json (1)

6-6: Port conflict: 3001 is already used by other benchmarks.

This was flagged in previous reviews: port 3001 is also used by benchmarks/cookie-control, benchmarks/iubenda, and other benchmarks. Running multiple benchmarks concurrently in development will fail with port binding errors. Assign a unique port (e.g., 3004 or use an environment variable like ${USERCENTRICS_PORT:-3004}).

benchmarks/c15t-nextjs/package.json (1)

7-7: Port conflict: 3001 is already used by multiple benchmarks.

This was flagged in previous reviews: port 3001 conflicts with benchmarks/usercentrics, benchmarks/cookie-control, benchmarks/cookie-yes, and others. Change the dev script to use a unique port (e.g., 3002 or 3005) to enable concurrent development.

benchmarks/cookie-control/package.json (1)

6-6: Port conflict: 3001 is already used by multiple benchmarks.

This was flagged in previous reviews: port 3001 is used by multiple benchmarks including benchmarks/iubenda, benchmarks/usercentrics, and benchmarks/cookie-yes. Running concurrently will cause port binding failures. Assign a unique port (e.g., 3005 or higher).

benchmarks/cookie-yes/package.json (1)

6-6: Port conflict: 3001 is already used by multiple benchmarks.

This was flagged in previous reviews: port 3001 is used by benchmarks/usercentrics, benchmarks/cookie-control, benchmarks/c15t-nextjs, and others. Running multiple benchmarks concurrently in development will fail. Assign a unique port to avoid conflicts.

package.json (1)

28-30: ⚠️ Node.js version mismatch: root constraint incompatible with benchmark packages.

Root package.json specifies "node": ">=18", but all 12 benchmark packages require "node": ">=20.9.0". Developers installing with Node 18 or 19 will be unable to run benchmarks (e.g., pnpm benchmark), causing runtime failures.

Align the root engines constraint to "node": ">=20.9.0" to match the benchmark packages' requirement.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aad9ccd and 44bef01.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • benchmarks/baseline/package.json (1 hunks)
  • benchmarks/c15t-nextjs/package.json (1 hunks)
  • benchmarks/c15t-react/package.json (1 hunks)
  • benchmarks/cookie-control/package.json (1 hunks)
  • benchmarks/cookie-yes/package.json (1 hunks)
  • benchmarks/didomi/package.json (2 hunks)
  • benchmarks/enzuzo/package.json (1 hunks)
  • benchmarks/iubenda/package.json (1 hunks)
  • benchmarks/ketch/package.json (1 hunks)
  • benchmarks/onetrust/package.json (1 hunks)
  • benchmarks/osano/package.json (1 hunks)
  • benchmarks/usercentrics/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/benchmark/package.json (1 hunks)
  • packages/cookiebench-cli/package.json (1 hunks)
  • packages/cookiebench-cli/tsconfig.json (1 hunks)
  • packages/runner/package.json (1 hunks)
  • packages/shared/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").
📚 Learning: 2025-11-07T16:17:07.589Z
Learnt from: danielsitek
Repo: consentdotio/benchmarks PR: 6
File: benchmarks/with-dgp-cookie-consent/config.json:15-20
Timestamp: 2025-11-07T16:17:07.589Z
Learning: In the cookiebench/benchmark-schema package (packages/benchmark-schema/schema.json), the valid enum values for bundleType are: "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife").

Applied to files:

  • packages/benchmark/package.json
  • packages/cookiebench-cli/tsconfig.json
  • benchmarks/iubenda/package.json
  • packages/cookiebench-cli/package.json
  • package.json
  • benchmarks/cookie-yes/package.json
  • benchmarks/c15t-nextjs/package.json
  • benchmarks/baseline/package.json
  • benchmarks/cookie-control/package.json
🔇 Additional comments (19)
packages/cookiebench-cli/tsconfig.json (1)

1-15: Remove unnecessary DOM libraries from CLI configuration.

A CLI package typically doesn't require DOM types. The inclusion of "DOM" and "DOM.Iterable" in the lib array is unusual unless this package explicitly generates or manipulates DOM-based benchmark code.

Verify these are necessary; if not, simplify to just ["ES2022"].

     "lib": ["ES2022", "DOM", "DOM.Iterable"],

Should be:

+    "lib": ["ES2022"],
benchmarks/osano/package.json (1)

1-27: Verify bundleType value in related config.json.

The related benchmarks/osano/config.json mentioned in the enriched summary uses bundleType: "iife". According to retrieved learnings, valid enum values in the benchmark schema are "esm", "cjs", "iffe", and "bundled". Note that it's "iffe" (not "iife"). Verify and correct if needed.

benchmarks/enzuzo/package.json (1)

1-27: Verify bundleType value in related config.json.

Like the osano benchmark, verify that the related benchmarks/enzuzo/config.json uses bundleType: "iffe" (not "iife"). According to retrieved learnings, valid values are "esm", "cjs", "iffe", and "bundled".

packages/shared/package.json (1)

1-28: LGTM!

The shared package manifest is well-structured with appropriate exports mapping, scripts, and dev dependencies. No structural issues identified.

benchmarks/iubenda/package.json (1)

1-27: Verify bundleType value in related config.json.

The AI summary indicates the related benchmarks/iubenda/config.json includes bundleType configuration. Verify that it uses bundleType: "iffe" (not "iife"). According to retrieved learnings, valid enum values in the benchmark schema are "esm", "cjs", "iffe", and "bundled".

benchmarks/onetrust/package.json (1)

1-27: Verify bundleType value in related config.json.

The AI summary indicates the related benchmarks/onetrust/config.json has bundleType configuration. Verify that it uses bundleType: "iffe" (not "iife"). According to retrieved learnings, valid enum values in the benchmark schema are "esm", "cjs", "iffe", and "bundled".

benchmarks/ketch/package.json (2)

24-26: ✅ Correct field naming: engines (plural) is now properly used.

The critical "engine" typo from the previous commit has been corrected to "engines". Node.js version constraint >=20.9.0 will now be properly enforced by package managers.


12-14: Dependency and type definition updates are consistent.

All dependency versions align with the benchmark standardization across the PR (Next 16.0.7, React 19.2.1, type definitions updated to ^19.2.x).

Also applies to: 19-21

benchmarks/c15t-react/package.json (2)

25-27: ✅ Correct field naming: engines (plural) is properly used.

The "engines" field correctly enforces Node.js version >=20.9.0.


6-6: Port assignment avoids conflicts.

The dev server port 3003 is unique and does not conflict with other benchmarks in this PR.

benchmarks/c15t-nextjs/package.json (1)

26-28: ✅ Correct field naming and engine constraint.

The "engines" field correctly specifies Node.js version >=20.9.0.

package.json (2)

5-5: ✅ Script migration to cookiebench CLI is correct.

The migration from direct benchmark-cli invocations to pnpm exec cookiebench commands aligns with the PR's CLI refactoring objectives. The commands (benchmark, db, results) properly delegate to the new cookiebench workspace package.

Also applies to: 8-8, 12-12


14-25: ✅ Dependency updates to support new cookiebench architecture.

New workspace dependencies (@consentio/benchmark, @consentio/runner, cookiebench) and updated tooling versions (biome, turbo, playwright, ultracite, drizzle-kit) are consistent with the PR's refactoring objectives and ecosystem updates.

benchmarks/didomi/package.json (2)

25-27: ✅ Correct field naming: engines (plural) is properly used.

The "engines" field correctly specifies Node.js version >=20.9.0.


13-15: Dependency and type definition updates are consistent.

All versions align with the benchmark standardization (Next 16.0.7, React 19.2.1, type definitions ^19.2.x).

Also applies to: 20-22

benchmarks/cookie-control/package.json (1)

24-26: ✅ Correct field naming: engines (plural) is properly used.

The "engines" field correctly specifies Node.js version >=20.9.0.

benchmarks/cookie-yes/package.json (1)

24-26: ✅ Correct field naming: engines (plural) is properly used.

The "engines" field correctly specifies Node.js version >=20.9.0.

benchmarks/baseline/package.json (2)

20-20: Verify @types/node version constraint aligns with engine minimum.

The Node.js engine constraint requires >=20.9.0, but @types/node@^20.10.1 only covers >=20.10.1. This creates a gap where developers on Node 20.9.x would lack type definitions. Either adjust the types constraint to ^20.9.0 or update the engine constraint to >=20.10.1 to ensure consistency.

Also applies to: 25-26


13-15: Previous critical issues successfully resolved.

The prior reviews' critical issues have been properly addressed: "engines" field is now correctly named (not "engine"), and Next.js has been upgraded from 16.0.1 to 16.0.7 to remediate RSC deserialization vulnerabilities. React/React-dom versions are aligned with Next.js 16.0.7 compatibility requirements.

Also applies to: 25-26

Comment on lines +21 to +27
"dependencies": {
"@c15t/logger": "1.0.1",
"@consentio/shared": "workspace:*",
"@playwright/test": "^1.57.0",
"perfume.js": "^9.4.0",
"playwright-performance-metrics": "^1.2.4"
},

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Inconsistent exact pin on @c15t/logger prevents security patches.

Line 22 pins @c15t/logger to 1.0.1 exactly, while other dependencies use semver ranges (^1.57.0, ^9.4.0). Exact pinning prevents automatic patch updates with security fixes. Consider using ^1.0.1 for consistency.

🤖 Prompt for AI Agents
In packages/benchmark/package.json around lines 21 to 27, the dependency
"@c15t/logger" is pinned exactly to "1.0.1" which blocks automatic patch
updates; change the version string to a semver range (e.g. "^1.0.1") to allow
patch upgrades, then update the lockfile (npm/yarn/pnpm install) and run the
package manager audit/CI to ensure no regressions.

Comment on lines +9 to +11
"bin": {
"cookiebench": "dist/index.mjs"
},

ghost Dec 5, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify shebang injection configuration for CLI binary

# Check rslib.config.ts for BannerPlugin
echo "=== Checking rslib.config.ts for BannerPlugin ==="
if [ -f packages/cookiebench-cli/rslib.config.ts ]; then
  cat packages/cookiebench-cli/rslib.config.ts
else
  echo "rslib.config.ts not found"
fi

# Check package.json for build scripts
echo -e "\n=== Checking package.json for build scripts ==="
if [ -f packages/cookiebench-cli/package.json ]; then
  cat packages/cookiebench-cli/package.json
else
  echo "package.json not found"
fi

Repository: consentdotio/benchmarks

Length of output: 1482


Add shebang to CLI binary via build configuration.

The dist/index.mjs output lacks a shebang directive. Add BannerPlugin to packages/cookiebench-cli/rslib.config.ts with stage: "bundle-js" and banner: "#!/usr/bin/env node\n", or implement a postbuild script to inject it after the build completes. Without this, the cookiebench CLI command will not be directly executable.

🤖 Prompt for AI Agents
In packages/cookiebench-cli/package.json around lines 9-11 the published CLI
target dist/index.mjs lacks a shebang so the installed cookiebench binary isn't
executable; fix by updating packages/cookiebench-cli/rslib.config.ts to add a
BannerPlugin configured with stage: "bundle-js" and banner: "#!/usr/bin/env
node\n" so the shebang is emitted during build, or alternatively add a postbuild
script that prepends the shebang to dist/index.mjs and sets the file executable
(chmod +x) after build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant