ljoaquim 4 mesiacov pred
commit
19c61059bb

+ 32 - 0
.env.example

@@ -0,0 +1,32 @@
+APP_PORT=8080
+DB_FILE=test.db
+JWT_SECRET=Aer8woa9zeec2gai4ahQuah3Ahbee5eiSefae8pheepahnootuShoo0oKahf
+
+
+#================= BUILD =================
+BUILD_APP="php-api"
+BUILD_VER="1.0.0"
+BUILD_STAGE="build/php-api"
+BUILD_DESCRIPTION="API PHP php-api (SmartPay)"
+BUILD_LICENSE="Proprietary"
+BUILD_VENDOR="Mixtech"
+BUILD_MAINTAINER="lucas@mixtech.net.br"
+BUILD_DEPENDS="php8.2-cli | php-cli (>= 8.2)"
+BUILD_ARCH="all"
+
+#================= DEPLOY =================
+DEPLOY_HOST="mixtech.dev.br"
+DEPLOY_TOKEN=""
+DEPLOY_FILE="./php-api_1.0.0_all.deb"
+
+#================= AUTH =================
+# Header containing the application ID to select the app and auth method
+APP_ID_HEADER=x-app-id
+# Default application ID used when header is absent
+DEFAULT_APPLICATION_ID=
+# (legacy) Header containing the application flag
+APP_FLAG_HEADER=x-app-flag
+# (legacy) Default application flag
+DEFAULT_APPLICATION_FLAG=
+# Timeout in seconds for the external auth HTTP request
+EXTERNAL_AUTH_TIMEOUT=5

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+vendor/
+.env
+*.log
+mail.db
+build/

+ 30 - 0
README.md

@@ -0,0 +1,30 @@
+# Mail API
+
+## Build
+
+```bash
+composer install
+```
+
+## Run
+
+```bash
+X_LISTEN=127.0.0.1:8081 php public/index.php
+```
+
+```bash
+fpm -s dir -t deb \
+  -n php-api -v 1.0.0 \
+  -C build/php-api \
+  --prefix / \
+  --description "API PHP php-api (SmartPay)" \
+  --license "Proprietary" \
+  --vendor "SmartPay" \
+  --maintainer "lucas.joaquim@smartpay.com.vc" \
+  --architecture all \
+  --deb-no-default-config-files \
+  --config-files /etc/php-api \
+  --depends "php8.2-cli | php-cli (>= 8.2)" \
+  opt/php-api etc/php-api var/log/php-api
+Created package {:path=>"php-api_1.0.0_all.deb"}
+```

+ 53 - 0
bin/build_stage

@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+[ -f ./.env ] || { echo "Error: .env file not found"; exit 1; }
+source ./.env || { echo "Error: failed to load .env"; exit 1; }
+
+command -v fpm >/dev/null 2>&1 || { echo "Error: fpm not found in PATH"; exit 1; }
+
+rm -rf ./build
+rm -f "${BUILD_APP}_${BUILD_VER}_${BUILD_ARCH}.deb"
+
+rm -rf "$BUILD_STAGE"
+mkdir -p "$BUILD_STAGE"/{opt/$BUILD_APP,usr/bin,var/log/$BUILD_APP}
+
+for item in *; do
+  [ "$item" = "build" ] && continue
+  case "$item" in
+    .*|*.deb) continue ;;
+  esac
+  cp -a "$item" "$BUILD_STAGE/opt/$BUILD_APP/" || { echo "Error: failed to copy '$item' to stage"; exit 1; }
+done
+
+touch "$BUILD_STAGE/var/log/$BUILD_APP/app.log" || { echo "Error: unable to create log in $BUILD_STAGE/var/log/$BUILD_APP"; exit 1; }
+cat > "$BUILD_STAGE/usr/bin/$BUILD_APP" <<EOF
+#!/usr/bin/env bash
+exec php -S 127.0.0.1:8080 -t /opt/$BUILD_APP/public
+EOF
+chmod +x "$BUILD_STAGE/usr/bin/$BUILD_APP" || { echo "Error: unable to mark wrapper as executable"; exit 1; }
+
+echo "[ok] stage ready at: $BUILD_STAGE"
+
+FPM_DEPENDS=()
+if [ -n "${BUILD_DEPENDS:-}" ]; then
+  IFS=',' read -ra __DEPS <<< "$BUILD_DEPENDS"
+  for d in "${__DEPS[@]}"; do
+    d_trim="$(echo "$d" | xargs)"
+    [ -n "$d_trim" ] && FPM_DEPENDS+=(--depends "$d_trim")
+  done
+fi
+
+fpm -s dir -t deb \
+  -n "$BUILD_APP" -v "$BUILD_VER" \
+  -C "$BUILD_STAGE" \
+  --prefix / \
+  --description "$BUILD_DESCRIPTION" \
+  --license "$BUILD_LICENSE" \
+  --vendor "$BUILD_VENDOR" \
+  --maintainer "$BUILD_MAINTAINER" \
+  --architecture "$BUILD_ARCH" \
+  --deb-no-default-config-files \
+  "${FPM_DEPENDS[@]}" \
+  opt/$BUILD_APP var/log/$BUILD_APP usr/bin/$BUILD_APP \
+  || { echo "Error: fpm failed to build the package"; exit 1; }

+ 28 - 0
bin/deploy

@@ -0,0 +1,28 @@
+#!/bin/bash
+
+source .env
+REPO=$1
+
+ENDPOINT="https://$DEPLOY_HOST/api/$REPO/upload/"
+
+
+if [ ! -f "$DEPLOY_FILE" ]; then
+    echo "Error: file $DEPLOY_FILE not found"
+    exit 1
+fi
+
+RESPONSE=$(curl -s -X POST \
+     -H "Authorization: Token $DEPLOY_TOKEN" \
+     -F "package_file=@$DEPLOY_FILE" \
+     -w "%{http_code}" \
+     "$ENDPOINT")
+
+HTTP_CODE="${RESPONSE: -3}"
+
+if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 201 ]; then
+    echo "Upload done to $REPO."
+else
+    echo "Error: failed to uploado to '$REPO'"
+    echo "$RESPONSE"
+    exit 1
+fi

+ 14 - 0
bin/setup

@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Nome do arquivo do banco de dados
+DB_FILE="mail.db"
+
+cat ./migrations/migrations_v1.sql | sqlite3 "$DB_FILE"
+
+# Executa comandos SQL no SQLite
+sqlite3 "$DB_FILE" <<EOF
+INSERT INTO aplication (aplication_name, aplication_flag, aplication_url) VALUES
+('mixtab', 'a', 'https://bartender.mixtab.com.br/jwthelloworld');
+EOF
+
+echo "Banco de dados '$DB_FILE' criado e populado com sucesso! Senhas estão hasheadas."

+ 55 - 0
bin/test/curl-test.sh

@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Simple curl tester for the Framework X API using Bearer token auth.
+# Usage:
+#   ./bin/curl-test.sh [METHOD] [PATH] [APP_ID]
+# Examples:
+#   ./bin/curl-test.sh GET /hello 1
+#   ./bin/curl-test.sh GET /hello    # if DEFAULT_APPLICATION_ID is set in .env
+
+METHOD="${1:-GET}"
+PATH_URL="${2:-/hello}"
+APP_ID_INPUT="${3:-1}"
+
+# Base URL
+HOST="${HOST:-localhost}"
+PORT="${APP_PORT:-8080}"
+BASE_URL="http://$HOST:$PORT"
+
+# Default headers
+APP_ID_HEADER="${APP_ID_HEADER:-x-app-id}"
+DEFAULT_APP_ID="${DEFAULT_APPLICATION_ID:-}"
+# (legacy) optional support for flag
+APP_FLAG_HEADER="${APP_FLAG_HEADER:-x-app-flag}"
+DEFAULT_APP_FLAG="${DEFAULT_APPLICATION_FLAG:-}"
+
+# Temporary token provided by request (DO NOT COMMIT TO PROD)
+TOKEN="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjksInVzZXJuYW1lIjoiTWl4dGVjaCIsInJvbGVfaWQiOjEsImNvbXBhbnlfaWQiOjIsImlhdCI6MTc1NTg1MDEzNywiZXhwIjoxNzU1ODY0NTM3fQ.9HXKAPw5Pw_K69KHPyncSJcW0FlRNNoEYNfnevB9vg4"
+
+# Choose app id (preferred)
+APP_ID="$APP_ID_INPUT"
+if [[ -z "$APP_ID" && -n "$DEFAULT_APP_ID" ]]; then
+  APP_ID="$DEFAULT_APP_ID"
+fi
+
+# Choose legacy flag only if ID is not provided
+APP_FLAG="${APP_FLAG:-}"
+if [[ -z "$APP_ID" ]]; then
+  if [[ -n "$DEFAULT_APP_FLAG" ]]; then APP_FLAG="$DEFAULT_APP_FLAG"; fi
+fi
+
+URL="$BASE_URL$PATH_URL"
+
+echo "=> Request: $METHOD $URL"
+echo "=> Using app id header: $APP_ID_HEADER=${APP_ID:-<none>} (legacy flag $APP_FLAG_HEADER=${APP_FLAG:-<none>})"
+
+HDRS=(-H "Authorization: $TOKEN" -H "Accept: application/json")
+if [[ -n "$APP_ID" ]]; then
+  HDRS+=( -H "$APP_ID_HEADER: $APP_ID" )
+elif [[ -n "$APP_FLAG" ]]; then
+  HDRS+=( -H "$APP_FLAG_HEADER: $APP_FLAG" )
+fi
+
+set -x
+curl -i -sS -X "$METHOD" "${HDRS[@]}" "$URL"

+ 25 - 0
bin/testgetjwt

@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Script 1: Obtém JWT via login
+# Configurações
+API_URL="http://localhost:8000/login"  # Rota de login
+USERNAME="du"
+PASSWORD="du123456"  # Substitua pela senha real
+
+# Body JSON para login
+BODY="{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}"
+
+# Faz POST para login e extrai o token JWT usando jq (instale jq se necessário: sudo apt install jq)
+JWT=$(curl -s -X POST "${API_URL}" \
+    -H "Content-Type: application/json" \
+    -d "${BODY}" | jq -r '.data.token')
+
+# Verifica se JWT foi obtido
+if [ -z "$JWT" ] || [ "$JWT" = "null" ]; then
+    echo "Erro ao obter JWT. Resposta do servidor:"
+    curl -v -X POST "${API_URL}" -H "Content-Type: application/json" -d "${BODY}"
+    exit 1
+fi
+
+echo "JWT obtido: ${JWT}"
+echo "Salve este JWT para usar no próximo script (ex: export JWT=${JWT})"

+ 30 - 0
composer.json

@@ -0,0 +1,30 @@
+{
+    "name": "lucas/bartender",
+    "autoload": {
+        "psr-4": {
+            "Lucas\\Bartender\\": "src/",
+            "Controllers\\": "controllers/",
+            "Middlewares\\": "middlewares/",
+            "Models\\": "models/",
+            "Services\\": "services/",
+            "Utils\\": "utils/",
+            "Libs\\": "libs/"
+        }
+    },
+    "authors": [
+        {
+            "name": "ljoaquim",
+            "email": "lucas.joaquim@smartpay.com.vc"
+        }
+    ],
+    "require": {
+        "clue/framework-x": "^0.17.0",
+        "psr/http-server-middleware": "^1.0",
+        "react/child-process": "^0.6.6",
+        "respect/validation": "^2.4",
+        "vlucas/phpdotenv": "^5.6",
+        "clue/block-react": "^1.5",
+        "clue/reactphp-sqlite": "^1.7",
+        "react/async": "^4.0"
+    }
+}

+ 1974 - 0
composer.lock

@@ -0,0 +1,1974 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "054b9729884221596ac2916faeed54ea",
+    "packages": [
+        {
+            "name": "clue/block-react",
+            "version": "v1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/reactphp-block.git",
+                "reference": "718b0571a94aa693c6fffc72182e87257ac900f3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/reactphp-block/zipball/718b0571a94aa693c6fffc72182e87257ac900f3",
+                "reference": "718b0571a94aa693c6fffc72182e87257ac900f3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.0 || ^2.7 || ^1.2.1",
+                "react/promise-timer": "^1.5"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
+                "react/http": "^1.4"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "Lightweight library that eases integrating async components built for ReactPHP in a traditional, blocking environment.",
+            "homepage": "https://github.com/clue/reactphp-block",
+            "keywords": [
+                "async",
+                "await",
+                "blocking",
+                "event loop",
+                "promise",
+                "reactphp",
+                "sleep",
+                "synchronous"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/reactphp-block/issues",
+                "source": "https://github.com/clue/reactphp-block/tree/v1.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "abandoned": "react/async",
+            "time": "2021-10-20T14:07:33+00:00"
+        },
+        {
+            "name": "clue/framework-x",
+            "version": "v0.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/framework-x.git",
+                "reference": "9b3a8e6a981953bb0d6e4a4c3c219589a70521d0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/framework-x/zipball/9b3a8e6a981953bb0d6e4a4c3c219589a70521d0",
+                "reference": "9b3a8e6a981953bb0d6e4a4c3c219589a70521d0",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/fast-route": "^1.3",
+                "php": ">=7.1",
+                "react/async": "^4.3 || ^3.1",
+                "react/http": "^1.11",
+                "react/promise": "^3.2",
+                "react/socket": "^1.15"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.12.11 || 1.4.10",
+                "phpunit/phpunit": "^9.6 || ^7.5",
+                "psr/container": "^2 || ^1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "FrameworkX\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "Framework X – the simple and fast micro framework for building reactive web applications that run anywhere.",
+            "homepage": "https://framework-x.org/",
+            "keywords": [
+                "async",
+                "event-driven",
+                "framework",
+                "http",
+                "micro",
+                "microframework",
+                "reactphp",
+                "web"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/framework-x/issues",
+                "source": "https://github.com/clue/framework-x/tree/v0.17.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "time": "2024-12-23T13:59:55+00:00"
+        },
+        {
+            "name": "clue/ndjson-react",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/reactphp-ndjson.git",
+                "reference": "392dc165fce93b5bb5c637b67e59619223c931b0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0",
+                "reference": "392dc165fce93b5bb5c637b67e59619223c931b0",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "react/stream": "^1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35",
+                "react/event-loop": "^1.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Clue\\React\\NDJson\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.",
+            "homepage": "https://github.com/clue/reactphp-ndjson",
+            "keywords": [
+                "NDJSON",
+                "json",
+                "jsonlines",
+                "newline",
+                "reactphp",
+                "streaming"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/reactphp-ndjson/issues",
+                "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-12-23T10:58:28+00:00"
+        },
+        {
+            "name": "clue/reactphp-sqlite",
+            "version": "v1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/clue/reactphp-sqlite.git",
+                "reference": "52695d451be0f0b985204824018dd66c658636cc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/clue/reactphp-sqlite/zipball/52695d451be0f0b985204824018dd66c658636cc",
+                "reference": "52695d451be0f0b985204824018dd66c658636cc",
+                "shasum": ""
+            },
+            "require": {
+                "clue/ndjson-react": "^1.0",
+                "ext-sqlite3": "*",
+                "php": ">=5.4",
+                "react/child-process": "^0.6.6",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Clue\\React\\SQLite\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering"
+                }
+            ],
+            "description": "Async SQLite database, lightweight non-blocking process wrapper around file-based database extension (ext-sqlite3), built on top of ReactPHP.",
+            "homepage": "https://github.com/clue/reactphp-sqlite",
+            "keywords": [
+                "async",
+                "database",
+                "non-blocking",
+                "reactphp",
+                "sqlite"
+            ],
+            "support": {
+                "issues": "https://github.com/clue/reactphp-sqlite/issues",
+                "source": "https://github.com/clue/reactphp-sqlite/tree/v1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://clue.engineering/support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/clue",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-01-17T14:49:38+00:00"
+        },
+        {
+            "name": "evenement/evenement",
+            "version": "v3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/igorw/evenement.git",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9 || ^6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Evenement\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                }
+            ],
+            "description": "Événement is a very simple event dispatching library for PHP",
+            "keywords": [
+                "event-dispatcher",
+                "event-emitter"
+            ],
+            "support": {
+                "issues": "https://github.com/igorw/evenement/issues",
+                "source": "https://github.com/igorw/evenement/tree/v3.0.2"
+            },
+            "time": "2023-08-08T05:53:35+00:00"
+        },
+        {
+            "name": "fig/http-message-util",
+            "version": "1.1.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message-util.git",
+                "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765",
+                "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3 || ^7.0 || ^8.0"
+            },
+            "suggest": {
+                "psr/http-message": "The package containing the PSR-7 interfaces"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Fig\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Utility classes and constants for use with PSR-7 (psr/http-message)",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/http-message-util/issues",
+                "source": "https://github.com/php-fig/http-message-util/tree/1.1.5"
+            },
+            "time": "2020-11-24T22:02:12+00:00"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
+                "reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-07-20T21:45:45+00:00"
+        },
+        {
+            "name": "nikic/fast-route",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/FastRoute.git",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35|~5.7"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "FastRoute\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov",
+                    "email": "nikic@php.net"
+                }
+            ],
+            "description": "Fast request router for PHP",
+            "keywords": [
+                "router",
+                "routing"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/FastRoute/issues",
+                "source": "https://github.com/nikic/FastRoute/tree/master"
+            },
+            "time": "2018-02-13T20:26:39+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
+                "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-08-21T11:53:16+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/1.1"
+            },
+            "time": "2023-04-04T09:50:52+00:00"
+        },
+        {
+            "name": "psr/http-server-handler",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-server-handler.git",
+                "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
+                "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0",
+                "psr/http-message": "^1.0 || ^2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Server\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP server-side request handler",
+            "keywords": [
+                "handler",
+                "http",
+                "http-interop",
+                "psr",
+                "psr-15",
+                "psr-7",
+                "request",
+                "response",
+                "server"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
+            },
+            "time": "2023-04-10T20:06:20+00:00"
+        },
+        {
+            "name": "psr/http-server-middleware",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-server-middleware.git",
+                "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
+                "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0",
+                "psr/http-message": "^1.0 || ^2.0",
+                "psr/http-server-handler": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Server\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP server-side middleware",
+            "keywords": [
+                "http",
+                "http-interop",
+                "middleware",
+                "psr",
+                "psr-15",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/http-server-middleware/issues",
+                "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2"
+            },
+            "time": "2023-04-11T06:14:47+00:00"
+        },
+        {
+            "name": "react/async",
+            "version": "v4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/async.git",
+                "reference": "635d50e30844a484495713e8cb8d9e079c0008a5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/async/zipball/635d50e30844a484495713e8cb8d9e079c0008a5",
+                "reference": "635d50e30844a484495713e8cb8d9e079c0008a5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.8 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.10.39",
+                "phpunit/phpunit": "^9.6"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Async\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async utilities and fibers for ReactPHP",
+            "keywords": [
+                "async",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/async/issues",
+                "source": "https://github.com/reactphp/async/tree/v4.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-04T14:40:02+00:00"
+        },
+        {
+            "name": "react/cache",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/cache.git",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b",
+                "reference": "d47c472b64aa5608225f47965a484b75c7817d5b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/promise": "^3.0 || ^2.0 || ^1.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, Promise-based cache interface for ReactPHP",
+            "keywords": [
+                "cache",
+                "caching",
+                "promise",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/cache/issues",
+                "source": "https://github.com/reactphp/cache/tree/v1.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-11-30T15:59:55+00:00"
+        },
+        {
+            "name": "react/child-process",
+            "version": "v0.6.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/child-process.git",
+                "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159",
+                "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.0",
+                "react/event-loop": "^1.2",
+                "react/stream": "^1.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/socket": "^1.16",
+                "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\ChildProcess\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven library for executing child processes with ReactPHP.",
+            "keywords": [
+                "event-driven",
+                "process",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/child-process/issues",
+                "source": "https://github.com/reactphp/child-process/tree/v0.6.6"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2025-01-01T16:37:48+00:00"
+        },
+        {
+            "name": "react/dns",
+            "version": "v1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/dns.git",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "react/cache": "^1.0 || ^0.6 || ^0.5",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.7 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.3 || ^3 || ^2",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Dns\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async DNS resolver for ReactPHP",
+            "keywords": [
+                "async",
+                "dns",
+                "dns-resolver",
+                "reactphp"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/dns/issues",
+                "source": "https://github.com/reactphp/dns/tree/v1.13.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-13T14:18:03+00:00"
+        },
+        {
+            "name": "react/event-loop",
+            "version": "v1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/event-loop.git",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "suggest": {
+                "ext-pcntl": "For signal handling support when using the StreamSelectLoop"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\EventLoop\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+            "keywords": [
+                "asynchronous",
+                "event-loop"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/event-loop/issues",
+                "source": "https://github.com/reactphp/event-loop/tree/v1.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2023-11-13T13:48:05+00:00"
+        },
+        {
+            "name": "react/http",
+            "version": "v1.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/http.git",
+                "reference": "8db02de41dcca82037367f67a2d4be365b1c4db9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/http/zipball/8db02de41dcca82037367f67a2d4be365b1c4db9",
+                "reference": "8db02de41dcca82037367f67a2d4be365b1c4db9",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "fig/http-message-util": "^1.1",
+                "php": ">=5.3.0",
+                "psr/http-message": "^1.0",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.3 || ^1.2.1",
+                "react/socket": "^1.16",
+                "react/stream": "^1.4"
+            },
+            "require-dev": {
+                "clue/http-proxy-react": "^1.8",
+                "clue/reactphp-ssh-proxy": "^1.4",
+                "clue/socks-react": "^1.4",
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.2 || ^3 || ^2",
+                "react/promise-stream": "^1.4",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Http\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven, streaming HTTP client and server implementation for ReactPHP",
+            "keywords": [
+                "async",
+                "client",
+                "event-driven",
+                "http",
+                "http client",
+                "http server",
+                "https",
+                "psr-7",
+                "reactphp",
+                "server",
+                "streaming"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/http/issues",
+                "source": "https://github.com/reactphp/http/tree/v1.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-11-20T15:24:08+00:00"
+        },
+        {
+            "name": "react/promise",
+            "version": "v3.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/promise.git",
+                "reference": "23444f53a813a3296c1368bb104793ce8d88f04a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a",
+                "reference": "23444f53a813a3296c1368bb104793ce8d88f04a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "1.12.28 || 1.4.10",
+                "phpunit/phpunit": "^9.6 || ^7.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Promise\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+            "keywords": [
+                "promise",
+                "promises"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/promise/issues",
+                "source": "https://github.com/reactphp/promise/tree/v3.3.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2025-08-19T18:57:03+00:00"
+        },
+        {
+            "name": "react/promise-timer",
+            "version": "v1.11.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/promise-timer.git",
+                "reference": "4f70306ed66b8b44768941ca7f142092600fafc1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/4f70306ed66b8b44768941ca7f142092600fafc1",
+                "reference": "4f70306ed66b8b44768941ca7f142092600fafc1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.7.0 || ^1.2.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "React\\Promise\\Timer\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
+            "homepage": "https://github.com/reactphp/promise-timer",
+            "keywords": [
+                "async",
+                "event-loop",
+                "promise",
+                "reactphp",
+                "timeout",
+                "timer"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/promise-timer/issues",
+                "source": "https://github.com/reactphp/promise-timer/tree/v1.11.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-04T14:27:45+00:00"
+        },
+        {
+            "name": "react/socket",
+            "version": "v1.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/socket.git",
+                "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+                "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.0",
+                "react/dns": "^1.13",
+                "react/event-loop": "^1.2",
+                "react/promise": "^3.2 || ^2.6 || ^1.2.1",
+                "react/stream": "^1.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
+                "react/async": "^4.3 || ^3.3 || ^2",
+                "react/promise-stream": "^1.4",
+                "react/promise-timer": "^1.11"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Socket\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
+            "keywords": [
+                "Connection",
+                "Socket",
+                "async",
+                "reactphp",
+                "stream"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/socket/issues",
+                "source": "https://github.com/reactphp/socket/tree/v1.16.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-07-26T10:38:09+00:00"
+        },
+        {
+            "name": "react/stream",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/reactphp/stream.git",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d",
+                "shasum": ""
+            },
+            "require": {
+                "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+                "php": ">=5.3.8",
+                "react/event-loop": "^1.2"
+            },
+            "require-dev": {
+                "clue/stream-filter": "~1.2",
+                "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "React\\Stream\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Lück",
+                    "email": "christian@clue.engineering",
+                    "homepage": "https://clue.engineering/"
+                },
+                {
+                    "name": "Cees-Jan Kiewiet",
+                    "email": "reactphp@ceesjankiewiet.nl",
+                    "homepage": "https://wyrihaximus.net/"
+                },
+                {
+                    "name": "Jan Sorgalla",
+                    "email": "jsorgalla@gmail.com",
+                    "homepage": "https://sorgalla.com/"
+                },
+                {
+                    "name": "Chris Boden",
+                    "email": "cboden@gmail.com",
+                    "homepage": "https://cboden.dev/"
+                }
+            ],
+            "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+            "keywords": [
+                "event-driven",
+                "io",
+                "non-blocking",
+                "pipe",
+                "reactphp",
+                "readable",
+                "stream",
+                "writable"
+            ],
+            "support": {
+                "issues": "https://github.com/reactphp/stream/issues",
+                "source": "https://github.com/reactphp/stream/tree/v1.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/reactphp",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2024-06-11T12:45:25+00:00"
+        },
+        {
+            "name": "respect/stringifier",
+            "version": "0.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Respect/Stringifier.git",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Respect/Stringifier/zipball/e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "reference": "e55af3c8aeaeaa2abb5fa47a58a8e9688cc23b59",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.8",
+                "malukenho/docheader": "^0.1.7",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/stringify.php"
+                ],
+                "psr-4": {
+                    "Respect\\Stringifier\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Stringifier Contributors",
+                    "homepage": "https://github.com/Respect/Stringifier/graphs/contributors"
+                }
+            ],
+            "description": "Converts any value to a string",
+            "homepage": "http://respect.github.io/Stringifier/",
+            "keywords": [
+                "respect",
+                "stringifier",
+                "stringify"
+            ],
+            "support": {
+                "issues": "https://github.com/Respect/Stringifier/issues",
+                "source": "https://github.com/Respect/Stringifier/tree/0.2.0"
+            },
+            "time": "2017-12-29T19:39:25+00:00"
+        },
+        {
+            "name": "respect/validation",
+            "version": "2.4.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Respect/Validation.git",
+                "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Respect/Validation/zipball/f13f10f19978aea33af2a102a2f58f2db1e63619",
+                "reference": "f13f10f19978aea33af2a102a2f58f2db1e63619",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.1",
+                "respect/stringifier": "^0.2.0",
+                "symfony/polyfill-mbstring": "^1.2"
+            },
+            "require-dev": {
+                "egulias/email-validator": "^3.0",
+                "giggsey/libphonenumber-for-php-lite": "^8.13 || ^9.0",
+                "malukenho/docheader": "^1.0",
+                "mikey179/vfsstream": "^1.6",
+                "phpstan/phpstan": "^1.9",
+                "phpstan/phpstan-deprecation-rules": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.3",
+                "phpunit/phpunit": "^9.6",
+                "psr/http-message": "^1.0",
+                "respect/coding-standard": "^4.0",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "suggest": {
+                "egulias/email-validator": "Improves the Email rule if available",
+                "ext-bcmath": "Arbitrary Precision Mathematics",
+                "ext-fileinfo": "File Information",
+                "ext-mbstring": "Multibyte String Functions",
+                "giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Respect\\Validation\\": "library/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Respect/Validation Contributors",
+                    "homepage": "https://github.com/Respect/Validation/graphs/contributors"
+                }
+            ],
+            "description": "The most awesome validation engine ever created for PHP",
+            "homepage": "http://respect.github.io/Validation/",
+            "keywords": [
+                "respect",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "issues": "https://github.com/Respect/Validation/issues",
+                "source": "https://github.com/Respect/Validation/tree/2.4.4"
+            },
+            "time": "2025-06-07T00:07:21+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.33.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-09-09T11:45:10+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.33.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "php": ">=7.2"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-12-23T08:48:59+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.33.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2"
+            },
+            "type": "library",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/polyfill",
+                    "name": "symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-01-02T08:10:11+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+                "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.1.3",
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9.3",
+                "symfony/polyfill-ctype": "^1.24",
+                "symfony/polyfill-mbstring": "^1.24",
+                "symfony/polyfill-php80": "^1.24"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "5.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2025-04-30T23:37:27+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {},
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {},
+    "platform-dev": {},
+    "plugin-api-version": "2.6.0"
+}

+ 16 - 0
controllers/HelloController.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+
+class HelloController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        //$apiUser = $request->getAttribute('api_user');  // Exemplo: usa atributo do middleware
+        $data = ["message" => "Hello World!"];
+        return ResponseLib::sendOk($data);
+    }
+}

+ 97 - 0
controllers/MailSendController.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Controllers;
+
+use Libs\ExecLib;
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+use function React\Async\await;
+
+class MailSendController
+{
+    public function __invoke(ServerRequestInterface $request)
+    {
+        $body = json_decode((string) $request->getBody(), true);
+        if (!is_array($body)) {
+            return ResponseLib::sendFail('Invalid JSON body', [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $templateName = trim((string)($body['mail_template'] ?? ''));
+        $config       = $body['mail_config'] ?? null;
+        if ($templateName === '' || !is_array($config)) {
+            return ResponseLib::sendFail('Missing mail_template or mail_config', [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        // Required fields for sending
+        $toEmail = trim((string)($config['to_email'] ?? ''));
+        $toName  = trim((string)($config['to_name']  ?? ''));
+        $subject = trim((string)($config['subject']  ?? ''));
+        if ($toEmail === '' || $subject === '') {
+            return ResponseLib::sendFail('Missing to_email or subject', [], 'E_VALIDATE')->withStatus(400);
+        }
+
+        $fromEmail = trim((string)($_ENV['MAIL_FROM'] ?? 'no-reply@example.com'));
+        $fromName  = trim((string)($_ENV['MAIL_FROM_NAME'] ?? 'No Reply'));
+
+        // Load template file from templates/{name}.tpl
+        $tplPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $templateName . '.tpl';
+        if (!is_file($tplPath)) {
+            return ResponseLib::sendFail('Template not found', ['template' => $templateName], 'E_NOT_FOUND')->withStatus(404);
+        }
+        $tpl = file_get_contents($tplPath);
+        if ($tpl === false) {
+            return ResponseLib::sendFail('Failed to load template', ['template' => $templateName], 'E_IO')->withStatus(500);
+        }
+
+        // Simple placeholder substitution: {{key}}
+        $replacements = [];
+        foreach ($config as $k => $v) {
+            if (is_scalar($v)) {
+                $replacements['{{' . $k . '}}'] = (string)$v;
+            }
+        }
+        $bodyText = strtr($tpl, $replacements);
+
+        // Build RFC 5322 message
+        $headers = [];
+        $headers[] = sprintf('From: %s <%s>', $fromName !== '' ? $fromName : $fromEmail, $fromEmail);
+        $headers[] = sprintf('To: %s <%s>', $toName !== '' ? $toName : $toEmail, $toEmail);
+        $headers[] = sprintf('Subject: %s', $subject);
+        $headers[] = 'MIME-Version: 1.0';
+        $headers[] = 'Content-Type: text/plain; charset=UTF-8';
+        $headers[] = 'Content-Transfer-Encoding: 8bit';
+
+        $message = implode("\r\n", $headers) . "\r\n\r\n" . $bodyText . "\r\n";
+
+        // Write to temp file
+        $tmpFile = tempnam(sys_get_temp_dir(), 'mail_');
+        if ($tmpFile === false) {
+            return ResponseLib::sendFail('Failed to create temp file', [], 'E_IO')->withStatus(500);
+        }
+        file_put_contents($tmpFile, $message);
+
+        // Execute sendmail (postfix)
+        $sendmail = $_ENV['SENDMAIL_PATH'] ?? '/usr/sbin/sendmail';
+        $cmd = sprintf("%s -t -i < %s", escapeshellcmd($sendmail), escapeshellarg($tmpFile));
+
+        try {
+            [$code, $output] = await(ExecLib::exec($cmd, (float)($_ENV['SENDMAIL_TIMEOUT'] ?? 10)));
+        } catch (\Throwable $e) {
+            @unlink($tmpFile);
+            return ResponseLib::sendFail('Sendmail failed', ['error' => $e->getMessage()], 'E_SENDMAIL')->withStatus(500);
+        } finally {
+            @unlink($tmpFile);
+        }
+
+        if ($code !== 0) {
+            return ResponseLib::sendFail('Sendmail exited with non-zero', ['code' => $code, 'output' => $output], 'E_SENDMAIL')->withStatus(500);
+        }
+
+        return ResponseLib::sendOk([
+            'status' => 'sent',
+            'to' => $toEmail,
+            'template' => $templateName,
+            'code' => $code,
+        ]);
+    }
+}

+ 71 - 0
libs/ExecLib.php

@@ -0,0 +1,71 @@
+<?php
+namespace Libs;
+
+use React\ChildProcess\Process;
+use React\Promise\Deferred;
+use React\EventLoop\Loop;
+
+final class ExecLib
+{
+    public static function exec(string $cmd, ?float $timeoutSecs = null)
+    {
+        $process  = new Process($cmd);
+        $deferred = new Deferred();
+
+        $bufferOut = '';
+        $bufferErr = '';
+        $timers    = [];
+
+        $process->start(Loop::get());
+
+        if ($process->stdin !== null) {
+            $process->stdin->end();
+        }
+
+        if ($process->stdout) {
+            $process->stdout->on('data', function ($chunk) use (&$bufferOut) {
+                $bufferOut .= (string)$chunk;
+            });
+        }
+        if ($process->stderr) {
+            $process->stderr->on('data', function ($chunk) use (&$bufferErr) {
+                $bufferErr .= (string)$chunk;
+            });
+        }
+
+        if ($timeoutSecs !== null && $timeoutSecs > 0) {
+            $timers['timeout'] = Loop::addTimer($timeoutSecs, function () use ($process, $deferred) {
+                if ($process->isRunning()) {
+                    $process->terminate(); // SIGTERM
+                    Loop::addTimer(1.0, function () use ($process) {
+                        if ($process->isRunning()) {
+                            $process->terminate(\defined('SIGKILL') ? \SIGKILL : null);
+                        }
+                    });
+                }
+                $deferred->reject(new \RuntimeException('CLI timeout'));
+            });
+        }
+
+        $process->on('exit', function ($exitCode) use (&$bufferOut, &$bufferErr, $deferred, &$timers) {
+            foreach ($timers as $t) {
+                if ($t) {
+                    Loop::cancelTimer($t);
+                }
+            }
+            $output = trim($bufferOut !== '' ? $bufferOut : $bufferErr);
+            $deferred->resolve([(int)$exitCode, $output]);
+        });
+
+        $process->on('error', function (\Throwable $e) use ($deferred, &$timers) {
+            foreach ($timers as $t) {
+                if ($t) {
+                    Loop::cancelTimer($t);
+                }
+            }
+            $deferred->reject($e);
+        });
+
+        return $deferred->promise();
+    }
+}

+ 31 - 0
libs/ModelFactory.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Libs;
+
+use Clue\React\SQLite\Factory as SqliteFactory;
+use Clue\React\SQLite\DatabaseInterface;
+
+class ModelFactory
+{
+    private static ?DatabaseInterface $adb = null;
+
+    private static function getDbPath(): string
+    {
+        // Prefer env var if present, else default to project-root/mail.db
+        $dbFile = $_ENV['DB_FILE'] ?? 'mail.db';
+        if ($dbFile && $dbFile[0] === '/') {
+            return $dbFile;
+        }
+        return dirname(__DIR__) . DIRECTORY_SEPARATOR . $dbFile;
+    }
+
+    public static function adb(): DatabaseInterface
+    {
+        if (!self::$adb) {
+            $factory = new SqliteFactory();
+            // For clue/reactphp-sqlite ^1.7, pass filename (not DSN) and options
+            self::$adb = $factory->openLazy(self::getDbPath(), null, ['idle' => 300]);
+        }
+        return self::$adb;
+    }
+}

+ 31 - 0
libs/ResponseLib.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Libs;
+
+use React\Http\Message\Response;
+
+class ResponseLib
+{
+    public static function sendOk($data, $code = "S_OK")
+    {
+        $reply = [
+            'status' => 'ok',
+            'msg' => '[100] Request ok.',
+            'code' => $code,
+            'data' => $data
+        ];
+        return Response::json($reply);
+    }
+
+    public static function sendFail($message, $data = [], $code = "E_GENERIC")
+    {
+        $reply = [
+            'status' => 'failed',
+            'msg' => $message,
+            'code' => $code,
+            'data' => $data
+        ];
+        return Response::json($reply);
+    }
+}
+

+ 60 - 0
middlewares/ExternalAuthMiddleware.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Middlewares;
+
+use Libs\ResponseLib;
+use Models\ApplicationModel;
+use Psr\Http\Message\ServerRequestInterface;
+use Middlewares\JwtAuthMiddleware;
+use Middlewares\HmacAuthMiddleware;
+use Middlewares\ExternalJwtAuthMiddleware;
+
+class ExternalAuthMiddleware
+{
+    private ApplicationModel $apps;
+    private string $appIdHeader;
+
+    public function __construct()
+    {
+        $this->apps = new ApplicationModel();
+        $this->appIdHeader = $_ENV['APP_ID_HEADER'] ?? 'x-app-id';
+    }
+
+    public function __invoke(ServerRequestInterface $request, callable $next)
+    {
+        // 1) Discover application by ID header (or default)
+        $idHeaderVal = $request->getHeaderLine($this->appIdHeader);
+        $id = $idHeaderVal !== '' ? (int)$idHeaderVal : (int)($_ENV['DEFAULT_APPLICATION_ID'] ?? 0);
+        if ($id <= 0) {
+            return ResponseLib::sendFail('Unauthorized: Missing application id', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $app = $this->apps->getById($id);
+        if (!$app) {
+            return ResponseLib::sendFail('Unauthorized: Application not found', [], 'E_VALIDATE')->withStatus(401);
+        }
+        // must be active
+        if (($app['aplication_flag'] ?? '') !== 'a') {
+            return ResponseLib::sendFail('Unauthorized: Inactive application', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        $method = strtolower($app['aplication_auth_method'] ?? 'external_jwt');
+        
+        // 2) Route based on method
+        if ($method === 'jwt') {
+            $jwt = new JwtAuthMiddleware();
+            return $jwt($request, $next);
+        }
+        if ($method === 'hmac') {
+            $hmac = new HmacAuthMiddleware();
+            return $hmac($request, $next);
+        }
+
+        // Default: external_jwt
+        if (empty($app['aplication_url'])) {
+            return ResponseLib::sendFail('Unauthorized: Application URL not configured for external auth', [], 'E_VALIDATE')->withStatus(401);
+        }
+        $external = new ExternalJwtAuthMiddleware($app['aplication_url']);
+        return $external($request, $next);
+    }
+}

+ 92 - 0
middlewares/ExternalJwtAuthMiddleware.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace Middlewares;
+
+use Libs\ResponseLib;
+use Psr\Http\Message\ServerRequestInterface;
+
+class ExternalJwtAuthMiddleware
+{
+    private string $verifyUrl;
+    private int $timeoutSeconds;
+
+    public function __construct(string $verifyUrl)
+    {
+        $this->verifyUrl = $verifyUrl;
+        $this->timeoutSeconds = (int)($_ENV['EXTERNAL_AUTH_TIMEOUT'] ?? 5);
+    }
+
+    public function __invoke(ServerRequestInterface $request, callable $next)
+    {
+        // Require Authorization: Bearer <token>
+        $authHeader = $request->getHeaderLine('Authorization');
+        if (empty($authHeader) || !preg_match('/Bearer\s+(.*)/', $authHeader, $matches)) {
+            return ResponseLib::sendFail('Unauthorized: Missing or invalid Authorization header', [], 'E_VALIDATE')->withStatus(401);
+        }
+
+        // Call external service
+        [$ok, $payload, $status, $err] = $this->callExternalVerify($this->verifyUrl, $authHeader);
+        if (!$ok) {
+            $message = 'Unauthorized';
+            if ($status >= 500 || $err) {
+                $message = 'Auth service error';
+            }
+            return ResponseLib::sendFail($message, ['status' => $status, 'error' => $err], 'E_VALIDATE')->withStatus(401);
+        }
+
+        // Attach attributes from response if present
+        if (is_array($payload)) {
+            if (isset($payload['sub'])) {
+                $request = $request->withAttribute('api_user_id', $payload['sub']);
+            }
+            if (isset($payload['username'])) {
+                $request = $request->withAttribute('api_user', $payload['username']);
+            }
+        }
+        $request = $request->withAttribute('external_auth', true);
+
+        return $next($request);
+    }
+
+    private function callExternalVerify(string $url, string $authorization): array
+    {
+        $ch = curl_init($url);
+        curl_setopt_array($ch, [
+            CURLOPT_RETURNTRANSFER => true,
+            CURLOPT_HEADER => true,
+            CURLOPT_NOBODY => false,
+            CURLOPT_CUSTOMREQUEST => 'GET',
+            CURLOPT_HTTPHEADER => [
+                'Authorization: ' . $authorization,
+                'Accept: application/json'
+            ],
+            CURLOPT_TIMEOUT => $this->timeoutSeconds,
+            CURLOPT_CONNECTTIMEOUT => min(2, $this->timeoutSeconds),
+        ]);
+
+        $response = curl_exec($ch);
+        if ($response === false) {
+            $err = curl_error($ch);
+            curl_close($ch);
+            return [false, null, 0, $err];
+        }
+
+        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+        $body = substr($response, $headerSize);
+        curl_close($ch);
+
+        $ok = ($status >= 200 && $status < 300);
+        $payload = null;
+        if ($body !== '') {
+            $decoded = json_decode($body, true);
+            if (json_last_error() === JSON_ERROR_NONE) {
+                $payload = $decoded;
+            } else {
+                $payload = $body;
+            }
+        }
+
+        return [$ok, $payload, $status, null];
+    }
+}

+ 9 - 0
migrations/migrations_v1.sql

@@ -0,0 +1,9 @@
+PRAGMA foreign_keys=ON;
+
+CREATE TABLE aplication (
+    aplication_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    aplication_name TEXT NOT NULL,
+    aplication_flag TEXT NOT NULL,
+    aplication_url TEXT NOT NULL,  -- Nova coluna para senha hasheada
+    aplication_auth_method TEXT NOT NULL DEFAULT 'external_jwt' -- values: external_jwt | jwt | hmac
+);

+ 45 - 0
models/ApplicationModel.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace Models;
+
+use Libs\ModelFactory;
+use function React\Async\await;
+
+class ApplicationModel
+{
+    public function __construct() {}
+
+    public function getByFlag(string $flag): ?array
+    {
+        $adb = ModelFactory::adb();
+        $sql = 'SELECT aplication_id, aplication_name, aplication_flag, aplication_url, aplication_auth_method FROM aplication WHERE aplication_flag = ? LIMIT 1';
+        try {
+            @error_log('[ApplicationModel] getByFlag SQL=' . $sql . ' param=' . $flag);
+            $r = await($adb->query($sql, [$flag]));
+        } catch (\Throwable $e) {
+            @error_log('[ApplicationModel] getByFlag Exception: ' . $e->getMessage());
+            throw $e;
+        }
+        if (!$r->rows || !isset($r->rows[0]['aplication_id'])) {
+            return null;
+        }
+        return $r->rows[0];
+    }
+
+    public function getById(int $id): ?array
+    {
+        $adb = ModelFactory::adb();
+        $sql = 'SELECT aplication_id, aplication_name, aplication_flag, aplication_url, aplication_auth_method FROM aplication WHERE aplication_id = ? LIMIT 1';
+        try {
+            @error_log('[ApplicationModel] getById SQL=' . $sql . ' param=' . $id);
+            $r = await($adb->query($sql, [$id]));
+        } catch (\Throwable $e) {
+            @error_log('[ApplicationModel] getById Exception: ' . $e->getMessage());
+            throw $e;
+        }
+        if (!$r->rows || !isset($r->rows[0]['aplication_id'])) {
+            return null;
+        }
+        return $r->rows[0];
+    }
+}

+ 41 - 0
public/index.php

@@ -0,0 +1,41 @@
+<?php
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+$file = __DIR__ . $path;
+if (php_sapi_name() === 'cli-server' && is_file($file)) {
+    return false;
+}
+
+if (class_exists(Dotenv\Dotenv::class) && file_exists(__DIR__ . '/../.env')) {
+    Dotenv\Dotenv::createImmutable(
+        dirname(__DIR__),
+        null,
+        true
+    )->safeLoad();
+}
+
+error_reporting(E_ALL);
+
+use FrameworkX\App;
+use Middlewares\ExternalAuthMiddleware;
+use Libs\ModelFactory;
+
+try {
+    $adb = ModelFactory::adb();
+    $GLOBALS['ADB'] = $adb;
+} catch (\Throwable $e) {
+    http_response_code(500);
+    header('Content-Type: application/json; charset=utf-8');
+    echo json_encode(['error' => 'DB init failed', 'detail' => $e->getMessage()], JSON_UNESCAPED_SLASHES);
+    exit(1);
+}
+
+$app = new App();
+$authExternal = new ExternalAuthMiddleware();
+
+$app->get('/hello', $authExternal, \Controllers\HelloController::class);
+$app->post('/send/mail', $authExternal, \Controllers\MailSendController::class);
+
+$app->run();

+ 8 - 0
templates/welcome.tpl

@@ -0,0 +1,8 @@
+Hello {{client_name}},
+
+Your account email is {{client_email}}.
+
+To change your password, use the code: {{client_pw_change_code}}
+
+Thank you,
+Support Team