From 6a9509c12616d16c1bf530c3d1d63483e8f15009 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Tue, 3 Jun 2025 10:56:58 -0400 Subject: [PATCH 1/8] local env --- .../wp-graphql-headless-webhooks/.wp-env.json | 24 + .../composer.json | 2 +- .../composer.lock | 812 +++++++++++++----- .../wp-graphql-headless-webhooks/package.json | 18 + pnpm-lock.yaml | 6 + 5 files changed, 624 insertions(+), 238 deletions(-) create mode 100644 plugins/wp-graphql-headless-webhooks/.wp-env.json create mode 100644 plugins/wp-graphql-headless-webhooks/package.json diff --git a/plugins/wp-graphql-headless-webhooks/.wp-env.json b/plugins/wp-graphql-headless-webhooks/.wp-env.json new file mode 100644 index 00000000..51dc3958 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/.wp-env.json @@ -0,0 +1,24 @@ +{ + "core": null, + "phpVersion": "8.0", + "plugins": [ + "https://downloads.wordpress.org/plugin/wp-graphql.latest-stable.zip", + "." + ], + "port": 8889, + "testsPort": 8890, + "config": { + "WP_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": false, + "WP_ENVIRONMENT_TYPE": "development" + }, + "mappings": { + "wp-content/plugins/wp-graphql-headless-webhooks": "." + }, + "env": { + "tests": { + "port": 8890 + } + } +} diff --git a/plugins/wp-graphql-headless-webhooks/composer.json b/plugins/wp-graphql-headless-webhooks/composer.json index 69dde89d..e7a4962f 100644 --- a/plugins/wp-graphql-headless-webhooks/composer.json +++ b/plugins/wp-graphql-headless-webhooks/composer.json @@ -11,7 +11,7 @@ } ], "require": { - "php": "^7.4 || ^8.0", + "php": "^7.4 || ^8.0 || ^8.4", "axepress/wp-graphql-plugin-boilerplate": "^0.1.0" }, "require-dev": { diff --git a/plugins/wp-graphql-headless-webhooks/composer.lock b/plugins/wp-graphql-headless-webhooks/composer.lock index b2fa58da..3d4b6bcb 100644 --- a/plugins/wp-graphql-headless-webhooks/composer.lock +++ b/plugins/wp-graphql-headless-webhooks/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": "c95ce760de9d78f5a1b0c1e16bbc33a4", + "content-hash": "038a2fe0d6aef203f3bc661d6115e6ed", "packages": [ { "name": "axepress/wp-graphql-plugin-boilerplate", @@ -69,43 +69,36 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.4", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "url": "https://api.github.com/repos/amphp/amp/zipball/7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", + "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "react/promise": "^2", - "vimeo/psalm": "^3.12" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { "files": [ - "lib/functions.php", - "lib/Internal/functions.php" + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "Amp\\": "lib" + "Amp\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -113,10 +106,6 @@ "MIT" ], "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, { "name": "Aaron Piotrowski", "email": "aaron@trowski.com" @@ -128,6 +117,10 @@ { "name": "Niklas Keller", "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], "description": "A non-blocking concurrency framework for PHP applications.", @@ -144,9 +137,8 @@ "promise" ], "support": { - "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.4" + "source": "https://github.com/amphp/amp/tree/v3.1.0" }, "funding": [ { @@ -154,41 +146,45 @@ "type": "github" } ], - "time": "2024-03-21T18:52:26+00:00" + "time": "2025-01-26T16:07:39+00:00" }, { "name": "amphp/byte-stream", - "version": "v1.8.2", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { - "amphp/amp": "^2", - "php": ">=7.1" + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" }, "type": "library", "autoload": { "files": [ - "lib/functions.php" + "src/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "Amp\\ByteStream\\": "lib" + "Amp\\ByteStream\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -217,7 +213,136 @@ ], "support": { "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "7b52598c2e9105ebcddf247fc523161581930367" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" }, "funding": [ { @@ -225,7 +350,140 @@ "type": "github" } ], - "time": "2024-04-13T18:00:56+00:00" + "time": "2025-03-16T16:33:53+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" }, { "name": "automattic/vipwpcs", @@ -392,25 +650,31 @@ }, { "name": "behat/gherkin", - "version": "v4.10.0", + "version": "v4.14.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6" + "reference": "34c9b59c59355a7b4c53b9f041c8dbd1c8acc3b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", - "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/34c9b59c59355a7b4c53b9f041c8dbd1c8acc3b4", + "reference": "34c9b59c59355a7b4c53b9f041c8dbd1c8acc3b4", "shasum": "" }, "require": { - "php": "~7.2|~8.0" + "composer-runtime-api": "^2.2", + "php": "8.1.* || 8.2.* || 8.3.* || 8.4.*" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-24.1.0", - "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5|~6|~7" + "cucumber/gherkin-monorepo": "dev-gherkin-v32.1.1", + "friendsofphp/php-cs-fixer": "^3.65", + "mikey179/vfsstream": "^1.6", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^2", + "phpstan/phpstan-phpunit": "^2", + "phpunit/phpunit": "^10.5", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -422,8 +686,8 @@ } }, "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" + "psr-4": { + "Behat\\Gherkin\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -434,11 +698,11 @@ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "homepage": "https://everzet.com" } ], "description": "Gherkin DSL parser for PHP", - "homepage": "http://behat.org/", + "homepage": "https://behat.org/", "keywords": [ "BDD", "Behat", @@ -449,9 +713,9 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.10.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.14.0" }, - "time": "2024-10-19T14:46:06+00:00" + "time": "2025-05-23T15:06:40+00:00" }, { "name": "codeception/codeception", @@ -1136,16 +1400,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.5.6", + "version": "1.5.7", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "f65c239c970e7f072f067ab78646e9f0b2935175" + "reference": "d665d22c417056996c59019579f1967dfe5c1e82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/f65c239c970e7f072f067ab78646e9f0b2935175", - "reference": "f65c239c970e7f072f067ab78646e9f0b2935175", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d665d22c417056996c59019579f1967dfe5c1e82", + "reference": "d665d22c417056996c59019579f1967dfe5c1e82", "shasum": "" }, "require": { @@ -1192,7 +1456,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.6" + "source": "https://github.com/composer/ca-bundle/tree/1.5.7" }, "funding": [ { @@ -1208,7 +1472,7 @@ "type": "tidelift" } ], - "time": "2025-03-06T14:30:56+00:00" + "time": "2025-05-26T15:08:54+00:00" }, { "name": "composer/class-map-generator", @@ -1937,30 +2201,30 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -1987,7 +2251,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -2003,7 +2267,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "eftec/bladeone", @@ -3178,25 +3442,27 @@ }, { "name": "nikic/php-parser", - "version": "v4.19.4", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -3204,7 +3470,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -3228,9 +3494,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-09-29T15:01:53+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -4377,16 +4643,16 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.16", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { @@ -4431,7 +4697,7 @@ "type": "github" } ], - "time": "2025-05-16T09:40:10+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -4960,22 +5226,27 @@ }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -5002,9 +5273,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -5218,30 +5489,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -5262,9 +5533,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/2.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:41:46+00:00" }, { "name": "ralouphie/getallheaders", @@ -5383,6 +5654,78 @@ ], "time": "2024-05-24T10:39:05+00:00" }, + { + "name": "revolt/event-loop", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3", + "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7" + }, + "time": "2025-01-25T19:27:39+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.2", @@ -6578,32 +6921,32 @@ }, { "name": "slevomat/coding-standard", - "version": "8.18.0", + "version": "8.18.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593" + "reference": "06b18b3f64979ab31d27c37021838439f3ed5919" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/f3b23cb9b26301b8c3c7bb03035a1bee23974593", - "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/06b18b3f64979ab31d27c37021838439f3ed5919", + "reference": "06b18b3f64979ab31d27c37021838439f3ed5919", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.4 || ^8.0", "phpstan/phpdoc-parser": "^2.1.0", - "squizlabs/php_codesniffer": "^3.12.2" + "squizlabs/php_codesniffer": "^3.13.0" }, "require-dev": { "phing/phing": "3.0.1", "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.13", - "phpstan/phpstan-deprecation-rules": "2.0.2", + "phpstan/phpstan": "2.1.17", + "phpstan/phpstan-deprecation-rules": "2.0.3", "phpstan/phpstan-phpunit": "2.0.6", "phpstan/phpstan-strict-rules": "2.0.4", - "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.17|12.1.3" + "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.21|12.1.3" }, "type": "phpcodesniffer-standard", "extra": { @@ -6627,7 +6970,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.18.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.18.1" }, "funding": [ { @@ -6639,33 +6982,32 @@ "type": "tidelift" } ], - "time": "2025-05-01T09:40:50+00:00" + "time": "2025-05-22T14:32:30+00:00" }, { "name": "softcreatr/jsonpath", - "version": "0.7.6", + "version": "0.8.3", "source": { "type": "git", "url": "https://github.com/SoftCreatR/JSONPath.git", - "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076" + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/e04c02cb78bcc242c69d17dac5b29436bf3e1076", - "reference": "e04c02cb78bcc242c69d17dac5b29436bf3e1076", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=7.1,<8.0" + "php": ">=8.0" }, "replace": { "flow/jsonpath": "*" }, "require-dev": { - "phpunit/phpunit": ">=7.0", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.5" + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "autoload": { @@ -6708,33 +7050,37 @@ "type": "github" } ], - "time": "2022-09-27T09:27:12+00:00" + "time": "2023-08-17T20:14:00+00:00" }, { "name": "spatie/array-to-xml", - "version": "2.17.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46" + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", - "reference": "5cbec9c6ab17e320c58a259f0cebe88bde4a7c46", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", "shasum": "" }, "require": { "ext-dom": "*", - "php": "^7.4|^8.0" + "php": "^8.0" }, "require-dev": { "mockery/mockery": "^1.2", "pestphp/pest": "^1.21", - "phpunit/phpunit": "^9.0", "spatie/pest-plugin-snapshots": "^1.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { "Spatie\\ArrayToXml\\": "src" @@ -6760,7 +7106,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/2.17.1" + "source": "https://github.com/spatie/array-to-xml/tree/3.4.0" }, "funding": [ { @@ -6772,7 +7118,7 @@ "type": "github" } ], - "time": "2022-12-26T08:22:07+00:00" + "time": "2024-12-16T12:45:15+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -6932,38 +7278,34 @@ }, { "name": "symfony/config", - "version": "v5.4.46", + "version": "v6.4.22", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "977c88a02d7d3f16904a81907531b19666a08e78" + "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/977c88a02d7d3f16904a81907531b19666a08e78", - "reference": "977c88a02d7d3f16904a81907531b19666a08e78", + "url": "https://api.github.com/repos/symfony/config/zipball/af5917a3b1571f54689e56677a3f06440d2fe4c7", + "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<4.4" + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6991,7 +7333,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/v5.4.46" + "source": "https://github.com/symfony/config/tree/v6.4.22" }, "funding": [ { @@ -7007,7 +7349,7 @@ "type": "tidelift" } ], - "time": "2024-10-30T07:58:02+00:00" + "time": "2025-05-14T06:00:01+00:00" }, { "name": "symfony/console", @@ -7176,20 +7518,20 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.4", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", - "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { @@ -7198,7 +7540,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -7223,7 +7565,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -7239,7 +7581,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/dom-crawler", @@ -7403,25 +7745,22 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.4", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f" + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", - "reference": "e0fe3d79b516eb75126ac6fa4cbf19b79b08c99f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "thanks": { @@ -7429,7 +7768,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -7462,7 +7801,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -7478,30 +7817,29 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.45", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/57c8294ed37d4a055b77057827c67f9558c95c54", - "reference": "57c8294ed37d4a055b77057827c67f9558c95c54", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4" + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7529,7 +7867,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.45" + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" }, "funding": [ { @@ -7545,7 +7883,7 @@ "type": "tidelift" } ], - "time": "2024-10-22T13:05:35+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/finder", @@ -8163,29 +8501,26 @@ }, { "name": "symfony/service-contracts", - "version": "v2.5.4", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f37b419f7aea2e9abf10abd261832cace12e3300" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f37b419f7aea2e9abf10abd261832cace12e3300", - "reference": "f37b419f7aea2e9abf10abd261832cace12e3300", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "thanks": { @@ -8193,13 +8528,16 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8226,7 +8564,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.4" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -8242,25 +8580,25 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/stopwatch", - "version": "v5.4.45", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004" + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fb2c199cf302eb207f8c23e7ee174c1c31a5c004", - "reference": "fb2c199cf302eb207f8c23e7ee174c1c31a5c004", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -8288,7 +8626,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.45" + "source": "https://github.com/symfony/stopwatch/tree/v7.3.0" }, "funding": [ { @@ -8304,38 +8642,38 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:11:13+00:00" + "time": "2025-02-24T10:49:57+00:00" }, { "name": "symfony/string", - "version": "v5.4.47", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799" + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/136ca7d72f72b599f2631aca474a4f8e26719799", - "reference": "136ca7d72f72b599f2631aca474a4f8e26719799", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -8374,7 +8712,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.47" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { @@ -8390,7 +8728,7 @@ "type": "tidelift" } ], - "time": "2024-11-10T20:33:58+00:00" + "time": "2025-04-18T15:23:29+00:00" }, { "name": "symfony/yaml", @@ -8581,21 +8919,21 @@ }, { "name": "vimeo/psalm", - "version": "5.26.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" + "reference": "b8e96bb617bf59382113b1b56cef751f648a7dc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/b8e96bb617bf59382113b1b56cef751f648a7dc9", + "reference": "b8e96bb617bf59382113b1b56cef751f648a7dc9", "shasum": "" }, "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", + "amphp/amp": "^3", + "amphp/byte-stream": "^2", "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", @@ -8608,26 +8946,24 @@ "ext-simplexml": "*", "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.1", - "felixfbecker/language-server-protocol": "^1.5.2", + "felixfbecker/language-server-protocol": "^1.5.3", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.17", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.17 || ~8.2.4 || ~8.3.0 || ~8.4.0", "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" }, - "conflict": { - "nikic/php-parser": "4.17.0" - }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { - "amphp/phpunit-util": "^2.0", + "amphp/phpunit-util": "^3", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", + "dg/bypass-finals": "^1.5", "ext-curl": "*", "mockery/mockery": "^1.5", "nunomaduro/mock-final-classes": "^1.1", @@ -8635,7 +8971,7 @@ "phpstan/phpdoc-parser": "^1.6", "phpunit/phpunit": "^9.6", "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18", + "psalm/plugin-phpunit": "^0.19", "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.6", "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" @@ -8658,7 +8994,9 @@ "dev-2.x": "2.x-dev", "dev-3.x": "3.x-dev", "dev-4.x": "4.x-dev", - "dev-master": "5.x-dev" + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" } }, "autoload": { @@ -8687,7 +9025,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2024-09-08T18:53:08+00:00" + "time": "2025-01-26T12:03:19+00:00" }, { "name": "webmozart/assert", @@ -11230,8 +11568,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.4 || ^8.0" + "php": "^7.4 || ^8.0 || ^8.4" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/plugins/wp-graphql-headless-webhooks/package.json b/plugins/wp-graphql-headless-webhooks/package.json new file mode 100644 index 00000000..025a6ed2 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/package.json @@ -0,0 +1,18 @@ +{ + "name": "@placeholder/wp-graphql-headless-webhooks", + "version": "0.0.1", + "private": true, + "description": "WPGraphQL Headless Webhooks plugin", + "author": "WPEngine Headless OSS Team", + "license": "BSD-3-Clause", + "scripts": { + "dev": "wp-env start && wp-env run cli wp plugin activate wp-graphql wp-graphql-headless-webhooks", + "start": "wp-env start", + "stop": "wp-env stop", + "clean": "wp-env destroy", + "cli": "wp-env run cli wp" + }, + "devDependencies": { + "@wordpress/env": "^8.0.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f6d9344..eab0e4c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,12 @@ importers: specifier: ^8.13.0 version: 8.13.0 + plugins/wp-graphql-headless-webhooks: + devDependencies: + '@wordpress/env': + specifier: ^8.0.0 + version: 8.13.0 + packages: '@isaacs/cliui@8.0.2': From 39b8e5ef05bde1b634040ddcd0991ae05b5f2ad8 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Mon, 9 Jun 2025 16:22:28 -0400 Subject: [PATCH 2/8] Consolidate clean up scripts --- scripts/clean.sh | 19 ++++--------------- scripts/cleanup-wp-env.sh | 10 ---------- 2 files changed, 4 insertions(+), 25 deletions(-) delete mode 100755 scripts/cleanup-wp-env.sh diff --git a/scripts/clean.sh b/scripts/clean.sh index 365fb2bf..fa6778ca 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -1,21 +1,10 @@ #!/bin/bash -echo "Stopping wp-env containers..." -docker-compose -f ~/.wp-env/*/docker-compose.yml down 2>/dev/null || true - -echo "Removing wp-env containers..." +# Stop and remove all Docker containers with wp-env prefix docker ps -a -q -f name=wp-env | xargs -r docker rm -f -echo "Removing wp-env volumes..." -docker volume ls -q -f name=wp-env | xargs -r docker volume rm -f - -echo "Removing WordPress volumes..." -docker volume ls -q -f name=wordpress | xargs -r docker volume rm -f +# Remove wp-env state directories +rm -rf ~/.wp-env -echo "Removing all test volumes..." -docker volume ls -q -f name=tests-wordpress | xargs -r docker volume rm -f - -echo "Cleaning up networks..." +# Prune Docker networks docker network prune -f - -echo "Cleanup complete! You may need to wait a few seconds before starting wp-env again." diff --git a/scripts/cleanup-wp-env.sh b/scripts/cleanup-wp-env.sh deleted file mode 100755 index bcb0da4b..00000000 --- a/scripts/cleanup-wp-env.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Stop and remove all Docker containers with wp-env prefix -docker ps -a -q -f name=wp-env | xargs -r docker rm -f - -# Remove wp-env state directories -sudo rm -rf ~/.wp-env/* - -# Prune Docker networks -docker network prune -f From d5cdde9e24f8cf88c1c3fa491b2a83a8714a093b Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Mon, 9 Jun 2025 17:22:04 -0400 Subject: [PATCH 3/8] Hide default CPT since we use our own UI --- .../src/PostTypes/WebhookPostType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/wp-graphql-headless-webhooks/src/PostTypes/WebhookPostType.php b/plugins/wp-graphql-headless-webhooks/src/PostTypes/WebhookPostType.php index ae78d8ca..23f0c7b3 100644 --- a/plugins/wp-graphql-headless-webhooks/src/PostTypes/WebhookPostType.php +++ b/plugins/wp-graphql-headless-webhooks/src/PostTypes/WebhookPostType.php @@ -44,8 +44,8 @@ public static function register_webhook_cpt(): void { 'description' => 'Manages GraphQL Webhooks', 'taxonomies' => [], 'public' => false, - 'show_ui' => true, - 'show_in_menu' => true, + 'show_ui' => false, + 'show_in_menu' => false, 'show_in_admin_bar' => false, 'menu_icon' => 'dashicons-share-alt', 'show_in_nav_menus' => false, From e3e5e5568c7ac4ed41588975b9b9c4de089c9188 Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Mon, 9 Jun 2025 17:59:33 -0400 Subject: [PATCH 4/8] Webhooks admin UI --- .../src/Admin/assets/admin.css | 77 +++++++++ .../src/Admin/assets/admin.js | 48 ++++++ .../src/Admin/views/admin-notice.php | 20 +++ .../views/partials/webhook-header-row.php | 23 +++ .../src/Admin/views/webhook-form.php | 113 +++++++++++++ .../src/Admin/views/webhooks-list.php | 78 +++++++++ .../src/Plugin.php | 35 +++- .../src/Repository/WebhookRepository.php | 149 ++++++++++-------- 8 files changed, 476 insertions(+), 67 deletions(-) create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.css create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/views/admin-notice.php create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/views/webhook-form.php create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.css b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.css new file mode 100644 index 00000000..0845bc07 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.css @@ -0,0 +1,77 @@ +/** + * Admin styles for WPGraphQL Webhooks + */ + +/* Webhook header rows */ +.webhook-header-row { + margin-bottom: 10px; + display: flex; + gap: 10px; + align-items: center; +} + +.webhook-header-row input[type="text"] { + flex: 1; + min-width: 150px; +} + +.webhook-header-row .remove-header { + flex-shrink: 0; +} + +/* Add some spacing */ +#webhook-headers { + margin-bottom: 10px; +} + +/* Delete link styling */ +.delete-webhook { + color: #a00; +} + +.delete-webhook:hover { + color: #dc3232; +} + +/* Form table adjustments */ +.form-table th { + width: 200px; +} + +/* Webhook list table */ +.wp-list-table.webhooks-table td { + vertical-align: middle; +} + +.wp-list-table.webhooks-table .column-actions { + white-space: nowrap; +} + +/* Empty state */ +.webhooks-empty-state { + text-align: center; + padding: 40px 20px; +} + +.webhooks-empty-state p { + font-size: 16px; + margin-bottom: 20px; +} + +/* Responsive adjustments */ +@media screen and (max-width: 782px) { + .webhook-header-row { + flex-wrap: wrap; + } + + .webhook-header-row input[type="text"] { + width: 100%; + margin-bottom: 5px; + } + + .wp-list-table.webhooks-table { + display: block; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } +} diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js new file mode 100644 index 00000000..8eb557b5 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js @@ -0,0 +1,48 @@ +/** + * Admin JavaScript for WPGraphQL Webhooks + */ + +(function ($) { + 'use strict'; + + $( document ).ready( + function () { + // Handle adding new header fields + $( '#add-header' ).on( + 'click', + function () { + var headerRow = $( graphqlWebhooksAdmin.headerRowTemplate ); + $( '#webhook-headers' ).append( headerRow ); + } + ); + + // Handle removing header fields + $( document ).on( + 'click', + '.remove-header', + function () { + var headerRows = $( '.webhook-header-row' ); + + // Keep at least one header row + if (headerRows.length > 1) { + $( this ).closest( '.webhook-header-row' ).remove(); + } else { + // Clear the values instead of removing the last row + $( this ).closest( '.webhook-header-row' ).find( 'input' ).val( '' ); + } + } + ); + + // Confirm webhook deletion + $( '.delete-webhook' ).on( + 'click', + function (e) { + if ( ! confirm( graphqlWebhooksAdmin.confirmDelete )) { + e.preventDefault(); + } + } + ); + } + ); + +})( jQuery ); diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/admin-notice.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/admin-notice.php new file mode 100644 index 00000000..4f0de032 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/admin-notice.php @@ -0,0 +1,20 @@ + +
+

+
diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php new file mode 100644 index 00000000..f10f68a9 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php @@ -0,0 +1,23 @@ + +
+ + + +
diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhook-form.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhook-form.php new file mode 100644 index 00000000..9099e927 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhook-form.php @@ -0,0 +1,113 @@ + +
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +

+
+ + + +

+
+ + + +

+
+ + + +

+
+ + +
+ + $value ) : ?> + + + + + +
+ +

+
+ +

+ + + + +

+
+
diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php new file mode 100644 index 00000000..74be7891 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php @@ -0,0 +1,78 @@ + +
+

+ + + +
+ + +
+

+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
name ); ?> + event ] ) ? $events[ $webhook->event ] : $webhook->event; + echo esc_html( $event_label ); + ?> + url ); ?>method ); ?> + + + + + + +
+ +
diff --git a/plugins/wp-graphql-headless-webhooks/src/Plugin.php b/plugins/wp-graphql-headless-webhooks/src/Plugin.php index a31713b6..db4784d0 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Plugin.php +++ b/plugins/wp-graphql-headless-webhooks/src/Plugin.php @@ -10,10 +10,14 @@ namespace WPGraphQL\Webhooks; use AxeWP\GraphQL\Helper\Helper; +use WPGraphQL\Webhooks\Admin\WebhooksAdmin; use WPGraphQL\Webhooks\Handlers\WebhookHandler; use WPGraphQL\Webhooks\PostTypes\WebhookPostType; use WPGraphQL\Webhooks\Repository\WebhookRepository; use WPGraphQL\Webhooks\Events\WebhookEventManager; +use WPGraphQL\Webhooks\Rest\WebhookEventsEndpoint; +use WPGraphQL\Webhooks\Rest\WebhookTestEndpoint; +use WPGraphQL\Webhooks\Mutation\CreateWebhook; /** * Plugin singleton class. @@ -50,6 +54,13 @@ final class Plugin { */ private WebhookEventManager $event_manager; + /** + * Webhooks admin. + * + * @var WebhooksAdmin + */ + private WebhooksAdmin $admin; + /** * Get singleton instance. * @@ -79,10 +90,28 @@ private function setup(): void { Helper::set_hook_prefix( 'graphql_webhooks' ); WebhookPostType::init(); - $this->repository = new WebhookRepository(); - $this->handler = new WebhookHandler(); + $this->repository = new WebhookRepository(); + $this->handler = new WebhookHandler(); $this->event_manager = new WebhookEventManager( $this->repository, $this->handler ); $this->event_manager->register_hooks(); + + // Register REST endpoints + if ( class_exists( WebhookEventsEndpoint::class ) ) { + $events_endpoint = new WebhookEventsEndpoint( $this->repository ); + add_action( 'rest_api_init', array( $events_endpoint, 'register' ) ); + } + + // Register test endpoint + if ( class_exists( WebhookTestEndpoint::class ) ) { + $test_endpoint = new WebhookTestEndpoint( $this->repository ); + add_action( 'rest_api_init', array( $test_endpoint, 'register' ) ); + } + + // Initialize admin UI + if ( is_admin() ) { + $this->admin = new WebhooksAdmin( $this->repository ); + $this->admin->init(); + } } /** @@ -113,4 +142,4 @@ public function __wakeup(): void { } } -endif; \ No newline at end of file +endif; diff --git a/plugins/wp-graphql-headless-webhooks/src/Repository/WebhookRepository.php b/plugins/wp-graphql-headless-webhooks/src/Repository/WebhookRepository.php index e1f102e4..167a58e0 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Repository/WebhookRepository.php +++ b/plugins/wp-graphql-headless-webhooks/src/Repository/WebhookRepository.php @@ -16,7 +16,7 @@ class WebhookRepository implements WebhookRepositoryInterface { /** * Allowed event keys and labels for UI. */ - private array $default_events = [ + private array $default_events = array( 'post_published' => 'Post Published', 'post_updated' => 'Post Updated', 'post_deleted' => 'Post Deleted', @@ -35,111 +35,132 @@ class WebhookRepository implements WebhookRepositoryInterface { 'media_deleted' => 'Media Deleted', 'comment_inserted' => 'Comment Inserted', 'comment_status' => 'Comment Status Changed', - ]; + ); public function get_allowed_events(): array { - return apply_filters('graphql_webhooks_allowed_events', $this->default_events); + return apply_filters( 'graphql_webhooks_allowed_events', $this->default_events ); + } + + /** + * Get allowed HTTP methods for webhooks. + * + * @return array Array of method values and labels. + */ + public function get_allowed_methods(): array { + return apply_filters( + 'graphql_webhooks_allowed_methods', + array( + 'POST' => __( 'POST', 'wp-graphql-headless-webhooks' ), + 'GET' => __( 'GET', 'wp-graphql-headless-webhooks' ), + ) + ); } public function get_all(): array { - $webhooks = []; - - $posts = get_posts([ - 'post_type' => 'graphql_webhook', - 'post_status' => 'publish', - 'numberposts' => -1, - ]); - - foreach ($posts as $post) { - $webhooks[] = $this->mapPostToEntity($post); + $webhooks = array(); + + $posts = get_posts( + array( + 'post_type' => 'graphql_webhook', + 'post_status' => 'publish', + 'numberposts' => -1, + ) + ); + + foreach ( $posts as $post ) { + $webhooks[] = $this->mapPostToEntity( $post ); } return $webhooks; } - public function get(int $id): ?Webhook { - $post = get_post($id); - if (!$post || $post->post_type !== 'graphql_webhook') { + public function get( int $id ): ?Webhook { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { return null; } - return $this->mapPostToEntity($post); + return $this->mapPostToEntity( $post ); } - public function create(string $name, string $event, string $url, string $method, array $headers): int|WP_Error { - $validation = $this->validate_data($event, $url, $method); - if (is_wp_error($validation)) { + public function create( string $name, string $event, string $url, string $method, array $headers ): int|WP_Error { + $validation = $this->validate_data( $event, $url, $method ); + if ( is_wp_error( $validation ) ) { return $validation; } - $postId = wp_insert_post([ - 'post_title' => $name, - 'post_type' => 'graphql_webhook', - 'post_status' => 'publish', - ], true); + $postId = wp_insert_post( + array( + 'post_title' => $name, + 'post_type' => 'graphql_webhook', + 'post_status' => 'publish', + ), + true + ); - if (is_wp_error($postId)) { + if ( is_wp_error( $postId ) ) { return $postId; } - update_post_meta($postId, '_webhook_event', sanitize_text_field($event)); - update_post_meta($postId, '_webhook_url', esc_url_raw($url)); - update_post_meta($postId, '_webhook_method', strtoupper($method)); - update_post_meta($postId, '_webhook_headers', wp_json_encode($headers)); + update_post_meta( $postId, '_webhook_event', sanitize_text_field( $event ) ); + update_post_meta( $postId, '_webhook_url', esc_url_raw( $url ) ); + update_post_meta( $postId, '_webhook_method', strtoupper( $method ) ); + update_post_meta( $postId, '_webhook_headers', wp_json_encode( $headers ) ); return $postId; } - public function update(int $id, string $name, string $event, string $url, string $method, array $headers): bool|WP_Error { - $post = get_post($id); - if (!$post || $post->post_type !== 'graphql_webhook') { - return new WP_Error('invalid_webhook', __('Webhook not found.', 'wp-graphql-headless-webhooks')); + public function update( int $id, string $name, string $event, string $url, string $method, array $headers ): bool|WP_Error { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { + return new WP_Error( 'invalid_webhook', __( 'Webhook not found.', 'wp-graphql-headless-webhooks' ) ); } - $validation = $this->validate_data($event, $url, $method); - if (is_wp_error($validation)) { + $validation = $this->validate_data( $event, $url, $method ); + if ( is_wp_error( $validation ) ) { return $validation; } - $postData = [ + $postData = array( 'ID' => $id, - 'post_title' => sanitize_text_field($name), - ]; + 'post_title' => sanitize_text_field( $name ), + ); - $updated = wp_update_post($postData, true); - if (is_wp_error($updated)) { + $updated = wp_update_post( $postData, true ); + if ( is_wp_error( $updated ) ) { return $updated; } - update_post_meta($id, '_webhook_event', sanitize_text_field($event)); - update_post_meta($id, '_webhook_url', esc_url_raw($url)); - update_post_meta($id, '_webhook_method', strtoupper($method)); - update_post_meta($id, '_webhook_headers', wp_json_encode($headers)); + update_post_meta( $id, '_webhook_event', sanitize_text_field( $event ) ); + update_post_meta( $id, '_webhook_url', esc_url_raw( $url ) ); + update_post_meta( $id, '_webhook_method', strtoupper( $method ) ); + update_post_meta( $id, '_webhook_headers', wp_json_encode( $headers ) ); return true; } - public function delete(int $id): bool { - $post = get_post($id); - if (!$post || $post->post_type !== 'graphql_webhook') { + public function delete( int $id ): bool { + $post = get_post( $id ); + if ( ! $post || $post->post_type !== 'graphql_webhook' ) { return false; } - $deleted = wp_delete_post($id, true); + $deleted = wp_delete_post( $id, true ); return (bool) $deleted; } - public function validate_data(string $event, string $url, string $method): bool|WP_Error { - if (!isset($this->get_allowed_events()[$event])) { - return new WP_Error('invalid_event', 'Invalid event type.'); + public function validate_data( string $event, string $url, string $method ): bool|WP_Error { + if ( ! isset( $this->get_allowed_events()[ $event ] ) ) { + return new WP_Error( 'invalid_event', 'Invalid event type.' ); } - if (!filter_var($url, FILTER_VALIDATE_URL)) { - return new WP_Error('invalid_url', 'Invalid URL.'); + if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { + return new WP_Error( 'invalid_url', 'Invalid URL.' ); } - if (!in_array(strtoupper($method), ['GET', 'POST'], true)) { - return new WP_Error('invalid_method', 'Invalid HTTP method.'); + $allowed_methods = array_keys( $this->get_allowed_methods() ); + if ( ! in_array( strtoupper( $method ), $allowed_methods, true ) ) { + return new WP_Error( 'invalid_method', 'Invalid HTTP method.' ); } - return apply_filters('graphql_webhooks_validate_data', true, $event, $url, $method); + return apply_filters( 'graphql_webhooks_validate_data', true, $event, $url, $method ); } /** @@ -149,12 +170,12 @@ public function validate_data(string $event, string $url, string $method): bool| * * @return Webhook The mapped Webhook entity. */ - private function mapPostToEntity(WP_Post $post): Webhook { - $event = get_post_meta($post->ID, '_webhook_event', true); - $url = get_post_meta($post->ID, '_webhook_url', true); - $method = get_post_meta($post->ID, '_webhook_method', true) ?: 'POST'; - $headers = get_post_meta($post->ID, '_webhook_headers', true); - $headers = $headers ? json_decode($headers, true) : []; + private function mapPostToEntity( WP_Post $post ): Webhook { + $event = get_post_meta( $post->ID, '_webhook_event', true ); + $url = get_post_meta( $post->ID, '_webhook_url', true ); + $method = get_post_meta( $post->ID, '_webhook_method', true ) ?: 'POST'; + $headers = get_post_meta( $post->ID, '_webhook_headers', true ); + $headers = $headers ? json_decode( $headers, true ) : array(); return new Webhook( $post->ID, @@ -165,4 +186,4 @@ private function mapPostToEntity(WP_Post $post): Webhook { $headers ); } -} \ No newline at end of file +} From 9cc8176f6529df52dd3b7364a93bd7198770d5cf Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Mon, 9 Jun 2025 18:29:02 -0400 Subject: [PATCH 5/8] Add missing file --- .../src/Admin/WebhooksAdmin.php | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php b/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php new file mode 100644 index 00000000..d5ccec09 --- /dev/null +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php @@ -0,0 +1,295 @@ +repository = $repository; + } + + /** + * Initialize admin hooks + */ + public function init(): void { + add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); + add_action( 'admin_init', array( $this, 'handle_actions' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); + } + + /** + * Add admin menu + */ + public function add_admin_menu(): void { + add_options_page( + __( 'Webhooks', 'wp-graphql-headless-webhooks' ), + __( 'Webhooks', 'wp-graphql-headless-webhooks' ), + 'manage_options', + self::ADMIN_PAGE_SLUG, + array( $this, 'render_admin_page' ) + ); + } + + /** + * Get admin URL helper + * + * @param array $args Query arguments. + * @return string + */ + public function get_admin_url( array $args = array() ): string { + $defaults = array( 'page' => self::ADMIN_PAGE_SLUG ); + $args = wp_parse_args( $args, $defaults ); + return admin_url( 'options-general.php?' . http_build_query( $args ) ); + } + + /** + * Enqueue admin assets + * + * @param string $hook_suffix Current admin page. + */ + public function enqueue_assets( string $hook_suffix ): void { + if ( 'settings_page_' . self::ADMIN_PAGE_SLUG !== $hook_suffix ) { + return; + } + + $plugin_url = plugin_dir_url( dirname( __DIR__ ) ); + + wp_enqueue_style( + 'wp-graphql-webhooks-admin', + $plugin_url . 'src/Admin/assets/admin.css', + array(), + '1.0.0' + ); + + wp_enqueue_script( + 'wp-graphql-webhooks-admin', + $plugin_url . 'src/Admin/assets/admin.js', + array( 'jquery' ), + '1.0.0', + true + ); + + wp_localize_script( + 'wp-graphql-webhooks-admin', + 'wpGraphQLWebhooks', + array( + 'restUrl' => rest_url( 'graphql-webhooks/v1/' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + 'headerTemplate' => $this->get_header_row_template(), + ) + ); + } + + /** + * Get header row template for JavaScript + * + * @return string + */ + private function get_header_row_template(): string { + ob_start(); + include __DIR__ . '/views/partials/webhook-header-row.php'; + return ob_get_clean(); + } + + /** + * Handle admin actions + */ + public function handle_actions(): void { + if ( ! isset( $_GET['page'] ) || self::ADMIN_PAGE_SLUG !== $_GET['page'] ) { + return; + } + + if ( isset( $_POST['action'] ) ) { + if ( 'save_webhook' === $_POST['action'] ) { + $this->handle_webhook_save(); + } + } + + if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] ) { + $this->handle_webhook_delete(); + } + } + + /** + * Verify admin permission + * + * @return bool + */ + private function verify_admin_permission(): bool { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-graphql-headless-webhooks' ) ); + return false; + } + return true; + } + + /** + * Verify nonce + * + * @param string $action Nonce action. + * @param string $nonce_field Nonce field name. + * @return bool + */ + private function verify_nonce( string $action, string $nonce_field = '_wpnonce' ): bool { + if ( ! isset( $_REQUEST[ $nonce_field ] ) || ! wp_verify_nonce( $_REQUEST[ $nonce_field ], $action ) ) { + wp_die( esc_html__( 'Security check failed.', 'wp-graphql-headless-webhooks' ) ); + return false; + } + return true; + } + + /** + * Handle webhook save + */ + private function handle_webhook_save(): void { + if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'webhook_save' ) ) { + return; + } + + $webhook_id = isset( $_POST['webhook_id'] ) ? intval( $_POST['webhook_id'] ) : 0; + $name = sanitize_text_field( $_POST['webhook_name'] ?? '' ); + $event = sanitize_text_field( $_POST['webhook_event'] ?? '' ); + $url = esc_url_raw( $_POST['webhook_url'] ?? '' ); + $method = sanitize_text_field( $_POST['webhook_method'] ?? 'POST' ); + + // Process headers + $headers = array(); + if ( ! empty( $_POST['webhook_headers']['name'] ) && is_array( $_POST['webhook_headers']['name'] ) ) { + foreach ( $_POST['webhook_headers']['name'] as $index => $header_name ) { + $header_name = sanitize_text_field( $header_name ); + $header_value = sanitize_text_field( $_POST['webhook_headers']['value'][ $index ] ?? '' ); + if ( ! empty( $header_name ) && ! empty( $header_value ) ) { + $headers[ $header_name ] = $header_value; + } + } + } + + if ( $webhook_id > 0 ) { + $result = $this->repository->update( $webhook_id, $name, $event, $url, $method, $headers ); + } else { + $result = $this->repository->create( $name, $event, $url, $method, $headers ); + } + + if ( is_wp_error( $result ) ) { + $redirect_url = $this->get_admin_url( array( 'error' => urlencode( $result->get_error_message() ) ) ); + } else { + $redirect_url = $this->get_admin_url( array( 'updated' => 'true' ) ); + } + + wp_safe_redirect( $redirect_url ); + exit; + } + + /** + * Handle webhook delete + */ + private function handle_webhook_delete(): void { + if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'delete_webhook' ) ) { + return; + } + + $webhook_id = isset( $_GET['webhook_id'] ) ? intval( $_GET['webhook_id'] ) : 0; + + if ( $webhook_id > 0 ) { + $this->repository->delete( $webhook_id ); + } + + wp_safe_redirect( $this->get_admin_url( array( 'deleted' => 'true' ) ) ); + exit; + } + + /** + * Render admin page + */ + public function render_admin_page(): void { + $action = $_GET['action'] ?? ''; + $admin = $this; + + // Display admin notices + if ( isset( $_GET['updated'] ) ) { + $message = __( 'Webhook saved successfully.', 'wp-graphql-headless-webhooks' ); + $type = 'success'; + include __DIR__ . '/views/admin-notice.php'; + } + + if ( isset( $_GET['deleted'] ) ) { + $message = __( 'Webhook deleted successfully.', 'wp-graphql-headless-webhooks' ); + $type = 'success'; + include __DIR__ . '/views/admin-notice.php'; + } + + if ( isset( $_GET['error'] ) ) { + $message = sanitize_text_field( $_GET['error'] ); + $type = 'error'; + include __DIR__ . '/views/admin-notice.php'; + } + + // Render appropriate view + if ( 'add' === $action || 'edit' === $action ) { + $webhook_id = isset( $_GET['webhook_id'] ) ? intval( $_GET['webhook_id'] ) : 0; + $webhook = null; + + if ( 'edit' === $action && $webhook_id > 0 ) { + $webhook = $this->repository->get( $webhook_id ); + if ( ! $webhook ) { + wp_die( esc_html__( 'Webhook not found.', 'wp-graphql-headless-webhooks' ) ); + } + } + + $events = $this->repository->get_allowed_events(); + $methods = $this->repository->get_allowed_methods(); + + // Set form variables + $form_title = 'edit' === $action ? __( 'Edit Webhook', 'wp-graphql-headless-webhooks' ) : __( 'Add New Webhook', 'wp-graphql-headless-webhooks' ); + $submit_text = 'edit' === $action ? __( 'Update Webhook', 'wp-graphql-headless-webhooks' ) : __( 'Add Webhook', 'wp-graphql-headless-webhooks' ); + + // Set default values for new webhook + if ( 'add' === $action ) { + $name = ''; + $event = ''; + $url = ''; + $method = 'POST'; + $headers = array(); + } else { + // Extract values from webhook entity + $name = $webhook->name; + $event = $webhook->event; + $url = $webhook->url; + $method = $webhook->method; + $headers = $webhook->headers; + } + + include __DIR__ . '/views/webhook-form.php'; + } else { + $webhooks = $this->repository->get_all(); + include __DIR__ . '/views/webhooks-list.php'; + } + } +} From 660de7016cf50a4f3cdf309adbb8d0dd6eba160b Mon Sep 17 00:00:00 2001 From: Joe Fusco Date: Mon, 9 Jun 2025 19:35:49 -0400 Subject: [PATCH 6/8] Fix headers --- .../src/Admin/WebhooksAdmin.php | 17 +++++++++-------- .../src/Admin/assets/admin.js | 4 ++-- .../Admin/views/partials/webhook-header-row.php | 4 ++-- .../src/Admin/views/webhooks-list.php | 9 ++++++++- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php b/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php index d5ccec09..cbdab529 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php @@ -42,6 +42,9 @@ public function init(): void { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_init', array( $this, 'handle_actions' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); + + // Register admin-post.php handlers + add_action( 'admin_post_graphql_webhook_save', array( $this, 'handle_webhook_save' ) ); } /** @@ -103,6 +106,7 @@ public function enqueue_assets( string $hook_suffix ): void { 'restUrl' => rest_url( 'graphql-webhooks/v1/' ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'headerTemplate' => $this->get_header_row_template(), + 'confirmDelete' => __( 'Are you sure you want to delete this webhook?', 'wp-graphql-headless-webhooks' ), ) ); } @@ -126,12 +130,7 @@ public function handle_actions(): void { return; } - if ( isset( $_POST['action'] ) ) { - if ( 'save_webhook' === $_POST['action'] ) { - $this->handle_webhook_save(); - } - } - + // Only handle delete action here since save is handled by admin-post.php if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] ) { $this->handle_webhook_delete(); } @@ -168,8 +167,8 @@ private function verify_nonce( string $action, string $nonce_field = '_wpnonce' /** * Handle webhook save */ - private function handle_webhook_save(): void { - if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'webhook_save' ) ) { + public function handle_webhook_save(): void { + if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'graphql_webhook_save', 'graphql_webhook_nonce' ) ) { return; } @@ -272,6 +271,7 @@ public function render_admin_page(): void { // Set default values for new webhook if ( 'add' === $action ) { + $webhook_id = 0; $name = ''; $event = ''; $url = ''; @@ -279,6 +279,7 @@ public function render_admin_page(): void { $headers = array(); } else { // Extract values from webhook entity + $webhook_id = $webhook->id; $name = $webhook->name; $event = $webhook->event; $url = $webhook->url; diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js index 8eb557b5..b73648a5 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/assets/admin.js @@ -11,7 +11,7 @@ $( '#add-header' ).on( 'click', function () { - var headerRow = $( graphqlWebhooksAdmin.headerRowTemplate ); + var headerRow = $( wpGraphQLWebhooks.headerTemplate ); $( '#webhook-headers' ).append( headerRow ); } ); @@ -37,7 +37,7 @@ $( '.delete-webhook' ).on( 'click', function (e) { - if ( ! confirm( graphqlWebhooksAdmin.confirmDelete )) { + if ( ! confirm( wpGraphQLWebhooks.confirmDelete )) { e.preventDefault(); } } diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php index f10f68a9..da7e0b6a 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/partials/webhook-header-row.php @@ -17,7 +17,7 @@ $value = isset( $value ) ? $value : ''; ?>
- - + +
diff --git a/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php index 74be7891..fd665e73 100644 --- a/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php +++ b/plugins/wp-graphql-headless-webhooks/src/Admin/views/webhooks-list.php @@ -20,7 +20,7 @@
- +

@@ -36,6 +36,7 @@ + @@ -51,6 +52,12 @@ url ); ?> method ); ?> + + headers ) ? count( $webhook->headers ) : 0; + echo esc_html( $header_count ); + ?> +