From e36d6f3e16975b97e78a9077474498df089a1707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Thu, 7 May 2026 20:40:13 +0100 Subject: [PATCH] Speed up PR CI browser path (#365) ## Summary - rebalance Browser tests with explicit class-filter shards - build PR browser assets inside shards, removing the build-assets dependency gate - run Browser shards with Pest parallelism () ## Autoresearch metric - baseline modeled PR critical path: 406.50s - final modeled PR critical path: 234.27s - modeled improvement: 172.23s (42.4%) ## Verification - ran METRIC ci_total_s=234.270 METRIC actual_recent_pr_total_s=422.000 METRIC build_assets_s=0.000 METRIC tests_s=170.000 METRIC browser_matrix_s=232.270 METRIC linter_s=60.000 METRIC static_analysis_s=26.500 METRIC performance_tests_s=63.000 METRIC job_count=13.000 METRIC browser_shards=6.000 for each experiment - coverage guard in autoresearch script checks Browser filters cover all recent Browser classes exactly once ## Notes - wall-clock faster; runner minutes likely higher due extra shards and duplicate asset builds - real CI should validate Browser parallelism flake risk --- .github/workflows/ci.yml | 78 ++++++- autoresearch.md | 38 +++ autoresearch.sh | 186 +++++++++++++++ composer.json | 2 +- composer.lock | 489 +++++++++++++++++++++++++-------------- tests/.pest/shards.json | 19 ++ 6 files changed, 628 insertions(+), 184 deletions(-) create mode 100644 autoresearch.md create mode 100755 autoresearch.sh create mode 100644 tests/.pest/shards.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9dda7cf..c9a6cb21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ on: jobs: build-assets: + if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - name: Checkout @@ -95,11 +96,10 @@ jobs: browser-tests-matrix: if: github.event_name == 'pull_request' runs-on: ubuntu-latest - needs: build-assets strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4] + shard: [1, 2, 3, 4, 5] services: mysql: image: mysql:8.0 @@ -135,11 +135,8 @@ jobs: run: npx playwright install-deps chromium if: steps.playwright-cache.outputs.cache-hit == 'true' - - name: Download Build Artifact - uses: actions/download-artifact@v4 - with: - name: build-assets - path: public/build + - name: Build Assets + run: bun run build - name: Copy Environment File run: cp .env.example .env @@ -148,7 +145,7 @@ jobs: run: php artisan key:generate - name: Browser Tests - run: ./vendor/bin/pest --testsuite=Browser --ci --shard=${{ matrix.shard }}/4 + run: ./vendor/bin/pest --testsuite=Browser --ci --shard=${{ matrix.shard }}/6 env: TESTCONTAINERS: false DB_CONNECTION: mysql @@ -158,6 +155,71 @@ jobs: DB_USERNAME: root DB_PASSWORD: password + update-browser-shards: + if: github.event_name == 'workflow_dispatch' && inputs.build_only == false + runs-on: ubuntu-latest + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: testing + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP & Composer Deps + uses: ./.github/actions/setup-php-deps + + - name: Setup Bun & Node Deps + uses: ./.github/actions/setup-bun-deps + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('bun.lock') }} + restore-keys: playwright-${{ runner.os }}- + + - name: Install Playwright Browsers + run: npx playwright install --with-deps chromium + if: steps.playwright-cache.outputs.cache-hit != 'true' + + - name: Install Playwright system deps + run: npx playwright install-deps chromium + if: steps.playwright-cache.outputs.cache-hit == 'true' + + - name: Build Assets + run: bun run build + + - name: Copy Environment File + run: cp .env.example .env + + - name: Generate Application Key + run: php artisan key:generate + + - name: Update Browser Shards + run: ./vendor/bin/pest --testsuite=Browser --ci --update-shards + env: + TESTCONTAINERS: false + DB_CONNECTION: mysql + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_DATABASE: testing + DB_USERNAME: root + DB_PASSWORD: password + + - name: Upload Browser Shards + uses: actions/upload-artifact@v4 + with: + name: browser-shards + path: tests/.pest/shards.json + retention-days: 1 + static-analysis: runs-on: ubuntu-latest steps: diff --git a/autoresearch.md b/autoresearch.md new file mode 100644 index 00000000..2bf9e887 --- /dev/null +++ b/autoresearch.md @@ -0,0 +1,38 @@ +# Autoresearch: CI total execution time + +## Objective +Reduce GitHub Actions CI wall-clock completion time for pull requests without weakening coverage or hiding failures. + +## Metrics +- **Primary**: ci_total_s (s, lower is better) — modeled PR workflow critical path from current `.github/workflows/ci.yml` plus recent successful CI job timings. +- **Secondary**: build_assets_s, tests_s, browser_matrix_s, linter_s, static_analysis_s, performance_tests_s, job_count — tradeoff and bottleneck monitors. + +## How to Run +`./autoresearch.sh` — validates workflow structure, samples recent successful `CI` pull_request runs through `gh`, and outputs `METRIC name=value` lines. + +## Files in Scope +- `.github/workflows/ci.yml` — CI graph, job dependencies, sharding, commands. +- `.github/actions/setup-php-deps/action.yml` — PHP/composer setup cache behavior. +- `.github/actions/setup-bun-deps/action.yml` — Bun/node setup cache behavior. +- `phpunit.xml`, `tests/Pest.php` — test suite partitioning only when coverage remains equivalent. +- `package.json`, `composer.json` — CI scripts only, no dependency changes without approval. + +## Off Limits +- No deleting or skipping real checks to win time. +- No weakening assertions, lowering static-analysis level, or excluding tests unless moved to an equivalent job. +- No production secrets or `.env` reads. +- No dependency changes without explicit approval. + +## Constraints +- Small experiments. +- Keep improvements only when primary metric improves. +- Preserve CI correctness and failure visibility. +- Prefer workflow graph/cache improvements before test-suite rewrites. + +## What's Been Tried +- Baseline: modeled PR CI critical path 406.50s. Browser path dominated: build-assets 65.5s + browser matrix 339s + aggregate. +- Kept: manual Browser class filters over 5 shards. Modeled 355.76s. Same Browser classes covered exactly once. +- Kept: skip separate build-assets job on PR and build assets inside Browser shards. Modeled 326.84s. Removes build-assets gate from PR critical path. +- Kept: rebalance Browser filters over 6 shards. Modeled 293.24s. +- Kept: run Browser filter shards with Pest `--parallel --processes=3`. Modeled 234.27s. +- Discarded: 4 Browser processes. No modeled improvement over 3; individual slow tests dominate. diff --git a/autoresearch.sh b/autoresearch.sh new file mode 100755 index 00000000..03bcd1df --- /dev/null +++ b/autoresearch.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +set -euo pipefail + +RUN_ID="${RUN_ID:-}" +BRANCH="${BRANCH:-$(git branch --show-current)}" +WORKFLOW="${WORKFLOW:-CI}" + +current_head() { + git rev-parse HEAD +} + +latest_run_for_head() { + local event="$1" + local head_sha="$2" + gh run list --workflow="$WORKFLOW" --branch="$BRANCH" --event="$event" --limit=20 --json databaseId,headSha,status,conclusion \ + --jq ".[] | select(.headSha == \"$head_sha\") | .databaseId" | head -1 +} + +wait_for_run_for_head() { + local event="$1" + local head_sha="$2" + local run_id="" + + for _ in {1..90}; do + run_id="$(latest_run_for_head "$event" "$head_sha")" + if [[ -n "$run_id" ]]; then + echo "$run_id" + return 0 + fi + sleep 5 + done + + echo "No $event CI run appeared for $head_sha" >&2 + return 1 +} + +commit_and_push_if_dirty() { + local message="$1" + + if ! git diff --quiet -- .github/workflows composer.json composer.lock tests/.pest autoresearch.md autoresearch.sh autoresearch.jsonl || \ + [[ -n "$(git ls-files --others --exclude-standard .github/workflows tests/.pest 2>/dev/null)" ]]; then + git add .github/workflows composer.json composer.lock tests/.pest autoresearch.md autoresearch.sh autoresearch.jsonl + git commit -m "$message" + git push + fi +} + +# Time-balanced sharding experiment bootstrap: +# 1. Push Pest/update-shards workflow changes. +# 2. Run CI workflow_dispatch with build_only=false to produce tests/.pest/shards.json. +# 3. Download and commit shards.json. +# 4. Wait for PR CI for the committed shard timings and measure it. +if [[ -z "$RUN_ID" && ! -f tests/.pest/shards.json && "${GENERATE_BROWSER_SHARDS:-1}" == "1" ]]; then + commit_and_push_if_dirty "Experiment: update Pest and enable browser shard timings" + + head_sha="$(current_head)" + dispatch_run="$(latest_run_for_head workflow_dispatch "$head_sha")" + if [[ -z "$dispatch_run" ]]; then + gh workflow run "$WORKFLOW" --ref "$BRANCH" -f build_only=false + dispatch_run="$(wait_for_run_for_head workflow_dispatch "$head_sha")" + fi + + gh run watch "$dispatch_run" --exit-status --interval 10 >/dev/null + + tmp_dir="$(mktemp -d)" + gh run download "$dispatch_run" --name browser-shards --dir "$tmp_dir" >/dev/null + mkdir -p tests/.pest + cp "$tmp_dir/shards.json" tests/.pest/shards.json + rm -rf "$tmp_dir" + + commit_and_push_if_dirty "Experiment: add Pest browser shard timings" + head_sha="$(current_head)" + RUN_ID="$(wait_for_run_for_head pull_request "$head_sha")" + gh run watch "$RUN_ID" --exit-status --interval 10 >/dev/null +elif [[ -z "$RUN_ID" ]]; then + commit_and_push_if_dirty "Experiment CI change" + head_sha="$(current_head)" + RUN_ID="$(wait_for_run_for_head pull_request "$head_sha")" + gh run watch "$RUN_ID" --exit-status --interval 10 >/dev/null +fi + +python3 - <<'PY' "$RUN_ID" "$BRANCH" "$WORKFLOW" +from __future__ import annotations + +import datetime as dt +import json +import subprocess +import sys +from collections import defaultdict + +run_id, branch, workflow = sys.argv[1:4] + + +def gh_json(args: list[str]) -> object: + raw = subprocess.check_output(['gh', *args], text=True, stderr=subprocess.DEVNULL) + return json.loads(raw) + +if not run_id: + head_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip() + runs = gh_json([ + 'run', 'list', + '--workflow', workflow, + '--branch', branch, + '--event', 'pull_request', + '--limit', '20', + '--json', 'databaseId,status,conclusion,headSha', + ]) + for run in runs: + if run.get('headSha') == head_sha and run.get('status') == 'completed' and run.get('conclusion') == 'success': + run_id = str(run['databaseId']) + break + +if not run_id: + print('No successful completed pull_request CI run found for HEAD', file=sys.stderr) + sys.exit(1) + +payload = gh_json(['run', 'view', run_id, '--json', 'jobs,status,conclusion,headSha,headBranch,event']) +if payload.get('status') != 'completed' or payload.get('conclusion') != 'success': + print(f'Run {run_id} not successful: {payload.get("status")} {payload.get("conclusion")}', file=sys.stderr) + sys.exit(1) + + +def parse_time(value: str) -> dt.datetime: + return dt.datetime.fromisoformat(value.replace('Z', '+00:00')) + + +def seconds(start: str, end: str) -> float: + return max(0.0, (parse_time(end) - parse_time(start)).total_seconds()) + +jobs = [job for job in payload.get('jobs', []) if job.get('startedAt') and job.get('completedAt')] +if not jobs: + print(f'Run {run_id} has no timed jobs', file=sys.stderr) + sys.exit(1) + +measured_jobs = [ + job for job in jobs + if job.get('conclusion') != 'skipped' or job['name'] in {'build-assets'} +] +measured_jobs = [ + job for job in measured_jobs + if not job['name'].startswith('build-image') and not job['name'].startswith('deploy') +] + +start = min(parse_time(job['startedAt']) for job in measured_jobs) +end = max(parse_time(job['completedAt']) for job in measured_jobs) +total = (end - start).total_seconds() + +buckets: dict[str, list[float]] = defaultdict(list) +for job in jobs: + name = job['name'] + duration = seconds(job['startedAt'], job['completedAt']) + if name == 'tests': + buckets['tests_s'].append(duration) + elif name == 'linter': + buckets['linter_s'].append(duration) + elif name == 'static-analysis': + buckets['static_analysis_s'].append(duration) + elif name == 'performance-tests': + buckets['performance_tests_s'].append(duration) + elif name == 'build-assets': + buckets['build_assets_s'].append(duration if job.get('conclusion') != 'skipped' else 0.0) + elif name == 'browser-tests': + buckets['browser_aggregate_s'].append(duration) + elif name.startswith('browser-tests-matrix'): + buckets['browser_matrix_shard_s'].append(duration) + elif name == 'update-browser-shards': + buckets['update_browser_shards_s'].append(duration) + +browser_shards = buckets['browser_matrix_shard_s'] +metrics = { + 'github_ci_total_s': total, + 'tests_s': max(buckets['tests_s'] or [0.0]), + 'linter_s': max(buckets['linter_s'] or [0.0]), + 'static_analysis_s': max(buckets['static_analysis_s'] or [0.0]), + 'performance_tests_s': max(buckets['performance_tests_s'] or [0.0]), + 'build_assets_s': max(buckets['build_assets_s'] or [0.0]), + 'browser_matrix_s': max(browser_shards or [0.0]), + 'browser_aggregate_s': max(buckets['browser_aggregate_s'] or [0.0]), + 'browser_shards': float(len(browser_shards)), + 'job_count': float(len(jobs)), + 'run_id': float(run_id), +} + +for key, value in metrics.items(): + print(f'METRIC {key}={value:.3f}') +PY diff --git a/composer.json b/composer.json index 4b1a8998..14157e01 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "laravel/sail": "^1.41", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", - "pestphp/pest": "^4.1", + "pestphp/pest": "^4.6", "pestphp/pest-plugin-browser": "^4.0", "pestphp/pest-plugin-laravel": "^4.0", "testcontainers/testcontainers": "^1.0", diff --git a/composer.lock b/composer.lock index 5baaf875..850e8b71 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "93761996d2a052e3c93087879c859b40", + "content-hash": "eb2d3df2c168ddf3a6bdeadf6038b1a6", "packages": [ { "name": "bacon/bacon-qr-code", @@ -4791,16 +4791,16 @@ }, { "name": "symfony/console", - "version": "v7.4.7", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "url": "https://api.github.com/repos/symfony/console/zipball/d7d2b64a45a89d607865927b176fa51c33ddbb58", + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58", "shasum": "" }, "require": { @@ -4865,7 +4865,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.7" + "source": "https://github.com/symfony/console/tree/v7.4.9" }, "funding": [ { @@ -4885,7 +4885,7 @@ "type": "tidelift" } ], - "time": "2026-03-06T14:06:20+00:00" + "time": "2026-04-22T15:21:55+00:00" }, { "name": "symfony/css-selector", @@ -4958,16 +4958,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -4980,7 +4980,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -5005,7 +5005,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -5016,12 +5016,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/error-handler", @@ -5191,16 +5195,16 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -5214,7 +5218,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -5247,7 +5251,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -5258,25 +5262,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/finder", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/441404f09a54de6d1bd6ad219e088cdf4c91f97c", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { @@ -5311,7 +5319,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.6" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -5331,7 +5339,7 @@ "type": "tidelift" } ], - "time": "2026-01-29T09:41:02+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/http-foundation", @@ -5771,16 +5779,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -5830,7 +5838,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -5850,20 +5858,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", "shasum": "" }, "require": { @@ -5912,7 +5920,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" }, "funding": [ { @@ -5932,7 +5940,7 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-04-26T13:13:48+00:00" }, { "name": "symfony/polyfill-intl-icu", @@ -6111,7 +6119,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6172,7 +6180,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" }, "funding": [ { @@ -6196,16 +6204,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -6257,7 +6265,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -6277,20 +6285,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -6341,7 +6349,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -6361,7 +6369,7 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php84", @@ -6608,16 +6616,16 @@ }, { "name": "symfony/process", - "version": "v8.0.5", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", "shasum": "" }, "require": { @@ -6649,7 +6657,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.5" + "source": "https://github.com/symfony/process/tree/v8.0.8" }, "funding": [ { @@ -6669,7 +6677,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:08:38+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -6840,16 +6848,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -6867,7 +6875,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -6903,7 +6911,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -6923,20 +6931,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "shasum": "" }, "require": { @@ -6993,7 +7001,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.6" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -7013,7 +7021,7 @@ "type": "tidelift" } ], - "time": "2026-02-09T10:14:57+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/translation", @@ -7110,16 +7118,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { @@ -7132,7 +7140,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -7168,7 +7176,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -7188,7 +7196,7 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/uid", @@ -8943,16 +8951,16 @@ }, { "name": "brianium/paratest", - "version": "v7.19.2", + "version": "v7.20.0", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "66e4f7910cecf67736bccf2b8bd53a2e3eb98bd9" + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/66e4f7910cecf67736bccf2b8bd53a2e3eb98bd9", - "reference": "66e4f7910cecf67736bccf2b8bd53a2e3eb98bd9", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/81c80677c9ec0ed4ef16b246167f11dec81a6e3d", + "reference": "81c80677c9ec0ed4ef16b246167f11dec81a6e3d", "shasum": "" }, "require": { @@ -8976,7 +8984,7 @@ "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.40", + "phpstan/phpstan": "^2.1.44", "phpstan/phpstan-deprecation-rules": "^2.0.4", "phpstan/phpstan-phpunit": "^2.0.16", "phpstan/phpstan-strict-rules": "^2.0.10", @@ -9020,7 +9028,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.19.2" + "source": "https://github.com/paratestphp/paratest/tree/v7.20.0" }, "funding": [ { @@ -9032,7 +9040,7 @@ "type": "paypal" } ], - "time": "2026-03-09T14:33:17+00:00" + "time": "2026-03-29T15:46:14+00:00" }, { "name": "clue/stream-filter", @@ -9100,6 +9108,151 @@ ], "time": "2023-12-20T15:40:13+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, { "name": "daverandom/libdns", "version": "v2.1.0", @@ -10392,23 +10545,23 @@ }, { "name": "nunomaduro/collision", - "version": "v8.9.1", + "version": "v8.9.4", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935" + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", - "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", "shasum": "" }, "require": { "filp/whoops": "^2.18.4", "nunomaduro/termwind": "^2.4.0", "php": "^8.2.0", - "symfony/console": "^7.4.4 || ^8.0.4" + "symfony/console": "^7.4.8 || ^8.0.8" }, "conflict": { "laravel/framework": "<11.48.0 || >=14.0.0", @@ -10416,12 +10569,12 @@ }, "require-dev": { "brianium/paratest": "^7.8.5", - "larastan/larastan": "^3.9.2", - "laravel/framework": "^11.48.0 || ^12.52.0", - "laravel/pint": "^1.27.1", - "orchestra/testbench-core": "^9.12.0 || ^10.9.0", - "pestphp/pest": "^3.8.5 || ^4.4.1 || ^5.0.0", - "sebastian/environment": "^7.2.1 || ^8.0.3 || ^9.0.0" + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" }, "type": "library", "extra": { @@ -10484,45 +10637,47 @@ "type": "patreon" } ], - "time": "2026-02-17T17:33:08+00:00" + "time": "2026-04-21T14:04:20+00:00" }, { "name": "pestphp/pest", - "version": "v4.4.3", + "version": "v4.7.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "e6ab897594312728ef2e32d586cb4f6780b1b495" + "reference": "2fc75cfcf03c041c804778fa894282234adc3c66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/e6ab897594312728ef2e32d586cb4f6780b1b495", - "reference": "e6ab897594312728ef2e32d586cb4f6780b1b495", + "url": "https://api.github.com/repos/pestphp/pest/zipball/2fc75cfcf03c041c804778fa894282234adc3c66", + "reference": "2fc75cfcf03c041c804778fa894282234adc3c66", "shasum": "" }, "require": { - "brianium/paratest": "^7.19.2", - "nunomaduro/collision": "^8.9.1", + "brianium/paratest": "^7.20.0", + "composer/xdebug-handler": "^3.0.5", + "nunomaduro/collision": "^8.9.4", "nunomaduro/termwind": "^2.4.0", "pestphp/pest-plugin": "^4.0.0", - "pestphp/pest-plugin-arch": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.2", "pestphp/pest-plugin-mutate": "^4.0.1", "pestphp/pest-plugin-profanity": "^4.2.1", "php": "^8.3.0", - "phpunit/phpunit": "^12.5.14", - "symfony/process": "^7.4.5|^8.0.5" + "phpunit/phpunit": "^12.5.24", + "symfony/process": "^7.4.8|^8.0.8" }, "conflict": { "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">12.5.14", + "phpunit/phpunit": ">12.5.24", "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, "require-dev": { + "mrpunyapal/peststan": "^0.2.9", "pestphp/pest-dev-tools": "^4.1.0", - "pestphp/pest-plugin-browser": "^4.3.0", - "pestphp/pest-plugin-type-coverage": "^4.0.3", - "psy/psysh": "^0.12.21" + "pestphp/pest-plugin-browser": "^4.3.1", + "pestphp/pest-plugin-type-coverage": "^4.0.4", + "psy/psysh": "^0.12.22" }, "bin": [ "bin/pest" @@ -10549,6 +10704,7 @@ "Pest\\Plugins\\Verbose", "Pest\\Plugins\\Version", "Pest\\Plugins\\Shard", + "Pest\\Plugins\\Tia", "Pest\\Plugins\\Parallel" ] }, @@ -10588,7 +10744,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v4.4.3" + "source": "https://github.com/pestphp/pest/tree/v4.7.0" }, "funding": [ { @@ -10600,7 +10756,7 @@ "type": "github" } ], - "time": "2026-03-21T13:14:39+00:00" + "time": "2026-05-03T16:09:32+00:00" }, { "name": "pestphp/pest-plugin", @@ -10674,26 +10830,26 @@ }, { "name": "pestphp/pest-plugin-arch", - "version": "v4.0.0", + "version": "v4.0.2", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", - "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", + "reference": "3fb0d02a91b9da504b139dc7ab2a31efb7c3215c", "shasum": "" }, "require": { "pestphp/pest-plugin": "^4.0.0", "php": "^8.3", - "ta-tikoma/phpunit-architecture-test": "^0.8.5" + "ta-tikoma/phpunit-architecture-test": "^0.8.7" }, "require-dev": { - "pestphp/pest": "^4.0.0", - "pestphp/pest-dev-tools": "^4.0.0" + "pestphp/pest": "^4.4.6", + "pestphp/pest-dev-tools": "^4.1.0" }, "type": "library", "extra": { @@ -10728,7 +10884,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.2" }, "funding": [ { @@ -10740,7 +10896,7 @@ "type": "github" } ], - "time": "2025-08-20T13:10:51+00:00" + "time": "2026-04-10T17:20:19+00:00" }, { "name": "pestphp/pest-plugin-browser", @@ -11877,16 +12033,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.5.3", + "version": "12.5.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d" + "reference": "876099a072646c7745f673d7aeab5382c4439691" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b015312f28dd75b75d3422ca37dff2cd1a565e8d", - "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691", + "reference": "876099a072646c7745f673d7aeab5382c4439691", "shasum": "" }, "require": { @@ -11895,7 +12051,6 @@ "ext-xmlwriter": "*", "nikic/php-parser": "^5.7.0", "php": ">=8.3", - "phpunit/php-file-iterator": "^6.0", "phpunit/php-text-template": "^5.0", "sebastian/complexity": "^5.0", "sebastian/environment": "^8.0.3", @@ -11942,7 +12097,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.3" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.6" }, "funding": [ { @@ -11962,7 +12117,7 @@ "type": "tidelift" } ], - "time": "2026-02-06T06:01:44+00:00" + "time": "2026-04-15T08:23:17+00:00" }, { "name": "phpunit/php-file-iterator", @@ -12223,16 +12378,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.5.14", + "version": "12.5.24", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "47283cfd98d553edcb1353591f4e255dc1bb61f0" + "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/47283cfd98d553edcb1353591f4e255dc1bb61f0", - "reference": "47283cfd98d553edcb1353591f4e255dc1bb61f0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046", + "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046", "shasum": "" }, "require": { @@ -12246,15 +12401,15 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.5.3", + "phpunit/php-code-coverage": "^12.5.6", "phpunit/php-file-iterator": "^6.0.1", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", "phpunit/php-timer": "^8.0.0", "sebastian/cli-parser": "^4.2.0", - "sebastian/comparator": "^7.1.4", + "sebastian/comparator": "^7.1.6", "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.3", + "sebastian/environment": "^8.1.0", "sebastian/exporter": "^7.0.2", "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", @@ -12301,31 +12456,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.14" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2026-02-18T12:38:40+00:00" + "time": "2026-05-01T04:21:04+00:00" }, { "name": "revolt/event-loop", @@ -12470,16 +12609,16 @@ }, { "name": "sebastian/comparator", - "version": "7.1.4", + "version": "7.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6" + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c769009dee98f494e0edc3fd4f4087501688f11e", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e", "shasum": "" }, "require": { @@ -12538,7 +12677,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.6" }, "funding": [ { @@ -12558,7 +12697,7 @@ "type": "tidelift" } ], - "time": "2026-01-24T09:28:48+00:00" + "time": "2026-04-14T08:23:15+00:00" }, { "name": "sebastian/complexity", @@ -12687,16 +12826,16 @@ }, { "name": "sebastian/environment", - "version": "8.0.4", + "version": "8.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "7b8842c2d8e85d0c3a5831236bf5869af6ab2a11" + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/7b8842c2d8e85d0c3a5831236bf5869af6ab2a11", - "reference": "7b8842c2d8e85d0c3a5831236bf5869af6ab2a11", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b121608b28a13f721e76ffbbd386d08eff58f3f6", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6", "shasum": "" }, "require": { @@ -12711,7 +12850,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -12739,7 +12878,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.4" + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.0" }, "funding": [ { @@ -12759,7 +12898,7 @@ "type": "tidelift" } ], - "time": "2026-03-15T07:05:40+00:00" + "time": "2026-04-15T12:13:01+00:00" }, { "name": "sebastian/exporter", @@ -13914,16 +14053,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.6", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", - "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", "shasum": "" }, "require": { @@ -13970,9 +14109,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.6" + "source": "https://github.com/webmozarts/assert/tree/2.3.0" }, - "time": "2026-02-27T10:28:38+00:00" + "time": "2026-04-11T10:33:05+00:00" } ], "aliases": [], diff --git a/tests/.pest/shards.json b/tests/.pest/shards.json new file mode 100644 index 00000000..758c48bb --- /dev/null +++ b/tests/.pest/shards.json @@ -0,0 +1,19 @@ +{ + "timings": { + "Tests\\Browser\\AccountsPageTest": 51.0428, + "Tests\\Browser\\AmountInputTest": 50.0611, + "Tests\\Browser\\AuthenticationTest": 13.8601, + "Tests\\Browser\\AutomationRuleBuilderTest": 129.1864, + "Tests\\Browser\\BankAccountsTest": 108.119, + "Tests\\Browser\\BudgetCrudTest": 77.7671, + "Tests\\Browser\\BudgetsFeatureNavigationTest": 16.7266, + "Tests\\Browser\\CashflowCategoryNavigationTest": 6.6828, + "Tests\\Browser\\CategoriesTest": 25.2693, + "Tests\\Browser\\ImportTransactionsTest": 16.7132, + "Tests\\Browser\\OnboardingFlowTest": 120.1495, + "Tests\\Browser\\RealEstateAccountTest": 53.3292, + "Tests\\Browser\\TransactionsTest": 50.5797 + }, + "checksum": "a1e4ee59f56f5fac14fc14f555ae4975", + "updated_at": "2026-05-07T19:14:20+00:00" +}