diff --git a/Dockerfile b/Dockerfile index 9904a2f..26ea565 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,6 +37,7 @@ RUN set -eux; \ gmp \ gd \ redis \ + pcntl \ ; # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser @@ -100,5 +101,4 @@ RUN set -eux; \ composer dump-autoload --classmap-authoritative --no-dev; \ composer dump-env prod; \ composer run-script --no-dev post-install-cmd; \ - chmod +x bin/console; sync; \ - docker exec newsroom-php-1 php bin/console asset-map:compile; + chmod +x bin/console; sync; diff --git a/assets/controllers/comments_mercure_controller.js b/assets/controllers/comments_mercure_controller.js new file mode 100644 index 0000000..0b2cb3b --- /dev/null +++ b/assets/controllers/comments_mercure_controller.js @@ -0,0 +1,124 @@ +import { Controller } from "@hotwired/stimulus"; + +// Connects to data-controller="comments-mercure" +export default class extends Controller { + static values = { + coordinate: String + } + static targets = ["list", "loading"]; + + connect() { + const coordinate = this.coordinateValue; + const topic = `/comments/${coordinate}`; + const hubUrl = window.MercureHubUrl || (document.querySelector('meta[name="mercure-hub"]')?.content); + console.log('[comments-mercure] connect', { coordinate, topic, hubUrl }); + if (!hubUrl) return; + const url = new URL(hubUrl); + url.searchParams.append('topic', topic); + this.eventSource = new EventSource(url.toString()); + this.eventSource.onopen = () => { + console.log('[comments-mercure] EventSource opened', url.toString()); + }; + this.eventSource.onerror = (e) => { + console.error('[comments-mercure] EventSource error', e); + }; + this.eventSource.onmessage = (event) => { + console.log('[comments-mercure] Event received', event.data); + const data = JSON.parse(event.data); + this.profiles = data.profiles || {}; + if (this.hasLoadingTarget) this.loadingTarget.style.display = 'none'; + if (this.hasListTarget) { + if (data.comments && data.comments.length > 0) { + this.listTarget.innerHTML = data.comments.map((item) => { + const zapData = this.parseZapAmount(item) || {}; + const zapAmount = zapData.amount; + const zapperPubkey = zapData.zapper; + const parsedContent = this.parseContent(item.content); + const isZap = item.kind === 9735; + const displayPubkey = isZap ? (zapperPubkey || item.pubkey) : item.pubkey; + const profile = this.profiles[displayPubkey]; + const displayName = profile?.name || displayPubkey; + return `
+
+ ${displayName} + ${item.created_at ? new Date(item.created_at * 1000).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) : ''} +
+
+ ${isZap ? `
${zapAmount ? `${zapAmount} sat` : 'Zap'}
` : ''} +
${parsedContent}
+
+
`; + }).join(''); + } else { + this.listTarget.innerHTML = '
No comments yet.
'; + } + this.listTarget.style.display = ''; + } + }; + } + + disconnect() { + if (this.eventSource) { + this.eventSource.close(); + console.log('[comments-mercure] EventSource closed'); + } + } + + parseContent(content) { + if (!content) return ''; + + // Escape HTML to prevent XSS + let html = content.replace(/&/g, '&').replace(//g, '>'); + + // Parse URLs + html = html.replace(/(https?:\/\/[^\s]+)/g, '$1'); + + // Parse Nostr npub + html = html.replace(/\b(npub1[a-z0-9]+)\b/g, '$1'); + + // Parse Nostr nevent + html = html.replace(/\b(nevent1[a-z0-9]+)\b/g, '$1'); + + // Parse Nostr nprofile + html = html.replace(/\b(nprofile1[a-z0-9]+)\b/g, '$1'); + + // Parse Nostr note + html = html.replace(/\b(note1[a-z0-9]+)\b/g, '$1'); + + return html; + } + + parseZapAmount(item) { + if (item.kind !== 9735) return null; + + const tags = item.tags || []; + let amount = null; + let zapper = null; + + // Find zapper from 'p' tag + const pTag = tags.find(tag => tag[0] === 'p'); + if (pTag && pTag[1]) { + zapper = pTag[1]; + } + + // Find amount in 'amount' tag (msat) + const amountTag = tags.find(tag => tag[0] === 'amount'); + if (amountTag && amountTag[1]) { + const msat = parseInt(amountTag[1], 10); + amount = Math.floor(msat / 1000); // Convert to sat + } + + // Fallback to description for content + const descTag = tags.find(tag => tag[0] === 'description'); + if (descTag && descTag[1]) { + try { + const desc = JSON.parse(descTag[1]); + if (desc.content) { + item.content = desc.content; // Update content + } + } catch (e) {} + } + + return { amount, zapper }; + } +} diff --git a/compose.yaml b/compose.yaml index 688c8b3..fd5caf8 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,16 +5,17 @@ services: context: . dockerfile: Dockerfile environment: - APP_ENV: ${APP_ENV:-dev} - SERVER_NAME: ${SERVER_NAME:-localhost}, php:80 - MERCURE_PUBLISHER_JWT_KEY: ${MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} - MERCURE_SUBSCRIBER_JWT_KEY: ${MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} - # Run "composer require symfony/orm-pack" to install and configure Doctrine ORM - DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-15}&charset=${POSTGRES_CHARSET:-utf8} - # Run "composer require symfony/mercure-bundle" to install and configure the Mercure integration - MERCURE_URL: ${MERCURE_URL:-http://php/.well-known/mercure} - MERCURE_PUBLIC_URL: ${MERCURE_PUBLIC_URL:-https://${SERVER_NAME:-localhost}/.well-known/mercure} - MERCURE_JWT_SECRET: ${MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} + APP_ENV: ${APP_ENV:-dev} + SERVER_NAME: ${SERVER_NAME:-localhost}, php:80 + MERCURE_PUBLISHER_JWT_ALG: ${MERCURE_PUBLISHER_JWT_ALG:-HS256} + MERCURE_SUBSCRIBER_JWT_ALG: ${MERCURE_SUBSCRIBER_JWT_ALG:-HS256} + # Removed overrides for JWT keys to use values from .env + # Run "composer require symfony/orm-pack" to install and configure Doctrine ORM + DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-17}&charset=${POSTGRES_CHARSET:-utf8} + # Run "composer require symfony/mercure-bundle" to install and configure the Mercure integration + MERCURE_URL: ${MERCURE_URL:-http://php/.well-known/mercure} + MERCURE_PUBLIC_URL: ${MERCURE_PUBLIC_URL:-https://${SERVER_NAME:-localhost}/.well-known/mercure} + MERCURE_JWT_SECRET: ${MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!} volumes: - caddy_data:/data - caddy_config:/config @@ -63,6 +64,30 @@ services: depends_on: - php + worker: + build: + context: . + dockerfile: Dockerfile + working_dir: /app + entrypoint: ["php"] # run PHP CLI, not Caddy/FrankenPHP + command: + - bin/console + - messenger:consume + - -vv + - --memory-limit=256M + - --keepalive + - "10" + - async + restart: unless-stopped + depends_on: + - php + - database + # IMPORTANT: ensure it does NOT bind ports or mount caddy volumes + ports: [] + expose: [] + volumes: + - .:/app + volumes: caddy_data: caddy_config: diff --git a/composer.json b/composer.json index 72820ea..694648a 100644 --- a/composer.json +++ b/composer.json @@ -27,32 +27,33 @@ "phpstan/phpdoc-parser": "^2.0", "runtime/frankenphp-symfony": "^0.2.0", "swentel/nostr-php": "^1.5", - "symfony/asset": "7.1.*", - "symfony/asset-mapper": "7.1.*", - "symfony/console": "7.1.*", - "symfony/dotenv": "7.1.*", - "symfony/expression-language": "7.1.*", + "symfony/asset": "7.2.*", + "symfony/asset-mapper": "7.2.*", + "symfony/console": "7.2.*", + "symfony/dotenv": "7.2.*", + "symfony/expression-language": "7.2.*", "symfony/flex": "^2", - "symfony/form": "7.1.*", - "symfony/framework-bundle": "7.1.*", - "symfony/html-sanitizer": "7.1.*", - "symfony/http-foundation": "7.1.*", - "symfony/intl": "7.1.*", + "symfony/form": "7.2.*", + "symfony/framework-bundle": "7.2.*", + "symfony/html-sanitizer": "7.2.*", + "symfony/http-foundation": "7.2.*", + "symfony/intl": "7.2.*", "symfony/mercure-bundle": "^0.3.9", - "symfony/messenger": "7.1.*", - "symfony/mime": "7.1.*", - "symfony/property-access": "7.1.*", - "symfony/property-info": "7.1.*", - "symfony/runtime": "7.1.*", - "symfony/security-bundle": "7.1.*", - "symfony/serializer": "7.1.*", + "symfony/messenger": "7.2.*", + "symfony/mime": "7.2.*", + "symfony/property-access": "7.2.*", + "symfony/property-info": "7.2.*", + "symfony/redis-messenger": "7.2.*", + "symfony/runtime": "7.2.*", + "symfony/security-bundle": "7.2.*", + "symfony/serializer": "7.2.*", "symfony/stimulus-bundle": "^2.22", - "symfony/translation": "7.1.*", - "symfony/twig-bundle": "7.1.*", + "symfony/translation": "7.2.*", + "symfony/twig-bundle": "7.2.*", "symfony/ux-icons": "^2.22", "symfony/ux-live-component": "^2.21", - "symfony/workflow": "7.1.*", - "symfony/yaml": "7.1.*", + "symfony/workflow": "7.2.*", + "symfony/yaml": "7.2.*", "tkijewski/php-lnurl": "*", "twig/extra-bundle": "^2.12|^3.0", "twig/markdown-extra": "^3.21", @@ -107,7 +108,7 @@ "extra": { "symfony": { "allow-contrib": false, - "require": "7.1.*", + "require": "7.2.*", "docker": true }, "runtime": { @@ -116,11 +117,11 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "symfony/browser-kit": "7.1.*", - "symfony/css-selector": "7.1.*", + "symfony/browser-kit": "7.2.*", + "symfony/css-selector": "7.2.*", "symfony/maker-bundle": "^1.63", "symfony/phpunit-bridge": "^7.2", - "symfony/stopwatch": "7.1.*", - "symfony/web-profiler-bundle": "7.1.*" + "symfony/stopwatch": "7.2.*", + "symfony/web-profiler-bundle": "7.2.*" } } diff --git a/composer.lock b/composer.lock index 95c3b95..41e0efc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7d6a112c76c6a0d1cfe2b43f9408a2f8", + "content-hash": "e294409f4f3b8647d655053fddae6168", "packages": [ { "name": "bacon/bacon-qr-code", @@ -510,16 +510,16 @@ }, { "name": "doctrine/dbal", - "version": "4.3.3", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "231959669bb2173194c95636eae7f1b41b2a8b19" + "reference": "1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/231959669bb2173194c95636eae7f1b41b2a8b19", - "reference": "231959669bb2173194c95636eae7f1b41b2a8b19", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc", + "reference": "1a2fbd0e93b8dec7c3d1ac2b6396a7b929b130dc", "shasum": "" }, "require": { @@ -529,15 +529,15 @@ "psr/log": "^1|^2|^3" }, "require-dev": { - "doctrine/coding-standard": "13.0.1", + "doctrine/coding-standard": "14.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.2", - "phpstan/phpstan": "2.1.22", - "phpstan/phpstan-phpunit": "2.0.6", + "phpstan/phpstan": "2.1.30", + "phpstan/phpstan-phpunit": "2.0.7", "phpstan/phpstan-strict-rules": "^2", "phpunit/phpunit": "11.5.23", - "slevomat/coding-standard": "8.16.2", - "squizlabs/php_codesniffer": "3.13.1", + "slevomat/coding-standard": "8.24.0", + "squizlabs/php_codesniffer": "4.0.0", "symfony/cache": "^6.3.8|^7.0", "symfony/console": "^5.4|^6.3|^7.0" }, @@ -596,7 +596,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.3.3" + "source": "https://github.com/doctrine/dbal/tree/4.3.4" }, "funding": [ { @@ -612,7 +612,7 @@ "type": "tidelift" } ], - "time": "2025-09-04T23:52:42+00:00" + "time": "2025-10-09T09:11:36+00:00" }, { "name": "doctrine/deprecations", @@ -664,20 +664,21 @@ }, { "name": "doctrine/doctrine-bundle", - "version": "2.16.2", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "1c10de0fe995f01eca6b073d1c2549ef0b603a7f" + "reference": "cd5d4da6a5f7cf3d8708e17211234657b5eb4e95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/1c10de0fe995f01eca6b073d1c2549ef0b603a7f", - "reference": "1c10de0fe995f01eca6b073d1c2549ef0b603a7f", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/cd5d4da6a5f7cf3d8708e17211234657b5eb4e95", + "reference": "cd5d4da6a5f7cf3d8708e17211234657b5eb4e95", "shasum": "" }, "require": { "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/deprecations": "^1.0", "doctrine/persistence": "^3.1 || ^4", "doctrine/sql-formatter": "^1.0.1", "php": "^8.1", @@ -685,7 +686,6 @@ "symfony/config": "^6.4 || ^7.0", "symfony/console": "^6.4 || ^7.0", "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/deprecation-contracts": "^2.1 || ^3", "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3", "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/service-contracts": "^2.5 || ^3" @@ -700,14 +700,13 @@ "require-dev": { "doctrine/annotations": "^1 || ^2", "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^13", - "doctrine/deprecations": "^1.0", + "doctrine/coding-standard": "^14", "doctrine/orm": "^2.17 || ^3.1", "friendsofphp/proxy-manager-lts": "^1.0", "phpstan/phpstan": "2.1.1", "phpstan/phpstan-phpunit": "2.0.3", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.53", + "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", @@ -721,7 +720,7 @@ "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.13 || ^3.0.4" + "twig/twig": "^2.14.7 || ^3.0.4" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -766,7 +765,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.16.2" + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.0" }, "funding": [ { @@ -782,24 +781,24 @@ "type": "tidelift" } ], - "time": "2025-09-10T19:14:48+00:00" + "time": "2025-10-11T04:43:27+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.4.2", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9" + "reference": "71c81279ca0e907c3edc718418b93fd63074856c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/5a6ac7120c2924c4c070a869d08b11ccf9e277b9", - "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/71c81279ca0e907c3edc718418b93fd63074856c", + "reference": "71c81279ca0e907c3edc718418b93fd63074856c", "shasum": "" }, "require": { - "doctrine/doctrine-bundle": "^2.4", + "doctrine/doctrine-bundle": "^2.4 || ^3.0", "doctrine/migrations": "^3.2", "php": "^7.2 || ^8.0", "symfony/deprecation-contracts": "^2.1 || ^3", @@ -807,7 +806,7 @@ }, "require-dev": { "composer/semver": "^3.0", - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^12 || ^14", "doctrine/orm": "^2.6 || ^3", "phpstan/phpstan": "^1.4 || ^2", "phpstan/phpstan-deprecation-rules": "^1 || ^2", @@ -851,7 +850,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.4.2" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.5.0" }, "funding": [ { @@ -867,7 +866,7 @@ "type": "tidelift" } ], - "time": "2025-03-11T17:36:26+00:00" + "time": "2025-10-12T17:06:40+00:00" }, { "name": "doctrine/event-manager", @@ -2191,20 +2190,20 @@ }, { "name": "laminas/laminas-diactoros", - "version": "3.6.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f" + "reference": "60c182916b2749480895601649563970f3f12ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/b068eac123f21c0e592de41deeb7403b88e0a89f", - "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4", + "reference": "60c182916b2749480895601649563970f3f12ec4", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "psr/http-factory": "^1.1", "psr/http-message": "^1.1 || ^2.0" }, @@ -2221,11 +2220,11 @@ "ext-gd": "*", "ext-libxml": "*", "http-interop/http-factory-tests": "^2.2.0", - "laminas/laminas-coding-standard": "~3.0.0", + "laminas/laminas-coding-standard": "~3.1.0", "php-http/psr7-integration-tests": "^1.4.0", "phpunit/phpunit": "^10.5.36", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26.1" + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13" }, "type": "library", "extra": { @@ -2275,7 +2274,7 @@ "type": "community_bridge" } ], - "time": "2025-05-05T16:03:34+00:00" + "time": "2025-10-12T15:31:36+00:00" }, { "name": "lcobucci/jwt", @@ -3338,16 +3337,16 @@ }, { "name": "pagerfanta/pagerfanta", - "version": "v4.7.1", + "version": "v4.7.2", "source": { "type": "git", "url": "https://github.com/BabDev/Pagerfanta.git", - "reference": "b09216fc53665c4d8a39b7f60e421165cb4693e4" + "reference": "9320a542e1ed8d1a099a3d380685870e8bb5d553" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/BabDev/Pagerfanta/zipball/b09216fc53665c4d8a39b7f60e421165cb4693e4", - "reference": "b09216fc53665c4d8a39b7f60e421165cb4693e4", + "url": "https://api.github.com/repos/BabDev/Pagerfanta/zipball/9320a542e1ed8d1a099a3d380685870e8bb5d553", + "reference": "9320a542e1ed8d1a099a3d380685870e8bb5d553", "shasum": "" }, "require": { @@ -3377,7 +3376,7 @@ "pagerfanta/twig": "self.version" }, "require-dev": { - "dg/bypass-finals": "^1.5.1", + "dg/bypass-finals": "^1.9", "doctrine/collections": "^1.8 || ^2.0", "doctrine/dbal": "^3.5 || ^4.0", "doctrine/mongodb-odm": "^2.4", @@ -3385,10 +3384,10 @@ "doctrine/phpcr-odm": "^1.7 || ^2.0", "jackalope/jackalope-doctrine-dbal": "^1.9 || ^2.0", "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "2.0.3", - "phpstan/phpstan-phpunit": "2.0.1", - "phpunit/phpunit": "10.5.39", - "rector/rector": "2.0.3", + "phpstan/phpstan": "2.1.31", + "phpstan/phpstan-phpunit": "2.0.7", + "phpunit/phpunit": "10.5.58", + "rector/rector": "2.2.3", "ruflin/elastica": "^7.3 || ^8.0", "solarium/solarium": "^6.2", "symfony/cache": "^5.4 || ^6.3 || ^7.0", @@ -3427,7 +3426,7 @@ ], "support": { "issues": "https://github.com/BabDev/Pagerfanta/issues", - "source": "https://github.com/BabDev/Pagerfanta/tree/v4.7.1" + "source": "https://github.com/BabDev/Pagerfanta/tree/v4.7.2" }, "funding": [ { @@ -3435,7 +3434,7 @@ "type": "github" } ], - "time": "2024-12-13T15:12:11+00:00" + "time": "2025-10-12T17:35:33+00:00" }, { "name": "paragonie/ecc", @@ -5072,16 +5071,16 @@ }, { "name": "symfony/asset", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/asset.git", - "reference": "0dcd51490d7fc9fbf3c8f5aec6df182920fc0426" + "reference": "cb926cd59fefa1f9b4900b3695f0f846797ba5c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/0dcd51490d7fc9fbf3c8f5aec6df182920fc0426", - "reference": "0dcd51490d7fc9fbf3c8f5aec6df182920fc0426", + "url": "https://api.github.com/repos/symfony/asset/zipball/cb926cd59fefa1f9b4900b3695f0f846797ba5c0", + "reference": "cb926cd59fefa1f9b4900b3695f0f846797ba5c0", "shasum": "" }, "require": { @@ -5121,7 +5120,7 @@ "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset/tree/v7.1.6" + "source": "https://github.com/symfony/asset/tree/v7.2.0" }, "funding": [ { @@ -5137,20 +5136,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/asset-mapper", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/asset-mapper.git", - "reference": "f30a77372995afb0f9323903b52e8e6527d12451" + "reference": "037b6c86d99357785c7b377773f23589509f8315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/f30a77372995afb0f9323903b52e8e6527d12451", - "reference": "f30a77372995afb0f9323903b52e8e6527d12451", + "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/037b6c86d99357785c7b377773f23589509f8315", + "reference": "037b6c86d99357785c7b377773f23589509f8315", "shasum": "" }, "require": { @@ -5200,7 +5199,7 @@ "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset-mapper/tree/v7.1.11" + "source": "https://github.com/symfony/asset-mapper/tree/v7.2.9" }, "funding": [ { @@ -5211,25 +5210,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/cache", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "3828c0375578ff3ca1ddc54cc5c6fa4cc89fb3fb" + "reference": "e874746cea6893d4b91943eab8376a0fe7528de6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/3828c0375578ff3ca1ddc54cc5c6fa4cc89fb3fb", - "reference": "3828c0375578ff3ca1ddc54cc5c6fa4cc89fb3fb", + "url": "https://api.github.com/repos/symfony/cache/zipball/e874746cea6893d4b91943eab8376a0fe7528de6", + "reference": "e874746cea6893d4b91943eab8376a0fe7528de6", "shasum": "" }, "require": { @@ -5257,6 +5260,7 @@ "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/clock": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/filesystem": "^6.4|^7.0", @@ -5297,7 +5301,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.1.11" + "source": "https://github.com/symfony/cache/tree/v7.2.9" }, "funding": [ { @@ -5308,12 +5312,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-30T17:03:27+00:00" }, { "name": "symfony/cache-contracts", @@ -5393,16 +5401,16 @@ }, { "name": "symfony/clock", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "97bebc53548684c17ed696bc8af016880f0f098d" + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/97bebc53548684c17ed696bc8af016880f0f098d", - "reference": "97bebc53548684c17ed696bc8af016880f0f098d", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", "shasum": "" }, "require": { @@ -5447,7 +5455,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.6" + "source": "https://github.com/symfony/clock/tree/v7.2.0" }, "funding": [ { @@ -5463,20 +5471,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/config", - "version": "v7.1.7", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "dc373a5cbd345354696f5dfd39c5c7a8ea23f4c8" + "reference": "4cfed7041ac389c242122e60884d4a9fc5376cb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/dc373a5cbd345354696f5dfd39c5c7a8ea23f4c8", - "reference": "dc373a5cbd345354696f5dfd39c5c7a8ea23f4c8", + "url": "https://api.github.com/repos/symfony/config/zipball/4cfed7041ac389c242122e60884d4a9fc5376cb3", + "reference": "4cfed7041ac389c242122e60884d4a9fc5376cb3", "shasum": "" }, "require": { @@ -5522,7 +5530,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.1.7" + "source": "https://github.com/symfony/config/tree/v7.2.9" }, "funding": [ { @@ -5533,25 +5541,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-04T11:34:07+00:00" + "time": "2025-07-26T13:54:51+00:00" }, { "name": "symfony/console", - "version": "v7.1.10", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "bb06e2d7f8dd9dffe5eada8a5cbe0f68f1482db7" + "reference": "93518c2ff7ce9c1818224c6effed3cf2429c63d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/bb06e2d7f8dd9dffe5eada8a5cbe0f68f1482db7", - "reference": "bb06e2d7f8dd9dffe5eada8a5cbe0f68f1482db7", + "url": "https://api.github.com/repos/symfony/console/zipball/93518c2ff7ce9c1818224c6effed3cf2429c63d7", + "reference": "93518c2ff7ce9c1818224c6effed3cf2429c63d7", "shasum": "" }, "require": { @@ -5615,7 +5627,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.10" + "source": "https://github.com/symfony/console/tree/v7.2.9" }, "funding": [ { @@ -5626,25 +5638,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-09T07:30:10+00:00" + "time": "2025-07-30T17:03:27+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "5ebf7d4dfda126b442450effaec421a106c010de" + "reference": "fae0971f518e81fd1ccbfb6218aef410392ac701" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5ebf7d4dfda126b442450effaec421a106c010de", - "reference": "5ebf7d4dfda126b442450effaec421a106c010de", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/fae0971f518e81fd1ccbfb6218aef410392ac701", + "reference": "fae0971f518e81fd1ccbfb6218aef410392ac701", "shasum": "" }, "require": { @@ -5652,7 +5668,7 @@ "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.4.20|^7.2.5" }, "conflict": { "ext-psr": "<1.1|>=2", @@ -5695,7 +5711,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.1.11" + "source": "https://github.com/symfony/dependency-injection/tree/v7.2.9" }, "funding": [ { @@ -5706,12 +5722,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-10T09:29:52+00:00" + "time": "2025-07-30T17:31:35+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5782,16 +5802,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "b0247c25b71409c23c0cf3e090030950a86cc61b" + "reference": "ef8b21518615585e91ae7ab552265f04fcfa9dcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/b0247c25b71409c23c0cf3e090030950a86cc61b", - "reference": "b0247c25b71409c23c0cf3e090030950a86cc61b", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/ef8b21518615585e91ae7ab552265f04fcfa9dcf", + "reference": "ef8b21518615585e91ae7ab552265f04fcfa9dcf", "shasum": "" }, "require": { @@ -5804,6 +5824,7 @@ "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "doctrine/collections": "<1.8", "doctrine/dbal": "<3.6", "doctrine/lexer": "<1.1", "doctrine/orm": "<2.15", @@ -5820,7 +5841,7 @@ "symfony/validator": "<6.4" }, "require-dev": { - "doctrine/collections": "^1.0|^2.0", + "doctrine/collections": "^1.8|^2.0", "doctrine/data-fixtures": "^1.1|^2", "doctrine/dbal": "^3.6|^4", "doctrine/orm": "^2.15|^3", @@ -5839,7 +5860,7 @@ "symfony/security-core": "^6.4|^7.0", "symfony/stopwatch": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", - "symfony/type-info": "^7.1", + "symfony/type-info": "^7.1.8", "symfony/uid": "^6.4|^7.0", "symfony/validator": "^6.4|^7.0", "symfony/var-dumper": "^6.4|^7.0" @@ -5870,7 +5891,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.1.11" + "source": "https://github.com/symfony/doctrine-bridge/tree/v7.2.9" }, "funding": [ { @@ -5881,25 +5902,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-15T09:47:02+00:00" }, { "name": "symfony/dotenv", - "version": "v7.1.9", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "245d1afe223664d2276afb75177d8988c328fb78" + "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/245d1afe223664d2276afb75177d8988c328fb78", - "reference": "245d1afe223664d2276afb75177d8988c328fb78", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/2192790a11f9e22cbcf9dc705a3ff22a5503923a", + "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a", "shasum": "" }, "require": { @@ -5944,7 +5969,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.1.9" + "source": "https://github.com/symfony/dotenv/tree/v7.2.9" }, "funding": [ { @@ -5955,25 +5980,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-27T11:17:28+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/error-handler", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "f4d1fd1bcb4bce9983d034111b7ea3edc88e1a57" + "reference": "df98c90e43fa8267da4a64a88dd9427e0277773a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/f4d1fd1bcb4bce9983d034111b7ea3edc88e1a57", - "reference": "f4d1fd1bcb4bce9983d034111b7ea3edc88e1a57", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/df98c90e43fa8267da4a64a88dd9427e0277773a", + "reference": "df98c90e43fa8267da4a64a88dd9427e0277773a", "shasum": "" }, "require": { @@ -6019,7 +6048,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.1.11" + "source": "https://github.com/symfony/error-handler/tree/v7.2.9" }, "funding": [ { @@ -6030,25 +6059,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-07T09:23:14+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87254c78dd50721cfd015b62277a8281c5589702" + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", - "reference": "87254c78dd50721cfd015b62277a8281c5589702", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", "shasum": "" }, "require": { @@ -6099,7 +6132,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.1.6" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" }, "funding": [ { @@ -6115,7 +6148,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6195,16 +6228,16 @@ }, { "name": "symfony/expression-language", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "c3a1224bc144b36cd79149b42c1aecd5f81395a5" + "reference": "32d2d19c62e58767e6552166c32fb259975d2b23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/c3a1224bc144b36cd79149b42c1aecd5f81395a5", - "reference": "c3a1224bc144b36cd79149b42c1aecd5f81395a5", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/32d2d19c62e58767e6552166c32fb259975d2b23", + "reference": "32d2d19c62e58767e6552166c32fb259975d2b23", "shasum": "" }, "require": { @@ -6239,7 +6272,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.1.6" + "source": "https://github.com/symfony/expression-language/tree/v7.2.9" }, "funding": [ { @@ -6250,25 +6283,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-09T08:46:59+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/filesystem", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", - "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -6305,7 +6342,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.6" + "source": "https://github.com/symfony/filesystem/tree/v7.2.9" }, "funding": [ { @@ -6316,25 +6353,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.1.10", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "b8b526e051ac0b33feabbec7893adcab96b23bf3" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/b8b526e051ac0b33feabbec7893adcab96b23bf3", - "reference": "b8b526e051ac0b33feabbec7893adcab96b23bf3", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -6369,7 +6410,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.10" + "source": "https://github.com/symfony/finder/tree/v7.2.9" }, "funding": [ { @@ -6380,12 +6421,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T18:59:46+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/flex", @@ -6461,16 +6506,16 @@ }, { "name": "symfony/form", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "7a48dda96fe16711fc042df38ca1a7dd4d9d6387" + "reference": "72dfbaf14138f0c857659536dd65af85f36b546c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/7a48dda96fe16711fc042df38ca1a7dd4d9d6387", - "reference": "7a48dda96fe16711fc042df38ca1a7dd4d9d6387", + "url": "https://api.github.com/repos/symfony/form/zipball/72dfbaf14138f0c857659536dd65af85f36b546c", + "reference": "72dfbaf14138f0c857659536dd65af85f36b546c", "shasum": "" }, "require": { @@ -6538,7 +6583,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v7.1.6" + "source": "https://github.com/symfony/form/tree/v7.2.9" }, "funding": [ { @@ -6549,25 +6594,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-09T08:46:59+00:00" + "time": "2025-07-24T11:52:02+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "1eae7a4e095a2a3851cb41ac2aea9aa60fba1df8" + "reference": "42ac8bbbf10d3113c335ba8af39fad63db96e4e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/1eae7a4e095a2a3851cb41ac2aea9aa60fba1df8", - "reference": "1eae7a4e095a2a3851cb41ac2aea9aa60fba1df8", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/42ac8bbbf10d3113c335ba8af39fad63db96e4e2", + "reference": "42ac8bbbf10d3113c335ba8af39fad63db96e4e2", "shasum": "" }, "require": { @@ -6576,14 +6625,14 @@ "php": ">=8.2", "symfony/cache": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^7.1.5", + "symfony/dependency-injection": "^7.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", + "symfony/http-kernel": "^7.2", "symfony/polyfill-mbstring": "~1.0", "symfony/routing": "^6.4|^7.0" }, @@ -6608,14 +6657,15 @@ "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": "<6.4", - "symfony/serializer": "<6.4", + "symfony/security-csrf": "<7.2", + "symfony/serializer": "<7.2.5", "symfony/stopwatch": "<6.4", "symfony/translation": "<6.4", "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/workflow": "<6.4" }, "require-dev": { @@ -6647,18 +6697,19 @@ "symfony/scheduler": "^6.4.4|^7.0.4", "symfony/security-bundle": "^6.4|^7.0", "symfony/semaphore": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/serializer": "^7.2.5", "symfony/stopwatch": "^6.4|^7.0", "symfony/string": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/twig-bundle": "^6.4|^7.0", - "symfony/type-info": "^7.1", + "symfony/type-info": "^7.1.8", "symfony/uid": "^6.4|^7.0", "symfony/validator": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", + "symfony/webhook": "^7.2", "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "type": "symfony-bundle", "autoload": { @@ -6686,7 +6737,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.1.11" + "source": "https://github.com/symfony/framework-bundle/tree/v7.2.9" }, "funding": [ { @@ -6697,25 +6748,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-29T07:13:42+00:00" + "time": "2025-07-30T17:03:27+00:00" }, { "name": "symfony/html-sanitizer", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/html-sanitizer.git", - "reference": "5d928d0dc7823d703282bded46ba80008ce40d81" + "reference": "3388e208450fcac57d24aef4d5ae41037b663630" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/5d928d0dc7823d703282bded46ba80008ce40d81", - "reference": "5d928d0dc7823d703282bded46ba80008ce40d81", + "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/3388e208450fcac57d24aef4d5ae41037b663630", + "reference": "3388e208450fcac57d24aef4d5ae41037b663630", "shasum": "" }, "require": { @@ -6755,7 +6810,7 @@ "sanitizer" ], "support": { - "source": "https://github.com/symfony/html-sanitizer/tree/v7.1.11" + "source": "https://github.com/symfony/html-sanitizer/tree/v7.2.9" }, "funding": [ { @@ -6766,25 +6821,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/http-client", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "71632c1f13b36cb4c23ccdd255946dc02753afef" + "reference": "e26182d286135dad99f58ad57c56eef41362d452" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/71632c1f13b36cb4c23ccdd255946dc02753afef", - "reference": "71632c1f13b36cb4c23ccdd255946dc02753afef", + "url": "https://api.github.com/repos/symfony/http-client/zipball/e26182d286135dad99f58ad57c56eef41362d452", + "reference": "e26182d286135dad99f58ad57c56eef41362d452", "shasum": "" }, "require": { @@ -6795,6 +6854,7 @@ "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "amphp/amp": "<2.5", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -6805,14 +6865,14 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", "amphp/socket": "^1.1", "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/dependency-injection": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/messenger": "^6.4|^7.0", @@ -6849,7 +6909,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.1.11" + "source": "https://github.com/symfony/http-client/tree/v7.2.9" }, "funding": [ { @@ -6860,12 +6920,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-28T15:50:57+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/http-client-contracts", @@ -6947,20 +7011,21 @@ }, { "name": "symfony/http-foundation", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7ced01aa123612666a7a4fb72c627f969c01fa8d" + "reference": "7d81b8184601176dff995cf959d3def142309049" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7ced01aa123612666a7a4fb72c627f969c01fa8d", - "reference": "7ced01aa123612666a7a4fb72c627f969c01fa8d", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7d81b8184601176dff995cf959d3def142309049", + "reference": "7d81b8184601176dff995cf959d3def142309049", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-mbstring": "~1.1", "symfony/polyfill-php83": "^1.27" }, @@ -7004,7 +7069,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.1.11" + "source": "https://github.com/symfony/http-foundation/tree/v7.2.9" }, "funding": [ { @@ -7015,25 +7080,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T10:33:21+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "576eb3368037dd139f67b8ac71db56c3f69f7d66" + "reference": "d0cc0295c9c2fd5e053fee2b2a143001f2d0c33c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/576eb3368037dd139f67b8ac71db56c3f69f7d66", - "reference": "576eb3368037dd139f67b8ac71db56c3f69f7d66", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d0cc0295c9c2fd5e053fee2b2a143001f2d0c33c", + "reference": "d0cc0295c9c2fd5e053fee2b2a143001f2d0c33c", "shasum": "" }, "require": { @@ -7062,7 +7131,7 @@ "symfony/twig-bridge": "<6.4", "symfony/validator": "<6.4", "symfony/var-dumper": "<6.4", - "twig/twig": "<3.0.4" + "twig/twig": "<3.12" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" @@ -7090,7 +7159,7 @@ "symfony/validator": "^6.4|^7.0", "symfony/var-dumper": "^6.4|^7.0", "symfony/var-exporter": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "type": "library", "autoload": { @@ -7118,7 +7187,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.1.11" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.9" }, "funding": [ { @@ -7129,25 +7198,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-29T07:34:05+00:00" + "time": "2025-07-31T09:36:38+00:00" }, { "name": "symfony/intl", - "version": "v7.1.8", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "e56b243fc0afa5a12bd11dace4002ada5a7d99f8" + "reference": "fdc4c87872b43af23a613a1c576109c51f63a36c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/e56b243fc0afa5a12bd11dace4002ada5a7d99f8", - "reference": "e56b243fc0afa5a12bd11dace4002ada5a7d99f8", + "url": "https://api.github.com/repos/symfony/intl/zipball/fdc4c87872b43af23a613a1c576109c51f63a36c", + "reference": "fdc4c87872b43af23a613a1c576109c51f63a36c", "shasum": "" }, "require": { @@ -7204,7 +7277,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v7.1.8" + "source": "https://github.com/symfony/intl/tree/v7.2.9" }, "funding": [ { @@ -7215,12 +7288,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-08T15:46:42+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/mercure", @@ -7391,25 +7468,26 @@ }, { "name": "symfony/messenger", - "version": "v7.1.9", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "51e2b8b6a14b78ad7db60ef5f195ae893c16b9cc" + "reference": "3fd1638aa11ef6f342ed6b10b930a1757c25417c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/51e2b8b6a14b78ad7db60ef5f195ae893c16b9cc", - "reference": "51e2b8b6a14b78ad7db60ef5f195ae893c16b9cc", + "url": "https://api.github.com/repos/symfony/messenger/zipball/3fd1638aa11ef6f342ed6b10b930a1757c25417c", + "reference": "3fd1638aa11ef6f342ed6b10b930a1757c25417c", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0" + "symfony/clock": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "symfony/console": "<6.4", + "symfony/console": "<7.2", "symfony/event-dispatcher": "<6.4", "symfony/event-dispatcher-contracts": "<2.5", "symfony/framework-bundle": "<6.4", @@ -7418,7 +7496,7 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^6.4|^7.0", + "symfony/console": "^7.2", "symfony/dependency-injection": "^6.4|^7.0", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -7457,7 +7535,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.1.9" + "source": "https://github.com/symfony/messenger/tree/v7.2.9" }, "funding": [ { @@ -7468,25 +7546,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-26T09:50:51+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/mime", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "c252e20d1179dd35a5bfdb4a61a2084387ce97f4" + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/c252e20d1179dd35a5bfdb4a61a2084387ce97f4", - "reference": "c252e20d1179dd35a5bfdb4a61a2084387ce97f4", + "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", "shasum": "" }, "require": { @@ -7541,7 +7623,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.11" + "source": "https://github.com/symfony/mime/tree/v7.2.9" }, "funding": [ { @@ -7552,25 +7634,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.9", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "0f4099f5306a92487d13b2a4589068c36a93c447" + "reference": "b48517d1cef0d533e241cb03801014f1bb832775" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0f4099f5306a92487d13b2a4589068c36a93c447", - "reference": "0f4099f5306a92487d13b2a4589068c36a93c447", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48517d1cef0d533e241cb03801014f1bb832775", + "reference": "b48517d1cef0d533e241cb03801014f1bb832775", "shasum": "" }, "require": { @@ -7608,7 +7694,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.9" + "source": "https://github.com/symfony/options-resolver/tree/v7.2.9" }, "funding": [ { @@ -7619,25 +7705,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-20T11:08:58+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/password-hasher", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "2e618d1af51805e5a1fbda326d00b77c6c1037d5" + "reference": "d8bd3d66d074c0acba1214a0d42f5941a8e1e94d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/2e618d1af51805e5a1fbda326d00b77c6c1037d5", - "reference": "2e618d1af51805e5a1fbda326d00b77c6c1037d5", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/d8bd3d66d074c0acba1214a0d42f5941a8e1e94d", + "reference": "d8bd3d66d074c0acba1214a0d42f5941a8e1e94d", "shasum": "" }, "require": { @@ -7680,7 +7770,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v7.1.6" + "source": "https://github.com/symfony/password-hasher/tree/v7.2.0" }, "funding": [ { @@ -7696,7 +7786,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -8287,16 +8377,16 @@ }, { "name": "symfony/property-access", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "83e46f0266186e76929257426ddaa151248781c6" + "reference": "9858acdf18abac17b9ab254706454d0cc0de7641" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/83e46f0266186e76929257426ddaa151248781c6", - "reference": "83e46f0266186e76929257426ddaa151248781c6", + "url": "https://api.github.com/repos/symfony/property-access/zipball/9858acdf18abac17b9ab254706454d0cc0de7641", + "reference": "9858acdf18abac17b9ab254706454d0cc0de7641", "shasum": "" }, "require": { @@ -8343,7 +8433,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.1.11" + "source": "https://github.com/symfony/property-access/tree/v7.2.9" }, "funding": [ { @@ -8354,31 +8444,35 @@ "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-01-10T14:50:02+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/property-info", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "df9002f8381cc074300dc2bf9726075e4bf00458" + "reference": "16bb9e15292f9f07c28a6f955d4f14c680d1e0b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/df9002f8381cc074300dc2bf9726075e4bf00458", - "reference": "df9002f8381cc074300dc2bf9726075e4bf00458", + "url": "https://api.github.com/repos/symfony/property-info/zipball/16bb9e15292f9f07c28a6f955d4f14c680d1e0b5", + "reference": "16bb9e15292f9f07c28a6f955d4f14c680d1e0b5", "shasum": "" }, "require": { "php": ">=8.2", "symfony/string": "^6.4|^7.0", - "symfony/type-info": "~7.1.9|^7.2.2" + "symfony/type-info": "~7.2.8|^7.3.1" }, "conflict": { "phpdocumentor/reflection-docblock": "<5.2", @@ -8428,7 +8522,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.1.11" + "source": "https://github.com/symfony/property-info/tree/v7.2.9" }, "funding": [ { @@ -8439,25 +8533,96 @@ "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-01-27T10:57:12+00:00" + "time": "2025-07-10T08:29:33+00:00" + }, + { + "name": "symfony/redis-messenger", + "version": "v7.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/redis-messenger.git", + "reference": "3ca27b479c53ab1004d7ee8a6e34e0506f919c74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/redis-messenger/zipball/3ca27b479c53ab1004d7ee8a6e34e0506f919c74", + "reference": "3ca27b479c53ab1004d7ee8a6e34e0506f919c74", + "shasum": "" + }, + "require": { + "ext-redis": "*", + "php": ">=8.2", + "symfony/messenger": "^6.4|^7.0" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0" + }, + "type": "symfony-messenger-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\Bridge\\Redis\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Redis extension Messenger Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/redis-messenger/tree/v7.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-07T19:09:28+00:00" }, { "name": "symfony/routing", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "07f6463a8ff4377944222b69b126bd5495e9d44e" + "reference": "d5a038e972be748e3f057c884897a41b1111e4a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/07f6463a8ff4377944222b69b126bd5495e9d44e", - "reference": "07f6463a8ff4377944222b69b126bd5495e9d44e", + "url": "https://api.github.com/repos/symfony/routing/zipball/d5a038e972be748e3f057c884897a41b1111e4a0", + "reference": "d5a038e972be748e3f057c884897a41b1111e4a0", "shasum": "" }, "require": { @@ -8509,7 +8674,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.1.11" + "source": "https://github.com/symfony/routing/tree/v7.2.9" }, "funding": [ { @@ -8520,25 +8685,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T10:33:21+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/runtime", - "version": "v7.1.7", + "version": "v7.2.8", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "9889783c17e8a68fa5e88c8e8a1a85e802558dba" + "reference": "40d1c481c2370362010a4da64af14dec62a9ec68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/9889783c17e8a68fa5e88c8e8a1a85e802558dba", - "reference": "9889783c17e8a68fa5e88c8e8a1a85e802558dba", + "url": "https://api.github.com/repos/symfony/runtime/zipball/40d1c481c2370362010a4da64af14dec62a9ec68", + "reference": "40d1c481c2370362010a4da64af14dec62a9ec68", "shasum": "" }, "require": { @@ -8588,7 +8757,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.1.7" + "source": "https://github.com/symfony/runtime/tree/v7.2.8" }, "funding": [ { @@ -8604,20 +8773,20 @@ "type": "tidelift" } ], - "time": "2024-11-05T16:45:54+00:00" + "time": "2025-06-13T07:47:28+00:00" }, { "name": "symfony/security-bundle", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "4012dbc0884fc7cbf555615a5aaa16f7c0d3f222" + "reference": "4661890f7d2571e9ef58431401a390c2dde68c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/4012dbc0884fc7cbf555615a5aaa16f7c0d3f222", - "reference": "4012dbc0884fc7cbf555615a5aaa16f7c0d3f222", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/4661890f7d2571e9ef58431401a390c2dde68c78", + "reference": "4661890f7d2571e9ef58431401a390c2dde68c78", "shasum": "" }, "require": { @@ -8631,9 +8800,9 @@ "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/password-hasher": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", + "symfony/security-core": "^7.2", "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^7.1", + "symfony/security-http": "^7.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -8665,7 +8834,7 @@ "symfony/twig-bundle": "^6.4|^7.0", "symfony/validator": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.0.4", + "twig/twig": "^3.12", "web-token/jwt-library": "^3.3.2|^4.0" }, "type": "symfony-bundle", @@ -8694,7 +8863,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.1.11" + "source": "https://github.com/symfony/security-bundle/tree/v7.2.9" }, "funding": [ { @@ -8705,29 +8874,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-12-31T17:57:35+00:00" + "time": "2025-07-22T07:39:44+00:00" }, { "name": "symfony/security-core", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "0931c6bb15b696e1a4da8405c255b3a8673dcb3c" + "reference": "28ec042a6aff580693cce7aa820778e6dfa5e5ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/0931c6bb15b696e1a4da8405c255b3a8673dcb3c", - "reference": "0931c6bb15b696e1a4da8405c255b3a8673dcb3c", + "url": "https://api.github.com/repos/symfony/security-core/zipball/28ec042a6aff580693cce7aa820778e6dfa5e5ef", + "reference": "28ec042a6aff580693cce7aa820778e6dfa5e5ef", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/password-hasher": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3" @@ -8780,7 +8954,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.1.11" + "source": "https://github.com/symfony/security-core/tree/v7.2.9" }, "funding": [ { @@ -8791,25 +8965,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-07-22T08:39:58+00:00" }, { "name": "symfony/security-csrf", - "version": "v7.1.6", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "23b460d3447fd61970e0ed5ec7a0301296a17f06" + "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/23b460d3447fd61970e0ed5ec7a0301296a17f06", - "reference": "23b460d3447fd61970e0ed5ec7a0301296a17f06", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/2b4b0c46c901729e4e90719eacd980381f53e0a3", + "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3", "shasum": "" }, "require": { @@ -8820,7 +8998,9 @@ "symfony/http-foundation": "<6.4" }, "require-dev": { - "symfony/http-foundation": "^6.4|^7.0" + "psr/log": "^1|^2|^3", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -8848,7 +9028,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v7.1.6" + "source": "https://github.com/symfony/security-csrf/tree/v7.2.3" }, "funding": [ { @@ -8864,20 +9044,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-01-02T18:42:10+00:00" }, { "name": "symfony/security-http", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "c5ef4cb3bb013cf02c1d8459d1c51bc9f616cd80" + "reference": "972dd874c2ce65e4c58dbf59054d2bf7ad70ec54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/c5ef4cb3bb013cf02c1d8459d1c51bc9f616cd80", - "reference": "c5ef4cb3bb013cf02c1d8459d1c51bc9f616cd80", + "url": "https://api.github.com/repos/symfony/security-http/zipball/972dd874c2ce65e4c58dbf59054d2bf7ad70ec54", + "reference": "972dd874c2ce65e4c58dbf59054d2bf7ad70ec54", "shasum": "" }, "require": { @@ -8887,7 +9067,7 @@ "symfony/http-kernel": "^6.4|^7.0", "symfony/polyfill-mbstring": "~1.0", "symfony/property-access": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", + "symfony/security-core": "^7.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -8936,7 +9116,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v7.1.11" + "source": "https://github.com/symfony/security-http/tree/v7.2.9" }, "funding": [ { @@ -8947,25 +9127,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-28T15:50:57+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/serializer", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "cb88edf0d4d63472c50b5f77bcb3227a103966e6" + "reference": "91b01d65b553a6687c7b13432da1c0f4af1bf875" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/cb88edf0d4d63472c50b5f77bcb3227a103966e6", - "reference": "cb88edf0d4d63472c50b5f77bcb3227a103966e6", + "url": "https://api.github.com/repos/symfony/serializer/zipball/91b01d65b553a6687c7b13432da1c0f4af1bf875", + "reference": "91b01d65b553a6687c7b13432da1c0f4af1bf875", "shasum": "" }, "require": { @@ -8979,7 +9163,6 @@ "symfony/dependency-injection": "<6.4", "symfony/property-access": "<6.4", "symfony/property-info": "<6.4", - "symfony/type-info": "<7.1.5", "symfony/uid": "<6.4", "symfony/validator": "<6.4", "symfony/yaml": "<6.4" @@ -8991,7 +9174,7 @@ "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/dependency-injection": "^7.2", "symfony/error-handler": "^6.4|^7.0", "symfony/filesystem": "^6.4|^7.0", "symfony/form": "^6.4|^7.0", @@ -9002,7 +9185,7 @@ "symfony/property-access": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/type-info": "^7.1.5", + "symfony/type-info": "^7.1.8", "symfony/uid": "^6.4|^7.0", "symfony/validator": "^6.4|^7.0", "symfony/var-dumper": "^6.4|^7.0", @@ -9035,7 +9218,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.1.11" + "source": "https://github.com/symfony/serializer/tree/v7.2.9" }, "funding": [ { @@ -9046,12 +9229,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-29T07:13:42+00:00" + "time": "2025-07-26T12:59:07+00:00" }, { "name": "symfony/service-contracts", @@ -9211,16 +9398,16 @@ }, { "name": "symfony/stopwatch", - "version": "v7.1.6", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05" + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8b4a434e6e7faf6adedffb48783a5c75409a1a05", - "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", "shasum": "" }, "require": { @@ -9253,7 +9440,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.1.6" + "source": "https://github.com/symfony/stopwatch/tree/v7.2.4" }, "funding": [ { @@ -9269,20 +9456,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-02-24T10:49:57+00:00" }, { "name": "symfony/string", - "version": "v7.1.8", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281" + "reference": "de5189e4def141dbdca2f2ce7a653cc6364f58e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/591ebd41565f356fcd8b090fe64dbb5878f50281", - "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281", + "url": "https://api.github.com/repos/symfony/string/zipball/de5189e4def141dbdca2f2ce7a653cc6364f58e6", + "reference": "de5189e4def141dbdca2f2ce7a653cc6364f58e6", "shasum": "" }, "require": { @@ -9340,7 +9527,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.8" + "source": "https://github.com/symfony/string/tree/v7.2.9" }, "funding": [ { @@ -9351,29 +9538,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-11-13T13:31:21+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/translation", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f" + "reference": "00b927b4948d49afbf5013411c0a91a1cea8b087" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/b9f72ab14efdb6b772f85041fa12f820dee8d55f", - "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f", + "url": "https://api.github.com/repos/symfony/translation/zipball/00b927b4948d49afbf5013411c0a91a1cea8b087", + "reference": "00b927b4948d49afbf5013411c0a91a1cea8b087", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -9434,7 +9626,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.1.6" + "source": "https://github.com/symfony/translation/tree/v7.2.9" }, "funding": [ { @@ -9445,12 +9637,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-28T12:35:13+00:00" + "time": "2025-07-30T17:31:35+00:00" }, { "name": "symfony/translation-contracts", @@ -9532,22 +9728,23 @@ }, { "name": "symfony/twig-bridge", - "version": "v7.1.10", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "c027c54611cd194273b924c8d24d9706695171ff" + "reference": "e62144411b277877d0e8583b48202673c36941d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/c027c54611cd194273b924c8d24d9706695171ff", - "reference": "c027c54611cd194273b924c8d24d9706695171ff", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/e62144411b277877d0e8583b48202673c36941d7", + "reference": "e62144411b277877d0e8583b48202673c36941d7", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/translation-contracts": "^2.5|^3", - "twig/twig": "^3.9" + "twig/twig": "^3.12" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.2.2", @@ -9572,7 +9769,7 @@ "symfony/emoji": "^7.1", "symfony/expression-language": "^6.4|^7.0", "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", + "symfony/form": "^6.4.20|^7.2.5", "symfony/html-sanitizer": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -9621,7 +9818,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.1.10" + "source": "https://github.com/symfony/twig-bridge/tree/v7.2.9" }, "funding": [ { @@ -9632,25 +9829,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-19T14:23:39+00:00" + "time": "2025-07-26T14:12:01+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "af902314a71fb412ae412094f7e1d7e49594507b" + "reference": "157de579a9ec25d5dfeb7ee3b3e9b57d2e635c39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/af902314a71fb412ae412094f7e1d7e49594507b", - "reference": "af902314a71fb412ae412094f7e1d7e49594507b", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/157de579a9ec25d5dfeb7ee3b3e9b57d2e635c39", + "reference": "157de579a9ec25d5dfeb7ee3b3e9b57d2e635c39", "shasum": "" }, "require": { @@ -9661,7 +9862,7 @@ "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/twig-bridge": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "conflict": { "symfony/framework-bundle": "<6.4", @@ -9705,7 +9906,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.1.6" + "source": "https://github.com/symfony/twig-bundle/tree/v7.2.9" }, "funding": [ { @@ -9716,40 +9917,37 @@ "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:20:29+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/type-info", - "version": "v7.1.10", + "version": "v7.2.8", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "8f980bdd1d6a2834503afbfcf3f39de8133e48fe" + "reference": "ec311f6f16ce2dffdffb6db6f89cdd1533723e42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/8f980bdd1d6a2834503afbfcf3f39de8133e48fe", - "reference": "8f980bdd1d6a2834503afbfcf3f39de8133e48fe", + "url": "https://api.github.com/repos/symfony/type-info/zipball/ec311f6f16ce2dffdffb6db6f89cdd1533723e42", + "reference": "ec311f6f16ce2dffdffb6db6f89cdd1533723e42", "shasum": "" }, "require": { "php": ">=8.2", "psr/container": "^1.1|^2.0" }, - "conflict": { - "phpstan/phpdoc-parser": "<1.0", - "symfony/dependency-injection": "<6.4", - "symfony/property-info": "<6.4" - }, "require-dev": { - "phpstan/phpdoc-parser": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0" + "phpstan/phpdoc-parser": "^1.0|^2.0" }, "type": "library", "autoload": { @@ -9787,7 +9985,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.1.10" + "source": "https://github.com/symfony/type-info/tree/v7.2.8" }, "funding": [ { @@ -9803,7 +10001,7 @@ "type": "tidelift" } ], - "time": "2024-12-11T12:11:39+00:00" + "time": "2025-06-27T15:23:16+00:00" }, { "name": "symfony/ux-icons", @@ -9900,23 +10098,23 @@ }, { "name": "symfony/ux-live-component", - "version": "v2.26.0", + "version": "v2.30.0", "source": { "type": "git", "url": "https://github.com/symfony/ux-live-component.git", - "reference": "fe860a47f164c4ead89b9cb5cf7d3a13835de0a3" + "reference": "332ab117586c755d2795c0db92c028a9eed71e1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-live-component/zipball/fe860a47f164c4ead89b9cb5cf7d3a13835de0a3", - "reference": "fe860a47f164c4ead89b9cb5cf7d3a13835de0a3", + "url": "https://api.github.com/repos/symfony/ux-live-component/zipball/332ab117586c755d2795c0db92c028a9eed71e1a", + "reference": "332ab117586c755d2795c0db92c028a9eed71e1a", "shasum": "" }, "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/property-access": "^5.4.5|^6.0|^7.0", - "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0|^7.0|^8.0", + "symfony/property-info": "^5.4|^6.0|^7.0|^8.0", "symfony/stimulus-bundle": "^2.9", "symfony/ux-twig-component": "^2.25.1", "twig/twig": "^3.10.3" @@ -9924,7 +10122,7 @@ "conflict": { "symfony/config": "<5.4.0", "symfony/property-info": "~7.0.0", - "symfony/type-info": "<7.1" + "symfony/type-info": "<7.2" }, "require-dev": { "doctrine/annotations": "^1.0", @@ -9933,17 +10131,17 @@ "doctrine/orm": "^2.9.4", "doctrine/persistence": "^2.5.2|^3.0", "phpdocumentor/reflection-docblock": "5.x-dev", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/form": "^5.4|^6.0|^7.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/options-resolver": "^5.4|^6.0|^7.0", - "symfony/phpunit-bridge": "^6.1|^7.0", - "symfony/security-bundle": "^5.4|^6.0|^7.0", - "symfony/serializer": "^5.4|^6.0|^7.0", - "symfony/twig-bundle": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "symfony/validator": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "symfony/expression-language": "^5.4|^6.0|^7.0|^8.0", + "symfony/form": "^5.4|^6.0|^7.0|^8.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/options-resolver": "^5.4|^6.0|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.1|^7.0|^8.0", + "symfony/security-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/serializer": "^5.4|^6.0|^7.0|^8.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/uid": "^5.4|^6.0|^7.0|^8.0", + "symfony/validator": "^5.4|^6.0|^7.0|^8.0", "zenstruck/browser": "^1.2.0", "zenstruck/foundry": "^2.0" }, @@ -9977,7 +10175,7 @@ "twig" ], "support": { - "source": "https://github.com/symfony/ux-live-component/tree/v2.26.0" + "source": "https://github.com/symfony/ux-live-component/tree/v2.30.0" }, "funding": [ { @@ -9988,12 +10186,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-31T15:09:39+00:00" + "time": "2025-08-27T15:25:48+00:00" }, { "name": "symfony/ux-twig-component", @@ -10084,16 +10286,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a563c5aeacb98cd46f9885a63cf241ea7794b307" + "reference": "67ad2a16e50f052c80fe03ccb6a8721bbeffe032" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a563c5aeacb98cd46f9885a63cf241ea7794b307", - "reference": "a563c5aeacb98cd46f9885a63cf241ea7794b307", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/67ad2a16e50f052c80fe03ccb6a8721bbeffe032", + "reference": "67ad2a16e50f052c80fe03ccb6a8721bbeffe032", "shasum": "" }, "require": { @@ -10104,12 +10306,11 @@ "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -10147,7 +10348,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.11" + "source": "https://github.com/symfony/var-dumper/tree/v7.2.9" }, "funding": [ { @@ -10158,25 +10359,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T11:38:41+00:00" + "time": "2025-07-29T19:57:35+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "90173ef89c40e7c8c616653241048705f84130ef" + "reference": "1aab3e8e2e63f7586dd9951746eababe339d3978" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/90173ef89c40e7c8c616653241048705f84130ef", - "reference": "90173ef89c40e7c8c616653241048705f84130ef", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1aab3e8e2e63f7586dd9951746eababe339d3978", + "reference": "1aab3e8e2e63f7586dd9951746eababe339d3978", "shasum": "" }, "require": { @@ -10223,7 +10428,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.1.6" + "source": "https://github.com/symfony/var-exporter/tree/v7.2.9" }, "funding": [ { @@ -10234,25 +10439,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/web-link", - "version": "v7.1.6", + "version": "v7.2.7", "source": { "type": "git", "url": "https://github.com/symfony/web-link.git", - "reference": "383aa7566f25e3a1ab323732c2cc6a1748120d3a" + "reference": "7697f74fce67555665339423ce453cc8216a98ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/383aa7566f25e3a1ab323732c2cc6a1748120d3a", - "reference": "383aa7566f25e3a1ab323732c2cc6a1748120d3a", + "url": "https://api.github.com/repos/symfony/web-link/zipball/7697f74fce67555665339423ce453cc8216a98ff", + "reference": "7697f74fce67555665339423ce453cc8216a98ff", "shasum": "" }, "require": { @@ -10306,7 +10515,7 @@ "push" ], "support": { - "source": "https://github.com/symfony/web-link/tree/v7.1.6" + "source": "https://github.com/symfony/web-link/tree/v7.3.0-RC1" }, "funding": [ { @@ -10322,20 +10531,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-05-19T13:28:18+00:00" }, { "name": "symfony/workflow", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/workflow.git", - "reference": "b5dca7ecc55ec401939cd101d1a23d64bbce113b" + "reference": "ac7c283f1e3e8e8e7c7f9edc27049b4bfe818814" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/workflow/zipball/b5dca7ecc55ec401939cd101d1a23d64bbce113b", - "reference": "b5dca7ecc55ec401939cd101d1a23d64bbce113b", + "url": "https://api.github.com/repos/symfony/workflow/zipball/ac7c283f1e3e8e8e7c7f9edc27049b4bfe818814", + "reference": "ac7c283f1e3e8e8e7c7f9edc27049b4bfe818814", "shasum": "" }, "require": { @@ -10393,7 +10602,7 @@ "workflow" ], "support": { - "source": "https://github.com/symfony/workflow/tree/v7.1.6" + "source": "https://github.com/symfony/workflow/tree/v7.2.9" }, "funding": [ { @@ -10404,29 +10613,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:20:29+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/yaml", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4921b8c1db90c13ba2ee0520080ef6800912b018" + "reference": "0df1031b6a03b9bef3cd052a59e270e006731e90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4921b8c1db90c13ba2ee0520080ef6800912b018", - "reference": "4921b8c1db90c13ba2ee0520080ef6800912b018", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0df1031b6a03b9bef3cd052a59e270e006731e90", + "reference": "0df1031b6a03b9bef3cd052a59e270e006731e90", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -10464,7 +10678,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.1.11" + "source": "https://github.com/symfony/yaml/tree/v7.2.9" }, "funding": [ { @@ -10475,12 +10689,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-07T12:50:05+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "tkijewski/php-lnurl", @@ -12561,16 +12779,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.1.6", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf" + "reference": "776d81d0c4859453aa35c316a542d74d0616330f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/714becc9ba9b20115ffededc58f6b7172dc394cf", - "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/776d81d0c4859453aa35c316a542d74d0616330f", + "reference": "776d81d0c4859453aa35c316a542d74d0616330f", "shasum": "" }, "require": { @@ -12609,7 +12827,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.1.6" + "source": "https://github.com/symfony/browser-kit/tree/v7.2.9" }, "funding": [ { @@ -12620,25 +12838,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", - "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", "shasum": "" }, "require": { @@ -12674,7 +12896,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.6" + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" }, "funding": [ { @@ -12690,20 +12912,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/dom-crawler", - "version": "v7.1.11", + "version": "v7.2.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "7361d8f7e7eecbca17efe68ca1ee677bf23cfe5a" + "reference": "a7473767124513e4099186dba43403dbc45f26dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7361d8f7e7eecbca17efe68ca1ee677bf23cfe5a", - "reference": "7361d8f7e7eecbca17efe68ca1ee677bf23cfe5a", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a7473767124513e4099186dba43403dbc45f26dc", + "reference": "a7473767124513e4099186dba43403dbc45f26dc", "shasum": "" }, "require": { @@ -12741,7 +12963,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.1.11" + "source": "https://github.com/symfony/dom-crawler/tree/v7.2.8" }, "funding": [ { @@ -12757,7 +12979,7 @@ "type": "tidelift" } ], - "time": "2025-01-27T10:57:12+00:00" + "time": "2025-06-15T10:06:57+00:00" }, { "name": "symfony/maker-bundle", @@ -12943,16 +13165,16 @@ }, { "name": "symfony/process", - "version": "v7.1.8", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892" + "reference": "e0a87c9a60045a354b041db2ea3cf047bf0b15f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/42783370fda6e538771f7c7a36e9fa2ee3a84892", - "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892", + "url": "https://api.github.com/repos/symfony/process/zipball/e0a87c9a60045a354b041db2ea3cf047bf0b15f5", + "reference": "e0a87c9a60045a354b041db2ea3cf047bf0b15f5", "shasum": "" }, "require": { @@ -12984,7 +13206,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.8" + "source": "https://github.com/symfony/process/tree/v7.2.9" }, "funding": [ { @@ -12995,25 +13217,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-06T14:23:19+00:00" + "time": "2025-07-10T08:29:33+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v7.1.11", + "version": "v7.2.9", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "328b2728bb5d85d0d38b18d1458834098202afe2" + "reference": "ac9d18238b37e7855ef1b0267b3540565cc95f5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/328b2728bb5d85d0d38b18d1458834098202afe2", - "reference": "328b2728bb5d85d0d38b18d1458834098202afe2", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/ac9d18238b37e7855ef1b0267b3540565cc95f5d", + "reference": "ac9d18238b37e7855ef1b0267b3540565cc95f5d", "shasum": "" }, "require": { @@ -13023,12 +13249,13 @@ "symfony/http-kernel": "^6.4|^7.0", "symfony/routing": "^6.4|^7.0", "symfony/twig-bundle": "^6.4|^7.0", - "twig/twig": "^3.10" + "twig/twig": "^3.12" }, "conflict": { "symfony/form": "<6.4", "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4" + "symfony/messenger": "<6.4", + "symfony/serializer": "<7.2" }, "require-dev": { "symfony/browser-kit": "^6.4|^7.0", @@ -13065,7 +13292,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.1.11" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v7.2.9" }, "funding": [ { @@ -13076,12 +13303,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-07T09:23:14+00:00" + "time": "2025-07-26T14:12:01+00:00" }, { "name": "theseer/tokenizer", diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml index fd13aaa..f7d2f2e 100644 --- a/config/packages/cache.yaml +++ b/config/packages/cache.yaml @@ -20,3 +20,7 @@ framework: adapter: cache.adapter.redis provider: Redis default_lifetime: 3600 + npub.cache: + adapter: cache.adapter.redis + provider: Redis + default_lifetime: 3600 diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml index 0cf892f..b4a43b1 100644 --- a/config/packages/messenger.yaml +++ b/config/packages/messenger.yaml @@ -12,7 +12,7 @@ framework: routing: # Route your messages to the transports - # 'App\Message\YourMessage': async + 'App\Message\FetchCommentsMessage': async # when@test: # framework: diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index ae71fbb..556fb22 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -4,6 +4,7 @@ twig: project_npub: 'npub1ez09adke4vy8udk3y2skwst8q5chjgqzym9lpq4u58zf96zcl7kqyry2lz' dev_npub: 'npub1636uujeewag8zv8593lcvdrwlymgqre6uax4anuq3y5qehqey05sl8qpl4' feature_flag_share_btn: false + mercure_public_hub_url: '%mercure_public_hub_url%' when@test: twig: diff --git a/config/services.yaml b/config/services.yaml index b38ef36..5f8229f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -5,6 +5,7 @@ # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: encryption_key: '%env(APP_ENCRYPTION_KEY)%' + mercure_public_hub_url: '%env(MERCURE_PUBLIC_URL)%' services: # default configuration for services in *this* file diff --git a/frankenphp/Caddyfile b/frankenphp/Caddyfile index 573219c..3f2a75a 100644 --- a/frankenphp/Caddyfile +++ b/frankenphp/Caddyfile @@ -1,9 +1,7 @@ -{ - {$CADDY_GLOBAL_OPTIONS} +{$CADDY_GLOBAL_OPTIONS} - frankenphp { - {$FRANKENPHP_CONFIG} - } +frankenphp { + {$FRANKENPHP_CONFIG} } {$CADDY_EXTRA_CONFIG} @@ -26,9 +24,7 @@ # Transport to use (default to Bolt) transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db} # Publisher JWT key - publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG} - # Subscriber JWT key - subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG} + publisher_jwt {env.MERCURE_JWT_SECRET} {env.MERCURE_PUBLISHER_JWT_ALG} # Allow anonymous subscribers (double-check that it's what you want) anonymous # Enable the subscription API (double-check that it's what you want) diff --git a/src/Controller/Administration/MagazineAdminController.php b/src/Controller/Administration/MagazineAdminController.php index 29d5b57..b272c64 100644 --- a/src/Controller/Administration/MagazineAdminController.php +++ b/src/Controller/Administration/MagazineAdminController.php @@ -20,10 +20,10 @@ class MagazineAdminController extends AbstractController { #[Route('/admin/magazines', name: 'admin_magazines')] #[IsGranted('ROLE_ADMIN')] - public function index(RedisClient $redis, CacheInterface $redisCache, EntityManagerInterface $em): Response + public function index(RedisClient $redis, CacheInterface $appCache, EntityManagerInterface $em): Response { // Optimized database-first approach - $magazines = $this->getMagazinesFromDatabase($em, $redis, $redisCache); + $magazines = $this->getMagazinesFromDatabase($em, $redis, $appCache); return $this->render('admin/magazines.html.twig', [ 'magazines' => $magazines, diff --git a/src/Controller/MagazineWizardController.php b/src/Controller/MagazineWizardController.php index c2c34bc..10a4f96 100644 --- a/src/Controller/MagazineWizardController.php +++ b/src/Controller/MagazineWizardController.php @@ -189,11 +189,11 @@ class MagazineWizardController extends AbstractController #[Route('/api/index/publish', name: 'api-index-publish', methods: ['POST'])] public function publishIndexEvent( - Request $request, - CacheItemPoolInterface $redisCache, + Request $request, + CacheItemPoolInterface $appCache, CsrfTokenManagerInterface $csrfTokenManager, - RedisClient $redis, - EntityManagerInterface $entityManager + RedisClient $redis, + EntityManagerInterface $entityManager ): JsonResponse { // Verify CSRF token $csrfToken = $request->headers->get('X-CSRF-TOKEN'); @@ -237,9 +237,9 @@ class MagazineWizardController extends AbstractController // Save to Redis under magazine- try { $key = 'magazine-' . $slug; - $item = $redisCache->getItem($key); + $item = $appCache->getItem($key); $item->set($eventObj); - $redisCache->save($item); + $appCache->save($item); } catch (\Throwable $e) { return new JsonResponse(['error' => 'Redis error'], 500); } @@ -278,13 +278,13 @@ class MagazineWizardController extends AbstractController #[Route('/api/nzine-index/publish', name: 'api-nzine-index-publish', methods: ['POST'])] public function publishNzineIndexEvent( - Request $request, + Request $request, CsrfTokenManagerInterface $csrfTokenManager, - NzineRepository $nzineRepository, - EncryptionService $encryptionService, - CacheItemPoolInterface $redisCache, - RedisClient $redis, - EntityManagerInterface $entityManager + NzineRepository $nzineRepository, + EncryptionService $encryptionService, + CacheItemPoolInterface $appCache, + RedisClient $redis, + EntityManagerInterface $entityManager ): JsonResponse { // Verify CSRF token $csrfToken = $request->headers->get('X-CSRF-TOKEN'); @@ -347,9 +347,9 @@ class MagazineWizardController extends AbstractController // Save to Redis $cacheKey = 'magazine-' . $slug; - $item = $redisCache->getItem($cacheKey); + $item = $appCache->getItem($cacheKey); $item->set($catEvent); - $redisCache->save($item); + $appCache->save($item); // Save to database $eventEntity = new \App\Entity\Event(); @@ -396,9 +396,9 @@ class MagazineWizardController extends AbstractController // Save magazine to Redis $cacheKey = 'magazine-' . $magSlug; - $item = $redisCache->getItem($cacheKey); + $item = $appCache->getItem($cacheKey); $item->set($magEvent); - $redisCache->save($item); + $appCache->save($item); // Save magazine to database $magEventEntity = new \App\Entity\Event(); diff --git a/src/Message/FetchCommentsMessage.php b/src/Message/FetchCommentsMessage.php new file mode 100644 index 0000000..151e1ec --- /dev/null +++ b/src/Message/FetchCommentsMessage.php @@ -0,0 +1,19 @@ +coordinate = $coordinate; + } + + public function getCoordinate(): string + { + return $this->coordinate; + } +} + diff --git a/src/MessageHandler/FetchCommentsHandler.php b/src/MessageHandler/FetchCommentsHandler.php new file mode 100644 index 0000000..aacf723 --- /dev/null +++ b/src/MessageHandler/FetchCommentsHandler.php @@ -0,0 +1,67 @@ +getCoordinate(); + $comments = $this->nostrClient->getComments($coordinate); + + // Collect all pubkeys: authors and zappers + $allPubKeys = []; + foreach ($comments as $c) { + $allPubKeys[] = $c->pubkey; + if ($c->kind == 9735) { + $tags = $c->tags ?? []; + foreach ($tags as $tag) { + if ($tag[0] === 'p' && isset($tag[1])) { + $allPubKeys[] = $tag[1]; + } + } + } + } + $allPubKeys = array_unique($allPubKeys); + $authorsMetadata = $this->redisCacheService->getMultipleMetadata($allPubKeys); + $this->logger->info('Fetched ' . count($comments) . ' comments for coordinate: ' . $coordinate); + $this->logger->info('Fetched ' . count($authorsMetadata) . ' profiles for ' . count($allPubKeys) . ' pubkeys'); + + usort($comments, fn($a, $b) => ($b->created_at ?? 0) <=> ($a->created_at ?? 0)); + // Optionally, reuse parseNostrLinks and parseZaps logic here if needed + // For now, just send the raw comments array + $data = [ + 'coordinate' => $coordinate, + 'comments' => $comments, + 'profiles' => $authorsMetadata + ]; + try { + $topic = "/comments/" . $coordinate; + $update = new Update($topic, json_encode($data), false); + $this->logger->info('Publishing comments update for coordinate: ' . $coordinate); + $this->hub->publish($update); + } catch (\Exception $e) { + // Handle exception (log it, etc.) + $this->logger->error('Error publishing comments update: ' . $e->getMessage()); + } + + } +} diff --git a/src/Service/Nip05VerificationService.php b/src/Service/Nip05VerificationService.php index dcc40d4..5b8d6cb 100644 --- a/src/Service/Nip05VerificationService.php +++ b/src/Service/Nip05VerificationService.php @@ -12,7 +12,7 @@ readonly class Nip05VerificationService private const REQUEST_TIMEOUT = 5; // 5 seconds public function __construct( - private CacheInterface $redisCache, + private CacheInterface $appCache, private LoggerInterface $logger ) { } @@ -39,7 +39,7 @@ readonly class Nip05VerificationService $cacheKey = 'nip05_' . md5($nip05); try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($localPart, $domain, $pubkeyHex, $nip05) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($localPart, $domain, $pubkeyHex, $nip05) { $item->expiresAfter(self::CACHE_TTL); $wellKnownUrl = "https://{$domain}/.well-known/nostr.json?name=" . urlencode(strtolower($localPart)); diff --git a/src/Service/NostrClient.php b/src/Service/NostrClient.php index 615683a..7f649f6 100644 --- a/src/Service/NostrClient.php +++ b/src/Service/NostrClient.php @@ -9,6 +9,7 @@ use App\Util\NostrPhp\TweakedRequest; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; use nostriphant\NIP19\Data; +use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; use swentel\nostr\Event\Event; use swentel\nostr\Filter\Filter; @@ -30,24 +31,24 @@ class NostrClient */ private const REPUTABLE_RELAYS = [ 'wss://theforest.nostr1.com', - 'wss://relay.damus.io', - 'wss://relay.primal.net', + 'wss://nostr.land', + 'wss://purplepag.es', 'wss://nos.lol', 'wss://relay.snort.social', - // 'wss://nostr.land', // requires auth that doesn't currently work! - 'wss://purplepag.es', + 'wss://relay.damus.io', + 'wss://relay.primal.net', ]; public function __construct(private readonly EntityManagerInterface $entityManager, private readonly ManagerRegistry $managerRegistry, private readonly ArticleFactory $articleFactory, private readonly TokenStorageInterface $tokenStorage, - private readonly LoggerInterface $logger) + private readonly LoggerInterface $logger, + private readonly CacheItemPoolInterface $npubCache) { $this->defaultRelaySet = new RelaySet(); $this->defaultRelaySet->addRelay(new Relay('wss://theforest.nostr1.com')); // public aggregator relay - $this->defaultRelaySet->addRelay(new Relay('wss://relay.damus.io')); // public aggregator relay - $this->defaultRelaySet->addRelay(new Relay('wss://relay.primal.net')); // public aggregator relay + $this->defaultRelaySet->addRelay(new Relay('wss://aggr.nostr.land')); // aggregator relay, has AUTH } /** @@ -397,6 +398,17 @@ class NostrClient */ public function getNpubRelays($npub): array { + $cacheKey = 'npub_relays_' . $npub; + try { + $cachedItem = $this->npubCache->getItem($cacheKey); + if ($cachedItem->isHit()) { + $this->logger->debug('Using cached relays for npub', ['npub' => $npub]); + return $cachedItem->get(); + } + } catch (\Exception $e) { + $this->logger->warning('Cache error', ['error' => $e->getMessage()]); + } + // Get relays $request = $this->createNostrRequest( kinds: [KindsEnum::RELAY_LIST], @@ -419,9 +431,21 @@ class NostrClient } } // Remove duplicates, localhost and any non-wss relays - return array_filter(array_unique($relays), function ($relay) { + $relays = array_filter(array_unique($relays), function ($relay) { return str_starts_with($relay, 'wss:') && !str_contains($relay, 'localhost'); }); + + // Cache the result + try { + $cachedItem = $this->npubCache->getItem($cacheKey); + $cachedItem->set($relays); + $cachedItem->expiresAfter(3600); // 1 hour + $this->npubCache->save($cachedItem); + } catch (\Exception $e) { + $this->logger->warning('Cache save error', ['error' => $e->getMessage()]); + } + + return $relays; } /** @@ -899,8 +923,14 @@ class NostrClient } } + $this->logger->info('Relay set for request', ['relays' => $relaySet ? $relaySet->getRelays() : 'default']); + $requestMessage = new RequestMessage($subscription->getId(), [$filter]); - return (new TweakedRequest($relaySet ?? $this->defaultRelaySet, $requestMessage))->stopOnEventId($stopGap); + return (new TweakedRequest( + $relaySet ?? $this->defaultRelaySet, + $requestMessage, + $this->logger + ))->stopOnEventId($stopGap); } private function processResponse(array $response, callable $eventHandler): array @@ -943,7 +973,7 @@ class NostrClient } break; case 'AUTH': - $this->logger->warning('Relay requires authentication', [ + $this->logger->info('Relay required authentication (handled during request)', [ 'relay' => $relayUrl, 'response' => $item ]); diff --git a/src/Service/RedisCacheService.php b/src/Service/RedisCacheService.php index 17efd07..97914a8 100644 --- a/src/Service/RedisCacheService.php +++ b/src/Service/RedisCacheService.php @@ -17,10 +17,10 @@ use Symfony\Contracts\Cache\ItemInterface; readonly class RedisCacheService { public function __construct( - private NostrClient $nostrClient, - private CacheItemPoolInterface $redisCache, + private NostrClient $nostrClient, + private CacheItemPoolInterface $appCache, private EntityManagerInterface $entityManager, - private LoggerInterface $logger + private LoggerInterface $logger ) {} /** @@ -50,7 +50,7 @@ readonly class RedisCacheService $content->name = $defaultName; try { - $content = $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { + $content = $this->appCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { $item->expiresAfter(3600); // 1 hour, adjust as needed $rawEvent = $this->fetchRawUserEvent($pubkey); return $this->parseUserMetadata($rawEvent, $pubkey); @@ -60,9 +60,9 @@ readonly class RedisCacheService } // If content is still default, delete cache to retry next time if (isset($content->name) && $content->name === $defaultName - && $this->redisCache->hasItem($cacheKey)) { + && $this->appCache->hasItem($cacheKey)) { try { - $this->redisCache->deleteItem($cacheKey); + $this->appCache->deleteItem($cacheKey); } catch (\Exception $e) { $this->logger->error('Error deleting user cache item.', ['exception' => $e]); } @@ -145,7 +145,7 @@ readonly class RedisCacheService } $cacheKey = '0_with_raw_' . $pubkey; try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { $item->expiresAfter(3600); // 1 hour, adjust as needed $rawEvent = $this->fetchRawUserEvent($pubkey); $contentData = $this->parseUserMetadata($rawEvent, $pubkey); @@ -186,7 +186,7 @@ readonly class RedisCacheService $result = []; $cacheKeys = array_map(fn($pubkey) => $this->getUserCacheKey($pubkey), $pubkeys); $pubkeyMap = array_combine($cacheKeys, $pubkeys); - $items = $this->redisCache->getItems($cacheKeys); + $items = $this->appCache->getItems($cacheKeys); foreach ($items as $cacheKey => $item) { $pubkey = $pubkeyMap[$cacheKey]; if ($item->isHit()) { @@ -205,7 +205,7 @@ readonly class RedisCacheService $cacheKey = '10002_' . $npub; try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($npub) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($npub) { $item->expiresAfter(3600); // 1 hour, adjust as needed try { $relays = $this->nostrClient->getNpubRelays($npub); @@ -230,7 +230,7 @@ readonly class RedisCacheService { // redis cache lookup of magazine index by slug $key = 'magazine-index-' . $slug; - return $this->redisCache->get($key, function (ItemInterface $item) use ($slug) { + return $this->appCache->get($key, function (ItemInterface $item) use ($slug) { $item->expiresAfter(3600); // 1 hour $nzines = $this->entityManager->getRepository(Event::class)->findBy(['kind' => KindsEnum::PUBLICATION_INDEX]); @@ -271,9 +271,9 @@ readonly class RedisCacheService // Insert the new article tag at the top array_unshift($index->tags, $articleTag); try { - $item = $this->redisCache->getItem($key); + $item = $this->appCache->getItem($key); $item->set($index); - $this->redisCache->save($item); + $this->appCache->save($item); return true; } catch (\Exception $e) { $this->logger->error('Error updating magazine index.', ['exception' => $e]); @@ -292,7 +292,7 @@ readonly class RedisCacheService { $cacheKey = 'media_' . $npub . '_' . $limit; try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($npub, $limit) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($npub, $limit) { $item->expiresAfter(600); // 10 minutes cache for media events try { @@ -340,7 +340,7 @@ readonly class RedisCacheService try { // Fetch and cache all media events - $allMediaEvents = $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { + $allMediaEvents = $this->appCache->get($cacheKey, function (ItemInterface $item) use ($pubkey) { $item->expiresAfter(600); // 10 minutes cache try { @@ -411,7 +411,7 @@ readonly class RedisCacheService $cacheKey = 'event_' . $eventId . ($relays ? '_' . md5(json_encode($relays)) : ''); try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($eventId, $relays) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($eventId, $relays) { $item->expiresAfter(1800); // 30 minutes cache for events try { @@ -439,7 +439,7 @@ readonly class RedisCacheService $cacheKey = 'naddr_' . $decodedData['kind'] . '_' . $decodedData['pubkey'] . '_' . $decodedData['identifier'] . '_' . md5(json_encode($decodedData['relays'] ?? [])); try { - return $this->redisCache->get($cacheKey, function (ItemInterface $item) use ($decodedData) { + return $this->appCache->get($cacheKey, function (ItemInterface $item) use ($decodedData) { $item->expiresAfter(1800); // 30 minutes cache for naddr events try { @@ -462,10 +462,10 @@ readonly class RedisCacheService $npub = $key->convertPublicKeyToBech32($event->getPublicKey()); $cacheKey = '0_' . $npub; try { - $item = $this->redisCache->getItem($cacheKey); + $item = $this->appCache->getItem($cacheKey); $item->set(json_decode($event->getContent())); $item->expiresAfter(3600); // 1 hour - $this->redisCache->save($item); + $this->appCache->save($item); } catch (\Exception $e) { $this->logger->error('Error setting user metadata.', ['exception' => $e]); } diff --git a/src/Twig/Components/Organisms/Comments.php b/src/Twig/Components/Organisms/Comments.php index 67f5c4a..c74da7b 100644 --- a/src/Twig/Components/Organisms/Comments.php +++ b/src/Twig/Components/Organisms/Comments.php @@ -2,12 +2,14 @@ namespace App\Twig\Components\Organisms; +use App\Message\FetchCommentsMessage; use App\Service\NostrClient; use App\Service\NostrLinkParser; use App\Service\RedisCacheService; +use Symfony\Component\Messenger\MessageBusInterface; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; -#[AsTwigComponent] +#[AsTwigComponent()] final class Comments { public array $list = []; @@ -16,13 +18,17 @@ final class Comments public array $zapAmounts = []; public array $zappers = []; public array $authorsMetadata = []; + public bool $loading = true; + + private MessageBusInterface $bus; public function __construct( private readonly NostrClient $nostrClient, private readonly NostrLinkParser $nostrLinkParser, - private readonly RedisCacheService $redisCacheService - + private readonly RedisCacheService $redisCacheService, + MessageBusInterface $bus ) { + $this->bus = $bus; } /** @@ -30,25 +36,11 @@ final class Comments */ public function mount($current): void { - // Fetch comments - $this->list = $this->nostrClient->getComments($current); - // sort list by created_at descending - usort($this->list, fn($a, $b) => ($b->created_at ?? 0) <=> ($a->created_at ?? 0)); - // Parse Nostr links in comments but don't fetch previews - $this->parseNostrLinks(); - // Parse Zaps to get amounts and zappers from receipts - $this->parseZaps(); - // Collect all unique pubkeys for batch metadata fetching - $pubkeys = []; - foreach ($this->list as $comment) { - if ($comment->kind != 9735) { - $pubkeys[] = $comment->pubkey; - } elseif (isset($this->zappers[$comment->id])) { - $pubkeys[] = $this->zappers[$comment->id]; - } - } - $pubkeys = array_unique($pubkeys); - $this->authorsMetadata = $this->redisCacheService->getMultipleMetadata($pubkeys); + // Instead of fetching comments directly, dispatch async message + $this->loading = true; + $this->list = []; + $this->bus->dispatch(new FetchCommentsMessage($current)); + // The actual comments will be loaded via Mercure on the frontend } /** diff --git a/src/Util/NostrPhp/TweakedRequest.php b/src/Util/NostrPhp/TweakedRequest.php index 53a49ea..20b1c1e 100644 --- a/src/Util/NostrPhp/TweakedRequest.php +++ b/src/Util/NostrPhp/TweakedRequest.php @@ -3,6 +3,8 @@ declare(strict_types=1); namespace App\Util\NostrPhp; +use Psr\Log\LoggerInterface; +use swentel\nostr\Key\Key; use swentel\nostr\Message\AuthMessage; use swentel\nostr\Message\CloseMessage; use swentel\nostr\MessageInterface; @@ -22,6 +24,7 @@ use WebSocket\Message\Text; */ final class TweakedRequest implements RequestInterface { + private $nsec; private RelaySet $relays; private string $payload; private array $responses = []; @@ -29,7 +32,7 @@ final class TweakedRequest implements RequestInterface /** Optional: when set, CLOSE & disconnect immediately once this id arrives */ private ?string $stopOnEventId = null; - public function __construct(Relay|RelaySet $relay, MessageInterface $message) + public function __construct(Relay|RelaySet $relay, MessageInterface $message, private readonly LoggerInterface $logger) { if ($relay instanceof RelaySet) { $this->relays = $relay; @@ -39,6 +42,10 @@ final class TweakedRequest implements RequestInterface $this->relays = $set; } $this->payload = $message->generate(); + + // Create an ephemeral key for NIP-42 auth + $key = new Key(); + $this->nsec = $key->generatePrivateKey(); } public function stopOnEventId(?string $hexId): self @@ -51,7 +58,6 @@ final class TweakedRequest implements RequestInterface public function send(): array { $result = []; - foreach ($this->relays->getRelays() as $relay) { $this->responses = []; // reset per relay try { @@ -125,6 +131,15 @@ final class TweakedRequest implements RequestInterface $client->text($this->payload); // continue loop } + + // NIP-42: handle AUTH challenge for subscriptions + if ($relayResponse->type === 'AUTH') { + $raw = json_decode($resp->getContent(), true); + $_SESSION['challenge'] = $raw[1] ?? ''; + $this->logger->warning('Received AUTH challenge from relay: ' . $relay->getUrl()); + $this->performAuth($relay, $client); + // continue loop, relay should now respond to the subscription + } } // Save what we got for this relay @@ -150,16 +165,14 @@ final class TweakedRequest implements RequestInterface /** Very lightweight NIP-42 auth flow: sign challenge and send AUTH + resume. */ private function performAuth(Relay $relay, WsClient $client): void { - // NOTE: This reuses the vendor types, but uses a dummy secret. You should inject your real sec key. if (!isset($_SESSION['challenge'])) { return; } try { $authEvent = new AuthEvent($relay->getUrl(), $_SESSION['challenge']); - $sec = '0000000000000000000000000000000000000000000000000000000000000001'; // TODO inject your real sec - (new Sign())->signEvent($authEvent, $sec); - + (new Sign())->signEvent($authEvent, $this->nsec); $authMsg = new AuthMessage($authEvent); + $this->logger->warning('Sending NIP-42 AUTH to relay: ' . $relay->getUrl()); $client->text($authMsg->generate()); } catch (\Throwable) { // ignore and continue; some relays won’t require it diff --git a/symfony.lock b/symfony.lock index 351cda0..b8dd67e 100644 --- a/symfony.lock +++ b/symfony.lock @@ -101,6 +101,18 @@ ".env.dev" ] }, + "symfony/form": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "7.2", + "ref": "7d86a6723f4a623f59e2bf966b6aad2fc461d36b" + }, + "files": [ + "config/packages/csrf.yaml" + ] + }, "symfony/framework-bundle": { "version": "7.1", "recipe": { diff --git a/templates/base.html.twig b/templates/base.html.twig index 8b20d82..2ed915b 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -23,6 +23,7 @@ {% block javascripts %} {% block importmap %}{{ importmap('app') }}{% endblock %} {% endblock %} + diff --git a/templates/components/Organisms/Comments.html.twig b/templates/components/Organisms/Comments.html.twig index 46aadb8..7201369 100644 --- a/templates/components/Organisms/Comments.html.twig +++ b/templates/components/Organisms/Comments.html.twig @@ -1,61 +1,54 @@ -
- {% for item in list %} -
- - -
- {% if item.kind is defined and item.kind == '9735' %} -
- {% if zapAmounts[item.id] is defined %} - {{ zapAmounts[item.id] }} sat +
+ {% if loading %} +
Loading comments…
+ {% endif %} +
+ {% for item in list %} +
+ - {% endif %} - -
+ {{ item.created_at|date('F j Y') }} +
+
+ {% if item.kind is defined and item.kind == '9735' %} +
+ {% if zapAmounts[item.id] is defined %} + {{ zapAmounts[item.id] }} sat + {% else %} + Zap + {% endif %} +
+ {% endif %} + +
- {# Display Nostr link previews if links detected #} - {% if commentLinks[item.id] is defined and commentLinks[item.id]|length > 0 %} - - {% endfor %} + {% endif %} +
+ {% else %} +
No comments yet.
+ {% endfor %} +