diff --git a/backend/composer.json b/backend/composer.json index 63bc366c8..f522e0c5a 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -1,107 +1,106 @@ { - "type": "project", - "license": "proprietary", - "minimum-stability": "dev", - "prefer-stable": true, - "require": { - "php": ">=8.4", - "ext-ctype": "*", - "ext-dom": "*", - "ext-iconv": "*", - "ext-openssl": "*", - "aws/aws-php-sns-message-validator": "^1.10", - "aws/aws-sdk-php": "^3.351.8", - "doctrine/dbal": "^3.10.0", - "doctrine/doctrine-bundle": "^2.15.0", - "doctrine/doctrine-migrations-bundle": "^3.4.2", - "doctrine/orm": "^3.5.0", - "dunglas/doctrine-json-odm": "^1.4.2", - "fakerphp/faker": "^1.24.1", - "hyvor/internal": "^4.0.10", - "hyvor/phrosemirror": "^1.0.5", - "league/flysystem-aws-s3-v3": "^3.29", - "nelmio/cors-bundle": "^2.5", - "runtime/frankenphp-symfony": "^0.2.0", - "symfony/clock": "7.4.*", - "symfony/console": "7.4.*", - "symfony/doctrine-messenger": "7.4.*", - "symfony/dotenv": "7.4.*", - "symfony/flex": "^2.8.1", - "symfony/framework-bundle": "7.4.*", - "symfony/lock": "7.4.*", - "symfony/mailer": "7.4.*", - "symfony/messenger": "7.4.*", - "symfony/monolog-bundle": "^3.10", - "symfony/rate-limiter": "7.4.*", - "symfony/runtime": "7.4.*", - "symfony/scheduler": "7.4.*", - "symfony/serializer": "7.4.*", - "symfony/string": "7.4.*", - "symfony/twig-bundle": "7.4.*", - "symfony/uid": "7.4.*", - "symfony/ux-twig-component": "^2.28.0", - "symfony/validator": "7.4.*", - "symfony/yaml": "7.4.*", - "twig/cssinliner-extra": "^3.21", - "twig/extra-bundle": "^3.21", - "symfony/dom-crawler": "7.4.*", - "zenstruck/messenger-monitor-bundle": "^0.6.0", - "sentry/sentry-symfony": "^5.9" - }, - "bump-after-update": true, - "sort-packages": true, - "autoload": { - "psr-4": { - "App\\": "src/" + "type": "project", + "license": "proprietary", + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": ">=8.4", + "ext-ctype": "*", + "ext-dom": "*", + "ext-iconv": "*", + "ext-openssl": "*", + "aws/aws-php-sns-message-validator": "^1.10", + "aws/aws-sdk-php": "^3.351.8", + "doctrine/dbal": "^4.4.4", + "doctrine/doctrine-bundle": "^3.3.1", + "doctrine/doctrine-migrations-bundle": "^3.4.2", + "doctrine/orm": "^3.5.0", + "dunglas/doctrine-json-odm": "^1.4.2", + "fakerphp/faker": "^1.24.1", + "hyvor/internal": "4.1.0-beta.3", + "hyvor/phrosemirror": "^1.0.5", + "league/flysystem-aws-s3-v3": "^3.29", + "nelmio/cors-bundle": "^2.5", + "symfony/clock": "8.0.*", + "symfony/console": "8.0.*", + "symfony/doctrine-messenger": "8.0.*", + "symfony/dotenv": "8.0.*", + "symfony/flex": "^2.8.1", + "symfony/framework-bundle": "8.0.*", + "symfony/lock": "8.0.*", + "symfony/mailer": "8.0.*", + "symfony/messenger": "8.0.*", + "symfony/monolog-bundle": "^4.0.2", + "symfony/rate-limiter": "8.0.*", + "symfony/runtime": "8.0.*", + "symfony/scheduler": "8.0.*", + "symfony/serializer": "8.0.*", + "symfony/string": "8.0.*", + "symfony/twig-bundle": "8.0.*", + "symfony/uid": "8.0.*", + "symfony/ux-twig-component": "^3.3.0", + "symfony/validator": "8.0.*", + "symfony/yaml": "8.0.*", + "twig/cssinliner-extra": "^3.21", + "twig/extra-bundle": "^3.21", + "symfony/dom-crawler": "8.0.*", + "zenstruck/messenger-monitor-bundle": "^0.6.0", + "sentry/sentry-symfony": "^5.9" + }, + "bump-after-update": true, + "sort-packages": true, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests/" + } + }, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*", + "symfony/polyfill-php82": "*" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + } + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "8.0.*" + } + }, + "require-dev": { + "dama/doctrine-test-bundle": "^8.3", + "phpstan/phpstan": "^2.1.21", + "phpunit/phpunit": "^11.5.27", + "symfony/browser-kit": "8.0.*", + "symfony/css-selector": "8.0.*", + "symfony/maker-bundle": "^1.64.0", + "symfony/phpunit-bridge": "^7.3.1", + "symfony/stopwatch": "8.0.*", + "symfony/web-profiler-bundle": "8.0.*", + "zenstruck/foundry": "^2.6.1", + "zenstruck/messenger-test": "^1.11.1" + }, + "config": { + "allow-plugins": { + "symfony/runtime": true, + "symfony/flex": true + } } - }, - "autoload-dev": { - "psr-4": { - "App\\Tests\\": "tests/" - } - }, - "replace": { - "symfony/polyfill-ctype": "*", - "symfony/polyfill-iconv": "*", - "symfony/polyfill-php72": "*", - "symfony/polyfill-php73": "*", - "symfony/polyfill-php74": "*", - "symfony/polyfill-php80": "*", - "symfony/polyfill-php81": "*", - "symfony/polyfill-php82": "*" - }, - "scripts": { - "auto-scripts": { - "cache:clear": "symfony-cmd", - "assets:install %PUBLIC_DIR%": "symfony-cmd" - } - }, - "conflict": { - "symfony/symfony": "*" - }, - "extra": { - "symfony": { - "allow-contrib": false, - "require": "7.4.*" - } - }, - "require-dev": { - "dama/doctrine-test-bundle": "^8.3", - "phpstan/phpstan": "^2.1.21", - "phpunit/phpunit": "^11.5.27", - "symfony/browser-kit": "7.4.*", - "symfony/css-selector": "7.4.*", - "symfony/maker-bundle": "^1.64.0", - "symfony/phpunit-bridge": "^7.3.1", - "symfony/stopwatch": "7.4.*", - "symfony/web-profiler-bundle": "7.4.*", - "zenstruck/foundry": "^2.6.1", - "zenstruck/messenger-test": "^1.11.1" - }, - "config": { - "allow-plugins": { - "symfony/runtime": true, - "symfony/flex": true - } - } } diff --git a/backend/composer.lock b/backend/composer.lock index a0b151a41..57360434c 100644 --- a/backend/composer.lock +++ b/backend/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": "bce2c44665ef9b751e1ae8a764dc6c74", + "content-hash": "9e6660e1fd255faad76f66bbe5d86318", "packages": [ { "name": "aws/aws-crt-php", @@ -121,16 +121,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.379.8", + "version": "3.389.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "856ddf3d241c29132fe1eb946e112351ab043542" + "reference": "784e0fb95e752e55c4654b5800b900c78f6d3990" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/856ddf3d241c29132fe1eb946e112351ab043542", - "reference": "856ddf3d241c29132fe1eb946e112351ab043542", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/784e0fb95e752e55c4654b5800b900c78f6d3990", + "reference": "784e0fb95e752e55c4654b5800b900c78f6d3990", "shasum": "" }, "require": { @@ -138,10 +138,10 @@ "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "guzzlehttp/guzzle": "^7.4.5", - "guzzlehttp/promises": "^2.0", - "guzzlehttp/psr7": "^2.4.5", - "mtdowling/jmespath.php": "^2.8.0", + "guzzlehttp/guzzle": "^7.8.2 || ^8.0", + "guzzlehttp/promises": "^2.0.3 || ^3.0", + "guzzlehttp/psr7": "^2.6.3 || ^3.0", + "mtdowling/jmespath.php": "^2.9.1", "php": ">=8.1", "psr/http-message": "^1.0 || ^2.0", "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" @@ -212,32 +212,32 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.379.8" + "source": "https://github.com/aws/aws-sdk-php/tree/3.389.2" }, - "time": "2026-04-27T19:13:21+00:00" + "time": "2026-07-28T18:10:25+00:00" }, { "name": "carbonphp/carbon-doctrine-types", - "version": "2.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "php": "^8.1" }, "conflict": { - "doctrine/dbal": "<3.7.0 || >=4.0.0" + "doctrine/dbal": "<4.0.0 || >=5.0.0" }, "require-dev": { - "doctrine/dbal": "^3.7.0", + "doctrine/dbal": "^4.0.0", "nesbot/carbon": "^2.71.0 || ^3.0.0", "phpunit/phpunit": "^10.3" }, @@ -267,7 +267,7 @@ ], "support": { "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", - "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" }, "funding": [ { @@ -283,7 +283,7 @@ "type": "tidelift" } ], - "time": "2023-12-11T17:09:12+00:00" + "time": "2024-02-09T16:56:22+00:00" }, { "name": "doctrine/collections", @@ -373,48 +373,40 @@ }, { "name": "doctrine/dbal", - "version": "3.10.5", + "version": "4.4.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "95d84866bf3c04b2ddca1df7c049714660959aef" + "reference": "fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/95d84866bf3c04b2ddca1df7c049714660959aef", - "reference": "95d84866bf3c04b2ddca1df7c049714660959aef", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce", + "reference": "fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce", "shasum": "" }, "require": { - "composer-runtime-api": "^2", - "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", + "doctrine/deprecations": "^1.1.5", + "php": "^8.2", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, - "conflict": { - "doctrine/cache": "< 1.11" - }, "require-dev": { - "doctrine/cache": "^1.11|^2.0", "doctrine/coding-standard": "14.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", + "jetbrains/phpstorm-stubs": "2023.2", "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "2.0.7", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "9.6.34", + "phpunit/phpunit": "11.5.50", "slevomat/coding-standard": "8.27.1", "squizlabs/php_codesniffer": "4.0.1", - "symfony/cache": "^5.4|^6.0|^7.0|^8.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0|^8.0" + "symfony/cache": "^6.3.8|^7.0|^8.0", + "symfony/console": "^5.4|^6.3|^7.0|^8.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "bin": [ - "bin/doctrine-dbal" - ], "type": "library", "autoload": { "psr-4": { @@ -467,7 +459,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.10.5" + "source": "https://github.com/doctrine/dbal/tree/4.4.4" }, "funding": [ { @@ -483,7 +475,7 @@ "type": "tidelift" } ], - "time": "2026-02-24T08:03:57+00:00" + "time": "2026-07-21T14:34:40+00:00" }, { "name": "doctrine/deprecations", @@ -535,63 +527,60 @@ }, { "name": "doctrine/doctrine-bundle", - "version": "2.18.2", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "0ff098b29b8b3c68307c8987dcaed7fd829c6546" + "reference": "9b231f957020de52d6ac94fd72f0cbe8cf1eaad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/0ff098b29b8b3c68307c8987dcaed7fd829c6546", - "reference": "0ff098b29b8b3c68307c8987dcaed7fd829c6546", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/9b231f957020de52d6ac94fd72f0cbe8cf1eaad5", + "reference": "9b231f957020de52d6ac94fd72f0cbe8cf1eaad5", "shasum": "" }, "require": { - "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/dbal": "^4.0", "doctrine/deprecations": "^1.0", - "doctrine/persistence": "^3.1 || ^4", + "doctrine/persistence": "^4", "doctrine/sql-formatter": "^1.0.1", - "php": "^8.1", - "symfony/cache": "^6.4 || ^7.0", - "symfony/config": "^6.4 || ^7.0", - "symfony/console": "^6.4 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3", - "symfony/framework-bundle": "^6.4 || ^7.0", - "symfony/service-contracts": "^2.5 || ^3" + "ext-mbstring": "*", + "php": "^8.4", + "symfony/cache": "^6.4 || ^7.0 || ^8.0", + "symfony/config": "^6.4 || ^7.0 || ^8.0", + "symfony/console": "^6.4 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", + "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/service-contracts": "^3" }, "conflict": { - "doctrine/annotations": ">=3.0", - "doctrine/cache": "< 1.11", - "doctrine/orm": "<2.17 || >=4.0", - "symfony/var-exporter": "< 6.4.1 || 7.0.0", - "twig/twig": "<2.13 || >=3.0 <3.0.4" + "doctrine/orm": "<3.0 || >=4.0", + "twig/twig": "<3.0.4" }, "require-dev": { - "doctrine/annotations": "^1 || ^2", - "doctrine/cache": "^1.11 || ^2.0", "doctrine/coding-standard": "^14", - "doctrine/orm": "^2.17 || ^3.1", - "friendsofphp/proxy-manager-lts": "^1.0", - "phpstan/phpstan": "2.1.1", + "doctrine/orm": "^3.4.4", + "phpstan/phpstan": "^2.1.13", "phpstan/phpstan-phpunit": "2.0.3", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.53 || ^12.3.10", - "psr/log": "^1.1.4 || ^2.0 || ^3.0", - "symfony/doctrine-messenger": "^6.4 || ^7.0", - "symfony/expression-language": "^6.4 || ^7.0", - "symfony/messenger": "^6.4 || ^7.0", - "symfony/property-info": "^6.4 || ^7.0", - "symfony/security-bundle": "^6.4 || ^7.0", - "symfony/stopwatch": "^6.4 || ^7.0", - "symfony/string": "^6.4 || ^7.0", - "symfony/twig-bridge": "^6.4 || ^7.0", - "symfony/validator": "^6.4 || ^7.0", - "symfony/var-exporter": "^6.4.1 || ^7.0.1", - "symfony/web-profiler-bundle": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.0", - "twig/twig": "^2.14.7 || ^3.0.4" + "phpstan/phpstan-symfony": "^2.0.9", + "phpunit/phpunit": "^12.3.10", + "psr/log": "^3.0", + "symfony/doctrine-messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/expression-language": "^6.4 || ^7.0 || ^8.0", + "symfony/http-kernel": "^6.4 || ^7.0 || ^8.0", + "symfony/messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/property-info": "^6.4 || ^7.0 || ^8.0", + "symfony/runtime": "^6.4 || ^7.0 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/stopwatch": "^6.4 || ^7.0 || ^8.0", + "symfony/string": "^6.4 || ^7.0 || ^8.0", + "symfony/twig-bridge": "^6.4 || ^7.0 || ^8.0", + "symfony/validator": "^6.4 || ^7.0 || ^8.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^6.4 || ^7.0 || ^8.0", + "twig/twig": "^3.21.1" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -636,7 +625,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.2" + "source": "https://github.com/doctrine/DoctrineBundle/tree/3.3.1" }, "funding": [ { @@ -652,7 +641,7 @@ "type": "tidelift" } ], - "time": "2025-12-20T21:35:32+00:00" + "time": "2026-07-23T10:37:01+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", @@ -1171,16 +1160,16 @@ }, { "name": "doctrine/orm", - "version": "3.6.3", + "version": "3.6.7", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "e88cd591f0786089dee22b972c28aa2076df51c0" + "reference": "bc217c0e19c3a9eadfa67697143b87c9ba01272c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/e88cd591f0786089dee22b972c28aa2076df51c0", - "reference": "e88cd591f0786089dee22b972c28aa2076df51c0", + "url": "https://api.github.com/repos/doctrine/orm/zipball/bc217c0e19c3a9eadfa67697143b87c9ba01272c", + "reference": "bc217c0e19c3a9eadfa67697143b87c9ba01272c", "shasum": "" }, "require": { @@ -1253,9 +1242,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.6.3" + "source": "https://github.com/doctrine/orm/tree/3.6.7" }, - "time": "2026-04-02T06:53:27+00:00" + "time": "2026-05-25T16:45:47+00:00" }, { "name": "doctrine/persistence", @@ -1622,16 +1611,16 @@ }, { "name": "firebase/php-jwt", - "version": "v6.11.1", + "version": "v7.1.0", "source": { "type": "git", "url": "https://github.com/googleapis/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0", "shasum": "" }, "require": { @@ -1639,6 +1628,8 @@ }, "require-dev": { "guzzlehttp/guzzle": "^7.4", + "phpfastcache/phpfastcache": "^9.2", + "phpseclib/phpseclib": "~3.0", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", "psr/cache": "^2.0||^3.0", @@ -1647,7 +1638,8 @@ }, "suggest": { "ext-sodium": "Support EdDSA (Ed25519) signatures", - "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present", + "phpseclib/phpseclib": "Support PS256 (RSASSA-PSS) signatures" }, "type": "library", "autoload": { @@ -1672,38 +1664,39 @@ } ], "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", - "homepage": "https://github.com/firebase/php-jwt", + "homepage": "https://github.com/googleapis/php-jwt", "keywords": [ "jwt", "php" ], "support": { "issues": "https://github.com/googleapis/php-jwt/issues", - "source": "https://github.com/googleapis/php-jwt/tree/v6.11.1" + "source": "https://github.com/googleapis/php-jwt/tree/v7.1.0" }, - "time": "2025-04-09T20:32:01+00:00" + "time": "2026-06-11T17:54:14+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.15.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "744101956d78b7c1384d0cbf379db13e859167bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/744101956d78b7c1384d0cbf379db13e859167bf", + "reference": "744101956d78b7c1384d0cbf379db13e859167bf", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", + "guzzlehttp/promises": "^2.5.1", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" @@ -1711,9 +1704,10 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1791,7 +1785,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.15.2" }, "funding": [ { @@ -1807,28 +1801,29 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-07-26T23:23:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.3.0", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29", + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -1874,7 +1869,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "source": "https://github.com/guzzle/promises/tree/2.5.1" }, "funding": [ { @@ -1890,27 +1885,29 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2026-07-08T15:48:39+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.9.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -1918,9 +1915,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", + "http-interop/http-factory-tests": "1.1.0", "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1991,7 +1988,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.9.0" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -2007,51 +2004,52 @@ "type": "tidelift" } ], - "time": "2026-03-10T16:41:02+00:00" + "time": "2026-07-16T22:23:49+00:00" }, { "name": "hyvor/internal", - "version": "4.0.11", + "version": "4.1.0-beta.3", "source": { "type": "git", "url": "https://github.com/hyvor/internal.git", - "reference": "57af3b2e31624767d5aedc019bdf508e9e9a5252" + "reference": "05ee9c9e62106566eb2cd55b44e853a85b915b92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyvor/internal/zipball/57af3b2e31624767d5aedc019bdf508e9e9a5252", - "reference": "57af3b2e31624767d5aedc019bdf508e9e9a5252", + "url": "https://api.github.com/repos/hyvor/internal/zipball/05ee9c9e62106566eb2cd55b44e853a85b915b92", + "reference": "05ee9c9e62106566eb2cd55b44e853a85b915b92", "shasum": "" }, "require": { "ext-apcu": "*", "ext-intl": "*", "ext-openssl": "*", - "firebase/php-jwt": "^6.11", - "guzzlehttp/guzzle": "^7.8", - "illuminate/encryption": "*", - "php": ">=8.3", + "firebase/php-jwt": "^7.1.0", + "illuminate/encryption": "^11.0 || ^12.0 || ^13.0", + "nyholm/psr7": "^1.8", + "php": ">=8.4", "promphp/prometheus_client_php": "^2.14", - "symfony/expression-language": "^7.4 || ^8.0", - "symfony/http-client": "^7.4 || ^8.0", - "symfony/messenger": "^7.4 || ^8.0", + "symfony/expression-language": "^8.0 || ^8.1", + "symfony/http-client": "^8.0 || ^8.1", + "symfony/messenger": "^8.0 || ^8.1", "symfony/orm-pack": "^2.4", - "symfony/scheduler": "^7.4 || ^8.0", - "symfony/serializer-pack": "^1.3", - "symfony/twig-bundle": "^7.4 || ^8.0", - "symfony/ux-twig-component": "^2.24", - "symfony/validator": "^7.4 || ^8.0", + "symfony/scheduler": "^8.0 || ^8.1", + "symfony/serializer": "^8.0 || ^8.1", + "symfony/twig-bundle": "^8.0 || ^8.1", + "symfony/ux-twig-component": "^3.3.0", + "symfony/validator": "^8.0 || ^8.1", "zenstruck/foundry": "^2.6", "zenstruck/messenger-test": "^1.13" }, "require-dev": { - "orchestra/testbench": "^9.0", + "hyvor/sdk": "0.0.4", + "orchestra/testbench": "^11.0", "phpstan/phpstan": "^2.1.7", - "phpunit/phpunit": "^10.5", - "symfony/browser-kit": "^7.4 || ^8.0", - "symfony/config": "^7.4 || ^8.0", - "symfony/dependency-injection": "^7.4 || ^8.0", - "symfony/framework-bundle": "^7.4 || ^8.0" + "phpunit/phpunit": "^13.0", + "symfony/browser-kit": "^8.0 || ^8.1", + "symfony/config": "^8.0 || ^8.1", + "symfony/dependency-injection": "^8.0 || ^8.1", + "symfony/framework-bundle": "^8.0 || ^8.1" }, "type": "library", "extra": { @@ -2080,22 +2078,22 @@ "description": "Internal Package for HYVOR Applications", "support": { "issues": "https://github.com/hyvor/internal/issues", - "source": "https://github.com/hyvor/internal/tree/4.0.11" + "source": "https://github.com/hyvor/internal/tree/4.1.0-beta.3" }, - "time": "2026-04-13T08:46:02+00:00" + "time": "2026-07-29T08:20:41+00:00" }, { "name": "hyvor/phrosemirror", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/hyvor/phrosemirror.git", - "reference": "b01c0d3474a0f00fec702129eab784882e7b38d5" + "reference": "e8439f9b4bce9d89ad6220a055454e4c13627024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hyvor/phrosemirror/zipball/b01c0d3474a0f00fec702129eab784882e7b38d5", - "reference": "b01c0d3474a0f00fec702129eab784882e7b38d5", + "url": "https://api.github.com/repos/hyvor/phrosemirror/zipball/e8439f9b4bce9d89ad6220a055454e4c13627024", + "reference": "e8439f9b4bce9d89ad6220a055454e4c13627024", "shasum": "" }, "require": { @@ -2127,22 +2125,22 @@ "description": "Prosemirror in PHP", "support": { "issues": "https://github.com/hyvor/phrosemirror/issues", - "source": "https://github.com/hyvor/phrosemirror/tree/1.0.5" + "source": "https://github.com/hyvor/phrosemirror/tree/1.0.6" }, - "time": "2025-04-18T09:20:31+00:00" + "time": "2026-07-06T13:03:10+00:00" }, { "name": "illuminate/collections", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "8337eab46f512633d36df6b2f0ee54bf309c1da1" + "reference": "79b5d04507a255e35ce34cec621696aca84ee242" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/8337eab46f512633d36df6b2f0ee54bf309c1da1", - "reference": "8337eab46f512633d36df6b2f0ee54bf309c1da1", + "url": "https://api.github.com/repos/illuminate/collections/zipball/79b5d04507a255e35ce34cec621696aca84ee242", + "reference": "79b5d04507a255e35ce34cec621696aca84ee242", "shasum": "" }, "require": { @@ -2150,8 +2148,9 @@ "illuminate/contracts": "^13.0", "illuminate/macroable": "^13.0", "php": "^8.3", - "symfony/polyfill-php84": "^1.33", - "symfony/polyfill-php85": "^1.33" + "symfony/polyfill-php84": "^1.36", + "symfony/polyfill-php85": "^1.36", + "symfony/polyfill-php86": "^1.36" }, "suggest": { "illuminate/http": "Required to convert collections to API resources (^13.0).", @@ -2188,11 +2187,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-04-18T13:28:59+00:00" + "time": "2026-07-26T13:07:32+00:00" }, { "name": "illuminate/conditionable", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -2238,16 +2237,16 @@ }, { "name": "illuminate/contracts", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "022d9816b4ff052a3db5946a86be3cd2e224db0f" + "reference": "98179ca5d07025c2fe4c5c70e0b57b5284fa0071" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/022d9816b4ff052a3db5946a86be3cd2e224db0f", - "reference": "022d9816b4ff052a3db5946a86be3cd2e224db0f", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/98179ca5d07025c2fe4c5c70e0b57b5284fa0071", + "reference": "98179ca5d07025c2fe4c5c70e0b57b5284fa0071", "shasum": "" }, "require": { @@ -2282,20 +2281,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-04-12T17:46:48+00:00" + "time": "2026-07-15T15:50:17+00:00" }, { "name": "illuminate/encryption", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/encryption.git", - "reference": "b2755a378450cc6d708428004d8bc1e4a1532864" + "reference": "0411bce6517ede864e59e3e98948609d3df081d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/encryption/zipball/b2755a378450cc6d708428004d8bc1e4a1532864", - "reference": "b2755a378450cc6d708428004d8bc1e4a1532864", + "url": "https://api.github.com/repos/illuminate/encryption/zipball/0411bce6517ede864e59e3e98948609d3df081d6", + "reference": "0411bce6517ede864e59e3e98948609d3df081d6", "shasum": "" }, "require": { @@ -2333,20 +2332,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-04-18T13:35:25+00:00" + "time": "2026-07-13T15:53:32+00:00" }, { "name": "illuminate/macroable", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", - "reference": "f108cb3a8680f26e23c6ce7367c64525412d85b0" + "reference": "59b5b5f3cf290a91db8cf6cd3d35ff56978bc057" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/f108cb3a8680f26e23c6ce7367c64525412d85b0", - "reference": "f108cb3a8680f26e23c6ce7367c64525412d85b0", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/59b5b5f3cf290a91db8cf6cd3d35ff56978bc057", + "reference": "59b5b5f3cf290a91db8cf6cd3d35ff56978bc057", "shasum": "" }, "require": { @@ -2379,20 +2378,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-03-28T19:16:13+00:00" + "time": "2026-04-29T09:35:06+00:00" }, { "name": "illuminate/reflection", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/reflection.git", - "reference": "4fe1659f068ab2b50131cf906c5d8bba4e34df0c" + "reference": "83788072c0f11f504c0a3dccfe9c15f5156d8b90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/reflection/zipball/4fe1659f068ab2b50131cf906c5d8bba4e34df0c", - "reference": "4fe1659f068ab2b50131cf906c5d8bba4e34df0c", + "url": "https://api.github.com/repos/illuminate/reflection/zipball/83788072c0f11f504c0a3dccfe9c15f5156d8b90", + "reference": "83788072c0f11f504c0a3dccfe9c15f5156d8b90", "shasum": "" }, "require": { @@ -2430,20 +2429,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-03-10T20:04:12+00:00" + "time": "2026-07-16T14:41:30+00:00" }, { "name": "illuminate/support", - "version": "v13.6.0", + "version": "v13.23.0", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "5394e4b6008175f5a42c1227b5c4b959aa1be963" + "reference": "c44c62550a18912175966ba340345ffd800ac54f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/5394e4b6008175f5a42c1227b5c4b959aa1be963", - "reference": "5394e4b6008175f5a42c1227b5c4b959aa1be963", + "url": "https://api.github.com/repos/illuminate/support/zipball/c44c62550a18912175966ba340345ffd800ac54f", + "reference": "c44c62550a18912175966ba340345ffd800ac54f", "shasum": "" }, "require": { @@ -2458,7 +2457,7 @@ "illuminate/reflection": "^13.0", "nesbot/carbon": "^3.8.4", "php": "^8.3", - "symfony/polyfill-php85": "^1.33", + "symfony/polyfill-php85": "^1.36", "voku/portable-ascii": "^2.0.2" }, "conflict": { @@ -2509,7 +2508,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-04-20T13:42:58+00:00" + "time": "2026-07-26T13:08:33+00:00" }, { "name": "jean85/pretty-package-versions", @@ -2573,16 +2572,16 @@ }, { "name": "league/flysystem", - "version": "3.33.0", + "version": "3.35.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "570b8871e0ce693764434b29154c54b434905350" + "reference": "b277b5dc3d56650b68904117124e79c851e12376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350", - "reference": "570b8871e0ce693764434b29154c54b434905350", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376", + "reference": "b277b5dc3d56650b68904117124e79c851e12376", "shasum": "" }, "require": { @@ -2650,26 +2649,26 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.33.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.35.2" }, - "time": "2026-03-25T07:59:30+00:00" + "time": "2026-07-06T14:42:07+00:00" }, { "name": "league/flysystem-aws-s3-v3", - "version": "3.32.0", + "version": "3.35.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "a1979df7c9784d334ea6df356aed3d18ac6673d0" + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/a1979df7c9784d334ea6df356aed3d18ac6673d0", - "reference": "a1979df7c9784d334ea6df356aed3d18ac6673d0", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/8475ef9adfc6498b85469e2abec6fe3118cd08c4", + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4", "shasum": "" }, "require": { - "aws/aws-sdk-php": "^3.295.10", + "aws/aws-sdk-php": "^3.371.5", "league/flysystem": "^3.10.0", "league/mime-type-detection": "^1.0.0", "php": "^8.0.2" @@ -2705,9 +2704,9 @@ "storage" ], "support": { - "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.32.0" + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.35.2" }, - "time": "2026-02-25T16:46:44+00:00" + "time": "2026-07-01T23:25:49+00:00" }, { "name": "league/flysystem-local", @@ -2760,16 +2759,16 @@ }, { "name": "league/mime-type-detection", - "version": "1.16.0", + "version": "1.17.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76", + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76", "shasum": "" }, "require": { @@ -2779,7 +2778,7 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0" }, "type": "library", "autoload": { @@ -2800,7 +2799,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0" }, "funding": [ { @@ -2812,74 +2811,7 @@ "type": "tidelift" } ], - "time": "2024-09-21T08:32:55+00:00" - }, - { - "name": "masterminds/html5", - "version": "2.10.0", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "fcf91eb64359852f00d921887b219479b4f21251" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", - "reference": "fcf91eb64359852f00d921887b219479b4f21251", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" - }, - "time": "2025-07-25T09:04:22+00:00" + "time": "2026-07-09T11:49:27+00:00" }, { "name": "monolog/monolog", @@ -2986,16 +2918,16 @@ }, { "name": "mtdowling/jmespath.php", - "version": "2.8.0", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", - "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", "shasum": "" }, "require": { @@ -3004,7 +2936,7 @@ }, "require-dev": { "composer/xdebug-handler": "^3.0.3", - "phpunit/phpunit": "^8.5.33" + "phpunit/phpunit": "^8.5.52" }, "bin": [ "bin/jp.php" @@ -3012,7 +2944,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "2.9-dev" } }, "autoload": { @@ -3046,9 +2978,9 @@ ], "support": { "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" + "source": "https://github.com/jmespath/jmespath.php/tree/2.9.2" }, - "time": "2024-09-04T18:46:31+00:00" + "time": "2026-07-06T18:56:19+00:00" }, { "name": "myclabs/deep-copy", @@ -3177,16 +3109,16 @@ }, { "name": "nesbot/carbon", - "version": "3.11.4", + "version": "3.13.1", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" + "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", - "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2", + "reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2", "shasum": "" }, "require": { @@ -3278,103 +3210,48 @@ "type": "tidelift" } ], - "time": "2026-04-07T09:57:54+00:00" + "time": "2026-07-09T18:23:49+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "nyholm/psr7", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "6.0.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", - "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", - "shasum": "" + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^2.0", - "phpstan/phpdoc-parser": "^2.0", - "webmozart/assert": "^1.9.1 || ^2" + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26", - "shipmonk/dead-code-detector": "^0.5.1" + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "1.8-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "Nyholm\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3383,138 +3260,48 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" }, { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" - }, - "time": "2026-03-18T20:49:53+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" }, - "time": "2026-01-06T21:53:42+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" + "time": "2024-09-09T07:06:30+00:00" }, { "name": "promphp/prometheus_client_php", - "version": "v2.15.0", + "version": "v2.15.1", "source": { "type": "git", "url": "https://github.com/PromPHP/prometheus_client_php.git", - "reference": "da86f1507b04dc44dc37ffb766d7d3a1d42c3050" + "reference": "e153786bda5ca1d07335a2b5f477742c37195c40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/da86f1507b04dc44dc37ffb766d7d3a1d42c3050", - "reference": "da86f1507b04dc44dc37ffb766d7d3a1d42c3050", + "url": "https://api.github.com/repos/PromPHP/prometheus_client_php/zipball/e153786bda5ca1d07335a2b5f477742c37195c40", + "reference": "e153786bda5ca1d07335a2b5f477742c37195c40", "shasum": "" }, "require": { @@ -3534,7 +3321,7 @@ "phpstan/phpstan-strict-rules": "^1.1.0", "phpunit/phpunit": "^9.4", "predis/predis": "^2.3", - "squizlabs/php_codesniffer": "^3.6", + "squizlabs/php_codesniffer": "^3.6 || ^4.0", "symfony/polyfill-apcu": "^1.6" }, "suggest": { @@ -3569,9 +3356,9 @@ "description": "Prometheus instrumentation library for PHP applications.", "support": { "issues": "https://github.com/PromPHP/prometheus_client_php/issues", - "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.15.0" + "source": "https://github.com/PromPHP/prometheus_client_php/tree/v2.15.1" }, - "time": "2026-03-24T22:44:50+00:00" + "time": "2026-05-28T07:51:04+00:00" }, { "name": "psr/cache", @@ -4078,70 +3865,18 @@ }, "time": "2019-03-08T08:55:37+00:00" }, - { - "name": "runtime/frankenphp-symfony", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/php-runtime/frankenphp-symfony.git", - "reference": "56822c3631d9522a3136a4c33082d006bdfe4bad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-runtime/frankenphp-symfony/zipball/56822c3631d9522a3136a4c33082d006bdfe4bad", - "reference": "56822c3631d9522a3136a4c33082d006bdfe4bad", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/runtime": "^5.4 || ^6.0 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Runtime\\FrankenPhpSymfony\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kévin Dunglas", - "email": "kevin@dunglas.dev" - } - ], - "description": "FrankenPHP runtime for Symfony", - "support": { - "issues": "https://github.com/php-runtime/frankenphp-symfony/issues", - "source": "https://github.com/php-runtime/frankenphp-symfony/tree/0.2.0" - }, - "funding": [ - { - "url": "https://github.com/nyholm", - "type": "github" - } - ], - "time": "2023-12-12T12:06:11+00:00" - }, { "name": "sentry/sentry", - "version": "4.25.0", + "version": "4.29.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "bfee3381e1f6dea8a5f3a18adba6419fe81c5f54" + "reference": "d732a4da195f231cedb2a2a78ae16dd73082afa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/bfee3381e1f6dea8a5f3a18adba6419fe81c5f54", - "reference": "bfee3381e1f6dea8a5f3a18adba6419fe81c5f54", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/d732a4da195f231cedb2a2a78ae16dd73082afa3", + "reference": "d732a4da195f231cedb2a2a78ae16dd73082afa3", "shasum": "" }, "require": { @@ -4158,15 +3893,15 @@ "raven/raven": "*" }, "require-dev": { + "carthage-software/mago": "1.30.0", "friendsofphp/php-cs-fixer": "^3.4", "guzzlehttp/promises": "^2.0.3", - "guzzlehttp/psr7": "^1.8.4|^2.1.1", "monolog/monolog": "^1.6|^2.0|^3.0", "nyholm/psr7": "^1.8", "open-telemetry/api": "^1.0", "open-telemetry/exporter-otlp": "^1.0", "open-telemetry/sdk": "^1.0", - "phpbench/phpbench": "^1.0", + "open-telemetry/sem-conv": "^1.27", "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^8.5.52|^9.6.34", "spiral/roadrunner-http": "^3.6", @@ -4210,7 +3945,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/4.25.0" + "source": "https://github.com/getsentry/sentry-php/tree/4.29.0" }, "funding": [ { @@ -4222,20 +3957,20 @@ "type": "custom" } ], - "time": "2026-04-23T12:50:03+00:00" + "time": "2026-06-29T14:47:44+00:00" }, { "name": "sentry/sentry-symfony", - "version": "5.10.0", + "version": "5.11.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-symfony.git", - "reference": "6f49255f4cdcfc43a3a283bd3a1f65d483e9192f" + "reference": "7d72a006c2099ef932f41233df9d1e8effe480d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-symfony/zipball/6f49255f4cdcfc43a3a283bd3a1f65d483e9192f", - "reference": "6f49255f4cdcfc43a3a283bd3a1f65d483e9192f", + "url": "https://api.github.com/repos/getsentry/sentry-symfony/zipball/7d72a006c2099ef932f41233df9d1e8effe480d7", + "reference": "7d72a006c2099ef932f41233df9d1e8effe480d7", "shasum": "" }, "require": { @@ -4262,7 +3997,7 @@ "phpstan/phpstan": "1.12.5", "phpstan/phpstan-phpunit": "1.4.0", "phpstan/phpstan-symfony": "1.4.10", - "phpunit/phpunit": "^8.5.40||^9.6.21", + "phpunit/phpunit": "^8.5.52||^9.6.34", "symfony/browser-kit": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0", "symfony/cache": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0", "symfony/dom-crawler": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0", @@ -4312,7 +4047,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-symfony/issues", - "source": "https://github.com/getsentry/sentry-symfony/tree/5.10.0" + "source": "https://github.com/getsentry/sentry-symfony/tree/5.11.0" }, "funding": [ { @@ -4324,38 +4059,33 @@ "type": "custom" } ], - "time": "2026-04-01T14:50:32+00:00" + "time": "2026-07-13T11:32:54+00:00" }, { "name": "symfony/cache", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "467464da294734b0fb17e853e5712abc8470f819" + "reference": "76ec84217eb39e4787d03928bba86a25cc859f76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/467464da294734b0fb17e853e5712abc8470f819", - "reference": "467464da294734b0fb17e853e5712abc8470f819", + "url": "https://api.github.com/repos/symfony/cache/zipball/76ec84217eb39e4787d03928bba86a25cc859f76", + "reference": "76ec84217eb39e4787d03928bba86a25cc859f76", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^3.6", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "doctrine/dbal": "<3.6", "ext-redis": "<6.1", - "ext-relay": "<0.12.1", - "symfony/dependency-injection": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/var-dumper": "<6.4" + "ext-relay": "<0.12.1" }, "provide": { "psr/cache-implementation": "2.0|3.0", @@ -4364,16 +4094,16 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4408,7 +4138,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.4.8" + "source": "https://github.com/symfony/cache/tree/v8.0.14" }, "funding": [ { @@ -4428,20 +4158,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:15:47+00:00" + "time": "2026-06-17T15:00:27+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868" + "reference": "9789738bc19af1106dc54d6afba9a0b467516cf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868", - "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/9789738bc19af1106dc54d6afba9a0b467516cf2", + "reference": "9789738bc19af1106dc54d6afba9a0b467516cf2", "shasum": "" }, "require": { @@ -4455,7 +4185,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4488,7 +4218,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.7.1" }, "funding": [ { @@ -4499,31 +4229,34 @@ "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": "2025-03-13T15:25:07+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/clock", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111" + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111", - "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -4562,7 +4295,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.4.8" + "source": "https://github.com/symfony/clock/tree/v8.0.8" }, "funding": [ { @@ -4582,38 +4315,37 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/config", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "2d19dde43fa2ff720b9a40763ace7226594f503b" + "reference": "79d7c29beb7b9f10e16000d1df8dcad745364ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/2d19dde43fa2ff720b9a40763ace7226594f503b", - "reference": "2d19dde43fa2ff720b9a40763ace7226594f503b", + "url": "https://api.github.com/repos/symfony/config/zipball/79d7c29beb7b9f10e16000d1df8dcad745364ab1", + "reference": "79d7c29beb7b9f10e16000d1df8dcad745364ab1", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1|^8.0", - "symfony/polyfill-ctype": "~1.8" + "symfony/filesystem": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/finder": "<6.4", "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4641,7 +4373,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.4.8" + "source": "https://github.com/symfony/config/tree/v8.0.14" }, "funding": [ { @@ -4661,51 +4393,43 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/console", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" + "reference": "e2a77289192c413abfe54f1d507159f911a20728" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "url": "https://api.github.com/repos/symfony/console/zipball/e2a77289192c413abfe54f1d507159f911a20728", + "reference": "e2a77289192c413abfe54f1d507159f911a20728", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4739,7 +4463,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.8" + "source": "https://github.com/symfony/console/tree/v8.0.14" }, "funding": [ { @@ -4759,24 +4483,24 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:54:39+00:00" + "time": "2026-06-16T12:45:11+00:00" }, { "name": "symfony/css-selector", - "version": "v7.4.8", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "b055f228a4178a1d6774909903905e3475f3eac8" + "reference": "3665cfade90565430909b906394c73c8739e57d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/b055f228a4178a1d6774909903905e3475f3eac8", - "reference": "b055f228a4178a1d6774909903905e3475f3eac8", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0", + "reference": "3665cfade90565430909b906394c73c8739e57d0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -4808,7 +4532,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.4.8" + "source": "https://github.com/symfony/css-selector/tree/v8.0.9" }, "funding": [ { @@ -4828,43 +4552,40 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f7025fd7b687c240426562f86ada06a93b1e771d" + "reference": "55d83c634f0bd7d6b5f64be8950dc389dca17a17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f7025fd7b687c240426562f86ada06a93b1e771d", - "reference": "f7025fd7b687c240426562f86ada06a93b1e771d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/55d83c634f0bd7d6b5f64be8950dc389dca17a17", + "reference": "55d83c634f0bd7d6b5f64be8950dc389dca17a17", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^3.6", - "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<6.4", - "symfony/finder": "<6.4", - "symfony/yaml": "<6.4" + "ext-psr": "<1.1|>=2" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4892,7 +4613,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.4.8" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.14" }, "funding": [ { @@ -4912,20 +4633,20 @@ "type": "tidelift" } ], - "time": "2026-03-31T06:50:29+00:00" + "time": "2026-06-27T06:17:20+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "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/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -4938,7 +4659,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -4963,7 +4684,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.1" }, "funding": [ { @@ -4974,77 +4695,70 @@ "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-06-05T06:23:12+00:00" }, { "name": "symfony/doctrine-bridge", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "3f8f805e54ecb5cbd487b1eff8837a8bbd278669" + "reference": "91ab2e8085ea19ada54bd820f0dcf7c2cef59494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/3f8f805e54ecb5cbd487b1eff8837a8bbd278669", - "reference": "3f8f805e54ecb5cbd487b1eff8837a8bbd278669", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/91ab2e8085ea19ada54bd820f0dcf7c2cef59494", + "reference": "91ab2e8085ea19ada54bd820f0dcf7c2cef59494", "shasum": "" }, "require": { "doctrine/event-manager": "^2", "doctrine/persistence": "^3.1|^4", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { "doctrine/collections": "<1.8", - "doctrine/dbal": "<3.6", + "doctrine/dbal": "<4.3", "doctrine/lexer": "<1.1", - "doctrine/orm": "<2.15", - "symfony/cache": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/form": "<6.4.6|>=7,<7.0.6", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/lock": "<6.4", - "symfony/messenger": "<6.4", - "symfony/property-info": "<6.4", - "symfony/security-bundle": "<6.4", - "symfony/security-core": "<6.4", - "symfony/validator": "<7.4" + "doctrine/orm": "<3.4", + "symfony/property-info": "<8.0" }, "require-dev": { "doctrine/collections": "^1.8|^2.0", "doctrine/data-fixtures": "^1.1|^2", - "doctrine/dbal": "^3.6|^4", - "doctrine/orm": "^2.15|^3", + "doctrine/dbal": "^4.3", + "doctrine/orm": "^3.4", "psr/log": "^1|^2|^3", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/doctrine-messenger": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/form": "^7.2|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/security-core": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", - "symfony/type-info": "^7.1.8|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/doctrine-messenger": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/type-info": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/var-dumper": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -5072,7 +4786,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.4.8" + "source": "https://github.com/symfony/doctrine-bridge/tree/v8.0.14" }, "funding": [ { @@ -5092,26 +4806,26 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-12T08:36:40+00:00" }, { "name": "symfony/doctrine-messenger", - "version": "v7.4.6", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "a429cd95983eaea2371ea279bed3b8a93b9ecdd3" + "reference": "280267d1e0c89e752573f0b588047d3633b1b272" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/a429cd95983eaea2371ea279bed3b8a93b9ecdd3", - "reference": "a429cd95983eaea2371ea279bed3b8a93b9ecdd3", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/280267d1e0c89e752573f0b588047d3633b1b272", + "reference": "280267d1e0c89e752573f0b588047d3633b1b272", "shasum": "" }, "require": { - "doctrine/dbal": "^3.6|^4", - "php": ">=8.2", - "symfony/messenger": "^7.2|^8.0", + "doctrine/dbal": "^4.3", + "php": ">=8.4", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -5119,8 +4833,8 @@ }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "symfony-messenger-bridge", "autoload": { @@ -5148,7 +4862,7 @@ "description": "Symfony Doctrine Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v7.4.6" + "source": "https://github.com/symfony/doctrine-messenger/tree/v8.0.14" }, "funding": [ { @@ -5168,31 +4882,29 @@ "type": "tidelift" } ], - "time": "2026-02-18T09:40:04+00:00" + "time": "2026-06-12T08:36:40+00:00" }, { "name": "symfony/dom-crawler", - "version": "v7.4.8", + "version": "v8.0.12", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "2918e7c2ba964defca1f5b69c6f74886529e2dc8" + "reference": "011b0ce60417f6d40052434d8ae6295b876ecbdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2918e7c2ba964defca1f5b69c6f74886529e2dc8", - "reference": "2918e7c2ba964defca1f5b69c6f74886529e2dc8", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/011b0ce60417f6d40052434d8ae6295b876ecbdd", + "reference": "011b0ce60417f6d40052434d8ae6295b876ecbdd", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0|^8.0" + "symfony/css-selector": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5220,7 +4932,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.4.8" + "source": "https://github.com/symfony/dom-crawler/tree/v8.0.12" }, "funding": [ { @@ -5240,32 +4952,28 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-20T07:22:03+00:00" }, { "name": "symfony/dotenv", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "5df79f11350166125fe754c85b87f7e13d735314" + "reference": "c81558d21cdc8f5b520c993ac1cfd5299c1826de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/5df79f11350166125fe754c85b87f7e13d735314", - "reference": "5df79f11350166125fe754c85b87f7e13d735314", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/c81558d21cdc8f5b520c993ac1cfd5299c1826de", + "reference": "c81558d21cdc8f5b520c993ac1cfd5299c1826de", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "conflict": { - "symfony/console": "<6.4", - "symfony/process": "<6.4" + "php": ">=8.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5298,7 +5006,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.4.8" + "source": "https://github.com/symfony/dotenv/tree/v8.0.14" }, "funding": [ { @@ -5318,37 +5026,36 @@ "type": "tidelift" } ], - "time": "2026-03-30T12:55:43+00:00" + "time": "2026-06-05T06:22:43+00:00" }, { "name": "symfony/error-handler", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa" + "reference": "cc0f2d74213375a6ff1caf23751e23afbb91eb8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", - "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/cc0f2d74213375a6ff1caf23751e23afbb91eb8c", + "reference": "cc0f2d74213375a6ff1caf23751e23afbb91eb8c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", "symfony/polyfill-php85": "^1.32", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/var-dumper": "^7.4|^8.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5", - "symfony/http-kernel": "<6.4" + "symfony/deprecation-contracts": "<2.5" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", + "symfony/console": "^7.4|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -5380,7 +5087,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.4.8" + "source": "https://github.com/symfony/error-handler/tree/v8.0.14" }, "funding": [ { @@ -5400,28 +5107,28 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-05T06:22:43+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "f57b899fa736fd71121168ef268f23c206083f0a" + "reference": "3973836dd335445d0c622ffe98e418eed304d95b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f57b899fa736fd71121168ef268f23c206083f0a", - "reference": "f57b899fa736fd71121168ef268f23c206083f0a", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3973836dd335445d0c622ffe98e418eed304d95b", + "reference": "3973836dd335445d0c622ffe98e418eed304d95b", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -5430,14 +5137,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5465,7 +5172,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.8" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.14" }, "funding": [ { @@ -5485,20 +5192,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:54:39+00:00" + "time": "2026-06-06T11:11:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" }, "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/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", "shasum": "" }, "require": { @@ -5512,7 +5219,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -5545,7 +5252,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.1" }, "funding": [ { @@ -5556,31 +5263,34 @@ "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-06-05T06:23:12+00:00" }, { "name": "symfony/expression-language", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83" + "reference": "05852d82e9a1ae3fb52a53d2998904f058e4cd81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/87ff95687748f4af65e4d5a6e917d448ec52aa83", - "reference": "87ff95687748f4af65e4d5a6e917d448ec52aa83", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/05852d82e9a1ae3fb52a53d2998904f058e4cd81", + "reference": "05852d82e9a1ae3fb52a53d2998904f058e4cd81", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", + "symfony/cache": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -5609,7 +5319,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v7.4.8" + "source": "https://github.com/symfony/expression-language/tree/v8.0.14" }, "funding": [ { @@ -5629,29 +5339,29 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/filesystem", - "version": "v7.4.8", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5" + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/58b9790d12f9670b7f53a1c1738febd3108970a5", - "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/224db910898ce1317b892a9a1338f1f8f17eb7c7", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5679,7 +5389,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.8" + "source": "https://github.com/symfony/filesystem/tree/v8.0.11" }, "funding": [ { @@ -5699,27 +5409,27 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-11T16:39:47+00:00" }, { "name": "symfony/finder", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e0be088d22278583a82da281886e8c3592fbf149" + "reference": "4a93f8c95fabc35fcfd2f5dbb29d138d3fe98f43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", - "reference": "e0be088d22278583a82da281886e8c3592fbf149", + "url": "https://api.github.com/repos/symfony/finder/zipball/4a93f8c95fabc35fcfd2f5dbb29d138d3fe98f43", + "reference": "4a93f8c95fabc35fcfd2f5dbb29d138d3fe98f43", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5747,7 +5457,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.8" + "source": "https://github.com/symfony/finder/tree/v8.0.14" }, "funding": [ { @@ -5767,20 +5477,20 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-27T08:56:37+00:00" }, { "name": "symfony/flex", - "version": "v2.10.0", + "version": "v2.11.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" + "reference": "4a6d98eea3ebc7f68d82810cb682eedca2649e99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", - "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", + "url": "https://api.github.com/repos/symfony/flex/zipball/4a6d98eea3ebc7f68d82810cb682eedca2649e99", + "reference": "4a6d98eea3ebc7f68d82810cb682eedca2649e99", "shasum": "" }, "require": { @@ -5793,9 +5503,9 @@ }, "require-dev": { "composer/composer": "^2.1", - "symfony/dotenv": "^6.4|^7.4|^8.0", + "phpunit/phpunit": "^12.4", + "symfony/dotenv": "^6.4.41|^7.4.13|^8.0.13", "symfony/filesystem": "^6.4|^7.4|^8.0", - "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", "symfony/process": "^6.4|^7.4|^8.0" }, "type": "composer-plugin", @@ -5820,7 +5530,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.10.0" + "source": "https://github.com/symfony/flex/tree/v2.11.0" }, "funding": [ { @@ -5840,37 +5550,37 @@ "type": "tidelift" } ], - "time": "2025-11-16T09:38:19+00:00" + "time": "2026-05-29T17:25:22+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "180533cfbac2144349044267db31d5d3df9957cb" + "reference": "74134f73e391698bf78b3dd05bb25a0bdfccbabb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/180533cfbac2144349044267db31d5d3df9957cb", - "reference": "180533cfbac2144349044267db31d5d3df9957cb", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/74134f73e391698bf78b3dd05bb25a0bdfccbabb", + "reference": "74134f73e391698bf78b3dd05bb25a0bdfccbabb", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", "ext-xml": "*", - "php": ">=8.2", - "symfony/cache": "^6.4.12|^7.0|^8.0", + "php": ">=8.4", + "symfony/cache": "^7.4|^8.0", "symfony/config": "^7.4.4|^8.0.4", "symfony/dependency-injection": "^7.4.4|^8.0.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^7.3|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^7.1|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/http-kernel": "^7.4|^8.0", - "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php85": "^1.32", "symfony/routing": "^7.4|^8.0" }, @@ -5878,79 +5588,62 @@ "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<5.2|>=7", "phpdocumentor/type-resolver": "<1.5.1", - "symfony/asset": "<6.4", - "symfony/asset-mapper": "<6.4", - "symfony/clock": "<6.4", - "symfony/console": "<6.4", - "symfony/dom-crawler": "<6.4", - "symfony/dotenv": "<6.4", + "symfony/console": "<7.4", "symfony/form": "<7.4", - "symfony/http-client": "<6.4", - "symfony/lock": "<6.4", - "symfony/mailer": "<6.4", + "symfony/json-streamer": "<7.4", "symfony/messenger": "<7.4", - "symfony/mime": "<6.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", - "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", - "symfony/security-core": "<6.4", - "symfony/security-csrf": "<7.2", - "symfony/serializer": "<7.2.5", - "symfony/stopwatch": "<6.4", - "symfony/translation": "<7.3", - "symfony/twig-bridge": "<6.4", - "symfony/twig-bundle": "<6.4", - "symfony/validator": "<6.4", - "symfony/web-profiler-bundle": "<6.4", - "symfony/webhook": "<7.2", + "symfony/mime": "<7.4.9|>=8.0,<8.0.9", + "symfony/security-csrf": "<7.4", + "symfony/serializer": "<7.4", + "symfony/translation": "<7.4", + "symfony/webhook": "<7.4", "symfony/workflow": "<7.4" }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", "dragonmantank/cron-expression": "^3.1", "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/asset": "^6.4|^7.0|^8.0", - "symfony/asset-mapper": "^6.4|^7.0|^8.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/dotenv": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/asset": "^7.4|^8.0", + "symfony/asset-mapper": "^7.4|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/dotenv": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", "symfony/form": "^7.4|^8.0", - "symfony/html-sanitizer": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/json-streamer": "^7.3|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/mailer": "^6.4|^7.0|^8.0", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/json-streamer": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/mailer": "^7.4|^8.0", "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/notifier": "^6.4|^7.0|^8.0", - "symfony/object-mapper": "^7.3|^8.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6|^8.0", - "symfony/scheduler": "^6.4.4|^7.0.4|^8.0", - "symfony/security-bundle": "^6.4|^7.0|^8.0", - "symfony/semaphore": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.2.5|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/string": "^6.4|^7.0|^8.0", - "symfony/translation": "^7.3|^8.0", - "symfony/twig-bundle": "^6.4|^7.0|^8.0", - "symfony/type-info": "^7.1.8|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.4.9|^8.0.9", + "symfony/notifier": "^7.4|^8.0", + "symfony/object-mapper": "^7.4.9|^8.0.9", + "symfony/polyfill-intl-icu": "^1.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/scheduler": "^7.4|^8.0", + "symfony/security-bundle": "^7.4|^8.0", + "symfony/semaphore": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0", + "symfony/type-info": "^7.4.1|^8.0.1", + "symfony/uid": "^7.4|^8.0", "symfony/validator": "^7.4|^8.0", - "symfony/web-link": "^6.4|^7.0|^8.0", - "symfony/webhook": "^7.2|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/webhook": "^7.4|^8.0", "symfony/workflow": "^7.4|^8.0", - "symfony/yaml": "^7.3|^8.0", - "twig/twig": "^3.12" + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -5978,7 +5671,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.4.8" + "source": "https://github.com/symfony/framework-bundle/tree/v8.0.14" }, "funding": [ { @@ -5998,35 +5691,31 @@ "type": "tidelift" } ], - "time": "2026-03-30T12:55:43+00:00" + "time": "2026-06-27T08:56:37+00:00" }, { "name": "symfony/http-client", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "01933e626c3de76bea1e22641e205e78f6a34342" + "reference": "4f7a48437beaad45849f9b94b7fb57b3da6aed6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/01933e626c3de76bea1e22641e205e78f6a34342", - "reference": "01933e626c3de76bea1e22641e205e78f6a34342", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4f7a48437beaad45849f9b94b7fb57b3da6aed6e", + "reference": "4f7a48437beaad45849f9b94b7fb57b3da6aed6e", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", - "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "amphp/amp": "<2.5", - "amphp/socket": "<1.1", - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.4" + "amphp/amp": "<3", + "php-http/discovery": "<1.15" }, "provide": { "php-http/async-client-implementation": "*", @@ -6035,20 +5724,19 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/http-client": "^4.2.1|^5.0", - "amphp/http-tunnel": "^1.0|^2.0", + "amphp/http-client": "^5.3.2", + "amphp/http-tunnel": "^2.0", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6079,7 +5767,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.4.8" + "source": "https://github.com/symfony/http-client/tree/v8.0.14" }, "funding": [ { @@ -6099,20 +5787,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T12:55:43+00:00" + "time": "2026-06-16T12:45:11+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "75d7043853a42837e68111812f4d964b01e5101c" + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", - "reference": "75d7043853a42837e68111812f4d964b01e5101c", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41fc42d276aeff21192465331ebbab7d83a743c0", + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0", "shasum": "" }, "require": { @@ -6125,7 +5813,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -6161,7 +5849,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.1" }, "funding": [ { @@ -6172,46 +5860,48 @@ "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": "2025-04-29T11:18:49+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab" + "reference": "45c47ca550e551a1f72e67eb4276e69e929e8a17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/45c47ca550e551a1f72e67eb4276e69e929e8a17", + "reference": "45c47ca550e551a1f72e67eb4276e69e929e8a17", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-mbstring": "^1.1" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "doctrine/dbal": "<4.3" }, "require-dev": { - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6239,7 +5929,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v8.0.14" }, "funding": [ { @@ -6259,78 +5949,63 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-12T08:36:40+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "017e76ad089bac281553389269e259e155935e1a" + "reference": "6685b981881100e5f92a98fb6c5d6739af6a8569" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/017e76ad089bac281553389269e259e155935e1a", - "reference": "017e76ad089bac281553389269e259e155935e1a", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6685b981881100e5f92a98fb6c5d6739af6a8569", + "reference": "6685b981881100e5f92a98fb6c5d6739af6a8569", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", "symfony/flex": "<2.10", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.12" + "twig/twig": "<3.21" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^7.1|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.1|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" }, "type": "library", "autoload": { @@ -6358,7 +6033,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.8" + "source": "https://github.com/symfony/http-kernel/tree/v8.0.14" }, "funding": [ { @@ -6378,32 +6053,31 @@ "type": "tidelift" } ], - "time": "2026-03-31T20:57:01+00:00" + "time": "2026-06-27T09:16:44+00:00" }, { "name": "symfony/lock", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/lock.git", - "reference": "e0cd253a8a043edc69c04a6b51b5f598b6a06515" + "reference": "74e7eae902fd59b674115494ee14e461ee35f299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/lock/zipball/e0cd253a8a043edc69c04a6b51b5f598b6a06515", - "reference": "e0cd253a8a043edc69c04a6b51b5f598b6a06515", + "url": "https://api.github.com/repos/symfony/lock/zipball/74e7eae902fd59b674115494ee14e461ee35f299", + "reference": "74e7eae902fd59b674115494ee14e461ee35f299", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4" + "doctrine/dbal": "<4.3" }, "require-dev": { - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", "symfony/serializer": "^6.4|^7.0" }, @@ -6441,7 +6115,7 @@ "semaphore" ], "support": { - "source": "https://github.com/symfony/lock/tree/v7.4.8" + "source": "https://github.com/symfony/lock/tree/v8.0.14" }, "funding": [ { @@ -6461,43 +6135,39 @@ "type": "tidelift" } ], - "time": "2026-03-31T07:11:17+00:00" + "time": "2026-06-12T08:36:40+00:00" }, { "name": "symfony/mailer", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62" + "reference": "09a4f89c4ee5dec57f66a115c157e4db119312d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62", - "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62", + "url": "https://api.github.com/repos/symfony/mailer/zipball/09a4f89c4ee5dec57f66a115c157e4db119312d8", + "reference": "09a4f89c4ee5dec57f66a115c157e4db119312d8", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.2", + "php": ">=8.4", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/mime": "^7.2|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/twig-bridge": "<6.4" + "symfony/http-client-contracts": "<2.5" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/twig-bridge": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6525,7 +6195,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.4.8" + "source": "https://github.com/symfony/mailer/tree/v8.0.14" }, "funding": [ { @@ -6545,53 +6215,49 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-16T12:45:11+00:00" }, { "name": "symfony/messenger", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "ddf5ab29bc0329ece30e16f01c86abb6241e92d8" + "reference": "4f1b672abbcace38c8054d7ab66a0c5529e594c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/ddf5ab29bc0329ece30e16f01c86abb6241e92d8", - "reference": "ddf5ab29bc0329ece30e16f01c86abb6241e92d8", + "url": "https://api.github.com/repos/symfony/messenger/zipball/4f1b672abbcace38c8054d7ab66a0c5529e594c5", + "reference": "4f1b672abbcace38c8054d7ab66a0c5529e594c5", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/deprecation-contracts": "^2.5|^3" + "symfony/clock": "^7.4|^8.0" }, "conflict": { - "symfony/console": "<7.2", - "symfony/event-dispatcher": "<6.4", + "symfony/console": "<7.4", "symfony/event-dispatcher-contracts": "<2.5", - "symfony/framework-bundle": "<6.4", - "symfony/http-kernel": "<7.3", "symfony/lock": "<7.4", - "symfony/serializer": "<6.4.32|>=7.3,<7.3.10|>=7.4,<7.4.4|>=8.0,<8.0.4" + "symfony/serializer": "<7.4.4|>=8.0,<8.0.4" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^7.2|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^7.3|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", "symfony/lock": "^7.4|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4.32|~7.3.10|^7.4.4|^8.0.4", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4.4|^8.0.4", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/stopwatch": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6619,7 +6285,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.4.8" + "source": "https://github.com/symfony/messenger/tree/v8.0.14" }, "funding": [ { @@ -6639,44 +6305,41 @@ "type": "tidelift" } ], - "time": "2026-03-30T12:55:43+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/mime", - "version": "v7.4.8", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379" + "reference": "7c078c464c5ce36e8bcaf54b3b3e3c51d30a9646" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/6df02f99998081032da3407a8d6c4e1dcb5d4379", - "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379", + "url": "https://api.github.com/repos/symfony/mime/zipball/7c078c464c5ce36e8bcaf54b3b3e3c51d30a9646", + "reference": "7c078c464c5ce36e8bcaf54b3b3e3c51d30a9646", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<5.2|>=7", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + "phpdocumentor/type-resolver": "<1.5.1" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^5.2|^6.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6708,7 +6371,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.4.8" + "source": "https://github.com/symfony/mime/tree/v8.0.13" }, "funding": [ { @@ -6728,42 +6391,36 @@ "type": "tidelift" } ], - "time": "2026-03-30T14:11:46+00:00" + "time": "2026-05-23T18:05:53+00:00" }, { "name": "symfony/monolog-bridge", - "version": "v7.4.8", + "version": "v8.0.12", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "b52aeb44645a9a84a1795b973cc5c77a76df0720" + "reference": "fc53ae60b2d01d3086d28aea876cfef9bcf75f1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/b52aeb44645a9a84a1795b973cc5c77a76df0720", - "reference": "b52aeb44645a9a84a1795b973cc5c77a76df0720", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/fc53ae60b2d01d3086d28aea876cfef9bcf75f1e", + "reference": "fc53ae60b2d01d3086d28aea876cfef9bcf75f1e", "shasum": "" }, "require": { "monolog/monolog": "^3", - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0|^8.0", + "php": ">=8.4", + "symfony/http-kernel": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, - "conflict": { - "symfony/console": "<6.4", - "symfony/http-foundation": "<6.4", - "symfony/security-core": "<6.4" - }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/mailer": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/security-core": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/mailer": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -6791,7 +6448,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v7.4.8" + "source": "https://github.com/symfony/monolog-bridge/tree/v8.0.12" }, "funding": [ { @@ -6811,37 +6468,36 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:54:39+00:00" + "time": "2026-05-20T07:22:03+00:00" }, { "name": "symfony/monolog-bundle", - "version": "v3.11.2", + "version": "v4.0.2", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "d87468010570b2ec766152184918ee8d267c7411" + "reference": "c012c6aba13129eb02aa7dd61e66e720911d8598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/d87468010570b2ec766152184918ee8d267c7411", - "reference": "d87468010570b2ec766152184918ee8d267c7411", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/c012c6aba13129eb02aa7dd61e66e720911d8598", + "reference": "c012c6aba13129eb02aa7dd61e66e720911d8598", "shasum": "" }, "require": { "composer-runtime-api": "^2.0", - "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=8.1", - "symfony/config": "^6.4 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/http-kernel": "^6.4 || ^7.0", - "symfony/monolog-bridge": "^6.4 || ^7.0", + "monolog/monolog": "^3.5", + "php": ">=8.2", + "symfony/config": "^7.3 || ^8.0", + "symfony/dependency-injection": "^7.3 || ^8.0", + "symfony/http-kernel": "^7.3 || ^8.0", + "symfony/monolog-bridge": "^7.3 || ^8.0", "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "symfony/console": "^6.4 || ^7.0", - "symfony/phpunit-bridge": "^7.3.3", - "symfony/yaml": "^6.4 || ^7.0" + "phpunit/phpunit": "^11.5.41 || ^12.3", + "symfony/console": "^7.3 || ^8.0", + "symfony/yaml": "^7.3 || ^8.0" }, "type": "symfony-bundle", "autoload": { @@ -6871,7 +6527,7 @@ ], "support": { "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.11.2" + "source": "https://github.com/symfony/monolog-bundle/tree/v4.0.2" }, "funding": [ { @@ -6891,24 +6547,24 @@ "type": "tidelift" } ], - "time": "2026-04-02T18:23:01+00:00" + "time": "2026-04-02T18:27:21+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4" + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", - "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8", + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -6942,7 +6598,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.4.8" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.8" }, "funding": [ { @@ -6962,7 +6618,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/orm-pack", @@ -7018,16 +6674,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.37.0", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -7076,7 +6732,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -7096,20 +6752,20 @@ "type": "tidelift" } ], - "time": "2026-04-26T13:13:48+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { @@ -7163,7 +6819,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -7183,20 +6839,20 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.37.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -7248,7 +6904,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -7268,20 +6924,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.37.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -7333,7 +6989,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -7353,20 +7009,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { - "name": "symfony/polyfill-php83", - "version": "v1.37.0", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { @@ -7384,7 +7040,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" + "Symfony\\Polyfill\\Php84\\": "" }, "classmap": [ "Resources/stubs" @@ -7404,7 +7060,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -7413,7 +7069,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -7433,20 +7089,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "symfony/polyfill-php84", - "version": "v1.37.0", + "name": "symfony/polyfill-php85", + "version": "v1.41.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", "shasum": "" }, "require": { @@ -7464,7 +7120,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" + "Symfony\\Polyfill\\Php85\\": "" }, "classmap": [ "Resources/stubs" @@ -7484,7 +7140,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -7493,7 +7149,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { @@ -7513,20 +7169,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T18:47:49+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { - "name": "symfony/polyfill-php85", - "version": "v1.37.0", + "name": "symfony/polyfill-php86", + "version": "v1.41.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee" + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee", - "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/6bc356ed3d8dbfeea8f0de235e34d670704e880e", + "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e", "shasum": "" }, "require": { @@ -7544,7 +7200,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" + "Symfony\\Polyfill\\Php86\\": "" }, "classmap": [ "Resources/stubs" @@ -7564,7 +7220,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -7573,7 +7229,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php86/tree/v1.41.0" }, "funding": [ { @@ -7593,7 +7249,7 @@ "type": "tidelift" } ], - "time": "2026-04-26T13:10:57+00:00" + "time": "2026-07-02T13:42:24+00:00" }, { "name": "symfony/polyfill-uuid", @@ -7680,25 +7336,25 @@ }, { "name": "symfony/property-access", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc" + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", - "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", + "url": "https://api.github.com/repos/symfony/property-access/zipball/704c7808116fcdd67327db7b17de56b8ef6169e4", + "reference": "704c7808116fcdd67327db7b17de56b8ef6169e4", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/property-info": "^6.4.32|~7.3.10|^7.4.4|^8.0.4" + "php": ">=8.4", + "symfony/property-info": "^7.4.4|^8.0.4" }, "require-dev": { - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4.1|^7.0.1|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7737,7 +7393,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.4.8" + "source": "https://github.com/symfony/property-access/tree/v8.0.8" }, "funding": [ { @@ -7757,41 +7413,37 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/property-info", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175" + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/ac5e82528b986c4f7cfccbf7764b5d2e824d6175", - "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175", + "url": "https://api.github.com/repos/symfony/property-info/zipball/c21711980653360d6ef5c26d0f9ca6f58a1135c6", + "reference": "c21711980653360d6ef5c26d0f9ca6f58a1135c6", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0|^8.0", + "php": ">=8.4", + "symfony/string": "^7.4|^8.0", "symfony/type-info": "^7.4.7|^8.0.7" }, "conflict": { "phpdocumentor/reflection-docblock": "<5.2|>=7", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/cache": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/serializer": "<6.4" + "phpdocumentor/type-resolver": "<1.5.1" }, "require-dev": { "phpdocumentor/reflection-docblock": "^5.2|^6.0", "phpstan/phpdoc-parser": "^1.0|^2.0", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7827,7 +7479,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.4.8" + "source": "https://github.com/symfony/property-info/tree/v8.0.8" }, "funding": [ { @@ -7847,41 +7499,40 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "76f1a57719a4a04c0ea18678a6c9305b5dcb9da8" + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/76f1a57719a4a04c0ea18678a6c9305b5dcb9da8", - "reference": "76f1a57719a4a04c0ea18678a6c9305b5dcb9da8", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/94facc221260c1d5f20e31ee43cd6c6a824b4a19", + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/http-message": "^1.0|^2.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0" + "symfony/http-foundation": "^7.4|^8.0" }, "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-kernel": "<6.4" + "php-http/discovery": "<1.15" }, "require-dev": { "nyholm/psr7": "^1.1", "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", - "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6|^8.0" + "symfony/browser-kit": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -7915,7 +7566,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.4.8" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v8.0.8" }, "funding": [ { @@ -7935,29 +7586,29 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/rate-limiter", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/rate-limiter.git", - "reference": "d55de9ec479418f58464e122e68d33886cf6f1fb" + "reference": "ebbcbc99afbecbd619f21c0c7e0a3c59760a6248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/d55de9ec479418f58464e122e68d33886cf6f1fb", - "reference": "d55de9ec479418f58464e122e68d33886cf6f1fb", + "url": "https://api.github.com/repos/symfony/rate-limiter/zipball/ebbcbc99afbecbd619f21c0c7e0a3c59760a6248", + "reference": "ebbcbc99afbecbd619f21c0c7e0a3c59760a6248", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/options-resolver": "^7.3|^8.0" + "php": ">=8.4", + "symfony/options-resolver": "^7.4|^8.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/lock": "^6.4|^7.0|^8.0" + "symfony/lock": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7989,7 +7640,7 @@ "rate-limiter" ], "support": { - "source": "https://github.com/symfony/rate-limiter/tree/v7.4.8" + "source": "https://github.com/symfony/rate-limiter/tree/v8.0.14" }, "funding": [ { @@ -8009,38 +7660,33 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/routing", - "version": "v7.4.8", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b" + "reference": "18e6213e536842285dfdd29604e43ac8f784d17d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", - "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", + "url": "https://api.github.com/repos/symfony/routing/zipball/18e6213e536842285dfdd29604e43ac8f784d17d", + "reference": "18e6213e536842285dfdd29604e43ac8f784d17d", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8074,7 +7720,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.8" + "source": "https://github.com/symfony/routing/tree/v8.0.13" }, "funding": [ { @@ -8094,35 +7740,36 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-24T11:21:57+00:00" }, { "name": "symfony/runtime", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "6d792a64fec1eae2f011cfe9ab5978a9eab3071e" + "reference": "a1319f8b484635991bbd7924e979e32bcad732d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/6d792a64fec1eae2f011cfe9ab5978a9eab3071e", - "reference": "6d792a64fec1eae2f011cfe9ab5978a9eab3071e", + "url": "https://api.github.com/repos/symfony/runtime/zipball/a1319f8b484635991bbd7924e979e32bcad732d0", + "reference": "a1319f8b484635991bbd7924e979e32bcad732d0", "shasum": "" }, "require": { "composer-plugin-api": "^1.0|^2.0", - "php": ">=8.2" + "php": ">=8.4" }, "conflict": { - "symfony/dotenv": "<6.4" + "symfony/error-handler": "<7.4" }, "require-dev": { "composer/composer": "^2.6", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dotenv": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dotenv": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0" }, "type": "composer-plugin", "extra": { @@ -8157,7 +7804,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.4.8" + "source": "https://github.com/symfony/runtime/tree/v8.0.14" }, "funding": [ { @@ -8177,35 +7824,35 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-05T06:22:43+00:00" }, { "name": "symfony/scheduler", - "version": "v7.4.8", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/scheduler.git", - "reference": "f95e696edaad466db9b087a6480ef936c766c3de" + "reference": "d6efd1d0d6ffc8402e4c421884ac7a3d363d360c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/scheduler/zipball/f95e696edaad466db9b087a6480ef936c766c3de", - "reference": "f95e696edaad466db9b087a6480ef936c766c3de", + "url": "https://api.github.com/repos/symfony/scheduler/zipball/d6efd1d0d6ffc8402e4c421884ac7a3d363d360c", + "reference": "d6efd1d0d6ffc8402e4c421884ac7a3d363d360c", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/clock": "^6.4|^7.0|^8.0" + "php": ">=8.4", + "symfony/clock": "^7.4|^8.0" }, "require-dev": { "dragonmantank/cron-expression": "^3.1", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.1|^8.0" + "symfony/cache": "^7.4.8|^8.0.8", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8242,7 +7889,7 @@ "scheduler" ], "support": { - "source": "https://github.com/symfony/scheduler/tree/v7.4.8" + "source": "https://github.com/symfony/scheduler/tree/v8.0.13" }, "funding": [ { @@ -8262,63 +7909,57 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-25T06:08:44+00:00" }, { "name": "symfony/serializer", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "006fd51717addf2df2bd1a64dafef6b7fab6b455" + "reference": "33d395158f1c3b6038738fbb8656e05ae7d2bf0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/006fd51717addf2df2bd1a64dafef6b7fab6b455", - "reference": "006fd51717addf2df2bd1a64dafef6b7fab6b455", + "url": "https://api.github.com/repos/symfony/serializer/zipball/33d395158f1c3b6038738fbb8656e05ae7d2bf0d", + "reference": "33d395158f1c3b6038738fbb8656e05ae7d2bf0d", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php84": "^1.30" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { "phpdocumentor/reflection-docblock": "<5.2|>=7", "phpdocumentor/type-resolver": "<1.5.1", - "symfony/dependency-injection": "<6.4", - "symfony/property-access": "<6.4", - "symfony/property-info": "<6.4", - "symfony/type-info": "<7.2.5", - "symfony/uid": "<6.4", - "symfony/validator": "<6.4", - "symfony/yaml": "<6.4" + "symfony/property-access": "<7.4.2|>=8.0,<8.0.2", + "symfony/property-info": "<7.4", + "symfony/type-info": "<7.4" }, "require-dev": { "phpdocumentor/reflection-docblock": "^5.2|^6.0", "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^7.2|^8.0", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/filesystem": "^6.4|^7.0|^8.0", - "symfony/form": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/filesystem": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/property-access": "^7.4.2|^8.0.2", + "symfony/property-info": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/type-info": "^7.2.5|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/type-info": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8346,62 +7987,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v7.4.8" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "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": "2026-03-30T21:34:42+00:00" - }, - { - "name": "symfony/serializer-pack", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer-pack.git", - "reference": "173d41c068b3cf150890a3d7700e203da13905bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer-pack/zipball/173d41c068b3cf150890a3d7700e203da13905bd", - "reference": "173d41c068b3cf150890a3d7700e203da13905bd", - "shasum": "" - }, - "require": { - "phpdocumentor/reflection-docblock": "*", - "phpstan/phpdoc-parser": "*", - "symfony/property-access": "*", - "symfony/property-info": "*", - "symfony/serializer": "*" - }, - "conflict": { - "symfony/property-info": "<7.4", - "symfony/serializer": "<7.4" - }, - "type": "symfony-pack", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A pack for the Symfony serializer", - "support": { - "issues": "https://github.com/symfony/serializer-pack/issues", - "source": "https://github.com/symfony/serializer-pack/tree/v1.4.0" + "source": "https://github.com/symfony/serializer/tree/v8.0.14" }, "funding": [ { @@ -8421,20 +8007,20 @@ "type": "tidelift" } ], - "time": "2026-03-03T10:13:59+00:00" + "time": "2026-06-27T08:56:37+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "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/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { @@ -8452,7 +8038,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8488,7 +8074,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { @@ -8508,24 +8094,24 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89" + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/70a852d72fec4d51efb1f48dcd968efcaf5ccb89", - "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -8554,7 +8140,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.4.8" + "source": "https://github.com/symfony/stopwatch/tree/v8.0.8" }, "funding": [ { @@ -8574,39 +8160,38 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/string", - "version": "v7.4.8", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "114ac57257d75df748eda23dd003878080b8e688" + "reference": "f2e3e4d33579350d1b12001ef2872f86b27ed3dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688", - "reference": "114ac57257d75df748eda23dd003878080b8e688", + "url": "https://api.github.com/repos/symfony/string/zipball/f2e3e4d33579350d1b12001ef2872f86b27ed3dc", + "reference": "f2e3e4d33579350d1b12001ef2872f86b27ed3dc", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8645,7 +8230,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.8" + "source": "https://github.com/symfony/string/tree/v8.0.13" }, "funding": [ { @@ -8665,38 +8250,31 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-23T18:05:53+00:00" }, { "name": "symfony/translation", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "33600f8489485425bfcddd0d983391038d3422e7" + "reference": "319ee314b21ddc0404f12fb16935f85b8a3520b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/33600f8489485425bfcddd0d983391038d3422e7", - "reference": "33600f8489485425bfcddd0d983391038d3422e7", + "url": "https://api.github.com/repos/symfony/translation/zipball/319ee314b21ddc0404f12fb16935f85b8a3520b6", + "reference": "319ee314b21ddc0404f12fb16935f85b8a3520b6", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5.3|^3.3" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -8704,17 +8282,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8745,7 +8323,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.4.8" + "source": "https://github.com/symfony/translation/tree/v8.0.14" }, "funding": [ { @@ -8765,20 +8343,20 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-06T11:11:34+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.1", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621" }, "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/ccb206b98faccc511ebae8e5fad50f2dc0b30621", + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621", "shasum": "" }, "require": { @@ -8791,7 +8369,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -8827,7 +8405,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1" }, "funding": [ { @@ -8847,71 +8425,64 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/twig-bridge", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "ac43e7e59298ed1ce98c8d228b651d46e907d02c" + "reference": "c136e8ab3e2507ab8bdc70f06f79a7943afdbd9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/ac43e7e59298ed1ce98c8d228b651d46e907d02c", - "reference": "ac43e7e59298ed1ce98c8d228b651d46e907d02c", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/c136e8ab3e2507ab8bdc70f06f79a7943afdbd9b", + "reference": "c136e8ab3e2507ab8bdc70f06f79a7943afdbd9b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/translation-contracts": "^2.5|^3", "twig/twig": "^3.21" }, "conflict": { "phpdocumentor/reflection-docblock": "<5.2|>=7", "phpdocumentor/type-resolver": "<1.5.1", - "symfony/console": "<6.4", - "symfony/form": "<6.4.32|>7,<7.3.10|>7.4,<7.4.4|>8.0,<8.0.4", - "symfony/http-foundation": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/mime": "<6.4.36|>7,<7.4.8|>8.0,<8.0.8", - "symfony/serializer": "<6.4", - "symfony/translation": "<6.4", - "symfony/workflow": "<6.4" + "symfony/form": "<7.4.4|>8.0,<8.0.4", + "symfony/mime": "<7.4.9|>8.0,<8.0.9" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^5.2|^6.0", - "symfony/asset": "^6.4|^7.0|^8.0", - "symfony/asset-mapper": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/emoji": "^7.1|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/form": "^6.4.32|~7.3.10|^7.4.4|^8.0.4", - "symfony/html-sanitizer": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^7.3|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4.36|^7.4.8|^8.0.8", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/asset": "^7.4|^8.0", + "symfony/asset-mapper": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/form": "^7.4.4|^8.0.4", + "symfony/html-sanitizer": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/mime": "^7.4.9|^8.0.9", + "symfony/polyfill-intl-icu": "^1.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^6.4|^7.0|^8.0", - "symfony/security-csrf": "^6.4|^7.0|^8.0", - "symfony/security-http": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4.3|^7.0.3|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/web-link": "^6.4|^7.0|^8.0", - "symfony/workflow": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/security-csrf": "^7.4|^8.0", + "symfony/security-http": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/workflow": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0", "twig/cssinliner-extra": "^3", "twig/inky-extra": "^3", "twig/markdown-extra": "^3" @@ -8942,7 +8513,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.4.8" + "source": "https://github.com/symfony/twig-bridge/tree/v8.0.14" }, "funding": [ { @@ -8962,49 +8533,43 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:17:09+00:00" + "time": "2026-06-17T15:00:27+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95" + "reference": "3ea5388a0286100980b9047005c8cfb49888eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95", - "reference": "ba1e06d7ff1ebb1d1799b6608d925f4eaba88d95", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/3ea5388a0286100980b9047005c8cfb49888eca9", + "reference": "3ea5388a0286100980b9047005c8cfb49888eca9", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.2", + "php": ">=8.4", "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", - "symfony/twig-bridge": "^7.3|^8.0", - "twig/twig": "^3.12" - }, - "conflict": { - "symfony/framework-bundle": "<6.4", - "symfony/translation": "<6.4" + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" }, "require-dev": { - "symfony/asset": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/form": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", - "symfony/web-link": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/asset": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/form": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/web-link": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -9032,7 +8597,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.4.8" + "source": "https://github.com/symfony/twig-bundle/tree/v8.0.14" }, "funding": [ { @@ -9052,26 +8617,25 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-05T06:22:43+00:00" }, { "name": "symfony/type-info", - "version": "v7.4.8", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd" + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd", - "reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd", + "url": "https://api.github.com/repos/symfony/type-info/zipball/08723aceb8c3271e8cb3db8b2565728b0c88e866", + "reference": "08723aceb8c3271e8cb3db8b2565728b0c88e866", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4", + "psr/container": "^1.1|^2.0" }, "conflict": { "phpstan/phpdoc-parser": "<1.30" @@ -9115,7 +8679,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.4.8" + "source": "https://github.com/symfony/type-info/tree/v8.0.9" }, "funding": [ { @@ -9135,28 +8699,28 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/uid", - "version": "v7.4.8", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56" + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/6883ebdf7bf6a12b37519dbc0df62b0222401b56", - "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56", + "url": "https://api.github.com/repos/symfony/uid/zipball/4d9d6510bbe88ebb4608b7200d18606cdf80825c", + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9193,7 +8757,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.4.8" + "source": "https://github.com/symfony/uid/tree/v8.0.9" }, "funding": [ { @@ -9213,42 +8777,42 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-04-30T16:10:06+00:00" }, { "name": "symfony/ux-twig-component", - "version": "v2.35.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/ux-twig-component.git", - "reference": "d64b068d8339e905cd48974bdd6e9ba54dc8f247" + "reference": "2f3a1b74e3ee04daed1a303245998e7b799eb9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/d64b068d8339e905cd48974bdd6e9ba54dc8f247", - "reference": "d64b068d8339e905cd48974bdd6e9ba54dc8f247", + "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/2f3a1b74e3ee04daed1a303245998e7b799eb9dc", + "reference": "2f3a1b74e3ee04daed1a303245998e7b799eb9dc", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "php": ">=8.4", + "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.2|^3.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0|^8.0", - "symfony/property-access": "^5.4|^6.0|^7.0|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", "twig/twig": "^3.10.3" }, "conflict": { - "symfony/config": "<5.4.0" + "symfony/config": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0|^8.0", - "symfony/css-selector": "^5.4|^6.0|^7.0|^8.0", - "symfony/dom-crawler": "^5.4|^6.0|^7.0|^8.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0|^8.0", - "symfony/phpunit-bridge": "^6.0|^7.0|^8.0", + "phpunit/phpunit": "^11.1|^12.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", "symfony/stimulus-bundle": "^2.9.1|^3.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0", - "symfony/webpack-encore-bundle": "^1.15|^2.3.0", + "symfony/twig-bundle": "^7.4|^8.0", "twig/extra-bundle": "^3.10.3", "twig/html-extra": "^3.10.3" }, @@ -9282,7 +8846,7 @@ "twig" ], "support": { - "source": "https://github.com/symfony/ux-twig-component/tree/v2.35.0" + "source": "https://github.com/symfony/ux-twig-component/tree/v3.3.0" }, "funding": [ { @@ -9302,62 +8866,53 @@ "type": "tidelift" } ], - "time": "2026-04-03T05:13:59+00:00" + "time": "2026-07-05T15:45:35+00:00" }, { "name": "symfony/validator", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "8f73cbddae916756f319b3e195088da216f0f12f" + "reference": "c32f14e595ccac17eb0ba919e030ee297694472c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/8f73cbddae916756f319b3e195088da216f0f12f", - "reference": "8f73cbddae916756f319b3e195088da216f0f12f", + "url": "https://api.github.com/repos/symfony/validator/zipball/c32f14e595ccac17eb0ba919e030ee297694472c", + "reference": "c32f14e595ccac17eb0ba919e030ee297694472c", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php83": "^1.27", + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.0", "symfony/translation-contracts": "^2.5|^3" }, "conflict": { "doctrine/lexer": "<1.1", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<7.0", - "symfony/expression-language": "<6.4", - "symfony/http-kernel": "<6.4", - "symfony/intl": "<6.4", - "symfony/property-info": "<6.4", - "symfony/translation": "<6.4.3|>=7.0,<7.0.3", - "symfony/var-exporter": "<6.4.25|>=7.0,<7.3.3", - "symfony/yaml": "<6.4" + "symfony/doctrine-bridge": "<7.4", + "symfony/expression-language": "<7.4" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3|^4", - "symfony/cache": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/string": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4.3|^7.0.3|^8.0", - "symfony/type-info": "^7.1.8", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/string": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/type-info": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9386,7 +8941,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v7.4.8" + "source": "https://github.com/symfony/validator/tree/v8.0.14" }, "funding": [ { @@ -9406,35 +8961,35 @@ "type": "tidelift" } ], - "time": "2026-03-30T12:55:43+00:00" + "time": "2026-06-27T06:17:20+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd" + "reference": "7d38aaa43d57e64b081fc838781a58886f54ff00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd", - "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7d38aaa43d57e64b081fc838781a58886f54ff00", + "reference": "7d38aaa43d57e64b081fc838781a58886f54ff00", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -9473,7 +9028,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.8" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.14" }, "funding": [ { @@ -9493,30 +9048,29 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:44:50+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225" + "reference": "5185b7a7ea87a177be5fb16e8dc7756bea9b6eee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/398907e89a2a56fe426f7955c6fa943ec0c77225", - "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/5185b7a7ea87a177be5fb16e8dc7756bea9b6eee", + "reference": "5185b7a7ea87a177be5fb16e8dc7756bea9b6eee", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9554,7 +9108,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.4.8" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.14" }, "funding": [ { @@ -9574,32 +9128,31 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-27T08:56:37+00:00" }, { "name": "symfony/yaml", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883" + "reference": "5e33ae125827c4445e28474223de226751bd7ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c58fdf7b3d6c2995368264c49e4e8b05bcff2883", - "reference": "c58fdf7b3d6c2995368264c49e4e8b05bcff2883", + "url": "https://api.github.com/repos/symfony/yaml/zipball/5e33ae125827c4445e28474223de226751bd7ed9", + "reference": "5e33ae125827c4445e28474223de226751bd7ed9", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -9630,7 +9183,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.8" + "source": "https://github.com/symfony/yaml/tree/v8.0.14" }, "funding": [ { @@ -9650,7 +9203,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -9709,16 +9262,16 @@ }, { "name": "twig/cssinliner-extra", - "version": "v3.24.0", + "version": "v3.26.0", "source": { "type": "git", "url": "https://github.com/twigphp/cssinliner-extra.git", - "reference": "c25fa18b09a418e4d1454ec291f9406f630675ba" + "reference": "1b0dc906bbad7226c967bd325e99cccb1a850c4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/c25fa18b09a418e4d1454ec291f9406f630675ba", - "reference": "c25fa18b09a418e4d1454ec291f9406f630675ba", + "url": "https://api.github.com/repos/twigphp/cssinliner-extra/zipball/1b0dc906bbad7226c967bd325e99cccb1a850c4b", + "reference": "1b0dc906bbad7226c967bd325e99cccb1a850c4b", "shasum": "" }, "require": { @@ -9762,7 +9315,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.24.0" + "source": "https://github.com/twigphp/cssinliner-extra/tree/v3.26.0" }, "funding": [ { @@ -9774,7 +9327,7 @@ "type": "tidelift" } ], - "time": "2025-12-02T14:45:16+00:00" + "time": "2026-05-15T13:14:14+00:00" }, { "name": "twig/extra-bundle", @@ -9852,16 +9405,16 @@ }, { "name": "twig/twig", - "version": "v3.24.0", + "version": "v3.28.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0" + "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", + "reference": "597c12ed286fb9d1701a36684ce6e0cbe28ebc8b", "shasum": "" }, "require": { @@ -9916,7 +9469,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.24.0" + "source": "https://github.com/twigphp/Twig/tree/v3.28.0" }, "funding": [ { @@ -9928,7 +9481,7 @@ "type": "tidelift" } ], - "time": "2026-03-17T21:31:11+00:00" + "time": "2026-07-03T20:44:34+00:00" }, { "name": "voku/portable-ascii", @@ -10004,68 +9557,6 @@ ], "time": "2026-04-26T05:33:54+00:00" }, - { - "name": "webmozart/assert", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", - "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^8.2" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-feature/2-0": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Woody Gilk", - "email": "woody.gilk@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.3.0" - }, - "time": "2026-04-11T10:33:05+00:00" - }, { "name": "zenstruck/assert", "version": "v1.7.0", @@ -10281,16 +9772,16 @@ }, { "name": "zenstruck/foundry", - "version": "v2.9.2", + "version": "v2.11.0", "source": { "type": "git", "url": "https://github.com/zenstruck/foundry.git", - "reference": "9f3fe969d37fd5a0799ca455af9990a88036b6a0" + "reference": "3063be3c941e1cdf74144a1a5e1f1c97abf4b8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zenstruck/foundry/zipball/9f3fe969d37fd5a0799ca455af9990a88036b6a0", - "reference": "9f3fe969d37fd5a0799ca455af9990a88036b6a0", + "url": "https://api.github.com/repos/zenstruck/foundry/zipball/3063be3c941e1cdf74144a1a5e1f1c97abf4b8d9", + "reference": "3063be3c941e1cdf74144a1a5e1f1c97abf4b8d9", "shasum": "" }, "require": { @@ -10319,7 +9810,7 @@ "doctrine/mongodb-odm-bundle": "^4.6|^5.0", "doctrine/orm": "^2.16|^3.0", "doctrine/persistence": "^2.0|^3.0|^4.0", - "phpunit/phpunit": "^9.5.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", + "phpunit/phpunit": "^9.5.0 || ^10.0 || ^11.0 || ^12.0 || ~13.1.0", "symfony/browser-kit": "^6.4|^7.0|^8.0", "symfony/console": "^6.4|^7.0|^8.0", "symfony/dotenv": "^6.4|^7.0|^8.0", @@ -10360,7 +9851,10 @@ "Zenstruck\\Foundry\\": "src/", "Zenstruck\\Foundry\\Psalm\\": "utils/psalm", "Zenstruck\\Foundry\\Utils\\Rector\\": "utils/rector/src/" - } + }, + "exclude-from-classmap": [ + "src/Test/Behat/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -10389,7 +9883,7 @@ ], "support": { "issues": "https://github.com/zenstruck/foundry/issues", - "source": "https://github.com/zenstruck/foundry/tree/v2.9.2" + "source": "https://github.com/zenstruck/foundry/tree/v2.11.0" }, "funding": [ { @@ -10401,7 +9895,7 @@ "type": "github" } ], - "time": "2026-02-17T15:48:50+00:00" + "time": "2026-07-27T20:25:22+00:00" }, { "name": "zenstruck/messenger-monitor-bundle", @@ -10632,20 +10126,19 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.8.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -10684,9 +10177,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-07-04T14:30:18+00:00" }, { "name": "phar-io/manifest", @@ -10808,11 +10301,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.51", + "version": "2.2.6", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59", - "reference": "dc3b523c45e714c70de2ac5113b958223b55dc59", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a6e9b5a9420f6109c091e87d82683bd1a80b87ed", + "reference": "a6e9b5a9420f6109c091e87d82683bd1a80b87ed", "shasum": "" }, "require": { @@ -10835,6 +10328,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -10857,7 +10361,7 @@ "type": "github" } ], - "time": "2026-04-21T18:22:01+00:00" + "time": "2026-07-26T21:22:49+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11208,24 +10712,24 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.55", + "version": "11.5.56", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256", + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256", "shasum": "" }, "require": { "ext-dom": "*", + "ext-filter": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", @@ -11290,31 +10794,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56" }, "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:37:06+00:00" + "time": "2026-07-06T14:52:39+00:00" }, { "name": "sebastian/cli-parser", @@ -12356,28 +11844,27 @@ }, { "name": "symfony/browser-kit", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "41850d8f8ddef9a9cd7314fa9f4902cf48885521" + "reference": "c2579f5efd5953384c3bc5d267c43bb225a9d2b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/41850d8f8ddef9a9cd7314fa9f4902cf48885521", - "reference": "41850d8f8ddef9a9cd7314fa9f4902cf48885521", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c2579f5efd5953384c3bc5d267c43bb225a9d2b4", + "reference": "c2579f5efd5953384c3bc5d267c43bb225a9d2b4", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/dom-crawler": "^6.4|^7.0|^8.0" + "php": ">=8.4", + "symfony/dom-crawler": "^7.4|^8.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/css-selector": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -12405,7 +11892,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.4.8" + "source": "https://github.com/symfony/browser-kit/tree/v8.0.14" }, "funding": [ { @@ -12425,7 +11912,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-09T10:20:00+00:00" }, { "name": "symfony/maker-bundle", @@ -12528,16 +12015,16 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v7.4.8", + "version": "v7.4.14", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "140bbbe1cd1c21a084494ccddeee33f3c3381d7d" + "reference": "11eeee9d109963145e66f5b1919e5cf5411da58b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/140bbbe1cd1c21a084494ccddeee33f3c3381d7d", - "reference": "140bbbe1cd1c21a084494ccddeee33f3c3381d7d", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/11eeee9d109963145e66f5b1919e5cf5411da58b", + "reference": "11eeee9d109963145e66f5b1919e5cf5411da58b", "shasum": "" }, "require": { @@ -12589,7 +12076,7 @@ "testing" ], "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.4.8" + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.4.14" }, "funding": [ { @@ -12609,24 +12096,24 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-08T20:24:16+00:00" }, { "name": "symfony/process", - "version": "v7.4.8", + "version": "v8.0.13", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" + "reference": "4f23d9c7637ead9ed19f697fe93cb87fd9b379d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", + "url": "https://api.github.com/repos/symfony/process/zipball/4f23d9c7637ead9ed19f697fe93cb87fd9b379d4", + "reference": "4f23d9c7637ead9ed19f697fe93cb87fd9b379d4", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -12654,7 +12141,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.8" + "source": "https://github.com/symfony/process/tree/v8.0.13" }, "funding": [ { @@ -12674,46 +12161,41 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-05-23T18:05:53+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v7.4.8", + "version": "v8.0.14", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "79f039096c67cc1cc3f607d2ba72af86cd27e6a4" + "reference": "ae70ab1d1c7ef3061d0769ca7322de65fe1fc6b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/79f039096c67cc1cc3f607d2ba72af86cd27e6a4", - "reference": "79f039096c67cc1cc3f607d2ba72af86cd27e6a4", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/ae70ab1d1c7ef3061d0769ca7322de65fe1fc6b9", + "reference": "ae70ab1d1c7ef3061d0769ca7322de65fe1fc6b9", "shasum": "" }, "require": { "composer-runtime-api": ">=2.1", - "php": ">=8.2", - "symfony/config": "^7.3|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", - "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/twig-bundle": "^6.4|^7.0|^8.0", - "twig/twig": "^3.15" + "php": ">=8.4", + "symfony/config": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/twig-bundle": "^7.4|^8.0" }, "conflict": { - "symfony/form": "<6.4", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/serializer": "<7.2", - "symfony/workflow": "<7.3" + "symfony/serializer": "<7.4", + "symfony/workflow": "<7.4" }, "require-dev": { - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/runtime": "^6.4.13|^7.1.6|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/browser-kit": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0" }, "type": "symfony-bundle", "autoload": { @@ -12744,7 +12226,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.8" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v8.0.14" }, "funding": [ { @@ -12764,7 +12246,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-06-05T06:22:43+00:00" }, { "name": "theseer/tokenizer", @@ -12819,7 +12301,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { @@ -12829,6 +12311,6 @@ "ext-iconv": "*", "ext-openssl": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/backend/config/packages/cache.php b/backend/config/packages/cache.php deleted file mode 100644 index c1055cf24..000000000 --- a/backend/config/packages/cache.php +++ /dev/null @@ -1,11 +0,0 @@ -cache() - ->app('cache.adapter.doctrine_dbal') - ->defaultDoctrineDbalProvider('doctrine.dbal.default_connection'); - -}; \ No newline at end of file diff --git a/backend/config/packages/cache.yaml b/backend/config/packages/cache.yaml new file mode 100644 index 000000000..4c65f48b1 --- /dev/null +++ b/backend/config/packages/cache.yaml @@ -0,0 +1,3 @@ +framework: + cache: + app: cache.adapter.doctrine_dbal diff --git a/backend/config/packages/doctrine.yaml b/backend/config/packages/doctrine.yaml index c892a7ee8..5216ca1e9 100644 --- a/backend/config/packages/doctrine.yaml +++ b/backend/config/packages/doctrine.yaml @@ -1,55 +1,55 @@ doctrine: - dbal: - url: '%env(resolve:DATABASE_URL)%' + dbal: + url: '%env(resolve:DATABASE_URL)%' - # IMPORTANT: You MUST configure your server version, - # either here or in the DATABASE_URL env var (see .env file) - #server_version: '16' + # IMPORTANT: You MUST configure your server version, + # either here or in the DATABASE_URL env var (see .env file) + #server_version: '16' - profiling_collect_backtrace: '%kernel.debug%' - use_savepoints: true - orm: - auto_generate_proxy_classes: true - enable_lazy_ghost_objects: true - report_fields_where_declared: true - validate_xml_mapping: true - naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware - identity_generation_preferences: - Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity - auto_mapping: true - mappings: - App: - type: attribute - is_bundle: false - dir: '%kernel.project_dir%/src/Entity' - prefix: 'App\Entity' - alias: App - controller_resolver: - enabled: false - auto_mapping: false + profiling_collect_backtrace: '%kernel.debug%' + #use_savepoints: true + orm: + #auto_generate_proxy_classes: true + #enable_lazy_ghost_objects: true + #report_fields_where_declared: true + validate_xml_mapping: true + naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware + identity_generation_preferences: + Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity + auto_mapping: true + mappings: + App: + type: attribute + is_bundle: false + dir: '%kernel.project_dir%/src/Entity' + prefix: 'App\Entity' + alias: App + controller_resolver: + enabled: false + auto_mapping: false when@test: - doctrine: - dbal: - # "TEST_TOKEN" is typically set by ParaTest - # dbname_suffix: '_test%env(default::TEST_TOKEN)%' + doctrine: + dbal: + # "TEST_TOKEN" is typically set by ParaTest + # dbname_suffix: '_test%env(default::TEST_TOKEN)%' when@prod: - doctrine: - orm: - auto_generate_proxy_classes: false - proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' - query_cache_driver: - type: pool - pool: doctrine.system_cache_pool - result_cache_driver: - type: pool - pool: doctrine.result_cache_pool + doctrine: + orm: + auto_generate_proxy_classes: false + proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' + query_cache_driver: + type: pool + pool: doctrine.system_cache_pool + result_cache_driver: + type: pool + pool: doctrine.result_cache_pool - framework: - cache: - pools: - doctrine.result_cache_pool: - adapter: cache.app - doctrine.system_cache_pool: - adapter: cache.system + framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/backend/config/packages/dunglas_json_odm.php b/backend/config/packages/dunglas_json_odm.php index b9aced10f..370396f69 100644 --- a/backend/config/packages/dunglas_json_odm.php +++ b/backend/config/packages/dunglas_json_odm.php @@ -1,12 +1,14 @@ typeMap('newsletters_meta', NewsletterMeta::class); - $config->typeMap('newsletters_meta_form_default_color_palette', NewsletterFormDefaultColorPalette::class); - -}; +use Symfony\Component\DependencyInjection\Loader\Configurator\App; + +return App::config([ + 'dunglas_doctrine_json_odm' => [ + 'type_map' => [ + 'newsletters_meta' => NewsletterMeta::class, + 'newsletters_meta_form_default_color_palette' => NewsletterFormDefaultColorPalette::class, + ], + ], +]); diff --git a/backend/config/packages/monolog.php b/backend/config/packages/monolog.php deleted file mode 100644 index fb0a87268..000000000 --- a/backend/config/packages/monolog.php +++ /dev/null @@ -1,28 +0,0 @@ -env() !== 'test') { - $monolog->handler('app') - ->type('buffer') - ->handler('final') - ->level("%env(LOG_LEVEL)%") - ->channels()->elements(['app']); - $monolog->handler('non_app') - ->type('buffer') - ->handler('final') - ->level('error') - ->bubble(false) - ->channels()->elements(['!app']); - $monolog->handler('final') - ->type('stream') - ->path('php://stderr') - ->formatter('monolog.formatter.json'); - } else { - $monolog->handler('test') - ->type('test') - ->level('info'); - } -}; diff --git a/backend/config/packages/monolog.yaml b/backend/config/packages/monolog.yaml new file mode 100644 index 000000000..4dbcf0ad5 --- /dev/null +++ b/backend/config/packages/monolog.yaml @@ -0,0 +1,29 @@ +when@prod: &prod + monolog: + handlers: + app: + type: buffer + handler: final + level: "%env(LOG_LEVEL)%" + channels: [ 'app' ] + + non_app: + type: buffer + handler: final + level: error + bubble: false + channels: [ '!app' ] + + final: + type: stream + path: 'php://stderr' + formatter: monolog.formatter.json + +when@dev: *prod + +when@test: + monolog: + handlers: + test: + type: test + level: debug diff --git a/backend/config/packages/nyholm_psr7.yaml b/backend/config/packages/nyholm_psr7.yaml new file mode 100644 index 000000000..ade831249 --- /dev/null +++ b/backend/config/packages/nyholm_psr7.yaml @@ -0,0 +1,11 @@ +services: + # Register nyholm/psr7 services for autowiring with PSR-17 (HTTP factories) + Psr\Http\Message\RequestFactoryInterface: '@nyholm.psr7.psr17_factory' + Psr\Http\Message\ResponseFactoryInterface: '@nyholm.psr7.psr17_factory' + Psr\Http\Message\ServerRequestFactoryInterface: '@nyholm.psr7.psr17_factory' + Psr\Http\Message\StreamFactoryInterface: '@nyholm.psr7.psr17_factory' + Psr\Http\Message\UploadedFileFactoryInterface: '@nyholm.psr7.psr17_factory' + Psr\Http\Message\UriFactoryInterface: '@nyholm.psr7.psr17_factory' + + nyholm.psr7.psr17_factory: + class: Nyholm\Psr7\Factory\Psr17Factory diff --git a/backend/config/packages/rate_limiter.php b/backend/config/packages/rate_limiter.php index d6b175320..a3e48961c 100644 --- a/backend/config/packages/rate_limiter.php +++ b/backend/config/packages/rate_limiter.php @@ -1,13 +1,15 @@ rateLimiter() - ->limiter('public_api') - ->policy('fixed_window') - ->limit(30) - ->interval('1 minute'); -}; +return App::config([ + 'framework' => [ + 'rate_limiter' => [ + 'public_api' => [ + 'policy' => 'fixed_window', + 'limit' => 30, + 'interval' => '1 minute', + ], + ], + ], +]); diff --git a/backend/config/packages/web_profiler.yaml b/backend/config/packages/web_profiler.yaml index 588a1a2c9..ff8bf8e09 100644 --- a/backend/config/packages/web_profiler.yaml +++ b/backend/config/packages/web_profiler.yaml @@ -1,11 +1,6 @@ when@dev: - web_profiler: - toolbar: false - - framework: - profiler: - collect_serializer_data: false - + web_profiler: + toolbar: false when@test: - framework: - profiler: { collect: true } + framework: + profiler: { collect: true } diff --git a/backend/config/reference.php b/backend/config/reference.php index 20f0329e6..bab6d7894 100644 --- a/backend/config/reference.php +++ b/backend/config/reference.php @@ -121,14 +121,14 @@ * } * @psalm-type ServicesConfig = array{ * _defaults?: DefaultsType, - * _instanceof?: InstanceofType, + * _instanceof?: array, * ... * } * @psalm-type ExtensionType = array * @psalm-type FrameworkConfig = array{ * secret?: scalar|Param|null, * http_method_override?: bool|Param, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false - * allowed_http_method_override?: list|null, + * allowed_http_method_override?: null|list, * trust_x_sendfile_type_header?: scalar|Param|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%" * ide?: scalar|Param|null, // Default: "%env(default::SYMFONY_IDE)%" * test?: bool|Param, @@ -136,9 +136,9 @@ * set_locale_from_accept_language?: bool|Param, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false * set_content_language_from_locale?: bool|Param, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false * enabled_locales?: list, - * trusted_hosts?: list, + * trusted_hosts?: string|list, * trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"] - * trusted_headers?: list, + * trusted_headers?: string|list, * error_controller?: scalar|Param|null, // Default: "error_controller" * handle_all_throwables?: bool|Param, // HttpKernel will handle all kinds of \Throwable. // Default: true * csrf_protection?: bool|array{ @@ -188,7 +188,7 @@ * only_exceptions?: bool|Param, // Default: false * only_main_requests?: bool|Param, // Default: false * dsn?: scalar|Param|null, // Default: "file:%kernel.cache_dir%/profiler" - * collect_serializer_data?: bool|Param, // Enables the serializer data collector and profiler panel. // Default: false + * collect_serializer_data?: true|Param, // Default: true * }, * workflows?: bool|array{ * enabled?: bool|Param, // Default: false @@ -202,23 +202,23 @@ * property?: scalar|Param|null, * service?: scalar|Param|null, * }, - * supports?: list, + * supports?: string|list, * definition_validators?: list, * support_strategy?: scalar|Param|null, - * initial_marking?: list, - * events_to_dispatch?: list|null, - * places?: list, + * events_to_dispatch?: null|list, + * places?: string|list, * }>, * transitions?: list, - * to?: list, @@ -232,7 +232,6 @@ * enabled?: bool|Param, // Default: false * resource?: scalar|Param|null, * type?: scalar|Param|null, - * cache_dir?: scalar|Param|null, // Deprecated: Setting the "framework.router.cache_dir.cache_dir" configuration option is deprecated. It will be removed in version 8.0. // Default: "%kernel.build_dir%" * default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null * http_port?: scalar|Param|null, // Default: 80 * https_port?: scalar|Param|null, // Default: 443 @@ -256,8 +255,6 @@ * gc_maxlifetime?: scalar|Param|null, * save_path?: scalar|Param|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null. * metadata_update_threshold?: int|Param, // Seconds to wait between 2 session metadata updates. // Default: 0 - * sid_length?: int|Param, // Deprecated: Setting the "framework.session.sid_length.sid_length" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option. - * sid_bits_per_character?: int|Param, // Deprecated: Setting the "framework.session.sid_bits_per_character.sid_bits_per_character" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option. * }, * request?: bool|array{ // Request configuration * enabled?: bool|Param, // Default: false @@ -271,7 +268,7 @@ * version_format?: scalar|Param|null, // Default: "%%s?%%s" * json_manifest_path?: scalar|Param|null, // Default: null * base_path?: scalar|Param|null, // Default: "" - * base_urls?: list, + * base_urls?: string|list, * packages?: array, + * base_urls?: string|list, * }>, * }, * asset_mapper?: bool|array{ // Asset Mapper configuration * enabled?: bool|Param, // Default: false - * paths?: array, + * paths?: string|array, * excluded_patterns?: list, * exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true * server?: bool|Param, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true @@ -303,7 +300,7 @@ * }, * translator?: bool|array{ // Translator configuration * enabled?: bool|Param, // Default: true - * fallbacks?: list, + * fallbacks?: string|list, * logging?: bool|Param, // Default: false * formatter?: scalar|Param|null, // Default: "translator.formatter.default" * cache_dir?: scalar|Param|null, // Default: "%kernel.cache_dir%/translations" @@ -331,11 +328,10 @@ * }, * validation?: bool|array{ // Validation configuration * enabled?: bool|Param, // Default: true - * cache?: scalar|Param|null, // Deprecated: Setting the "framework.validation.cache.cache" configuration option is deprecated. It will be removed in version 8.0. * enable_attributes?: bool|Param, // Default: true - * static_method?: list, + * static_method?: string|list, * translation_domain?: scalar|Param|null, // Default: "validators" - * email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|"loose"|Param, // Default: "html5" + * email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|Param, // Default: "html5" * mapping?: array{ * paths?: list, * }, @@ -348,9 +344,6 @@ * services?: list, * }>, * }, - * annotations?: bool|array{ - * enabled?: bool|Param, // Default: false - * }, * serializer?: bool|array{ // Serializer configuration * enabled?: bool|Param, // Default: true * enable_attributes?: bool|Param, // Default: true @@ -382,7 +375,7 @@ * }, * property_info?: bool|array{ // Property info configuration * enabled?: bool|Param, // Default: true - * with_constructor_extractor?: bool|Param, // Registers the constructor extractor. + * with_constructor_extractor?: bool|Param, // Registers the constructor extractor. // Default: true * }, * cache?: array{ // Cache configuration * prefix_seed?: scalar|Param|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%" @@ -396,7 +389,7 @@ * default_doctrine_dbal_provider?: scalar|Param|null, // Default: "database_connection" * default_pdo_provider?: scalar|Param|null, // Default: null * pools?: array, + * adapters?: string|list, * tags?: scalar|Param|null, // Default: null * public?: bool|Param, // Default: false * default_lifetime?: scalar|Param|null, // Default lifetime of the pool. @@ -419,11 +412,11 @@ * }, * lock?: bool|string|array{ // Lock configuration * enabled?: bool|Param, // Default: true - * resources?: array>, + * resources?: string|array>, * }, * semaphore?: bool|string|array{ // Semaphore configuration * enabled?: bool|Param, // Default: false - * resources?: array, + * resources?: string|array, * }, * messenger?: bool|array{ // Messenger configuration * enabled?: bool|Param, // Default: true @@ -453,7 +446,7 @@ * rate_limiter?: scalar|Param|null, // Rate limiter name to use when processing messages. // Default: null * }>, * failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null - * stop_worker_on_signals?: list, + * stop_worker_on_signals?: int|string|list, * default_bus?: scalar|Param|null, // Default: null * buses?: array, * }>, @@ -510,9 +503,9 @@ * retry_failed?: bool|array{ * enabled?: bool|Param, // Default: false * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null - * http_codes?: array, + * methods?: string|list, * }>, * max_retries?: int|Param, // Default: 3 * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 @@ -563,9 +556,9 @@ * retry_failed?: bool|array{ * enabled?: bool|Param, // Default: false * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null - * http_codes?: array, + * methods?: string|list, * }>, * max_retries?: int|Param, // Default: 3 * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 @@ -582,8 +575,8 @@ * transports?: array, * envelope?: array{ // Mailer Envelope configuration * sender?: scalar|Param|null, - * recipients?: list, - * allowed_recipients?: list, + * recipients?: string|list, + * allowed_recipients?: string|list, * }, * headers?: array, + * limiters?: string|list, * limit?: int|Param, // The maximum allowed hits in a fixed interval or burst. * interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). * rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket". @@ -658,20 +651,20 @@ * allow_safe_elements?: bool|Param, // Allows "safe" elements and attributes. // Default: false * allow_static_elements?: bool|Param, // Allows all static elements and attributes from the W3C Sanitizer API standard. // Default: false * allow_elements?: array, - * block_elements?: list, - * drop_elements?: list, + * block_elements?: string|list, + * drop_elements?: string|list, * allow_attributes?: array, * drop_attributes?: array, * force_attributes?: array>, * force_https_urls?: bool|Param, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false - * allowed_link_schemes?: list, - * allowed_link_hosts?: list|null, + * allowed_link_schemes?: string|list, + * allowed_link_hosts?: null|string|list, * allow_relative_links?: bool|Param, // Allows relative URLs to be used in links href attributes. // Default: false - * allowed_media_schemes?: list, - * allowed_media_hosts?: list|null, + * allowed_media_schemes?: string|list, + * allowed_media_hosts?: null|string|list, * allow_relative_medias?: bool|Param, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false - * with_attribute_sanitizers?: list, - * without_attribute_sanitizers?: list, + * with_attribute_sanitizers?: string|list, + * without_attribute_sanitizers?: string|list, * max_input_length?: int|Param, // The maximum length allowed for the sanitized input. // Default: 0 * }>, * }, @@ -695,7 +688,6 @@ * default_connection?: scalar|Param|null, * types?: array, * driver_schemes?: array, * connections?: array, * mapping_types?: array, * default_table_options?: array, - * schema_manager_factory?: scalar|Param|null, // Default: "doctrine.dbal.legacy_schema_manager_factory" + * schema_manager_factory?: scalar|Param|null, // Default: "doctrine.dbal.default_schema_manager_factory" * result_cache?: scalar|Param|null, - * slaves?: array, * replicas?: array, @@ -817,14 +770,10 @@ * }, * orm?: array{ * default_entity_manager?: scalar|Param|null, - * auto_generate_proxy_classes?: scalar|Param|null, // Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL", "FILE_NOT_EXISTS_OR_CHANGED", this option is ignored when the "enable_native_lazy_objects" option is true // Default: false - * enable_lazy_ghost_objects?: bool|Param, // Enables the new implementation of proxies based on lazy ghosts instead of using the legacy implementation // Default: true - * enable_native_lazy_objects?: bool|Param, // Enables the new native implementation of PHP lazy objects instead of generated proxies // Default: false - * proxy_dir?: scalar|Param|null, // Configures the path where generated proxy classes are saved when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "%kernel.build_dir%/doctrine/orm/Proxies" - * proxy_namespace?: scalar|Param|null, // Defines the root namespace for generated proxy classes when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "Proxies" + * enable_native_lazy_objects?: bool|Param, // Deprecated: The "enable_native_lazy_objects" option is deprecated and will be removed in DoctrineBundle 4.0, as native lazy objects are now always enabled. // Default: true * controller_resolver?: bool|array{ * enabled?: bool|Param, // Default: true - * auto_mapping?: bool|Param|null, // Set to false to disable using route placeholders as lookup criteria when the primary key doesn't match the argument name // Default: null + * auto_mapping?: bool|Param, // Deprecated: The "doctrine.orm.controller_resolver.auto_mapping.auto_mapping" option is deprecated and will be removed in DoctrineBundle 4.0, as it only accepts `false` since 3.0. // Set to true to enable using route placeholders as lookup criteria when the primary key doesn't match the argument name // Default: false * evict_cache?: bool|Param, // Set to true to fetch the entity from the database instead of using the cache, if any // Default: false * }, * entity_managers?: array, - * report_fields_where_declared?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.16 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/10455. // Default: true - * validate_xml_mapping?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14. See https://github.com/doctrine/orm/pull/6728. // Default: false + * validate_xml_mapping?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/6728. // Default: false * second_level_cache?: array{ * region_cache_driver?: string|array{ * type?: scalar|Param|null, // Default: null @@ -886,7 +834,7 @@ * lock_path?: scalar|Param|null, // Default: "%kernel.cache_dir%/doctrine/orm/slc/filelock" * lock_lifetime?: scalar|Param|null, // Default: 60 * type?: scalar|Param|null, // Default: "default" - * lifetime?: scalar|Param|null, // Default: 0 + * lifetime?: scalar|Param|null, // Default: null * service?: scalar|Param|null, * name?: scalar|Param|null, * }>, @@ -1018,7 +966,6 @@ * }>, * autoescape_service?: scalar|Param|null, // Default: null * autoescape_service_method?: scalar|Param|null, // Default: null - * base_template_class?: scalar|Param|null, // Deprecated: The child node "base_template_class" at path "twig.base_template_class" is deprecated. * cache?: scalar|Param|null, // Default: true * charset?: scalar|Param|null, // Default: "%kernel.charset%" * debug?: bool|Param, // Default: "%kernel.debug%" @@ -1026,7 +973,7 @@ * auto_reload?: scalar|Param|null, * optimizations?: int|Param, * default_path?: scalar|Param|null, // The default path used to load templates. // Default: "%kernel.project_dir%/templates" - * file_name_pattern?: list, + * file_name_pattern?: string|list, * paths?: array, * date?: array{ // The default format options used by the date filter. * format?: scalar|Param|null, // Default: "F j, Y H:i" @@ -1057,7 +1004,6 @@ * bubble?: bool|Param, // Default: true * interactive_only?: bool|Param, // Default: false * app_name?: scalar|Param|null, // Default: null - * fill_extra_context?: bool|Param, // Default: false * include_stacktraces?: bool|Param, // Default: false * process_psr_3_messages?: array{ * enabled?: bool|Param|null, // Default: null @@ -1077,7 +1023,6 @@ * activation_strategy?: scalar|Param|null, // Default: null * stop_buffering?: bool|Param, // Default: true * passthru_level?: scalar|Param|null, // Default: null - * excluded_404s?: list, * excluded_http_codes?: list, @@ -1091,9 +1036,6 @@ * url?: scalar|Param|null, * exchange?: scalar|Param|null, * exchange_name?: scalar|Param|null, // Default: "log" - * room?: scalar|Param|null, - * message_format?: scalar|Param|null, // Default: "text" - * api_version?: scalar|Param|null, // Default: null * channel?: scalar|Param|null, // Default: null * bot_name?: scalar|Param|null, // Default: "Monolog" * use_attachment?: scalar|Param|null, // Default: true @@ -1102,9 +1044,6 @@ * icon_emoji?: scalar|Param|null, // Default: null * webhook_url?: scalar|Param|null, * exclude_fields?: list, - * team?: scalar|Param|null, - * notify?: scalar|Param|null, // Default: false - * nickname?: scalar|Param|null, // Default: "Monolog" * token?: scalar|Param|null, * region?: scalar|Param|null, * source?: scalar|Param|null, @@ -1122,12 +1061,6 @@ * store?: scalar|Param|null, // Default: null * connection_timeout?: scalar|Param|null, * persistent?: bool|Param, - * dsn?: scalar|Param|null, - * hub_id?: scalar|Param|null, // Default: null - * client_id?: scalar|Param|null, // Default: null - * auto_log_stacks?: scalar|Param|null, // Default: false - * release?: scalar|Param|null, // Default: null - * environment?: scalar|Param|null, // Default: null * message_type?: scalar|Param|null, // Default: 0 * parse_mode?: scalar|Param|null, // Default: null * disable_webpage_preview?: bool|Param|null, // Default: null @@ -1136,8 +1069,7 @@ * delay_between_messages?: bool|Param, // Default: false * topic?: int|Param, // Default: null * factor?: int|Param, // Default: 1 - * tags?: list, - * console_formater_options?: mixed, // Deprecated: "monolog.handlers..console_formater_options.console_formater_options" is deprecated, use "monolog.handlers..console_formater_options.console_formatter_options" instead. + * tags?: string|list, * console_formatter_options?: mixed, // Default: [] * formatter?: scalar|Param|null, * nested?: bool|Param, // Default: false @@ -1148,15 +1080,6 @@ * chunk_size?: scalar|Param|null, // Default: 1420 * encoder?: "json"|"compressed_json"|Param, * }, - * mongo?: string|array{ - * id?: scalar|Param|null, - * host?: scalar|Param|null, - * port?: scalar|Param|null, // Default: 27017 - * user?: scalar|Param|null, - * pass?: scalar|Param|null, - * database?: scalar|Param|null, // Default: "monolog" - * collection?: scalar|Param|null, // Default: "logs" - * }, * mongodb?: string|array{ * id?: scalar|Param|null, // ID of a MongoDB\Client service * uri?: scalar|Param|null, @@ -1190,7 +1113,7 @@ * host?: scalar|Param|null, * }, * from_email?: scalar|Param|null, - * to_email?: list, + * to_email?: string|list, * subject?: scalar|Param|null, * content_type?: scalar|Param|null, // Default: null * headers?: list, @@ -1199,7 +1122,6 @@ * id?: scalar|Param|null, * method?: scalar|Param|null, // Default: null * }, - * lazy?: bool|Param, // Default: true * verbosity_levels?: array{ * VERBOSITY_QUIET?: scalar|Param|null, // Default: "ERROR" * VERBOSITY_NORMAL?: scalar|Param|null, // Default: "WARNING" @@ -1250,7 +1172,7 @@ * }>, * } * @psalm-type TwigComponentConfig = array{ - * defaults?: array, @@ -1259,7 +1181,6 @@ * enabled?: bool|Param, // Default: "%kernel.debug%" * collect_components?: bool|Param, // Collect components instances // Default: true * }, - * controllers_json?: scalar|Param|null, // Deprecated: The "twig_component.controllers_json" config option is deprecated, and will be removed in 3.0. // Default: null * } * @psalm-type TwigExtraConfig = array{ * cache?: bool|array{ diff --git a/backend/config/routes/framework.yaml b/backend/config/routes/framework.yaml index 0fc74bbac..e69de29bb 100644 --- a/backend/config/routes/framework.yaml +++ b/backend/config/routes/framework.yaml @@ -1,4 +0,0 @@ -when@dev: - _errors: - resource: '@FrameworkBundle/Resources/config/routing/errors.xml' - prefix: /_error diff --git a/backend/migrations/Version20260719160654.php b/backend/migrations/Version20260719160654.php index 6fa1f0b61..afc44bd45 100644 --- a/backend/migrations/Version20260719160654.php +++ b/backend/migrations/Version20260719160654.php @@ -18,9 +18,11 @@ public function up(Schema $schema): void { $this->addSql( <<addSql( << + */ #[AsEventListener(event: KernelEvents::CONTROLLER, priority: 200)] -class AuthorizationListener +class AuthorizationListener extends ConsoleApiAuthorizationListenerAbstract { use ClockAwareTrait; - public const string RESOLVED_NEWSLETTER_ATTRIBUTE_KEY = 'console_api_resolved_newsletter'; - public const string RESOLVED_API_KEY_ATTRIBUTE_KEY = 'console_api_resolved_api_key'; - public const string RESOLVED_USER_ATTRIBUTE_KEY = 'console_api_resolved_user'; - public const string RESOLVED_ORGANIZATION_ATTRIBUTE_KEY = 'console_api_resolved_organization'; - public function __construct( - private AuthInterface $auth, - private ApiKeyService $apiKeyService, + private ApiKeyService $apiKeyService, private NewsletterService $newsletterService, - private UserService $userService - ) - { + private UserService $userService, + InternalConfig $internalConfig, + CloudApiService $cloudApiService, + AuthInterface $auth, + ) { + parent::__construct( + $internalConfig, + $cloudApiService, + $auth, + ); } - public function __invoke(ControllerEvent $event): void + protected function getBasePath(): string { - // only console API requests - if (!str_starts_with($event->getRequest()->getPathInfo(), '/api/console')) { - return; - } - if ($event->isMainRequest() === false) { - return; - } - - $request = $event->getRequest(); - - if ($request->headers->has('authorization')) { - $this->handleAuthorizationHeader($event); - } else { - $this->handleSession($event); - } + return '/api/console'; } - private function handleAuthorizationHeader(ControllerEvent $event): void + protected function getBypassPaths(): array { - $request = $event->getRequest(); - $authorizationHeader = $request->headers->get('authorization'); - assert(is_string($authorizationHeader)); - - if (!str_starts_with($authorizationHeader, 'Bearer ')) { - throw new AccessDeniedHttpException('Authorization header must start with "Bearer ".'); - } - - $apiKey = trim(substr($authorizationHeader, 7)); + return [ + '/api/console/init', + ]; + } - if ($apiKey === '') { - throw new AccessDeniedHttpException('API key is missing or empty.'); - } + protected function isResourceApiKey(string $bearerToken): bool + { + return strlen($bearerToken) === ApiKeyService::API_KEY_LENGTH && ctype_xdigit($bearerToken); + } + protected function getResourceFromApiKey(string $apiKey): null|array + { $apiKeyModel = $this->apiKeyService->getByRawKey($apiKey); if ($apiKeyModel === null) { - throw new AccessDeniedHttpException('Invalid API key.'); + return null; } - $scopes = $apiKeyModel->getScopes(); - $this->verifyScopes($scopes, $event); - - $newsletter = $apiKeyModel->getNewsletter(); - - $request->attributes->set(self::RESOLVED_API_KEY_ATTRIBUTE_KEY, $apiKeyModel); - $request->attributes->set(self::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY, $newsletter); - - $apiKeyUpdates = new UpdateApiKeyDto(); - $apiKeyUpdates->lastAccessedAt = $this->now(); - $this->apiKeyService->updateApiKey($apiKeyModel, $apiKeyUpdates); + return [ + 'resource' => $apiKeyModel->getNewsletter(), + 'scopes' => $apiKeyModel->getScopes(), + 'apiKey' => $apiKeyModel, + ]; } - private function handleSession(ControllerEvent $event): void + protected function getResourceFromRequest(ControllerEvent $event): ?object { - $request = $event->getRequest(); - $newsletterId = $request->headers->get('x-newsletter-id'); - $isOrganizationLevelEndpoint = count($event->getAttributes(OrganizationLevelEndpoint::class)) > 0; - $noOrganizationRequired = count($event->getAttributes(OrganizationOptional::class)) > 0; - - $me = $this->auth->me($request); - - if ($me === null) { - throw new DataCarryingHttpException( - 401, - [ - 'login_url' => $this->auth->authUrl('login'), - 'signup_url' => $this->auth->authUrl('signup'), - ], - 'Unauthorized' - ); - } - - $user = $me->getUser(); - $organization = $me->getOrganization(); - $request->attributes->set(self::RESOLVED_USER_ATTRIBUTE_KEY, $user); - $request->attributes->set(self::RESOLVED_ORGANIZATION_ATTRIBUTE_KEY, $organization); - - if ($noOrganizationRequired) { - assert($isOrganizationLevelEndpoint === true); - return; - } - - if ($organization === null) { - throw new AccessDeniedHttpException('Current organization is missing.'); - } + $newsletterId = $event->getRequest()->headers->get('x-newsletter-id'); + $newsletter = $this->newsletterService->getNewsletterById((int)$newsletterId); - $organizationFromFrontend = (int)$request->headers->get('x-organization-id'); - - if ($organizationFromFrontend !== $organization->id) { - throw new AccessDeniedHttpException('org_mismatch'); - } - - // organization-level endpoints do not have a newsletter ID - if ($isOrganizationLevelEndpoint === false) { - if ($newsletterId === null) { - throw new AccessDeniedHttpException('X-Newsletter-ID is required for this endpoint.'); - } - - $newsletter = $this->newsletterService->getNewsletterById((int)$newsletterId); - - if ($newsletter === null) { - throw new AccessDeniedHttpException('Invalid newsletter ID.'); - } - - if ($newsletter->getOrganizationId() !== $organization->id) { - throw new AccessDeniedHttpException('does_not_belong_the_resource'); - } - - if (!$this->userService->hasAccessToNewsletter($newsletter, $user->id)) { - throw new AccessDeniedHttpException('You do not have access to this newsletter.'); - } - - $request->attributes->set(self::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY, $newsletter); + if ($newsletter === null) { + return null; } + return $newsletter; } - /** - * @param string[] $scopes - */ - private function verifyScopes(array $scopes, ControllerEvent $event): void - { - $attributes = $event->getAttributes(ScopeRequired::class); - $scopeRequiredAttribute = $attributes[0] ?? null; - - assert( - $scopeRequiredAttribute instanceof ScopeRequired, - 'ScopeRequired attribute must be set on the controller method' - ); - - $requiredScope = $scopeRequiredAttribute->scope->value; - - if (!in_array($requiredScope, $scopes, true)) { - throw new AccessDeniedHttpException( - "You do not have the required scope '$requiredScope' to access this resource." - ); - } - } - - public static function hasUser(Request $request): bool + protected function getResourceFromRequestError(): string { - return $request->attributes->has(self::RESOLVED_USER_ATTRIBUTE_KEY); + return 'Unable to find the newsletter from the request. Please provide a valid x-newsletter-id header.'; } - // only call after hasUser() - public static function getUser(Request $request): AuthUser + protected function getOrganizationIdFromResource(object $resource): int { - $user = $request->attributes->get(self::RESOLVED_USER_ATTRIBUTE_KEY); - assert($user instanceof AuthUser, 'User must be an instance of AuthUser'); - return $user; + return $resource->getOrganizationId(); } - public static function hasNewsletter(Request $request): bool + protected function getUserResourceScopes(object $resource, int $userId): null|array { - return $request->attributes->has(self::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY); - } + $user = $this->userService->getUser($resource, hyvorUserId: $userId); - // make sure the newsletter is set before calling this - public static function getNewsletter(Request $request): Newsletter - { - $newsletter = $request->attributes->get(self::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY); - assert($newsletter instanceof Newsletter); - return $newsletter; - } + if ($user === null) { + return null; + } - public static function hasOrganization(Request $request): bool - { - return $request->attributes->has(self::RESOLVED_ORGANIZATION_ATTRIBUTE_KEY) - && $request->attributes->get(self::RESOLVED_ORGANIZATION_ATTRIBUTE_KEY) instanceof AuthUserOrganization; + return array_map(fn($scope) => $scope->value, $user->getRole()->scopes()); } - // make sure the organization is set before calling this - public static function getOrganization(Request $request): AuthUserOrganization + protected function onProductApiKeyUse(object $apiKeyModel): void { - $organization = $request->attributes->get(self::RESOLVED_ORGANIZATION_ATTRIBUTE_KEY); - assert($organization instanceof AuthUserOrganization); - return $organization; + $apiKeyUpdates = new UpdateApiKeyDto(); + $apiKeyUpdates->lastAccessedAt = $this->now(); + assert($apiKeyModel instanceof ApiKey); + $this->apiKeyService->updateApiKey($apiKeyModel, $apiKeyUpdates); } - // make sure the API key is set before calling this - public static function getApiKey(Request $request): ApiKey - { - $apiKey = $request->attributes->get(self::RESOLVED_API_KEY_ATTRIBUTE_KEY); - assert($apiKey instanceof ApiKey); - return $apiKey; - } } diff --git a/backend/src/Api/Console/Authorization/OrganizationLevelEndpoint.php b/backend/src/Api/Console/Authorization/OrganizationLevelEndpoint.php deleted file mode 100644 index 3c484e338..000000000 --- a/backend/src/Api/Console/Authorization/OrganizationLevelEndpoint.php +++ /dev/null @@ -1,13 +0,0 @@ -getOrganizationId(); $approval = $this->approvalService->getApprovalOfOrganization($organization); return new JsonResponse([ - 'approval' => ($approval !== null) ? new ApprovalObject($approval) : null + 'approval' => ($approval !== null) ? new ApprovalObject($approval) : null, ]); } #[Route('/approvals', methods: 'POST')] - #[OrganizationLevelEndpoint] + #[OrgEndpoint] public function approve( - Request $request, - #[MapRequestPayload] CreateApprovalInput $input - ): JsonResponse - { - $user = AuthorizationListener::getUser($request); - $organization = AuthorizationListener::getOrganization($request); - - if ($this->approvalService->getApprovalStatusOfOrganization($organization) === ApprovalStatus::APPROVED) { + #[MapRequestPayload] CreateApprovalInput $input, + ConsoleAuthResults $consoleAuth, + ): JsonResponse { + $user = $consoleAuth->getNullableUser(); + assert($user !== null); + $organizationId = $consoleAuth->getOrganizationId(); + + if ($this->approvalService->getApprovalStatusOfOrganization($organizationId) === ApprovalStatus::APPROVED) { throw new UnprocessableEntityHttpException('Account already approved'); } - if ($this->approvalService->getApprovalStatusOfOrganization($organization) === ApprovalStatus::REJECTED) { + if ($this->approvalService->getApprovalStatusOfOrganization($organizationId) === ApprovalStatus::REJECTED) { throw new UnprocessableEntityHttpException('Account already rejected'); } $approval = $this->approvalService->createApproval( userId: $user->id, - organizationId: $organization->id, + organizationId: $organizationId, companyName: $input->company_name, country: $input->country, website: $input->website, @@ -79,23 +76,21 @@ public function approve( frequency: $input->frequency, existingList: $input->existing_list, sample: $input->sample, - whyPost: $input->why_post + whyPost: $input->why_post, ); return new JsonResponse(new ApprovalObject($approval)); } #[Route('/approvals/{id}', methods: 'PATCH')] - #[OrganizationLevelEndpoint] + #[OrgEndpoint] public function updateApproval( - Request $request, - string $id, - #[MapRequestPayload] UpdateApprovalInput $input - ): JsonResponse - { - $organization = AuthorizationListener::getOrganization($request); + string $id, + #[MapRequestPayload] UpdateApprovalInput $input, + ConsoleAuthResults $consoleAuth, + ): JsonResponse { $approval = $this->resolveApproval($id); - $userApprovalStatus = $this->approvalService->getApprovalStatusOfOrganization($organization); + $userApprovalStatus = $this->approvalService->getApprovalStatusOfOrganization($consoleAuth->getOrganizationId()); if (($userApprovalStatus !== ApprovalStatus::REVIEWING) && ($userApprovalStatus !== ApprovalStatus::PENDING)) { throw new UnprocessableEntityHttpException('Approval is not in pending or reviewing status'); } diff --git a/backend/src/Api/Console/Controller/BillingController.php b/backend/src/Api/Console/Controller/BillingController.php index d4c952df4..1fb70044b 100644 --- a/backend/src/Api/Console/Controller/BillingController.php +++ b/backend/src/Api/Console/Controller/BillingController.php @@ -2,40 +2,37 @@ namespace App\Api\Console\Controller; -use App\Api\Console\Authorization\AuthorizationListener; -use App\Api\Console\Authorization\OrganizationLevelEndpoint; use App\Service\Issue\SendService; use Hyvor\Internal\Billing\License\PostLicense; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use Hyvor\Internal\Billing\BillingInterface; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResults; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\OrgEndpoint; class BillingController extends AbstractController { public function __construct( - private SendService $sendService - ) - { - } + private SendService $sendService, + ) {} #[Route('/billing/usage', methods: 'GET')] - #[OrganizationLevelEndpoint] - public function getUsage(Request $request, BillingInterface $billing): JsonResponse + #[OrgEndpoint] + public function getUsage(ConsoleAuthResults $consoleAuth, BillingInterface $billing): JsonResponse { - $organization = AuthorizationListener::getOrganization($request); + $organizationId = $consoleAuth->getOrganizationId(); /** @var ?PostLicense $license */ - $license = $billing->license($organization->id)->license; + $license = $billing->license($organizationId)->license; return new JsonResponse([ 'emails' => [ 'limit' => $license->emails ?? 0, - 'this_month' => $this->sendService->getSendsCountThisMonthOfOrganization($organization->id), - 'last_12_months' => $this->sendService->getSendsCountLast12MonthsOfOrganization($organization->id), - ] + 'this_month' => $this->sendService->getSendsCountThisMonthOfOrganization($organizationId), + 'last_12_months' => $this->sendService->getSendsCountLast12MonthsOfOrganization($organizationId), + ], ]); } diff --git a/backend/src/Api/Console/Controller/ConsoleController.php b/backend/src/Api/Console/Controller/ConsoleController.php index ed6e2e229..2d02d1257 100644 --- a/backend/src/Api/Console/Controller/ConsoleController.php +++ b/backend/src/Api/Console/Controller/ConsoleController.php @@ -4,11 +4,10 @@ namespace App\Api\Console\Controller; -use App\Api\Console\Authorization\AuthorizationListener; -use App\Api\Console\Authorization\OrganizationOptional; +use Hyvor\Internal\Auth\AuthInterface; +use Hyvor\Internal\Bundle\Api\DataCarryingHttpException; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; -use App\Api\Console\Authorization\OrganizationLevelEndpoint; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Object\ListObject; use App\Api\Console\Object\NewsletterListObject; use App\Api\Console\Object\NewsletterObject; @@ -31,31 +30,41 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Attribute\Route; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\OrgEndpoint; class ConsoleController extends AbstractController { public function __construct( - private NewsletterService $newsletterService, - private NewsletterListService $listService, - private ListRepository $listRepository, - private InternalConfig $internalConfig, - private AppConfig $appConfig, + private NewsletterService $newsletterService, + private NewsletterListService $listService, + private ListRepository $listRepository, + private InternalConfig $internalConfig, + private AppConfig $appConfig, private SubscriberMetadataService $subscriberMetadataService, - private SendingProfileService $sendingProfileService, - private BillingInterface $billing, - ) - { - } + private SendingProfileService $sendingProfileService, + private BillingInterface $billing, + private AuthInterface $auth, + ) {} #[Route('/init', methods: 'GET')] - #[OrganizationLevelEndpoint] - #[OrganizationOptional] public function initConsole(Request $request): JsonResponse { - $user = AuthorizationListener::getUser($request); - $organization = AuthorizationListener::hasOrganization($request) ? AuthorizationListener::getOrganization($request) : null; + $me = $this->auth->me($request); + + if ($me === null) { + throw new DataCarryingHttpException( + 401, + [ + 'login_url' => $this->auth->authUrl('login'), + 'signup_url' => $this->auth->authUrl('signup'), + ], + 'Unauthorized', + ); + } + + $user = $me->getUser(); + $organization = $me->getOrganization(); $newsletters = []; $license = new ResolvedLicense(ResolvedLicenseType::NONE); @@ -63,7 +72,7 @@ public function initConsole(Request $request): JsonResponse if ($organization) { $newsletters = array_map( fn(array $pair) => new NewsletterListObject($pair['newsletter'], $pair['user']), - $this->newsletterService->getUserNewslettersOfOrganization($user->id, $organization->id) + $this->newsletterService->getUserNewslettersOfOrganization($user->id, $organization->id), ); $license = $this->billing->license($organization->id); } @@ -98,7 +107,7 @@ public function initNewsletter(Newsletter $newsletter): JsonResponse [ 'newsletter' => $newsletter, 'deleted_at' => null, - ] + ], ); $listIds = array_map(fn($list) => $list->getId(), $lists); $subscriberCounts = $this->listService->getSubscriberCountOfLists($listIds); @@ -121,7 +130,8 @@ public function initNewsletter(Newsletter $newsletter): JsonResponse return new JsonResponse([ 'newsletter' => new NewsletterObject($newsletter), 'lists' => array_map(fn($list) => new ListObject($list, $subscriberCounts[$list->getId()]), $lists), - 'sending_profiles' => array_map(fn($address) => new SendingProfileObject($address), $this->sendingProfileService->getSendingProfiles($newsletter)), + 'sending_profiles' => array_map(fn($address) => new SendingProfileObject($address), + $this->sendingProfileService->getSendingProfiles($newsletter)), 'subscriber_metadata_definitions' => array_map(fn($def) => new SubscriberMetadataDefinitionObject($def), $subscriberMetadataDefinitions), 'stats' => $newsletterStats, diff --git a/backend/src/Api/Console/Controller/DomainController.php b/backend/src/Api/Console/Controller/DomainController.php index 3e9d38304..4129f9e74 100644 --- a/backend/src/Api/Console/Controller/DomainController.php +++ b/backend/src/Api/Console/Controller/DomainController.php @@ -2,8 +2,6 @@ namespace App\Api\Console\Controller; -use App\Api\Console\Authorization\AuthorizationListener; -use App\Api\Console\Authorization\OrganizationLevelEndpoint; use App\Api\Console\Input\Domain\CreateDomainInput; use App\Api\Console\Object\DomainObject; use App\Entity\Domain; @@ -14,21 +12,20 @@ use App\Service\Domain\VerifyDomainException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; use Symfony\Component\Routing\Attribute\Route; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResults; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\OrgEndpoint; class DomainController extends AbstractController { public function __construct( private DomainService $domainService, - private AppConfig $appConfig, - ) - { - } + private AppConfig $appConfig, + ) {} private function resolveDomainEntity(string $id): Domain { @@ -42,24 +39,22 @@ private function resolveDomainEntity(string $id): Domain } #[Route('/domains', methods: 'GET')] - #[OrganizationLevelEndpoint] - public function getDomains(Request $request): JsonResponse + #[OrgEndpoint] + public function getDomains(ConsoleAuthResults $consoleAuth): JsonResponse { - $organization = AuthorizationListener::getOrganization($request); - - $domains = $this->domainService->getDomainsByOrganizationId($organization->id); + $domains = $this->domainService->getDomainsByOrganizationId($consoleAuth->getOrganizationId()); return $this->json(array_map(fn(Domain $domain) => new DomainObject($domain), $domains)); } #[Route('/domains', methods: 'POST')] - #[OrganizationLevelEndpoint] + #[OrgEndpoint] public function createDomain( - Request $request, - #[MapRequestPayload] CreateDomainInput $input - ): JsonResponse - { - $user = AuthorizationListener::getUser($request); - $organization = AuthorizationListener::getOrganization($request); + #[MapRequestPayload] CreateDomainInput $input, + ConsoleAuthResults $consoleAuth, + ): JsonResponse { + $user = $consoleAuth->getNullableUser(); + assert($user !== null); + $organizationId = $consoleAuth->getOrganizationId(); if ($input->domain === $this->appConfig->getSystemMailDomain()) { throw new BadRequestHttpException('This domain is reserved and cannot be registered'); @@ -69,14 +64,14 @@ public function createDomain( if ($domainInDb) { throw new BadRequestHttpException( - $domainInDb->getOrganizationId() === $organization->id ? + $domainInDb->getOrganizationId() === $organizationId ? 'This domain is already registered' : - 'This domain is already registered by another organization' + 'This domain is already registered by another organization', ); } try { - $domain = $this->domainService->createDomain($input->domain, $user->id, $organization->id); + $domain = $this->domainService->createDomain($input->domain, $user->id, $organizationId); return $this->json(new DomainObject($domain)); } catch (CreateDomainException) { throw new BadRequestHttpException('Failed to create domain. Contact support for more details'); @@ -84,13 +79,12 @@ public function createDomain( } #[Route('/domains/{id}/verify', methods: 'POST')] - #[OrganizationLevelEndpoint] - public function verifyDomain(Request $request, string $id): JsonResponse + #[OrgEndpoint] + public function verifyDomain(string $id, ConsoleAuthResults $consoleAuth): JsonResponse { - $organization = AuthorizationListener::getOrganization($request); $domain = $this->resolveDomainEntity($id); - if ($domain->getOrganizationId() !== $organization->id) { + if ($domain->getOrganizationId() !== $consoleAuth->getOrganizationId()) { throw new BadRequestHttpException('Your current organization does not own this domain'); } @@ -110,13 +104,12 @@ public function verifyDomain(Request $request, string $id): JsonResponse } #[Route('/domains/{id}', methods: 'DELETE')] - #[OrganizationLevelEndpoint] - public function deleteDomain(Request $request, string $id): JsonResponse + #[OrgEndpoint] + public function deleteDomain(string $id, ConsoleAuthResults $consoleAuth): JsonResponse { - $organization = AuthorizationListener::getOrganization($request); $domain = $this->resolveDomainEntity($id); - if ($domain->getOrganizationId() !== $organization->id) { + if ($domain->getOrganizationId() !== $consoleAuth->getOrganizationId()) { throw new BadRequestHttpException('Your current organization does not own this domain'); } diff --git a/backend/src/Api/Console/Controller/ExportController.php b/backend/src/Api/Console/Controller/ExportController.php index bb40fb2c9..16eba2b8a 100644 --- a/backend/src/Api/Console/Controller/ExportController.php +++ b/backend/src/Api/Console/Controller/ExportController.php @@ -3,23 +3,21 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Object\SubscriberExportObject; use App\Entity\Newsletter; use App\Service\Media\MediaService; use App\Service\Subscriber\SubscriberService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class ExportController extends AbstractController { public function __construct( private SubscriberService $subscriberService, - private MediaService $mediaService, - ) - { - } + private MediaService $mediaService, + ) {} #[Route('/export', methods: 'POST')] #[ScopeRequired(PostScope::DATA_WRITE)] @@ -36,8 +34,9 @@ public function listExports(Newsletter $newsletter): JsonResponse $exports = $this->subscriberService->getExports($newsletter); $exportObjects = array_map(function ($export) { $media = $export->getMedia(); - if ($media) + if ($media) { return new SubscriberExportObject($export, $this->mediaService->getPublicUrl($media)); + } return new SubscriberExportObject($export, null); }, $exports); return $this->json($exportObjects); diff --git a/backend/src/Api/Console/Controller/ImportController.php b/backend/src/Api/Console/Controller/ImportController.php index 1155dfcc9..9d5b19392 100644 --- a/backend/src/Api/Console/Controller/ImportController.php +++ b/backend/src/Api/Console/Controller/ImportController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\Import\ImportInput; use App\Api\Console\Input\Import\UploadImportInput; use App\Api\Console\Object\SubscriberImportObject; @@ -20,26 +20,23 @@ use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; use Symfony\Component\Messenger\MessageBusInterface; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class ImportController extends AbstractController { public function __construct( - private ImportService $importService, + private ImportService $importService, private MessageBusInterface $messageBus, - private MediaController $mediaController, - ) - { - } + private MediaController $mediaController, + ) {} #[Route('/imports/upload', methods: 'POST')] #[ScopeRequired(PostScope::DATA_WRITE)] public function upload( - Newsletter $newsletter, - Request $request, - #[MapRequestPayload] UploadImportInput $input - ): JsonResponse - { + Newsletter $newsletter, + Request $request, + #[MapRequestPayload] UploadImportInput $input, + ): JsonResponse { $importCounts = $this->importService->getNewsletterImportCounts($newsletter); if ($importCounts['month'] >= ImportService::MONTHLY_IMPORT_LIMIT) { @@ -64,11 +61,10 @@ public function upload( #[Route('/imports/{id}', methods: 'POST')] #[ScopeRequired(PostScope::DATA_WRITE)] public function import( - Newsletter $newsletter, - SubscriberImport $subscriberImport, - #[MapRequestPayload] ImportInput $input - ): JsonResponse - { + Newsletter $newsletter, + SubscriberImport $subscriberImport, + #[MapRequestPayload] ImportInput $input, + ): JsonResponse { $importCounts = $this->importService->getNewsletterImportCounts($newsletter); if ($importCounts['month'] >= ImportService::MONTHLY_IMPORT_LIMIT) { @@ -95,7 +91,7 @@ public function import( $subscriberImport = $this->importService->updateSubscriberImport( $subscriberImport, - $updates + $updates, ); return new JsonResponse(new SubscriberImportObject($subscriberImport)); diff --git a/backend/src/Api/Console/Controller/IssueController.php b/backend/src/Api/Console/Controller/IssueController.php index 8380a2cac..6445e3e6f 100644 --- a/backend/src/Api/Console/Controller/IssueController.php +++ b/backend/src/Api/Console/Controller/IssueController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\Issue\SendTestInput; use App\Api\Console\Input\Issue\UpdateIssueInput; use App\Api\Console\Object\IssueObject; @@ -37,15 +37,15 @@ class IssueController extends AbstractController { public function __construct( - private IssueService $issueService, - private SendService $sendService, + private IssueService $issueService, + private SendService $sendService, private NewsletterListService $newsletterListService, - private TextTemplateRenderer $textTemplateRenderer, - private HtmlTemplateRenderer $htmlTemplateRenderer, - private BillingInterface $billing, - private DomainService $domainService, - private UserService $userService, - private SendingProfileService $sendingProfileService + private TextTemplateRenderer $textTemplateRenderer, + private HtmlTemplateRenderer $htmlTemplateRenderer, + private BillingInterface $billing, + private DomainService $domainService, + private UserService $userService, + private SendingProfileService $sendingProfileService, ) {} #[Route('/issues', methods: 'GET')] @@ -76,18 +76,20 @@ public function createIssue(Newsletter $newsletter): JsonResponse #[ScopeRequired(PostScope::ISSUES_READ)] public function getById(Issue $issue): JsonResponse { - return $this->json(new IssueObject( - $issue, - $this->sendService->getSendableSubscribersCount($issue) - )); + return $this->json( + new IssueObject( + $issue, + $this->sendService->getSendableSubscribersCount($issue), + ), + ); } #[Route('/issues/{id}', methods: 'PATCH')] #[ScopeRequired(PostScope::ISSUES_WRITE)] public function updateIssue( - Issue $issue, - Newsletter $newsletter, - #[MapRequestPayload] UpdateIssueInput $input + Issue $issue, + Newsletter $newsletter, + #[MapRequestPayload] UpdateIssueInput $input, ): JsonResponse { $updates = new UpdateIssueDto(); @@ -102,7 +104,7 @@ public function updateIssue( if ($input->has('sending_profile_id')) { $sendingProfile = $this->sendingProfileService->getSendingProfileOfNewsletterById( $newsletter, - $input->sending_profile_id + $input->sending_profile_id, ); if ($sendingProfile === null) { @@ -124,10 +126,12 @@ public function updateIssue( $issueUpdated = $this->issueService->updateIssue($issue, $updates); - return $this->json(new IssueObject( - $issueUpdated, - $this->sendService->getSendableSubscribersCount($issue) - )); + return $this->json( + new IssueObject( + $issueUpdated, + $this->sendService->getSendableSubscribersCount($issue), + ), + ); } #[Route('/issues/{id}', methods: 'DELETE')] @@ -175,7 +179,9 @@ public function sendIssue(Issue $issue, MessageBusInterface $bus): JsonResponse } if ($resolvedLicense->type === ResolvedLicenseType::TRIAL) { - throw new UnprocessableEntityHttpException("Cannot send issues during trial. Please upgrade your subscription."); + throw new UnprocessableEntityHttpException( + "Cannot send issues during trial. Please upgrade your subscription.", + ); } $sendCountThisMonth = $this->sendService->getSendsCountThisMonthOfOrganization($organizationId); @@ -184,8 +190,8 @@ public function sendIssue(Issue $issue, MessageBusInterface $bus): JsonResponse 'message' => 'would_exceed_limit', 'data' => [ 'limit' => $license->emails, - 'exceed_amount' => abs($license->emails - $sendCountThisMonth - $subscribersCount) - ] + 'exceed_amount' => abs($license->emails - $sendCountThisMonth - $subscribersCount), + ], ], 422); } @@ -229,8 +235,8 @@ public function getTestData(Issue $issue): JsonResponse #[Route('/issues/{id}/test', methods: 'POST')] #[ScopeRequired(PostScope::ISSUES_WRITE)] public function sendTest( - Issue $issue, - #[MapRequestPayload] SendTestInput $input + Issue $issue, + #[MapRequestPayload] SendTestInput $input, ): JsonResponse { if ($issue->getStatus() != IssueStatus::DRAFT) { throw new UnprocessableEntityHttpException("Issue is not a draft."); @@ -245,7 +251,9 @@ public function sendTest( } if (!$this->issueService->isTestEmailAllowed($issue, $input->emails)) { - throw new UnprocessableEntityHttpException("Test emails can only be sent to verified domains or emails of newsletter users."); + throw new UnprocessableEntityHttpException( + "Test emails can only be sent to verified domains or emails of newsletter users.", + ); } $sendCount = $this->issueService->sendTestEmails($issue, $input->emails); @@ -267,7 +275,7 @@ public function previewIssue(Issue $issue): JsonResponse return $this->json([ 'html' => $preview, - 'sendable_subscribers_count' => $this->sendService->getSendableSubscribersCount($issue) + 'sendable_subscribers_count' => $this->sendService->getSendableSubscribersCount($issue), ]); } @@ -308,8 +316,8 @@ public function getIssueReport(Issue $issue): JsonResponse $counts = $this->sendService->getIssueStats($issue, full: true); return $this->json( [ - 'counts' => $counts - ] + 'counts' => $counts, + ], ); } } diff --git a/backend/src/Api/Console/Controller/ListController.php b/backend/src/Api/Console/Controller/ListController.php index c0a295934..52f6c3532 100644 --- a/backend/src/Api/Console/Controller/ListController.php +++ b/backend/src/Api/Console/Controller/ListController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\List\CreateListInput; use App\Api\Console\Input\List\UpdateListInput; use App\Api\Console\Object\ListObject; @@ -20,18 +20,15 @@ class ListController extends AbstractController { public function __construct( - private NewsletterListService $newsletterListService - ) - { - } + private NewsletterListService $newsletterListService, + ) {} #[Route('/lists', methods: 'POST')] #[ScopeRequired(PostScope::NEWSLETTER_WRITE)] public function createNewsletterList( - Newsletter $newsletter, - #[MapRequestPayload] CreateListInput $input - ): JsonResponse - { + Newsletter $newsletter, + #[MapRequestPayload] CreateListInput $input, + ): JsonResponse { $listCounter = $this->newsletterListService->getListCounter($newsletter); if ($listCounter >= $this->newsletterListService::MAX_LIST_DEFINITIONS_PER_NEWSLETTER) { @@ -49,7 +46,7 @@ public function createNewsletterList( $list = $this->newsletterListService->createNewsletterList( $newsletter, $input->name, - $input->description + $input->description, ); return $this->json(new ListObject($list, 0)); } @@ -57,14 +54,13 @@ public function createNewsletterList( #[Route('/lists/{id}', methods: 'PATCH')] #[ScopeRequired(PostScope::NEWSLETTER_WRITE)] public function updateNewsletterList( - NewsletterList $list, - #[MapRequestPayload] UpdateListInput $input - ): JsonResponse - { + NewsletterList $list, + #[MapRequestPayload] UpdateListInput $input, + ): JsonResponse { $list = $this->newsletterListService->updateNewsletterList( $list, $input->name ?? $list->getName(), - $input->description ?? $list->getDescription() + $input->description ?? $list->getDescription(), ); $subscriberCounts = $this->newsletterListService->getSubscriberCountOfLists([$list->getId()]); diff --git a/backend/src/Api/Console/Controller/MediaController.php b/backend/src/Api/Console/Controller/MediaController.php index 960fbd0ab..f5a2b7048 100644 --- a/backend/src/Api/Console/Controller/MediaController.php +++ b/backend/src/Api/Console/Controller/MediaController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\Media\MediaUploadInput; use App\Api\Console\Object\MediaObject; use App\Entity\Media; @@ -27,22 +27,18 @@ class MediaController extends AbstractController public function __construct( private ValidatorInterface $validator, - private MediaService $mediaService - ) - { - } + private MediaService $mediaService, + ) {} public function doUpload( Newsletter $newsletter, MediaFolder $folder, mixed $file, int $maxSizeMb = 10, - ): Media - { - + ): Media { $constraint = new Constraints\File( maxSize: $maxSizeMb . 'M', - extensions: $folder->getAllowedExtensions() + extensions: $folder->getAllowedExtensions(), ); $errors = $this->validator->validate($file, $constraint); @@ -50,8 +46,8 @@ public function doUpload( throw new UnprocessableEntityHttpException( previous: new ValidationFailedException( 'Invalid file upload', - $errors - ) + $errors, + ), ); } @@ -61,22 +57,20 @@ public function doUpload( return $this->mediaService->upload( $newsletter, $folder, - $file + $file, ); } catch (MediaUploadException $e) { throw new UnprocessableEntityHttpException($e->getMessage()); } - } #[Route('/media', methods: 'POST')] #[ScopeRequired(PostScope::MEDIA_WRITE)] public function upload( - Newsletter $newsletter, - Request $request, - #[MapRequestPayload] MediaUploadInput $input - ): JsonResponse - { + Newsletter $newsletter, + Request $request, + #[MapRequestPayload] MediaUploadInput $input, + ): JsonResponse { $file = $request->files->get(key: 'file'); $folder = $input->folder; @@ -89,8 +83,8 @@ public function upload( return $this->json( new MediaObject( $media, - $this->mediaService->getPublicUrl($media) - ) + $this->mediaService->getPublicUrl($media), + ), ); } diff --git a/backend/src/Api/Console/Controller/NewsletterController.php b/backend/src/Api/Console/Controller/NewsletterController.php index 3f0937644..51010cc1c 100644 --- a/backend/src/Api/Console/Controller/NewsletterController.php +++ b/backend/src/Api/Console/Controller/NewsletterController.php @@ -2,10 +2,10 @@ namespace App\Api\Console\Controller; -use App\Api\Console\Authorization\AuthorizationListener; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; -use App\Api\Console\Authorization\OrganizationLevelEndpoint; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\OrgEndpoint; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResults; use App\Api\Console\Input\Newsletter\CreateNewsletterInput; use App\Api\Console\Input\Newsletter\SubdomainAvailabilityInput; use App\Api\Console\Input\Newsletter\UpdateNewsletterInput; @@ -26,17 +26,14 @@ class NewsletterController extends AbstractController { public function __construct( private NewsletterService $newsletterService, - ) - { - } + ) {} #[Route('/newsletter/subdomain', methods: 'POST')] - #[OrganizationLevelEndpoint] + #[OrgEndpoint] public function getSubdomainAvailability( - Request $request, - #[MapRequestPayload] SubdomainAvailabilityInput $input - ): JsonResponse - { + Request $request, + #[MapRequestPayload] SubdomainAvailabilityInput $input, + ): JsonResponse { if (!$input->subdomain) { throw new UnprocessableEntityHttpException('Subdomain is required.'); } @@ -48,20 +45,18 @@ public function getSubdomainAvailability( } return $this->json([ - 'available' => $available + 'available' => $available, ]); } - #[Route('/newsletter', methods: 'POST')] - #[OrganizationLevelEndpoint] + #[Route('/newsletters', methods: 'POST')] + #[OrgEndpoint] + #[ScopeRequired(PostScope::ORG_NEWSLETTERS_CREATE)] public function createNewsletter( - Request $request, - #[MapRequestPayload] CreateNewsletterInput $input - ): JsonResponse - { - $user = AuthorizationListener::getUser($request); - $organization = AuthorizationListener::getOrganization($request); - + Request $request, + #[MapRequestPayload] CreateNewsletterInput $input, + ConsoleAuthResults $consoleAuth, + ): JsonResponse { $subdomain = $input->subdomain; if ($this->newsletterService->isSubdomainTaken($subdomain)) { @@ -73,11 +68,12 @@ public function createNewsletter( } $newsletter = $this->newsletterService->createNewsletter( - $user->id, - $organization->id, + $consoleAuth->getOrganizationId(), $input->name, $subdomain, - $input->metadata + $consoleAuth->getSourceString(), + $input->metadata, + userId: $consoleAuth->getNullableUser()?->id, ); return $this->json(new NewsletterObject($newsletter)); } @@ -100,10 +96,9 @@ public function deleteNewsletter(Newsletter $newsletter): JsonResponse #[Route('/newsletter', methods: 'PATCH')] #[ScopeRequired(PostScope::NEWSLETTER_WRITE)] public function updateNewsletter( - Newsletter $newsletter, - #[MapRequestPayload(resolver: UpdateNewsletterInputResolver::class)] UpdateNewsletterInput $input - ): JsonResponse - { + Newsletter $newsletter, + #[MapRequestPayload(resolver: UpdateNewsletterInputResolver::class)] UpdateNewsletterInput $input, + ): JsonResponse { $updates = new UpdateNewsletterDto(); if ($input->has('name')) { $updates->name = $input->name; diff --git a/backend/src/Api/Console/Controller/SendingProfileController.php b/backend/src/Api/Console/Controller/SendingProfileController.php index 95b0fdffc..109d28756 100644 --- a/backend/src/Api/Console/Controller/SendingProfileController.php +++ b/backend/src/Api/Console/Controller/SendingProfileController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\SendingProfile\CreateSendingProfileInput; use App\Api\Console\Input\SendingProfile\UpdateSendingProfileInput; use App\Api\Console\Object\SendingProfileObject; @@ -23,10 +23,8 @@ class SendingProfileController extends AbstractController { public function __construct( private SendingProfileService $sendingProfileService, - private DomainService $domainService - ) - { - } + private DomainService $domainService, + ) {} private function getDomainFromEmail(string $email): Domain { @@ -47,7 +45,7 @@ public function getSendingProfiles(Newsletter $newsletter): JsonResponse { $sendingProfiles = array_map( fn(SendingProfile $sendingProfile) => new SendingProfileObject($sendingProfile), - $this->sendingProfileService->getSendingProfiles($newsletter) + $this->sendingProfileService->getSendingProfiles($newsletter), ); return $this->json($sendingProfiles); } @@ -56,9 +54,8 @@ public function getSendingProfiles(Newsletter $newsletter): JsonResponse #[ScopeRequired(PostScope::SENDING_PROFILES_WRITE)] public function createSendingProfile( #[MapRequestPayload] CreateSendingProfileInput $input, - Newsletter $newsletter, - ): JsonResponse - { + Newsletter $newsletter, + ): JsonResponse { $domain = $this->getDomainFromEmail($input->from_email); $sendingProfile = $this->sendingProfileService->createSendingProfile( $newsletter, @@ -68,7 +65,7 @@ public function createSendingProfile( $input->reply_to_email, $input->brand_name, $input->brand_logo, - $input->brand_url + $input->brand_url, ); return $this->json(new SendingProfileObject($sendingProfile)); @@ -77,11 +74,9 @@ public function createSendingProfile( #[Route('/sending-profiles/{id}', methods: 'PATCH')] #[ScopeRequired(PostScope::SENDING_PROFILES_WRITE)] public function updateSendingProfile( - SendingProfile $sendingProfile, - #[MapRequestPayload] UpdateSendingProfileInput $input - ): JsonResponse - { - + SendingProfile $sendingProfile, + #[MapRequestPayload] UpdateSendingProfileInput $input, + ): JsonResponse { $updates = new UpdateSendingProfileDto(); if ($input->has('from_email')) { $domain = $this->getDomainFromEmail($input->from_email); @@ -122,7 +117,6 @@ public function updateSendingProfile( #[ScopeRequired(PostScope::SENDING_PROFILES_WRITE)] public function deleteSendingProfile(SendingProfile $sendingProfile): JsonResponse { - if ($sendingProfile->getIsSystem()) { throw new BadRequestHttpException("Cannot delete system sending profile"); } @@ -133,8 +127,8 @@ public function deleteSendingProfile(SendingProfile $sendingProfile): JsonRespon return $this->json( array_map( fn(SendingProfile $profile) => new SendingProfileObject($profile), - $sendingProfiles - ) + $sendingProfiles, + ), ); } } diff --git a/backend/src/Api/Console/Controller/SubscriberController.php b/backend/src/Api/Console/Controller/SubscriberController.php index 64e7ac73f..f681b27d8 100644 --- a/backend/src/Api/Console/Controller/SubscriberController.php +++ b/backend/src/Api/Console/Controller/SubscriberController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\Subscriber\BulkActionSubscriberInput; use App\Api\Console\Input\Subscriber\CreateSubscriberInput; use App\Api\Console\Input\Subscriber\ListsStrategy; diff --git a/backend/src/Api/Console/Controller/SubscriberMetadataController.php b/backend/src/Api/Console/Controller/SubscriberMetadataController.php index 83861a2c4..a2be629b4 100644 --- a/backend/src/Api/Console/Controller/SubscriberMetadataController.php +++ b/backend/src/Api/Console/Controller/SubscriberMetadataController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\SubscriberMetadata\CreateSubscriberMetadataDefinitionInput; use App\Api\Console\Input\SubscriberMetadata\UpdateSubscriberMetadataDefinitionInput; use App\Api\Console\Object\SubscriberMetadataDefinitionObject; @@ -20,18 +20,15 @@ class SubscriberMetadataController extends AbstractController { public function __construct( - private SubscriberMetadataService $subscriberMetadataService - ) - { - } + private SubscriberMetadataService $subscriberMetadataService, + ) {} #[Route('/subscriber-metadata-definitions', methods: 'POST')] #[ScopeRequired(PostScope::SUBSCRIBERS_WRITE)] public function createMetadata( - Newsletter $newsletter, - #[MapRequestPayload] CreateSubscriberMetadataDefinitionInput $input - ): JsonResponse - { + Newsletter $newsletter, + #[MapRequestPayload] CreateSubscriberMetadataDefinitionInput $input, + ): JsonResponse { $current = $this->subscriberMetadataService->getMetadataDefinitionByKey($newsletter, $input->key); if ($current) { @@ -47,7 +44,7 @@ public function createMetadata( $metadataDefinition = $this->subscriberMetadataService->createMetadataDefinition( $newsletter, $input->key, - $input->name + $input->name, ); return $this->json(new SubscriberMetadataDefinitionObject($metadataDefinition)); @@ -56,10 +53,9 @@ public function createMetadata( #[Route('/subscriber-metadata-definitions/{id}', methods: 'PATCH')] #[ScopeRequired(PostScope::SUBSCRIBERS_WRITE)] public function updateMetadata( - SubscriberMetadataDefinition $metadataDefinition, - #[MapRequestPayload] UpdateSubscriberMetadataDefinitionInput $input - ): JsonResponse - { + SubscriberMetadataDefinition $metadataDefinition, + #[MapRequestPayload] UpdateSubscriberMetadataDefinitionInput $input, + ): JsonResponse { $this->subscriberMetadataService->updateMetadataDefinition($metadataDefinition, $input->name); return $this->json(new SubscriberMetadataDefinitionObject($metadataDefinition)); } diff --git a/backend/src/Api/Console/Controller/TemplateController.php b/backend/src/Api/Console/Controller/TemplateController.php index 476a2718a..8f4b38a54 100644 --- a/backend/src/Api/Console/Controller/TemplateController.php +++ b/backend/src/Api/Console/Controller/TemplateController.php @@ -3,7 +3,7 @@ namespace App\Api\Console\Controller; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\Template\UpdateTemplateInput; use App\Api\Console\Input\Template\RenderTemplateInput; use App\Api\Console\Object\TemplateObject; @@ -23,13 +23,11 @@ class TemplateController extends AbstractController { public function __construct( - private TemplateService $templateService, + private TemplateService $templateService, private TemplateVariableService $templateVariableService, - private HtmlTemplateRenderer $htmlTemplateRenderer, - private ContentDefaultStyle $contentDefaultStyle, - ) - { - } + private HtmlTemplateRenderer $htmlTemplateRenderer, + private ContentDefaultStyle $contentDefaultStyle, + ) {} #[Route('/templates', methods: 'GET')] #[ScopeRequired(PostScope::TEMPLATES_READ)] @@ -40,7 +38,7 @@ public function getNewsletterTemplate(Newsletter $newsletter): JsonResponse if (!$template) { // Load default template return $this->json([ - 'template' => $this->templateService->readDefaultTemplate() + 'template' => $this->templateService->readDefaultTemplate(), ]); } @@ -50,10 +48,9 @@ public function getNewsletterTemplate(Newsletter $newsletter): JsonResponse #[Route('/templates', methods: 'PATCH')] #[ScopeRequired(PostScope::TEMPLATES_WRITE)] public function updateTemplate( - Newsletter $newsletter, - #[MapRequestPayload] UpdateTemplateInput $input - ): JsonResponse - { + Newsletter $newsletter, + #[MapRequestPayload] UpdateTemplateInput $input, + ): JsonResponse { $templateString = $input->template ?? $this->templateService->readDefaultTemplate(); $template = $this->templateService->getTemplate($newsletter); @@ -71,10 +68,9 @@ public function updateTemplate( #[Route('/templates/render', methods: 'POST')] #[ScopeRequired(PostScope::TEMPLATES_READ)] public function renderTemplate( - Newsletter $newsletter, - #[MapRequestPayload] RenderTemplateInput $input - ): JsonResponse - { + Newsletter $newsletter, + #[MapRequestPayload] RenderTemplateInput $input, + ): JsonResponse { $subject = 'Hyvor Post Default Email'; $defaultContentHtml = $this->contentDefaultStyle->html(); diff --git a/backend/src/Api/Console/Controller/UserController.php b/backend/src/Api/Console/Controller/UserController.php index 8c18bae64..ea0ee16e6 100644 --- a/backend/src/Api/Console/Controller/UserController.php +++ b/backend/src/Api/Console/Controller/UserController.php @@ -2,7 +2,7 @@ namespace App\Api\Console\Controller; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Api\Console\Input\User\CreateUserInput; use App\Api\Console\Input\User\DeleteUserInput; use App\Api\Console\Object\UserObject; @@ -23,18 +23,17 @@ class UserController extends AbstractController { public function __construct( - private AuthInterface $auth, - private UserService $userService, + private AuthInterface $auth, + private UserService $userService, private CommsInterface $comms, - ) - { - } + ) {} #[Route('/users', methods: 'GET')] #[ScopeRequired(PostScope::USERS_READ)] public function getUsers(Newsletter $newsletter): JsonResponse { - $users = $this->userService->getNewsletterUsers($newsletter) + $users = $this->userService + ->getNewsletterUsers($newsletter) ->map(function ($user) { $hyvorUser = $this->auth->fromId($user->getHyvorUserId()); if ($hyvorUser === null) { diff --git a/backend/src/Api/Console/RateLimit/RateLimitListener.php b/backend/src/Api/Console/RateLimit/RateLimitListener.php index c19809e7b..1396cdac7 100644 --- a/backend/src/Api/Console/RateLimit/RateLimitListener.php +++ b/backend/src/Api/Console/RateLimit/RateLimitListener.php @@ -2,7 +2,7 @@ namespace App\Api\Console\RateLimit; -use App\Api\Console\Authorization\AuthorizationListener; +use App\Entity\Newsletter; use App\Service\App\RateLimit\RateLimiterProvider; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpFoundation\Request; @@ -11,19 +11,19 @@ use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\RateLimiter\LimiterInterface; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResultsResolver; // priority less than AuthorizationListener +// this is only for the test issue endpoint for now. #[AsEventListener(event: KernelEvents::CONTROLLER, method: 'onController', priority: 150)] #[AsEventListener(event: KernelEvents::RESPONSE, method: 'onResponse')] class RateLimitListener { public function __construct( - private RateLimit $rateLimit, + private RateLimit $rateLimit, private RateLimiterProvider $rateLimiterProvider, - ) - { - } + ) {} private const string RATE_LIMIT_HEADERS_ATTRIBUTE_KEY = 'console_api_rate_limit_headers'; @@ -34,16 +34,20 @@ private function isConsoleApiRequest(Request $request): bool public function isTestIssueEndpoint(Request $request): bool { - return $request->getMethod() === 'POST' && preg_match('#^/api/console/issues/\d+/test$#', $request->getPathInfo()); + return $request->getMethod() === 'POST' && preg_match( + '#^/api/console/issues/\d+/test$#', + $request->getPathInfo(), + ); } private function getRateLimiter(Request $request): LimiterInterface { - $newsletter = AuthorizationListener::getNewsletter($request); + $newsletter = ConsoleAuthResultsResolver::fromRequest($request)->getResource(); + assert($newsletter instanceof Newsletter); return $this->rateLimiterProvider->rateLimiter( $this->rateLimit->testIssues(), - 'test:issue:newsletter:' . $newsletter->getId() + 'test:issue:newsletter:' . $newsletter->getId(), ); } @@ -100,4 +104,4 @@ public function onResponse(ResponseEvent $responseEvent): void } } } -} \ No newline at end of file +} diff --git a/backend/src/Api/Console/Resolver/EntityResolver.php b/backend/src/Api/Console/Resolver/EntityResolver.php index 5927b9be6..14f7bdfe3 100644 --- a/backend/src/Api/Console/Resolver/EntityResolver.php +++ b/backend/src/Api/Console/Resolver/EntityResolver.php @@ -2,7 +2,6 @@ namespace App\Api\Console\Resolver; -use App\Api\Console\Authorization\AuthorizationListener; use App\Entity\ApiKey; use App\Entity\Approval; use App\Entity\Domain; @@ -37,10 +36,8 @@ class EntityResolver implements ValueResolverInterface public function __construct( private EntityManagerInterface $em, - private NewsletterResolver $newsletterResolver, - ) - { - } + private NewsletterResolver $newsletterResolver, + ) {} /** * @return iterable @@ -100,7 +97,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable false, false, null, - controllerName: $controllerName + controllerName: $controllerName, ); $currentNewsletter = (array)$this->newsletterResolver->resolve($request, $argumentMetadata); if ($newsletterOfEntity->getId() !== $currentNewsletter[0]->getId()) { diff --git a/backend/src/Api/Console/Resolver/NewsletterResolver.php b/backend/src/Api/Console/Resolver/NewsletterResolver.php index 070f30e89..1b4a92774 100644 --- a/backend/src/Api/Console/Resolver/NewsletterResolver.php +++ b/backend/src/Api/Console/Resolver/NewsletterResolver.php @@ -2,14 +2,13 @@ namespace App\Api\Console\Resolver; -use App\Api\Console\Authorization\AuthorizationListener; use App\Entity\Newsletter; -use App\Repository\NewsletterRepository; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleApiAuthorizationListenerAbstract; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResults; use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class NewsletterResolver implements ValueResolverInterface { @@ -32,10 +31,13 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable return []; } - if (!AuthorizationListener::hasNewsletter($request)) { + $consoleAuth = $request->attributes->get(ConsoleApiAuthorizationListenerAbstract::ATTRIBUTE_KEY); + $resource = $consoleAuth instanceof ConsoleAuthResults ? $consoleAuth->getResource() : null; + + if (!$resource instanceof Newsletter) { throw new BadRequestException('Missing X-Newsletter-Id header'); } - return [AuthorizationListener::getNewsletter($request)]; + return [$resource]; } } diff --git a/backend/src/Api/Public/Listener/RateLimiterListener.php b/backend/src/Api/Public/Listener/RateLimiterListener.php index 96e51cf69..718c6e1f5 100644 --- a/backend/src/Api/Public/Listener/RateLimiterListener.php +++ b/backend/src/Api/Public/Listener/RateLimiterListener.php @@ -2,6 +2,7 @@ namespace App\Api\Public\Listener; +use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -18,10 +19,9 @@ class RateLimiterListener private const string RATE_LIMIT_HEADERS_KEY = 'rate_limit_headers'; public function __construct( - private RateLimiterFactory $publicApiLimiter - ) - { - } + #[Autowire(service: 'limiter.public_api')] + private RateLimiterFactory $publicApiLimiter, + ) {} private function isPublicApiRequest(Request $request): bool { @@ -30,7 +30,6 @@ private function isPublicApiRequest(Request $request): bool public function onKernelRequest(RequestEvent $event): void { - $request = $event->getRequest(); if (!$this->isPublicApiRequest($request)) { @@ -52,11 +51,10 @@ public function onKernelRequest(RequestEvent $event): void $response = new Response( null, Response::HTTP_TOO_MANY_REQUESTS, - $headers + $headers, ); $event->setResponse($response); } - } public function onKernelResponse(ResponseEvent $event): void diff --git a/backend/src/Entity/Newsletter.php b/backend/src/Entity/Newsletter.php index fe5199ba1..6802ff8d3 100644 --- a/backend/src/Entity/Newsletter.php +++ b/backend/src/Entity/Newsletter.php @@ -25,11 +25,18 @@ class Newsletter private \DateTimeImmutable $updated_at; #[ORM\Column] - private int $user_id; + private ?string $created_by_source = null; #[ORM\Column] private int $organization_id; + /** + * legacy column, now newsletters are owned by organizations + * however, we still save the user ID if it was created by a user session + */ + #[ORM\Column] + private ?int $user_id = null; + #[ORM\Column(length: 255)] private string $name; @@ -102,11 +109,31 @@ public function setUpdatedAt(\DateTimeImmutable $updated_at): static return $this; } - public function getUserId(): int + public function getCreatedBySource(): ?string + { + return $this->created_by_source; + } + + public function setCreatedBySource(?string $created_by_source): static + { + $this->created_by_source = $created_by_source; + + return $this; + } + + public function getUserId(): ?int { return $this->user_id; } + + public function setUserId(?int $user_id): static + { + $this->user_id = $user_id; + + return $this; + } + public function getOrganizationId(): int { return $this->organization_id; @@ -131,13 +158,6 @@ public function getMeta(): NewsletterMeta return $this->meta; } - public function setUserId(int $user_id): static - { - $this->user_id = $user_id; - - return $this; - } - public function getName(): string { return $this->name; diff --git a/backend/src/Entity/Type/UserRole.php b/backend/src/Entity/Type/UserRole.php index 282ee29c5..4d1ddc0f2 100644 --- a/backend/src/Entity/Type/UserRole.php +++ b/backend/src/Entity/Type/UserRole.php @@ -2,8 +2,21 @@ namespace App\Entity\Type; +use Hyvor\Internal\CloudApi\Scope\PostScope; + enum UserRole: string { case OWNER = 'owner'; case ADMIN = 'admin'; + + /** + * @return PostScope[] + */ + public function scopes(): array + { + return match ($this) { + self::OWNER => PostScope::cases(), + self::ADMIN => PostScope::cases(), + }; + } } diff --git a/backend/src/Service/ApiKey/ApiKeyService.php b/backend/src/Service/ApiKey/ApiKeyService.php index 2fbebc71a..f6d177e45 100644 --- a/backend/src/Service/ApiKey/ApiKeyService.php +++ b/backend/src/Service/ApiKey/ApiKeyService.php @@ -15,11 +15,11 @@ class ApiKeyService const int MAX_API_KEY_PER_NEWSLETTER = 10; + public const int API_KEY_LENGTH = 32; + public function __construct( private EntityManagerInterface $em, - ) - { - } + ) {} /** * @param string[] $scopes @@ -27,9 +27,10 @@ public function __construct( */ public function createApiKey(Newsletter $newsletter, string $name, array $scopes): array { - $key = bin2hex(random_bytes(16)); + $key = bin2hex(random_bytes(self::API_KEY_LENGTH / 2)); $apiKey = new ApiKey(); - $apiKey->setNewsletter($newsletter) + $apiKey + ->setNewsletter($newsletter) ->setName($name) ->setScopes($scopes) ->setKeyHashed(hash('sha256', $key)) diff --git a/backend/src/Service/Approval/ApprovalService.php b/backend/src/Service/Approval/ApprovalService.php index fc3c529f2..6eabf5e62 100644 --- a/backend/src/Service/Approval/ApprovalService.php +++ b/backend/src/Service/Approval/ApprovalService.php @@ -11,7 +11,6 @@ use Doctrine\ORM\EntityManagerInterface; use Hyvor\Internal\Auth\AuthInterface; use Hyvor\Internal\Auth\AuthUser; -use Hyvor\Internal\Auth\AuthUserOrganization; use Hyvor\Internal\Internationalization\StringsFactory; use Symfony\Component\Clock\ClockAwareTrait; use Symfony\Component\HttpKernel\Exception\HttpException; @@ -23,22 +22,24 @@ class ApprovalService use ClockAwareTrait; public function __construct( - private EntityManagerInterface $em, - private AuthInterface $auth, - private readonly Environment $mailTemplate, + private EntityManagerInterface $em, + private AuthInterface $auth, + private readonly Environment $mailTemplate, private readonly StringsFactory $stringsFactory, private NotificationMailService $emailNotificationService, - private MessageBusInterface $messageBus, - private AppConfig $appConfig - ) - { - } + private MessageBusInterface $messageBus, + private AppConfig $appConfig, + ) {} /** * @return Approval[] */ - public function getApprovals(?int $userId = null, ?ApprovalStatus $status = null, int $limit = 50, int $offset = 0): array - { + public function getApprovals( + ?int $userId = null, + ?ApprovalStatus $status = null, + int $limit = 50, + int $offset = 0, + ): array { $criteria = []; if ($userId !== null) { @@ -48,53 +49,56 @@ public function getApprovals(?int $userId = null, ?ApprovalStatus $status = null $criteria['status'] = $status; } - return $this->em->getRepository(Approval::class) + return $this->em + ->getRepository(Approval::class) ->findBy( $criteria, ['id' => 'DESC'], $limit, - $offset + $offset, ); } public function getReviewingApprovalsCount(): int { - return $this->em->getRepository(Approval::class) + return $this->em + ->getRepository(Approval::class) ->count(['status' => ApprovalStatus::REVIEWING]); } public function getApporvalById(int $id): ?Approval { - return $this->em->getRepository(Approval::class) + return $this->em + ->getRepository(Approval::class) ->findOneBy(['id' => $id]); } - public function getApprovalOfOrganization(AuthUserOrganization $organization): ?Approval + public function getApprovalOfOrganization(int $orgId): ?Approval { - return $this->em->getRepository(Approval::class) - ->findOneBy(['organization_id' => $organization->id]); + return $this->em + ->getRepository(Approval::class) + ->findOneBy(['organization_id' => $orgId]); } - public function getApprovalStatusOfOrganization(AuthUserOrganization $organization): ApprovalStatus + public function getApprovalStatusOfOrganization(int $organizationId): ApprovalStatus { - $approval = $this->getApprovalOfOrganization($organization); + $approval = $this->getApprovalOfOrganization($organizationId); return $approval === null ? ApprovalStatus::PENDING : $approval->getStatus(); } public function createApproval( - int $userId, - int $organizationId, - string $companyName, - string $country, - string $website, + int $userId, + int $organizationId, + string $companyName, + string $country, + string $website, ?string $socialLinks, ?string $typeOfContent, ?string $frequency, ?string $existingList, ?string $sample, - ?string $whyPost - ): Approval - { + ?string $whyPost, + ): Approval { $otherInfo = []; if ($typeOfContent) { $otherInfo['type_of_content'] = $typeOfContent; @@ -113,7 +117,8 @@ public function createApproval( } $approval = new Approval(); - $approval->setUserId($userId) + $approval + ->setUserId($userId) ->setOrganizationId($organizationId) ->setStatus(ApprovalStatus::REVIEWING) ->setCompanyName($companyName) @@ -133,10 +138,9 @@ public function createApproval( } public function updateApproval( - Approval $approval, - UpdateApprovalDto $updates - ): Approval - { + Approval $approval, + UpdateApprovalDto $updates, + ): Approval { if ($updates->has('companyName')) { $approval->setCompanyName($updates->companyName); } @@ -210,12 +214,11 @@ public function updateApproval( } public function approvalSudoAction( - Approval $approval, + Approval $approval, ApprovalStatus $status, - ?string $public_note, - ?string $private_note - ): Approval - { + ?string $public_note, + ?string $private_note, + ): Approval { $approval->setStatus($status); $approval->setPublicNote($public_note); $approval->setPrivateNote($private_note); @@ -261,7 +264,6 @@ private function sendApprovalMail(Approval $approval, ApprovalStatus $status, Au $publicNote = $approval->getPublicNote(); if ($status === ApprovalStatus::APPROVED) { - $content['body'] = $strings->get('mail.approval.bodyApproved'); $content['buttonText'] = $strings->get('mail.approval.buttonText'); $renderContext['buttonUrl'] = $this->appConfig->getUrlApp() . '/console'; @@ -272,7 +274,6 @@ private function sendApprovalMail(Approval $approval, ApprovalStatus $status, Au } if ($status === ApprovalStatus::REJECTED) { - $content['body'] = $strings->get('mail.approval.bodyRejected'); if ($publicNote) { @@ -286,7 +287,7 @@ private function sendApprovalMail(Approval $approval, ApprovalStatus $status, Au $this->emailNotificationService->send( $user->email, $subject, - $mail + $mail, ); } } diff --git a/backend/src/Service/Newsletter/NewsletterService.php b/backend/src/Service/Newsletter/NewsletterService.php index e04be75ac..00baa2663 100644 --- a/backend/src/Service/Newsletter/NewsletterService.php +++ b/backend/src/Service/Newsletter/NewsletterService.php @@ -33,10 +33,10 @@ class NewsletterService public function __construct( private EntityManagerInterface $em, - private AppConfig $config, - private SendingProfileService $sendingProfileService, - private CommsInterface $comms, - private AuthInterface $auth, + private AppConfig $config, + private SendingProfileService $sendingProfileService, + private CommsInterface $comms, + private AuthInterface $auth, ) {} @@ -44,60 +44,70 @@ public function __construct( * @param array $metadata */ public function createNewsletter( - int $userId, - int $organizationId, + int $organizationId, string $name, string $subdomain, - array $metadata = [] + string $createdBySource, + array $metadata = [], + ?int $userId = null, ): Newsletter { - return $this->em->wrapInTransaction(function () use ($userId, $organizationId, $name, $subdomain, $metadata) { - $newsletter = new Newsletter() - ->setName($name) - ->setUserId($userId) - ->setOrganizationId($organizationId) - ->setMeta(new NewsletterMeta()) - ->setSubdomain($subdomain) - ->setMetadata($metadata) - ->setCreatedAt($this->now()) - ->setUpdatedAt($this->now()); - - $user = new User() - ->setCreatedAt($this->now()) - ->setUpdatedAt($this->now()) - ->setHyvorUserId($userId) - ->setNewsletter($newsletter) - ->setRole(UserRole::OWNER); - - $list = new NewsletterList() - ->setName('Default List') - ->setCreatedAt($this->now()) - ->setUpdatedAt($this->now()) - ->setNewsletter($newsletter); - - $systemAddress = $this->sendingProfileService->getSystemAddressOfNewsletter($newsletter); - - $this->sendingProfileService - ->createSendingProfile( - $newsletter, - null, - fromEmail: $systemAddress, - fromName: $newsletter->getName(), - system: true, - flush: false + return $this->em->wrapInTransaction( + function () use ($userId, $organizationId, $name, $subdomain, $metadata, $createdBySource) { + $newsletter = new Newsletter() + ->setName($name) + ->setOrganizationId($organizationId) + ->setMeta(new NewsletterMeta()) + ->setSubdomain($subdomain) + ->setMetadata($metadata) + ->setCreatedAt($this->now()) + ->setUpdatedAt($this->now()) + ->setUserId($userId) + ->setCreatedBySource($createdBySource); + + // TODO: take user_id from outside + // TODO: remove owner + if ($userId) { + $user = new User() + ->setCreatedAt($this->now()) + ->setUpdatedAt($this->now()) + ->setHyvorUserId($userId) + ->setNewsletter($newsletter) + ->setRole(UserRole::OWNER); + $this->em->persist($user); + } + + $list = new NewsletterList() + ->setName('Default List') + ->setCreatedAt($this->now()) + ->setUpdatedAt($this->now()) + ->setNewsletter($newsletter); + + $systemAddress = $this->sendingProfileService->getSystemAddressOfNewsletter($newsletter); + + $this->sendingProfileService + ->createSendingProfile( + $newsletter, + null, + fromEmail: $systemAddress, + fromName: $newsletter->getName(), + system: true, + flush: false, + ); + + $this->em->persist($newsletter); + $this->em->persist($list); + $this->em->flush(); + + $this->comms->send( + new ResourceCreated( + Component::POST, + $organizationId, + ), ); - $this->em->persist($user); - $this->em->persist($newsletter); - $this->em->persist($list); - $this->em->flush(); - - $this->comms->send(new ResourceCreated( - Component::POST, - $organizationId - )); - - return $newsletter; - }); + return $newsletter; + }, + ); } public function deleteNewsletter(Newsletter $newsletter): void @@ -109,9 +119,15 @@ public function deleteNewsletter(Newsletter $newsletter): void /** * @return Newsletter[] */ - public function getNewsletters(?string $name, ?int $organizationId, int $limit, int $offset, string $sort = 'id_desc'): array - { - $qb = $this->em->getRepository(Newsletter::class)->createQueryBuilder('n') + public function getNewsletters( + ?string $name, + ?int $organizationId, + int $limit, + int $offset, + string $sort = 'id_desc', + ): array { + $qb = $this->em + ->getRepository(Newsletter::class)->createQueryBuilder('n') ->setMaxResults($limit) ->setFirstResult($offset); @@ -131,12 +147,14 @@ public function getNewsletters(?string $name, ?int $organizationId, int $limit, }; if ($name) { - $qb->andWhere('LOWER(n.name) LIKE LOWER(:q) OR LOWER(n.subdomain) LIKE LOWER(:q)') + $qb + ->andWhere('LOWER(n.name) LIKE LOWER(:q) OR LOWER(n.subdomain) LIKE LOWER(:q)') ->setParameter('q', '%' . $name . '%'); } if ($organizationId !== null) { - $qb->andWhere('n.organization_id = :organizationId') + $qb + ->andWhere('n.organization_id = :organizationId') ->setParameter('organizationId', $organizationId); } @@ -155,7 +173,8 @@ public function getNewslettersBatchStats(array $newsletterIds): array } /** @var list $issueCounts */ - $issueCounts = $this->em->getRepository(Issue::class)->createQueryBuilder('i') + $issueCounts = $this->em + ->getRepository(Issue::class)->createQueryBuilder('i') ->select('IDENTITY(i.newsletter) as newsletter_id, COUNT(i.id) as cnt') ->where('i.newsletter IN (:ids)') ->setParameter('ids', $newsletterIds) @@ -163,7 +182,8 @@ public function getNewslettersBatchStats(array $newsletterIds): array ->getQuery()->getArrayResult(); /** @var list $subscriberCounts */ - $subscriberCounts = $this->em->getRepository(Subscriber::class)->createQueryBuilder('s') + $subscriberCounts = $this->em + ->getRepository(Subscriber::class)->createQueryBuilder('s') ->select('IDENTITY(s.newsletter) as newsletter_id, COUNT(s.id) as cnt') ->where('s.newsletter IN (:ids)') ->setParameter('ids', $newsletterIds) @@ -218,7 +238,7 @@ public function getUserNewslettersOfOrganization(int $userId, int $organizationI JOIN u.newsletter p WHERE p.organization_id = :organization_id AND u.hyvor_user_id = :user_id - DQL; + DQL; $query = $this->em->createQuery($query); $query->setParameter('organization_id', $organizationId); @@ -255,8 +275,8 @@ public function getNewsletterUser(Newsletter $newsletter, int $userId): User */ public function getNewsletterStats(Newsletter $newsletter): array { - - $subscribersQuery = $this->em->getRepository(Subscriber::class)->createQueryBuilder('s') + $subscribersQuery = $this->em + ->getRepository(Subscriber::class)->createQueryBuilder('s') ->select('count(s.id)') ->where('s.newsletter = :newsletter') ->andWhere('s.status = :status') @@ -264,12 +284,14 @@ public function getNewsletterStats(Newsletter $newsletter): array ->setParameter('status', SubscriberStatus::SUBSCRIBED); $subscribers = (int)$subscribersQuery->getQuery()->getSingleScalarResult(); - $subscribersLast30d = (int)$subscribersQuery->andWhere('s.subscribed_at > :date') + $subscribersLast30d = (int)$subscribersQuery + ->andWhere('s.subscribed_at > :date') ->setParameter('date', new \DateTimeImmutable()->sub(new \DateInterval('P30D'))) ->getQuery() ->getSingleScalarResult(); - $issuesQuery = $this->em->getRepository(Issue::class)->createQueryBuilder('i') + $issuesQuery = $this->em + ->getRepository(Issue::class)->createQueryBuilder('i') ->select('count(i.id)') ->where('i.newsletter = :newsletter') ->andWhere('i.status = :status') @@ -277,13 +299,15 @@ public function getNewsletterStats(Newsletter $newsletter): array ->setParameter('status', IssueStatus::SENT); $issues = (int)$issuesQuery->getQuery()->getSingleScalarResult(); - $issuesLast30d = (int)$issuesQuery->andWhere('i.sent_at > :date') + $issuesLast30d = (int)$issuesQuery + ->andWhere('i.sent_at > :date') ->setParameter('date', (new \DateTimeImmutable())->sub(new \DateInterval('P30D'))) ->getQuery() ->getSingleScalarResult(); // bounced rate and complained rate - $bouncedAndComplainedRateQuery = $this->em->getRepository(Send::class)->createQueryBuilder('s') + $bouncedAndComplainedRateQuery = $this->em + ->getRepository(Send::class)->createQueryBuilder('s') ->select('COUNT(s.id) as totalSends') ->addSelect('SUM(CASE WHEN s.bounced_at IS NOT NULL THEN 1 ELSE 0 END) as bouncedSends') ->addSelect('SUM(CASE WHEN s.complained_at IS NOT NULL THEN 1 ELSE 0 END) as complainedSends') @@ -301,24 +325,33 @@ public function getNewsletterStats(Newsletter $newsletter): array $complainedRate = $totalSends > 0 ? round(($complainedSends / $totalSends) * 100, 2) : 0.0; /** @var array $bouncedAndComplainedRateLast30dValues */ - $bouncedAndComplainedRateLast30dValues = $bouncedAndComplainedRateQuery->andWhere('s.sent_at > :date') + $bouncedAndComplainedRateLast30dValues = $bouncedAndComplainedRateQuery + ->andWhere('s.sent_at > :date') ->setParameter('date', (new \DateTimeImmutable())->sub(new \DateInterval('P30D'))) ->getQuery() ->getSingleResult(); $totalSendsLast30d = (int)$bouncedAndComplainedRateLast30dValues['totalSends']; $bouncedSendsLast30d = (int)$bouncedAndComplainedRateLast30dValues['bouncedSends']; $complainedSendsLast30d = (int)$bouncedAndComplainedRateLast30dValues['complainedSends']; - $bouncedRateLast30d = $totalSendsLast30d > 0 ? round(($bouncedSendsLast30d / $totalSendsLast30d) * 100, 2) : 0.0; - $complainedRateLast30d = $totalSendsLast30d > 0 ? round(($complainedSendsLast30d / $totalSendsLast30d) * 100, 2) : 0.0; - - $listsCount = (int) $this->em->getRepository(NewsletterList::class)->createQueryBuilder('l') + $bouncedRateLast30d = $totalSendsLast30d > 0 ? round( + ($bouncedSendsLast30d / $totalSendsLast30d) * 100, + 2, + ) : 0.0; + $complainedRateLast30d = $totalSendsLast30d > 0 ? round( + ($complainedSendsLast30d / $totalSendsLast30d) * 100, + 2, + ) : 0.0; + + $listsCount = (int)$this->em + ->getRepository(NewsletterList::class)->createQueryBuilder('l') ->select('COUNT(l.id)') ->where('l.newsletter = :newsletter') ->setParameter('newsletter', $newsletter) ->getQuery() ->getSingleScalarResult(); - $sendingProfilesCount = (int) $this->em->getRepository(SendingProfile::class)->createQueryBuilder('sp') + $sendingProfilesCount = (int)$this->em + ->getRepository(SendingProfile::class)->createQueryBuilder('sp') ->select('COUNT(sp.id)') ->where('sp.newsletter = :newsletter') ->setParameter('newsletter', $newsletter) diff --git a/backend/symfony.lock b/backend/symfony.lock index 16134c87c..0a3d74202 100644 --- a/backend/symfony.lock +++ b/backend/symfony.lock @@ -68,6 +68,18 @@ "config/packages/nelmio_cors.yaml" ] }, + "nyholm/psr7": { + "version": "1.8", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.0", + "ref": "4a8c0345442dcca1d8a2c65633dcf0285dd5a5a2" + }, + "files": [ + "config/packages/nyholm_psr7.yaml" + ] + }, "phpstan/phpstan": { "version": "2.1", "recipe": { diff --git a/backend/tests/Api/Console/AuthorizationTest.php b/backend/tests/Api/Console/AuthorizationTest.php index c16b08bea..0a687f62f 100644 --- a/backend/tests/Api/Console/AuthorizationTest.php +++ b/backend/tests/Api/Console/AuthorizationTest.php @@ -2,9 +2,9 @@ namespace App\Tests\Api\Console; -use App\Api\Console\Authorization\AuthorizationListener; +use Hyvor\Internal\Auth\Oidc\Testing\OidcTestingUtils; use Hyvor\Internal\CloudApi\Scope\PostScope; -use App\Api\Console\Authorization\ScopeRequired; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ScopeRequired; use App\Entity\ApiKey; use App\Entity\Newsletter; use App\Tests\Case\WebTestCase; @@ -17,14 +17,13 @@ use Hyvor\Internal\Billing\License\PostLicense; use Hyvor\Internal\Billing\License\Resolved\ResolvedLicense; use Hyvor\Internal\Billing\License\Resolved\ResolvedLicenseType; -use Hyvor\Internal\Bundle\Comms\Event\ToCore\License\GetLicensesResponse; +use Hyvor\Internal\Component\Component; use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\BrowserKit\Cookie; -use Symfony\Component\Clock\Clock; -use Symfony\Component\Clock\MockClock; use Symfony\Component\Clock\Test\ClockSensitiveTrait; +use Hyvor\Internal\CloudApi\CloudApiService; +use Hyvor\Internal\CloudApi\Scope\ScopeBuilder; -#[CoversClass(AuthorizationListener::class)] #[CoversClass(ScopeRequired::class)] class AuthorizationTest extends WebTestCase { @@ -387,4 +386,34 @@ public function test_when_no_organization_is_required(): void $this->assertArrayHasKey('newsletters', $json); $this->assertArrayHasKey('config', $json); } + + // cloud JWT + + public function test_works_with_cloud_jwt(): void + { + $orgId = 15; + + $cloudApiService = $this->getService(CloudApiService::class); + $key = OidcTestingUtils::generateKey(); + + $scopeBuilder = new ScopeBuilder(); + $scopeBuilder->addScopes(Component::POST, PostScope::ISSUES_READ); + + $jwt = $cloudApiService->createJwtToken( + $key, + $orgId, + $scopeBuilder, + ); + + $this->client->request( + "GET", + "/api/console/issues", + server: [ + "HTTP_X_ORGANIZATION_ID" => $orgId, + "HTTP_X_NEWSLETTER_ID" => $newsletter->getId(), + ], + ); + $this->assertResponseStatusCodeSame(403); + } + } diff --git a/backend/tests/Api/Console/Newsletter/CreateNewsletterTest.php b/backend/tests/Api/Console/Newsletter/CreateNewsletterTest.php index ecb6c432e..ec796ef5f 100644 --- a/backend/tests/Api/Console/Newsletter/CreateNewsletterTest.php +++ b/backend/tests/Api/Console/Newsletter/CreateNewsletterTest.php @@ -43,9 +43,9 @@ public function test_subdomain_validation(string $subdomain): void '/newsletter', [ 'name' => 'Valid Newsletter Name', - 'subdomain' => $subdomain + 'subdomain' => $subdomain, ], - useSession: true + useSession: true, ); $this->assertResponseStatusCodeSame(422); @@ -63,12 +63,12 @@ public function testCreateNewsletterValid(): void $response = $this->consoleApi( null, 'POST', - '/newsletter', + '/newsletters', [ 'name' => 'Valid Newsletter Name', - 'subdomain' => 'valid-newsletter-subdomain' + 'subdomain' => 'valid-newsletter-subdomain', ], - useSession: true + useSession: true, ); $this->assertSame(200, $response->getStatusCode()); @@ -104,7 +104,7 @@ public function testCreateNewsletterValid(): void Component::CORE, eventValidator: function (ResourceCreated $event) use ($newsletter) { $this->assertSame($newsletter->getOrganizationId(), $event->getOrganizationId()); - } + }, ); } @@ -117,9 +117,9 @@ public function testCreateNewsletterInvalid(): void '/newsletter', [ 'name' => $long_string, - 'subdomain' => 'valid-newsletter-subdomain' + 'subdomain' => 'valid-newsletter-subdomain', ], - useSession: true + useSession: true, ); $this->assertSame(422, $response->getStatusCode()); @@ -136,7 +136,6 @@ public function testCreateNewsletterInvalid(): void #[TestWith(['other', true])] public function test_subdomain_taken(string $subdomain, bool $create = false): void { - if ($create) { NewsletterFactory::createOne(['subdomain' => $subdomain]); } @@ -147,14 +146,13 @@ public function test_subdomain_taken(string $subdomain, bool $create = false): v '/newsletter', [ 'name' => 'Valid Newsletter Name', - 'subdomain' => $subdomain + 'subdomain' => $subdomain, ], - useSession: true + useSession: true, ); $this->assertResponseStatusCodeSame(422); $this->assertSame('Subdomain is already taken.', $this->getJson()['message']); - } public function test_autogenerate_subdomain_on_duplicate(): void @@ -170,7 +168,7 @@ public function test_autogenerate_subdomain_on_duplicate(): void 'subdomain' => 'taken-subdomain', 'autogenerate_subdomain_on_duplicate' => true, ], - useSession: true + useSession: true, ); $this->assertSame(200, $response->getStatusCode()); @@ -202,7 +200,7 @@ public function test_create_newsletter_with_metadata(): void 'source' => 'onboarding', ], ], - useSession: true + useSession: true, ); $this->assertSame(200, $response->getStatusCode()); diff --git a/backend/tests/Api/Console/Resolver/EntityResolverTest.php b/backend/tests/Api/Console/Resolver/EntityResolverTest.php index f33f18a03..6c43c3f22 100644 --- a/backend/tests/Api/Console/Resolver/EntityResolverTest.php +++ b/backend/tests/Api/Console/Resolver/EntityResolverTest.php @@ -2,13 +2,16 @@ namespace App\Tests\Api\Console\Resolver; -use App\Api\Console\Authorization\AuthorizationListener; use App\Api\Console\Resolver\EntityResolver; use App\Api\Console\Resolver\NewsletterResolver; +use App\Entity\Newsletter; use App\Entity\NewsletterList; use App\Tests\Case\KernelTestCase; use App\Tests\Factory\NewsletterListFactory; use App\Tests\Factory\NewsletterFactory; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\AccessType; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleApiAuthorizationListenerAbstract; +use Hyvor\Internal\CloudApi\ConsoleApiAuth\ConsoleAuthResults; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestWith; use Symfony\Component\HttpFoundation\Request; @@ -20,6 +23,16 @@ class EntityResolverTest extends KernelTestCase { + private function consoleAuthResultsFor(Newsletter $newsletter): ConsoleAuthResults + { + return new ConsoleAuthResults( + accessType: AccessType::PRODUCT_API_KEY, + organizationId: $newsletter->getOrganizationId(), + resource: $newsletter, + productApiKey: new \stdClass(), + ); + } + public function testDoesNotResolveClassesOutsideConsoleApiControllers(): void { /** @var EntityResolver $resolver */ @@ -28,7 +41,7 @@ public function testDoesNotResolveClassesOutsideConsoleApiControllers(): void $request = new Request(); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\SomeOther\Controller\NewsletterController::getNewsletters' + 'App\Api\SomeOther\Controller\NewsletterController::getNewsletters', ); $output = $resolver->resolve($request, $argument); @@ -45,7 +58,7 @@ public function testDoesNotResolveNonEntityAndNewsletterArguments(string $class) $request = new Request(); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterController::getNewsletters' + 'App\Api\Console\Controller\NewsletterController::getNewsletters', ); $argument->method('getType')->willReturn($class); @@ -62,7 +75,7 @@ public function testDoesNotResolveInvalidId(): void $request->attributes->set('id', 'invalid'); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); @@ -79,7 +92,7 @@ public function testDoesNotResolveInvalidResource(): void $request->attributes->set('id', '1'); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); @@ -94,11 +107,11 @@ public function testDoesNotResolveEntityForPath(): void $request = new Request( attributes: ['id' => '1'], - server: ['REQUEST_URI' => '/api/console/invalid'] + server: ['REQUEST_URI' => '/api/console/invalid'], ); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); @@ -117,10 +130,13 @@ public function testResolvesEntityForPath(): void $request = new Request(); $request->attributes->set('id', (string)$newsletterList->getId()); $request->server->set('REQUEST_URI', '/api/console/lists'); - $request->attributes->set(AuthorizationListener::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY, $newsletter); + $request->attributes->set( + ConsoleApiAuthorizationListenerAbstract::ATTRIBUTE_KEY, + $this->consoleAuthResultsFor($newsletter), + ); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); @@ -145,7 +161,7 @@ public function testDoesNotResolveEntityForPathWhenEntityNotFound(): void $request->headers->set('X-Newsletter-Id', (string)$newsletter->getId()); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); @@ -167,10 +183,13 @@ public function testDoesNotResolveEntityForPathWhenDoesNotBelongToNewsletter(): $request = new Request(); $request->attributes->set('id', (string)$newsletterList->getId()); $request->server->set('REQUEST_URI', '/api/console/lists'); - $request->attributes->set(AuthorizationListener::RESOLVED_NEWSLETTER_ATTRIBUTE_KEY, NewsletterFactory::createOne()); + $request->attributes->set( + ConsoleApiAuthorizationListenerAbstract::ATTRIBUTE_KEY, + $this->consoleAuthResultsFor(NewsletterFactory::createOne()), + ); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); diff --git a/backend/tests/Api/Console/Resolver/NewsletterResolverTest.php b/backend/tests/Api/Console/Resolver/NewsletterResolverTest.php index 7961ad788..ddf61fc0c 100644 --- a/backend/tests/Api/Console/Resolver/NewsletterResolverTest.php +++ b/backend/tests/Api/Console/Resolver/NewsletterResolverTest.php @@ -2,7 +2,6 @@ namespace App\Tests\Api\Console\Resolver; -use App\Api\Console\Authorization\AuthorizationListener; use App\Api\Console\Resolver\EntityResolver; use App\Api\Console\Resolver\NewsletterResolver; use App\Tests\Case\KernelTestCase; @@ -24,7 +23,7 @@ public function testDoesNotResolveClassesOutsideConsoleApiControllers(): void $request = new Request(); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\SomeOther\Controller\NewsletterController::getNewsletters' + 'App\Api\SomeOther\Controller\NewsletterController::getNewsletters', ); $output = $resolver->resolve($request, $argument); @@ -44,7 +43,7 @@ public function testDoesNotResolveWhenMissingHeader(): void $request->server->set('REQUEST_URI', '/api/console/lists'); $argument = $this->createMock(ArgumentMetadata::class); $argument->method('getControllerName')->willReturn( - 'App\Api\Console\Controller\NewsletterListController::getLists' + 'App\Api\Console\Controller\NewsletterListController::getLists', ); $argument->method('getType')->willReturn('App\Entity\NewsletterList'); diff --git a/compose.yaml b/compose.yaml index e365cd3ec..c8cd1c7ab 100644 --- a/compose.yaml +++ b/compose.yaml @@ -31,7 +31,7 @@ services: target: backend-dev volumes: - ./backend:/app/backend - # - ../internal:/app/backend/vendor/hyvor/internal:ro + #- ../internal:/app/backend/vendor/hyvor/internal:ro - ./shared:/app/shared labels: traefik.enable: true @@ -43,7 +43,9 @@ services: # service traefik.http.services.hyvor-post-backend.loadbalancer.server.port: 80 networks: - - hyvor-network + hyvor-network: + aliases: + - post.hyvor.internal healthcheck: test: ls disable: true diff --git a/frontend/src/routes/console/+layout.svelte b/frontend/src/routes/console/+layout.svelte index ce59aa77d..fb35304c5 100644 --- a/frontend/src/routes/console/+layout.svelte +++ b/frontend/src/routes/console/+layout.svelte @@ -5,6 +5,7 @@ Loader, toast } from '@hyvor/design/components'; + import { isEmbedded } from './lib/embedded'; import { CloudContext, type CloudContextOrganization, @@ -46,6 +47,11 @@ let isLoading = $state(true); function startConsole(switchingOrg = false) { + if (page.url.searchParams.has('embedded')) { + $isEmbedded = true; + document.body.style.backgroundColor = 'transparent'; + } + consoleApi .get({ userApi: true, @@ -105,7 +111,7 @@ } ]} > -
+
{#if isLoading}
@@ -135,7 +141,10 @@ }} style="display:flex; flex-direction: column; width: 100%; height: 100vh" > - + + {#if !$isEmbedded} + + {/if} {@render children?.()} {/if} @@ -159,4 +168,17 @@ justify-content: center; align-items: center; } + + main.embedded :global(#nav-wrap) { + height: 100%; + } + main.embedded :global(#nav-wrap > .newsletter-nav) { + flex: 1; + } + main.embedded :global(#nav-wrap > .newsletter-nav > .wrap) { + height: 100%; + } + main.embedded :global(#nav-wrap > .newsletter-nav > .wrap) { + height: 100%; + } diff --git a/frontend/src/routes/console/@components/Nav/Nav.svelte b/frontend/src/routes/console/@components/Nav/Nav.svelte index 974887af7..b36a91484 100644 --- a/frontend/src/routes/console/@components/Nav/Nav.svelte +++ b/frontend/src/routes/console/@components/Nav/Nav.svelte @@ -2,22 +2,27 @@ import { DarkToggle, LanguageToggle } from '@hyvor/design/components'; import AccountNav from './AccountNav.svelte'; import NewsletterNav from './NewsletterNav.svelte'; + import { isEmbedded } from '../../lib/embedded';