From a8b0ed1dc8e8e4516260d202a1c796a7c553d688 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 01:56:01 +0200 Subject: [PATCH 01/10] 44 - Complete nix-shell dev commands --- shell.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shell.nix b/shell.nix index d4b44be..dfcd838 100644 --- a/shell.nix +++ b/shell.nix @@ -68,6 +68,10 @@ pkgs.mkShell { podman-compose logs -f "$@" } + dev-logs-db() { + podman-compose logs -f db "$@" + } + dev-shell() { podman-compose exec app sh } @@ -76,6 +80,10 @@ pkgs.mkShell { podman-compose exec app php artisan "$@" } + dev-test() { + podman-compose exec -T app php -d memory_limit=512M vendor/bin/phpunit "$@" + } + dev-fix-permissions() { echo "🔧 Fixing file permissions..." echo "This will require sudo to fix Docker-created files" @@ -149,8 +157,10 @@ pkgs.mkShell { echo " dev-rebuild - Full rebuild (removes volumes)" echo " dev-rebuild-quick - Quick rebuild (keeps volumes)" echo " dev-logs [svc] - Follow logs (default: all)" + echo " dev-logs-db - Tail database logs" echo " dev-shell - Enter app container" echo " dev-artisan - Run artisan commands" + echo " dev-test [path] - Run PHPUnit suite (CI invocation)" echo " dev-fix-permissions - Fix Docker-created file permissions" echo "" echo "Production commands:" From c4ae6c2e69c71a94c9c87c2b2c00e2cce624ee42 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 13:30:49 +0200 Subject: [PATCH 02/10] 45 - Add Forgejo CI workflow --- .env.testing | 25 + .forgejo/workflows/ci.yml | 37 + app/Actions/User/CreateUserAction.php | 8 +- app/Actions/User/EditUserAction.php | 26 +- .../Commands/GenerateSchedulesCommand.php | 1 + app/Http/Controllers/Api/ApiController.php | 3 +- app/Http/Controllers/Auth/LoginController.php | 2 +- .../Controllers/Auth/RegisterController.php | 2 +- app/Http/Resources/UserDishResource.php | 2 +- app/Livewire/Dishes/DishesList.php | 30 +- app/Livewire/Schedule/ScheduleCalendar.php | 61 +- app/Livewire/Schedule/ScheduleGenerator.php | 34 +- app/Livewire/Users/UsersList.php | 23 +- app/Models/Dish.php | 1 + app/Models/Planner.php | 1 + app/Models/Schedule.php | 5 +- app/Models/ScheduledUserDish.php | 3 +- app/Models/User.php | 3 +- app/Models/UserDish.php | 1 + app/Models/UserDishRecurrence.php | 2 +- app/Models/WeeklyRecurrence.php | 3 +- app/Providers/AppServiceProvider.php | 4 +- app/Services/OutputService.php | 1 - bootstrap/providers.php | 4 +- composer.json | 2 + config/auth.php | 4 +- config/sanctum.php | 9 +- database/factories/UserDishFactory.php | 2 +- database/seeders/DatabaseSeeder.php | 1 - database/seeders/DevelopmentSeeder.php | 2 +- database/seeders/PlannersSeeder.php | 2 +- database/seeders/ScheduleSeeder.php | 19 +- database/seeders/UsersSeeder.php | 3 +- phpstan-baseline.neon | 2839 +++++++++++++++++ phpstan.neon | 16 + phpunit.xml | 15 +- pint.json | 3 + routes/api.php | 10 +- routes/api/auth.php | 1 - routes/web.php | 2 +- shell.nix | 2 +- .../Controllers/PlannerAuthController.php | 2 +- .../Dish/Controllers/DishController.php | 12 +- .../Dish/Exceptions/InvalidDishException.php | 1 + .../Actions/DraftScheduleForDateAction.php | 2 +- .../GenerateScheduleForMonthAction.php | 3 +- .../RegenerateScheduleDayForUserAction.php | 2 +- .../Controllers/ScheduleController.php | 4 +- .../ScheduleUserDishController.php | 2 +- .../Requests/ScheduleUserDishRequest.php | 2 +- .../Requests/UpdateScheduleRequest.php | 2 +- .../Services/ScheduleCalendarService.php | 4 +- .../Schedule/Services/ScheduleGenerator.php | 2 +- .../Actions/CreateScheduledUserDishAction.php | 2 +- .../ScheduledUserDishController.php | 8 +- .../Policies/ScheduledUserDishPolicy.php | 1 - .../User/Actions/DeleteUserAction.php | 1 - .../User/Actions/UpdateUserAction.php | 1 - .../User/Controllers/UserController.php | 6 +- .../Actions/CreateFixedRecurrenceAction.php | 4 +- .../Actions/CreateMinimumRecurrenceAction.php | 2 +- .../UserDish/Actions/CreateUserDishAction.php | 6 +- .../Actions/DeleteFixedRecurrenceAction.php | 2 +- .../Actions/DeleteMinimumRecurrenceAction.php | 2 +- .../SyncRecurrencesForUserDishAction.php | 6 +- .../Actions/UpdateFixedRecurrenceAction.php | 2 +- .../Actions/UpdateMinimumRecurrenceAction.php | 2 +- .../Controllers/ListUserDishesController.php | 2 +- .../Controllers/UserDishController.php | 4 +- .../UserDishRecurrenceController.php | 10 +- .../Interfaces/FixedRecurrenceInterface.php | 3 +- .../Interfaces/RecurrenceInterface.php | 3 +- .../Repositories/UserDishRepository.php | 3 +- .../StoreUserDishRecurrenceRequest.php | 2 +- .../UpdateUserDishFixedRecurrenceRequest.php | 6 +- tests/Browser/Auth/LoginTest.php | 52 +- tests/Browser/Components/DishModal.php | 12 +- tests/Browser/Components/LoginForm.php | 16 +- .../Dishes/CreateDishFormValidationTest.php | 31 +- .../Browser/Dishes/CreateDishSuccessTest.php | 37 +- tests/Browser/Dishes/CreateDishTest.php | 24 +- tests/Browser/Dishes/DeleteDishTest.php | 27 +- .../Browser/Dishes/DishDeletionSafetyTest.php | 15 +- tests/Browser/Dishes/EditDishTest.php | 39 +- tests/Browser/LoginHelpers.php | 36 +- tests/Browser/Pages/DishesPage.php | 8 +- tests/Browser/Pages/LoginPage.php | 6 +- tests/Browser/Pages/Page.php | 2 +- tests/Browser/Pages/SchedulePage.php | 30 +- tests/Browser/Pages/UsersPage.php | 12 +- tests/Browser/RedirectTest.php | 22 +- .../Browser/Schedule/GenerateScheduleTest.php | 64 +- tests/Browser/Schedule/SchedulePageTest.php | 55 +- tests/Browser/Users/CreateUserTest.php | 73 +- tests/DuskTestCase.php | 4 +- tests/Feature/AuthenticationTest.php | 8 +- tests/Feature/Dish/CreateDishTest.php | 1 - tests/Feature/Dish/DeleteDishTest.php | 1 - tests/Feature/RegistrationTest.php | 10 +- .../Feature/Schedule/GenerateScheduleTest.php | 22 +- tests/Feature/Schedule/ListScheduleTest.php | 2 + tests/Feature/Schedule/UpdateScheduleTest.php | 4 +- .../CreateScheduledUserDishTest.php | 4 +- .../DeleteScheduledUserDishTest.php | 2 +- .../ReadScheduledUserDishTest.php | 2 +- .../UpdateScheduledUserDishTest.php | 4 +- tests/Feature/User/CreateUserTest.php | 1 - tests/Feature/User/DeleteUserTest.php | 2 +- .../Feature/User/Dish/ListUserDishesTest.php | 2 - .../User/Dish/RemoveDishesForUserTest.php | 2 +- tests/Feature/User/Dish/ShowUserDishTest.php | 1 - .../Dish/StoreRecurrenceForUserDishTest.php | 28 +- tests/Feature/User/UpdateUserTest.php | 2 +- tests/Traits/DishesTestTrait.php | 2 +- tests/Traits/ScheduledDishesTestTrait.php | 10 +- tests/Unit/Actions/EditUserActionTest.php | 2 +- .../RegenerateScheduleDayActionTest.php | 3 +- ...RegenerateScheduleDayForUserActionTest.php | 6 - .../Actions/User/CreateUserActionTest.php | 35 +- .../Actions/User/DeleteUserActionTest.php | 43 +- .../Actions/UserActionIntegrationTest.php | 31 +- .../ClearScheduleForMonthActionTest.php | 10 +- .../DraftScheduleForDateActionTest.php | 3 +- .../DraftScheduleForPeriodActionTest.php | 4 +- .../GenerateScheduleForMonthActionTest.php | 2 +- ...erateScheduleForDateForUsersActionTest.php | 2 +- tests/Unit/Schedule/ScheduleGeneratorTest.php | 11 +- .../Services/ScheduleCalendarServiceTest.php | 2 +- tests/Unit/ScheduleRepositoryTest.php | 4 +- ...leteScheduledUserDishForDateActionTest.php | 2 +- ...SkipScheduledUserDishForDateActionTest.php | 2 +- .../UpdateScheduledUserDishActionTest.php | 2 +- .../Repositories/UserDishRepositoryTest.php | 4 - 133 files changed, 3568 insertions(+), 605 deletions(-) create mode 100644 .env.testing create mode 100644 .forgejo/workflows/ci.yml create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon create mode 100644 pint.json diff --git a/.env.testing b/.env.testing new file mode 100644 index 0000000..5aea046 --- /dev/null +++ b/.env.testing @@ -0,0 +1,25 @@ +APP_NAME=DishPlanner +APP_ENV=testing +APP_KEY=base64:2ADW3imsmRMo+UDw3GR6852wQu37/aNK1ooxEdaJIC0= +APP_DEBUG=true +APP_URL=http://localhost + +APP_MAINTENANCE_DRIVER=file + +BCRYPT_ROUNDS=4 + +LOG_CHANNEL=stack +LOG_STACK=single + +DB_CONNECTION=sqlite +DB_DATABASE=:memory: + +SESSION_DRIVER=array + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync + +CACHE_STORE=array + +MAIL_MAILER=array diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 0000000..b51e604 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: ['release/*'] + pull_request: + branches: [main, 'release/*'] + +jobs: + ci: + runs-on: docker + container: + image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-1 + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Cache Composer dependencies + uses: https://data.forgejo.org/actions/cache@v4 + with: + path: ~/.cache/composer + key: composer-${{ hashFiles('composer.lock') }} + restore-keys: composer- + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Prepare environment + run: cp .env.testing .env + + - name: Lint + run: vendor/bin/pint --test + + - name: Static analysis + run: vendor/bin/phpstan analyse --memory-limit=1G + + - name: Tests + run: php -d memory_limit=512M vendor/bin/phpunit diff --git a/app/Actions/User/CreateUserAction.php b/app/Actions/User/CreateUserAction.php index 07e91a9..f3c8952 100644 --- a/app/Actions/User/CreateUserAction.php +++ b/app/Actions/User/CreateUserAction.php @@ -17,11 +17,11 @@ public function execute(array $data): User { try { // Validate required fields first - if (!isset($data['name']) || empty($data['name'])) { + if (! isset($data['name']) || empty($data['name'])) { throw new InvalidArgumentException('Name is required'); } - if (!isset($data['planner_id']) || empty($data['planner_id'])) { + if (! isset($data['planner_id']) || empty($data['planner_id'])) { throw new InvalidArgumentException('Planner ID is required'); } @@ -38,7 +38,7 @@ public function execute(array $data): User 'planner_id' => $data['planner_id'], ]); - if (!$user) { + if (! $user) { throw new Exception('User creation returned null'); } @@ -50,7 +50,7 @@ public function execute(array $data): User // Verify the user was actually created $createdUser = User::find($user->id); - if (!$createdUser) { + if (! $createdUser) { throw new Exception('User creation did not persist to database'); } diff --git a/app/Actions/User/EditUserAction.php b/app/Actions/User/EditUserAction.php index ccb931a..e5b31e3 100644 --- a/app/Actions/User/EditUserAction.php +++ b/app/Actions/User/EditUserAction.php @@ -12,52 +12,52 @@ public function execute(User $user, array $data): bool { try { DB::beginTransaction(); - + Log::info('EditUserAction: Starting user update', [ 'user_id' => $user->id, 'old_name' => $user->name, 'new_name' => $data['name'], 'planner_id' => $user->planner_id, ]); - + $result = $user->update([ 'name' => $data['name'], ]); - + Log::info('EditUserAction: Update result', [ 'result' => $result, 'user_id' => $user->id, ]); - - if (!$result) { + + if (! $result) { throw new \Exception('User update returned false'); } - + // Verify the update actually happened $user->refresh(); if ($user->name !== $data['name']) { throw new \Exception('User update did not persist to database'); } - + DB::commit(); - + Log::info('EditUserAction: User successfully updated', [ 'user_id' => $user->id, 'updated_name' => $user->name, ]); - + return true; - + } catch (\Exception $e) { DB::rollBack(); - + Log::error('EditUserAction: User update failed', [ 'user_id' => $user->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); - + throw $e; } } -} \ No newline at end of file +} diff --git a/app/Console/Commands/GenerateSchedulesCommand.php b/app/Console/Commands/GenerateSchedulesCommand.php index 98271bd..b62ae0e 100644 --- a/app/Console/Commands/GenerateSchedulesCommand.php +++ b/app/Console/Commands/GenerateSchedulesCommand.php @@ -24,6 +24,7 @@ public function handle(): int if ($planners->isEmpty()) { $this->warn('No planners found. Aborting schedule generation.'); + return self::FAILURE; } diff --git a/app/Http/Controllers/Api/ApiController.php b/app/Http/Controllers/Api/ApiController.php index a87c877..fa4df3f 100755 --- a/app/Http/Controllers/Api/ApiController.php +++ b/app/Http/Controllers/Api/ApiController.php @@ -13,8 +13,7 @@ public function response( ?array $payload = null, array|string|null $errors = null, int $statusCode = 200, - ): JsonResponse - { + ): JsonResponse { return response()->json(resolve(OutputService::class)->response($success, $payload, $errors), $statusCode); } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 80375a9..ded9ff5 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -41,4 +41,4 @@ public function logout(Request $request) return redirect('/'); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 8a4c546..4a08f82 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -34,4 +34,4 @@ public function register(Request $request) return redirect(route('dashboard')); } -} \ No newline at end of file +} diff --git a/app/Http/Resources/UserDishResource.php b/app/Http/Resources/UserDishResource.php index 2f7acb6..6aefd46 100644 --- a/app/Http/Resources/UserDishResource.php +++ b/app/Http/Resources/UserDishResource.php @@ -17,7 +17,7 @@ public function toArray(Request $request): array 'recurrences' => $this->recurrences->map(fn ($recurrence) => [ 'id' => $recurrence->id, 'type' => $recurrence->recurrence_type, - 'value' => $recurrence->getValue() + 'value' => $recurrence->getValue(), ]), ]; } diff --git a/app/Livewire/Dishes/DishesList.php b/app/Livewire/Dishes/DishesList.php index ffe8049..f7809c9 100644 --- a/app/Livewire/Dishes/DishesList.php +++ b/app/Livewire/Dishes/DishesList.php @@ -12,16 +12,20 @@ class DishesList extends Component use WithPagination; public $showCreateModal = false; + public $showEditModal = false; + public $showDeleteModal = false; - + public $editingDish = null; + public $deletingDish = null; - + // Form fields public $name = ''; + public $selectedUsers = []; - + protected $rules = [ 'name' => 'required|string|max:255', 'selectedUsers' => 'array', @@ -32,14 +36,14 @@ public function render() $dishes = Dish::with('users') ->orderBy('name') ->paginate(10); - + $users = User::where('planner_id', auth()->id()) ->orderBy('name') ->get(); - + return view('livewire.dishes.dishes-list', [ 'dishes' => $dishes, - 'users' => $users + 'users' => $users, ]); } @@ -60,13 +64,13 @@ public function store() ]); // Attach selected users - if (!empty($this->selectedUsers)) { + if (! empty($this->selectedUsers)) { $dish->users()->attach($this->selectedUsers); } $this->showCreateModal = false; $this->reset(['name', 'selectedUsers']); - + session()->flash('success', 'Dish created successfully.'); } @@ -86,13 +90,13 @@ public function update() $this->editingDish->update([ 'name' => $this->name, ]); - + // Sync users $this->editingDish->users()->sync($this->selectedUsers); $this->showEditModal = false; $this->reset(['name', 'selectedUsers', 'editingDish']); - + session()->flash('success', 'Dish updated successfully.'); } @@ -108,7 +112,7 @@ public function delete() $this->deletingDish->delete(); $this->showDeleteModal = false; $this->deletingDish = null; - + session()->flash('success', 'Dish deleted successfully.'); } @@ -126,7 +130,7 @@ public function toggleAllUsers(): void if (count($this->selectedUsers) === $users->count()) { $this->selectedUsers = []; } else { - $this->selectedUsers = $users->pluck('id')->map(fn($id) => (string) $id)->toArray(); + $this->selectedUsers = $users->pluck('id')->map(fn ($id) => (string) $id)->toArray(); } } -} \ No newline at end of file +} diff --git a/app/Livewire/Schedule/ScheduleCalendar.php b/app/Livewire/Schedule/ScheduleCalendar.php index 9d2a911..19e100f 100644 --- a/app/Livewire/Schedule/ScheduleCalendar.php +++ b/app/Livewire/Schedule/ScheduleCalendar.php @@ -19,25 +19,39 @@ class ScheduleCalendar extends Component { public $currentMonth; + public $currentYear; + public $calendarDays = []; + public $showRegenerateModal = false; + public $regenerateDate = null; + public $regenerateUserId = null; // Edit dish modal public $showEditDishModal = false; + public $editDate = null; + public $editUserId = null; + public $selectedDishId = null; + public $availableDishes = []; // Add dish modal public $showAddDishModal = false; + public $addDate = null; + public $addUserIds = []; + public $addSelectedDishId = null; + public $addAvailableUsers = []; + public $addAvailableDishes = []; public function mount(): void @@ -61,7 +75,7 @@ public function refreshCalendar(): void public function loadCalendar(): void { - $service = new ScheduleCalendarService(); + $service = new ScheduleCalendarService; $this->calendarDays = $service->getCalendarDays( auth()->user(), $this->currentMonth, @@ -93,8 +107,9 @@ public function nextMonth(): void public function regenerateForUserDate($date, $userId): void { - if (!$this->authorizeUser($userId)) { + if (! $this->authorizeUser($userId)) { session()->flash('error', 'Unauthorized action.'); + return; } @@ -106,12 +121,13 @@ public function regenerateForUserDate($date, $userId): void public function confirmRegenerate(): void { try { - if (!$this->authorizeUser($this->regenerateUserId)) { + if (! $this->authorizeUser($this->regenerateUserId)) { session()->flash('error', 'Unauthorized action.'); + return; } - $action = new DeleteScheduledUserDishForDateAction(); + $action = new DeleteScheduledUserDishForDateAction; $action->execute( auth()->user(), Carbon::parse($this->regenerateDate), @@ -131,12 +147,13 @@ public function confirmRegenerate(): void public function skipDay($date, $userId): void { try { - if (!$this->authorizeUser($userId)) { + if (! $this->authorizeUser($userId)) { session()->flash('error', 'Unauthorized action.'); + return; } - $action = new SkipScheduledUserDishForDateAction(); + $action = new SkipScheduledUserDishForDateAction; $action->execute( auth()->user(), Carbon::parse($date), @@ -155,6 +172,7 @@ public function skipDay($date, $userId): void private function authorizeUser(int $userId): bool { $user = User::find($userId); + return $user && $user->planner_id === auth()->id(); } @@ -179,8 +197,9 @@ public function cancel(): void public function removeDish($date, $userId): void { try { - if (!$this->authorizeUser($userId)) { + if (! $this->authorizeUser($userId)) { session()->flash('error', 'Unauthorized action.'); + return; } @@ -223,7 +242,7 @@ public function toggleAllUsers(): void if (count($this->addUserIds) === count($this->addAvailableUsers)) { $this->addUserIds = []; } else { - $this->addUserIds = $this->addAvailableUsers->pluck('id')->map(fn($id) => (string) $id)->toArray(); + $this->addUserIds = $this->addAvailableUsers->pluck('id')->map(fn ($id) => (string) $id)->toArray(); } $this->updateAvailableDishes(); } @@ -252,11 +271,13 @@ public function saveAddDish(): void try { if (empty($this->addUserIds)) { session()->flash('error', 'Please select at least one user.'); + return; } - if (!$this->addSelectedDishId) { + if (! $this->addSelectedDishId) { session()->flash('error', 'Please select a dish.'); + return; } @@ -273,8 +294,9 @@ public function saveAddDish(): void $skippedCount = 0; foreach ($this->addUserIds as $userId) { - if (!$this->authorizeUser((int) $userId)) { + if (! $this->authorizeUser((int) $userId)) { $skippedCount++; + continue; } @@ -285,6 +307,7 @@ public function saveAddDish(): void if ($existing) { $skippedCount++; + continue; } @@ -293,8 +316,9 @@ public function saveAddDish(): void ->where('dish_id', $this->addSelectedDishId) ->first(); - if (!$userDish) { + if (! $userDish) { $skippedCount++; + continue; } @@ -336,8 +360,9 @@ private function closeAddDishModal(): void public function editDish($date, $userId): void { - if (!$this->authorizeUser($userId)) { + if (! $this->authorizeUser($userId)) { session()->flash('error', 'Unauthorized action.'); + return; } @@ -370,13 +395,15 @@ public function editDish($date, $userId): void public function saveDish(): void { try { - if (!$this->authorizeUser($this->editUserId)) { + if (! $this->authorizeUser($this->editUserId)) { session()->flash('error', 'Unauthorized action.'); + return; } - if (!$this->selectedDishId) { + if (! $this->selectedDishId) { session()->flash('error', 'Please select a dish.'); + return; } @@ -394,8 +421,9 @@ public function saveDish(): void ->where('dish_id', $this->selectedDishId) ->first(); - if (!$userDish) { + if (! $userDish) { session()->flash('error', 'This dish is not assigned to this user.'); + return; } @@ -427,7 +455,8 @@ public function saveDish(): void public function getMonthNameProperty(): string { - $service = new ScheduleCalendarService(); + $service = new ScheduleCalendarService; + return $service->getMonthName($this->currentMonth, $this->currentYear); } } diff --git a/app/Livewire/Schedule/ScheduleGenerator.php b/app/Livewire/Schedule/ScheduleGenerator.php index a2f3b71..fc2f361 100644 --- a/app/Livewire/Schedule/ScheduleGenerator.php +++ b/app/Livewire/Schedule/ScheduleGenerator.php @@ -7,19 +7,27 @@ use DishPlanner\Schedule\Actions\ClearScheduleForMonthAction; use DishPlanner\Schedule\Actions\GenerateScheduleForMonthAction; use DishPlanner\Schedule\Actions\RegenerateScheduleForDateForUsersAction; +use Illuminate\Contracts\View\Factory; +use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Log; use Livewire\Component; class ScheduleGenerator extends Component { private const YEARS_IN_PAST = 1; + private const YEARS_IN_FUTURE = 5; public $selectedMonth; + public $selectedYear; + public $selectedUsers = []; + public $clearExisting = true; + public $showAdvancedOptions = false; + public $isGenerating = false; public function mount(): void @@ -32,7 +40,7 @@ public function mount(): void ->toArray(); } - public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + public function render(): Factory|View { $users = User::where('planner_id', auth()->id()) ->orderBy('name') @@ -43,7 +51,7 @@ public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contrac return view('livewire.schedule.schedule-generator', [ 'users' => $users, 'months' => $this->getMonthNames(), - 'years' => $years + 'years' => $years, ]); } @@ -52,13 +60,13 @@ public function generate(): void $this->validate([ 'selectedUsers' => 'required|array|min:1', 'selectedMonth' => 'required|integer|min:1|max:12', - 'selectedYear' => 'required|integer|min:' . (now()->year - self::YEARS_IN_PAST) . '|max:' . (now()->year + self::YEARS_IN_FUTURE), + 'selectedYear' => 'required|integer|min:'.(now()->year - self::YEARS_IN_PAST).'|max:'.(now()->year + self::YEARS_IN_FUTURE), ]); $this->isGenerating = true; try { - $action = new GenerateScheduleForMonthAction(); + $action = new GenerateScheduleForMonthAction; $action->execute( auth()->user(), $this->selectedMonth, @@ -70,8 +78,8 @@ public function generate(): void $this->isGenerating = false; $this->dispatch('schedule-generated'); - session()->flash('success', 'Schedule generated successfully for ' . - $this->getSelectedMonthName() . ' ' . $this->selectedYear); + session()->flash('success', 'Schedule generated successfully for '. + $this->getSelectedMonthName().' '.$this->selectedYear); } catch (\Exception $e) { $this->isGenerating = false; @@ -83,7 +91,7 @@ public function generate(): void public function regenerateForDate($date): void { try { - $action = new RegenerateScheduleForDateForUsersAction(); + $action = new RegenerateScheduleForDateForUsersAction; $action->execute( auth()->user(), Carbon::parse($date), @@ -91,7 +99,7 @@ public function regenerateForDate($date): void ); $this->dispatch('schedule-generated'); - session()->flash('success', 'Schedule regenerated for ' . Carbon::parse($date)->format('M d, Y')); + session()->flash('success', 'Schedule regenerated for '.Carbon::parse($date)->format('M d, Y')); } catch (\Exception $e) { Log::error('Schedule regeneration failed', ['exception' => $e, 'date' => $date]); @@ -102,7 +110,7 @@ public function regenerateForDate($date): void public function clearMonth(): void { try { - $action = new ClearScheduleForMonthAction(); + $action = new ClearScheduleForMonthAction; $action->execute( auth()->user(), $this->selectedMonth, @@ -111,8 +119,8 @@ public function clearMonth(): void ); $this->dispatch('schedule-generated'); - session()->flash('success', 'Schedule cleared for ' . - $this->getSelectedMonthName() . ' ' . $this->selectedYear); + session()->flash('success', 'Schedule cleared for '. + $this->getSelectedMonthName().' '.$this->selectedYear); } catch (\Exception $e) { Log::error('Clear month failed', ['exception' => $e]); @@ -122,7 +130,7 @@ public function clearMonth(): void public function toggleAdvancedOptions() { - $this->showAdvancedOptions = !$this->showAdvancedOptions; + $this->showAdvancedOptions = ! $this->showAdvancedOptions; } private function getMonthNames(): array @@ -130,7 +138,7 @@ private function getMonthNames(): array return [ 1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', - 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December' + 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December', ]; } diff --git a/app/Livewire/Users/UsersList.php b/app/Livewire/Users/UsersList.php index e9b3762..e641526 100644 --- a/app/Livewire/Users/UsersList.php +++ b/app/Livewire/Users/UsersList.php @@ -2,10 +2,10 @@ namespace App\Livewire\Users; -use App\Models\User; use App\Actions\User\CreateUserAction; use App\Actions\User\DeleteUserAction; use App\Actions\User\EditUserAction; +use App\Models\User; use Exception; use Illuminate\Contracts\View\View; use Livewire\Component; @@ -16,10 +16,13 @@ class UsersList extends Component use WithPagination; public bool $showCreateModal = false; + public bool $showEditModal = false; + public bool $showDeleteModal = false; public ?User $editingUser = null; + public ?User $deletingUser = null; // Form fields @@ -36,7 +39,7 @@ public function render(): View ->paginate(10); return view('livewire.users.users-list', [ - 'users' => $users + 'users' => $users, ]); } @@ -52,7 +55,7 @@ public function store(): void $this->validate(); try { - (new CreateUserAction())->execute([ + (new CreateUserAction)->execute([ 'name' => $this->name, 'planner_id' => auth()->id(), ]); @@ -62,7 +65,7 @@ public function store(): void session()->flash('success', 'User created successfully.'); } catch (Exception $e) { - session()->flash('error', 'Failed to create user: ' . $e->getMessage()); + session()->flash('error', 'Failed to create user: '.$e->getMessage()); } } @@ -79,7 +82,7 @@ public function update(): void $this->validate(); try { - (new EditUserAction())->execute($this->editingUser, ['name' => $this->name]); + (new EditUserAction)->execute($this->editingUser, ['name' => $this->name]); $this->showEditModal = false; $this->reset(['name', 'editingUser']); @@ -89,7 +92,7 @@ public function update(): void // Force component to re-render with fresh data $this->resetPage(); } catch (Exception $e) { - session()->flash('error', 'Failed to update user: ' . $e->getMessage()); + session()->flash('error', 'Failed to update user: '.$e->getMessage()); } } @@ -102,17 +105,17 @@ public function confirmDelete(User $user): void public function delete(): void { try { - (new DeleteUserAction())->execute($this->deletingUser); - + (new DeleteUserAction)->execute($this->deletingUser); + $this->showDeleteModal = false; $this->deletingUser = null; session()->flash('success', 'User deleted successfully.'); - + // Force component to re-render with fresh data $this->resetPage(); } catch (Exception $e) { - session()->flash('error', 'Failed to delete user: ' . $e->getMessage()); + session()->flash('error', 'Failed to delete user: '.$e->getMessage()); } } diff --git a/app/Models/Dish.php b/app/Models/Dish.php index a1eb1a2..da1b8ea 100755 --- a/app/Models/Dish.php +++ b/app/Models/Dish.php @@ -21,6 +21,7 @@ * @property Carbon $updated_at * @property Collection $users * @property Collection $userDishes + * * @method static create(array $data) * @method static findOrFail(int $dish_id) * @method static DishFactory factory($count = null, $state = []) diff --git a/app/Models/Planner.php b/app/Models/Planner.php index c53777e..fb18ff2 100644 --- a/app/Models/Planner.php +++ b/app/Models/Planner.php @@ -13,6 +13,7 @@ * @property int $id * @property static PlannerFactory factory($count = null, $state = []) * @property Collection $users + * * @method static first() * @method static create(array $array) */ diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php index d1e95e6..f6c15be 100644 --- a/app/Models/Schedule.php +++ b/app/Models/Schedule.php @@ -22,8 +22,9 @@ * @property Dish $dish * @property User $user * @property Carbon $date - * @property boolean $is_skipped + * @property bool $is_skipped * @property Collection $scheduledUserDishes + * * @method static create(array $array) * @method static Builder where(array|Closure|Expression|string $column, mixed $operator = null, mixed $value = null, string $boolean = 'and') * @method static ScheduleFactory factory($count = null, $state = []) @@ -38,6 +39,8 @@ class Schedule extends Model public $timestamps = false; + protected $dateFormat = 'Y-m-d'; + protected $fillable = ['planner_id', 'date', 'is_skipped']; protected $casts = [ diff --git a/app/Models/ScheduledUserDish.php b/app/Models/ScheduledUserDish.php index eb54251..8334e2a 100644 --- a/app/Models/ScheduledUserDish.php +++ b/app/Models/ScheduledUserDish.php @@ -17,6 +17,7 @@ * @property int $user_dish_id * @property UserDish $userDish * @property bool $is_skipped + * * @method static create(array $array) * @method static ScheduledUserDishFactory factory($count = null, $state = []) * @method static firstOrCreate(array $array, array $array1) @@ -29,7 +30,7 @@ class ScheduledUserDish extends Model 'schedule_id', 'user_id', 'user_dish_id', - 'is_skipped' + 'is_skipped', ]; protected $casts = [ diff --git a/app/Models/User.php b/app/Models/User.php index 6639c90..2875ea8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,10 +6,10 @@ use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; -use Illuminate\Database\Eloquent\Model; /** * @property int $id @@ -17,6 +17,7 @@ * @property string $name * @property Collection $dishes * @property Collection $userDishes + * * @method static User findOrFail(int $user_id) * @method static UserFactory factory($count = null, $state = []) * @method static create(array $array) diff --git a/app/Models/UserDish.php b/app/Models/UserDish.php index a30aab0..6981187 100644 --- a/app/Models/UserDish.php +++ b/app/Models/UserDish.php @@ -17,6 +17,7 @@ * @method static UserDish|null find(int|null $user_dish_id) * @method static create(array $array) * @method static where(string $string, int $id) + * * @property int $id * @property int $dish_id * @property int $user_id diff --git a/app/Models/UserDishRecurrence.php b/app/Models/UserDishRecurrence.php index f968d30..935ee56 100755 --- a/app/Models/UserDishRecurrence.php +++ b/app/Models/UserDishRecurrence.php @@ -36,7 +36,7 @@ public function getValue(): int return match ($this->recurrence_type) { WeeklyRecurrence::class => $this->recurrence->weekday->value, MinimumRecurrence::class => $this->recurrence->days, - default => throw new InvalidRecurrenceTypeException() + default => throw new InvalidRecurrenceTypeException }; } } diff --git a/app/Models/WeeklyRecurrence.php b/app/Models/WeeklyRecurrence.php index fa3b418..58456fb 100755 --- a/app/Models/WeeklyRecurrence.php +++ b/app/Models/WeeklyRecurrence.php @@ -12,10 +12,11 @@ /** * @property int $weekday + * * @method static create(array $array) * @method static WeeklyRecurrenceFactory factory($count = null, $state = []) */ -class WeeklyRecurrence extends Model implements RecurrenceInterface, FixedRecurrenceInterface +class WeeklyRecurrence extends Model implements FixedRecurrenceInterface, RecurrenceInterface { /** @use HasFactory */ use HasFactory; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0bbbb1e..8b1ea1e 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,7 +16,6 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as BaseHandler; use Illuminate\Support\Facades\Gate; -use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; use Throwable; @@ -25,7 +24,8 @@ class AppServiceProvider extends ServiceProvider public function register(): void { $this->app->bind(ExceptionHandler::class, function ($app) { - return new class($app) extends BaseHandler { + return new class($app) extends BaseHandler + { public function render($request, Throwable $e) { // Handle specific custom exception diff --git a/app/Services/OutputService.php b/app/Services/OutputService.php index 2e7882e..430f2e1 100644 --- a/app/Services/OutputService.php +++ b/app/Services/OutputService.php @@ -2,7 +2,6 @@ namespace App\Services; - class OutputService { public function response(bool $success = true, ?array $payload = null, array|string|null $errors = null): array diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 38b258d..fc94ae6 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -1,5 +1,7 @@ [ 'planners' => [ 'driver' => 'eloquent', - 'model' => App\Models\Planner::class, + 'model' => Planner::class, ], // 'users' => [ diff --git a/config/sanctum.php b/config/sanctum.php index 764a82f..b660703 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -1,5 +1,8 @@ [ - 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, - 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, - 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + 'authenticate_session' => AuthenticateSession::class, + 'encrypt_cookies' => EncryptCookies::class, + 'validate_csrf_token' => ValidateCsrfToken::class, ], ]; diff --git a/database/factories/UserDishFactory.php b/database/factories/UserDishFactory.php index 7c235ef..00fcbd8 100644 --- a/database/factories/UserDishFactory.php +++ b/database/factories/UserDishFactory.php @@ -3,8 +3,8 @@ namespace Database\Factories; use App\Models\Dish; -use App\Models\UserDish; use App\Models\User; +use App\Models\UserDish; use Illuminate\Database\Eloquent\Factories\Factory; /** diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 3a08996..b1502d2 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; diff --git a/database/seeders/DevelopmentSeeder.php b/database/seeders/DevelopmentSeeder.php index 0b470d8..c7f06cc 100644 --- a/database/seeders/DevelopmentSeeder.php +++ b/database/seeders/DevelopmentSeeder.php @@ -73,4 +73,4 @@ public function run(): void $this->command->info('Development data seeded successfully!'); $this->command->info('Login credentials: myrmidex@myrmidex.net / Password'); } -} \ No newline at end of file +} diff --git a/database/seeders/PlannersSeeder.php b/database/seeders/PlannersSeeder.php index 2d98cc8..f46a205 100644 --- a/database/seeders/PlannersSeeder.php +++ b/database/seeders/PlannersSeeder.php @@ -14,7 +14,7 @@ public function run(): void [ 'name' => 'Admin', 'email' => 'admin@test.com', - 'password' => 'password' + 'password' => 'password', ], ])->each(fn (array $data) => Planner::create([ 'name' => $data['name'], diff --git a/database/seeders/ScheduleSeeder.php b/database/seeders/ScheduleSeeder.php index 82652c6..2864378 100755 --- a/database/seeders/ScheduleSeeder.php +++ b/database/seeders/ScheduleSeeder.php @@ -41,17 +41,16 @@ private function createScheduleForPeriod(CarbonPeriod $period): void $planner = Planner::all()->first() ?? Planner::factory()->create(); collect($period) - ->each(fn (Carbon $date) => - User::query() - ->inRandomOrder() - ->get() - ->each(fn (User $user) => (new CreateScheduledUserDishAction()) - ->execute( - planner: $planner, - schedule: resolve(ScheduleRepository::class)->findOrCreate($planner, $date), - userDish: $user->userDishes->random(), - ) + ->each(fn (Carbon $date) => User::query() + ->inRandomOrder() + ->get() + ->each(fn (User $user) => (new CreateScheduledUserDishAction) + ->execute( + planner: $planner, + schedule: resolve(ScheduleRepository::class)->findOrCreate($planner, $date), + userDish: $user->userDishes->random(), ) + ) ); } } diff --git a/database/seeders/UsersSeeder.php b/database/seeders/UsersSeeder.php index 2217a9a..ef9f08a 100644 --- a/database/seeders/UsersSeeder.php +++ b/database/seeders/UsersSeeder.php @@ -16,7 +16,6 @@ public function run(): void ->each(fn (string $name) => User::factory()->create([ 'planner_id' => $planner->id, 'name' => $name, - ])) - ; + ])); } } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..9c757af --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,2839 @@ +parameters: + ignoreErrors: + - + message: '#^Method App\\Actions\\User\\CreateUserAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Actions/User/CreateUserAction.php + + - + message: '#^Negated boolean expression is always false\.$#' + identifier: booleanNot.alwaysFalse + count: 1 + path: app/Actions/User/CreateUserAction.php + + - + message: '#^Method App\\Actions\\User\\EditUserAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Actions/User/EditUserAction.php + + - + message: '#^Method App\\Http\\Controllers\\Api\\ApiController\:\:error\(\) has parameter \$errors with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Controllers/Api/ApiController.php + + - + message: '#^Method App\\Http\\Controllers\\Api\\ApiController\:\:response\(\) has parameter \$errors with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Controllers/Api/ApiController.php + + - + message: '#^Method App\\Http\\Controllers\\Api\\ApiController\:\:response\(\) has parameter \$payload with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Controllers/Api/ApiController.php + + - + message: '#^Method App\\Http\\Controllers\\Api\\ApiController\:\:success\(\) has parameter \$payload with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Controllers/Api/ApiController.php + + - + message: '#^Method App\\Http\\Controllers\\Auth\\LoginController\:\:login\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Http/Controllers/Auth/LoginController.php + + - + message: '#^Method App\\Http\\Controllers\\Auth\\LoginController\:\:logout\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Http/Controllers/Auth/LoginController.php + + - + message: '#^Method App\\Http\\Controllers\\Auth\\LoginController\:\:showLoginForm\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Http/Controllers/Auth/LoginController.php + + - + message: '#^Method App\\Http\\Controllers\\Auth\\RegisterController\:\:register\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Http/Controllers/Auth/RegisterController.php + + - + message: '#^Method App\\Http\\Controllers\\Auth\\RegisterController\:\:showRegistrationForm\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Http/Controllers/Auth/RegisterController.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\MinimalScheduleResource\:\:\$date\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/MinimalScheduleResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\MinimalScheduleResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/MinimalScheduleResource.php + + - + message: '#^Method App\\Http\\Resources\\MinimalScheduleResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/MinimalScheduleResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\MinimalScheduledUserDishResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/MinimalScheduledUserDishResource.php + + - + message: '#^Method App\\Http\\Resources\\MinimalScheduledUserDishResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/MinimalScheduledUserDishResource.php + + - + message: '#^Using nullsafe property access on non\-nullable type App\\Models\\UserDish\. Use \-\> instead\.$#' + identifier: nullsafe.neverNull + count: 2 + path: app/Http/Resources/MinimalScheduledUserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\ScheduledUserDishResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/ScheduledUserDishResource.php + + - + message: '#^Method App\\Http\\Resources\\ScheduledUserDishResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/ScheduledUserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserDishResource\:\:\$dish\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserDishResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserDishResource\:\:\$recurrences\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserDishResource\:\:\$user\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserDishResource.php + + - + message: '#^Method App\\Http\\Resources\\UserDishResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserDishResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserResource\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserResource.php + + - + message: '#^Method App\\Http\\Resources\\UserResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithDishesResource\:\:\$dishes\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithDishesResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithDishesResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithDishesResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithDishesResource\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithDishesResource.php + + - + message: '#^Method App\\Http\\Resources\\UserWithDishesResource\:\:mapDishes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserWithDishesResource.php + + - + message: '#^Method App\\Http\\Resources\\UserWithDishesResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserWithDishesResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithUserDishesResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithUserDishesResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithUserDishesResource\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithUserDishesResource.php + + - + message: '#^Access to an undefined property App\\Http\\Resources\\UserWithUserDishesResource\:\:\$userDishes\.$#' + identifier: property.notFound + count: 1 + path: app/Http/Resources/UserWithUserDishesResource.php + + - + message: '#^Method App\\Http\\Resources\\UserWithUserDishesResource\:\:mapDishes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserWithUserDishesResource.php + + - + message: '#^Method App\\Http\\Resources\\UserWithUserDishesResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Http/Resources/UserWithUserDishesResource.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:cancel\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:confirmDelete\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:create\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:delete\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:edit\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:render\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:store\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Dishes\\DishesList\:\:update\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$deletingDish has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$editingDish has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$name has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$rules has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$selectedUsers has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$showCreateModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$showDeleteModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Property App\\Livewire\\Dishes\\DishesList\:\:\$showEditModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Dishes/DishesList.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:editDish\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:editDish\(\) has parameter \$userId with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:openAddDishModal\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:regenerateForUserDate\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:regenerateForUserDate\(\) has parameter \$userId with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:removeDish\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:removeDish\(\) has parameter \$userId with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:skipDay\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleCalendar\:\:skipDay\(\) has parameter \$userId with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$addAvailableDishes has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$addAvailableUsers has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$addDate has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$addSelectedDishId has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$addUserIds has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$availableDishes has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$calendarDays has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$currentMonth has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$currentYear has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$editDate has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$editUserId has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$listeners has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$regenerateDate has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$regenerateUserId has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$selectedDishId has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$showAddDishModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$showEditDishModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleCalendar\:\:\$showRegenerateModal has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Right side of && is always true\.$#' + identifier: booleanAnd.rightAlwaysTrue + count: 1 + path: app/Livewire/Schedule/ScheduleCalendar.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleGenerator\:\:getMonthNames\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleGenerator\:\:regenerateForDate\(\) has parameter \$date with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Method App\\Livewire\\Schedule\\ScheduleGenerator\:\:toggleAdvancedOptions\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$clearExisting has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$isGenerating has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$selectedMonth has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$selectedUsers has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$selectedYear has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Schedule\\ScheduleGenerator\:\:\$showAdvancedOptions has no type specified\.$#' + identifier: missingType.property + count: 1 + path: app/Livewire/Schedule/ScheduleGenerator.php + + - + message: '#^Property App\\Livewire\\Users\\UsersList\:\:\$rules type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Livewire/Users/UsersList.php + + - + message: '#^Class App\\Models\\Dish has PHPDoc tag @method for method create\(\) parameter \#1 \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/Dish.php + + - + message: '#^Method App\\Models\\Dish\:\:recurrences\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough does not specify its types\: TRelatedModel, TIntermediateModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Dish.php + + - + message: '#^Method App\\Models\\Dish\:\:userDishes\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Dish.php + + - + message: '#^Method App\\Models\\Dish\:\:users\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany does not specify its types\: TRelatedModel, TDeclaringModel, TPivotModel, TAccessor \(2\-4 required\)$#' + identifier: missingType.generics + count: 1 + path: app/Models/Dish.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\Dish\:\:\$userDishes contains generic class Illuminate\\Support\\Collection but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: app/Models/Dish.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\Dish\:\:\$users contains generic class Illuminate\\Support\\Collection but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: app/Models/Dish.php + + - + message: '#^Method App\\Models\\MinimumRecurrence\:\:recurrence\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\MorphOne does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/MinimumRecurrence.php + + - + message: '#^Class App\\Models\\Planner has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/Planner.php + + - + message: '#^Class App\\Models\\Planner uses generic trait Illuminate\\Database\\Eloquent\\Factories\\HasFactory but does not specify its types\: TFactory$#' + identifier: missingType.generics + count: 1 + path: app/Models/Planner.php + + - + message: '#^Method App\\Models\\Planner\:\:schedules\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Planner.php + + - + message: '#^Method App\\Models\\Planner\:\:users\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Planner.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\Planner\:\:\$users contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Planner.php + + - + message: '#^PHPDoc tag @property has invalid value \(static PlannerFactory factory\(\$count \= null, \$state \= \[\]\)\)\: Unexpected token "PlannerFactory", expected variable at offset 45 on line 3$#' + identifier: phpDoc.parseError + count: 1 + path: app/Models/Planner.php + + - + message: '#^Class App\\Models\\Schedule has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/Schedule.php + + - + message: '#^Class App\\Models\\Schedule has PHPDoc tag @method for method firstOrCreate\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/Schedule.php + + - + message: '#^Class App\\Models\\Schedule has PHPDoc tag @method for method where\(\) parameter \#1 \$column with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/Schedule.php + + - + message: '#^Method App\\Models\\Schedule\:\:scheduledUserDishes\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Schedule.php + + - + message: '#^PHPDoc tag @method for method App\\Models\\Schedule\:\:where\(\) parameter \#1 \$column contains generic class Illuminate\\Database\\Query\\Expression but does not specify its types\: TValue$#' + identifier: missingType.generics + count: 1 + path: app/Models/Schedule.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\Schedule\:\:\$scheduledUserDishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/Schedule.php + + - + message: '#^Class App\\Models\\ScheduledUserDish has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Class App\\Models\\ScheduledUserDish has PHPDoc tag @method for method firstOrCreate\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Class App\\Models\\ScheduledUserDish has PHPDoc tag @method for method firstOrCreate\(\) parameter \#2 \$array1 with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Class App\\Models\\ScheduledUserDish uses generic trait Illuminate\\Database\\Eloquent\\Factories\\HasFactory but does not specify its types\: TFactory$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Method App\\Models\\ScheduledUserDish\:\:schedule\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Method App\\Models\\ScheduledUserDish\:\:scopeForUser\(\) has parameter \$query with generic class Illuminate\\Database\\Eloquent\\Builder but does not specify its types\: TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Method App\\Models\\ScheduledUserDish\:\:scopeForUser\(\) return type with generic class Illuminate\\Database\\Eloquent\\Builder does not specify its types\: TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Method App\\Models\\ScheduledUserDish\:\:user\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Method App\\Models\\ScheduledUserDish\:\:userDish\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/ScheduledUserDish.php + + - + message: '#^Class App\\Models\\User has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/User.php + + - + message: '#^Method App\\Models\\User\:\:dishes\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany does not specify its types\: TRelatedModel, TDeclaringModel, TPivotModel, TAccessor \(2\-4 required\)$#' + identifier: missingType.generics + count: 1 + path: app/Models/User.php + + - + message: '#^Method App\\Models\\User\:\:recurrences\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough does not specify its types\: TRelatedModel, TIntermediateModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/User.php + + - + message: '#^Method App\\Models\\User\:\:userDishes\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/User.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\User\:\:\$dishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/User.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\User\:\:\$userDishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/User.php + + - + message: '#^Class App\\Models\\UserDish has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Class App\\Models\\UserDish uses generic trait Illuminate\\Database\\Eloquent\\Factories\\HasFactory but does not specify its types\: TFactory$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Method App\\Models\\UserDish\:\:dish\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Method App\\Models\\UserDish\:\:fixedRecurrences\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Method App\\Models\\UserDish\:\:recurrences\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\HasMany does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Method App\\Models\\UserDish\:\:user\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\UserDish\:\:\$fixedRecurrences contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^PHPDoc tag @property for property App\\Models\\UserDish\:\:\$recurrences contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDish.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$days\.$#' + identifier: property.notFound + count: 1 + path: app/Models/UserDishRecurrence.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$weekday\.$#' + identifier: property.notFound + count: 1 + path: app/Models/UserDishRecurrence.php + + - + message: '#^Method App\\Models\\UserDishRecurrence\:\:dishUser\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDishRecurrence.php + + - + message: '#^Method App\\Models\\UserDishRecurrence\:\:recurrence\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\MorphTo does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/UserDishRecurrence.php + + - + message: '#^Class App\\Models\\WeeklyRecurrence has PHPDoc tag @method for method create\(\) parameter \#1 \$array with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Models/WeeklyRecurrence.php + + - + message: '#^Method App\\Models\\WeeklyRecurrence\:\:recurrence\(\) return type with generic class Illuminate\\Database\\Eloquent\\Relations\\MorphOne does not specify its types\: TRelatedModel, TDeclaringModel$#' + identifier: missingType.generics + count: 1 + path: app/Models/WeeklyRecurrence.php + + - + message: '#^Expression on left side of \?\? is not nullable\.$#' + identifier: nullCoalesce.expr + count: 1 + path: app/Providers/AppServiceProvider.php + + - + message: '#^Method App\\Services\\OutputService\:\:error\(\) has parameter \$errors with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:error\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:response\(\) has parameter \$errors with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:response\(\) has parameter \$payload with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:response\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:success\(\) has parameter \$payload with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method App\\Services\\OutputService\:\:success\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: app/Services/OutputService.php + + - + message: '#^Method DishPlanner\\Auth\\Controllers\\PlannerAuthController\:\:register\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/DishPlanner/Auth/Controllers/PlannerAuthController.php + + - + message: '#^Method DishPlanner\\Dish\\Actions\\AddUsersToDishAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Actions/AddUsersToDishAction.php + + - + message: '#^Method DishPlanner\\Dish\\Actions\\CreateDishAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Actions/CreateDishAction.php + + - + message: '#^Method DishPlanner\\Dish\\Actions\\RemoveUsersFromDishAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Actions/RemoveUsersFromDishAction.php + + - + message: '#^Method DishPlanner\\Dish\\Actions\\SyncUsersAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Actions/SyncUsersAction.php + + - + message: '#^Method DishPlanner\\Dish\\Actions\\UpdateDishAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Actions/UpdateDishAction.php + + - + message: '#^Property DishPlanner\\Dish\\Exceptions\\InvalidDishException\:\:\$code has no type specified\.$#' + identifier: missingType.property + count: 1 + path: src/DishPlanner/Dish/Exceptions/InvalidDishException.php + + - + message: '#^Method DishPlanner\\Dish\\Repositories\\DishRepository\:\:getRandomDish\(\) should return App\\Models\\Dish but returns \(Illuminate\\Database\\Eloquent\\Model&object\{pivot\: Illuminate\\Database\\Eloquent\\Relations\\Pivot\}\)\|null\.$#' + identifier: return.type + count: 1 + path: src/DishPlanner/Dish/Repositories/DishRepository.php + + - + message: '#^Method DishPlanner\\Dish\\Requests\\AddUsersToDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Requests/AddUsersToDishRequest.php + + - + message: '#^Method DishPlanner\\Dish\\Requests\\RemoveUsersFromDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Requests/RemoveUsersFromDishRequest.php + + - + message: '#^Method DishPlanner\\Dish\\Requests\\StoreDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Requests/StoreDishRequest.php + + - + message: '#^Method DishPlanner\\Dish\\Requests\\SyncUsersRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Requests/SyncUsersRequest.php + + - + message: '#^Method DishPlanner\\Dish\\Requests\\UpdateDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Requests/UpdateDishRequest.php + + - + message: '#^Access to an undefined property DishPlanner\\Dish\\Resources\\DishResource\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Dish/Resources/DishResource.php + + - + message: '#^Access to an undefined property DishPlanner\\Dish\\Resources\\DishResource\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Dish/Resources/DishResource.php + + - + message: '#^Access to an undefined property DishPlanner\\Dish\\Resources\\DishResource\:\:\$planner_id\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Dish/Resources/DishResource.php + + - + message: '#^Access to an undefined property DishPlanner\\Dish\\Resources\\DishResource\:\:\$userDishes\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Dish/Resources/DishResource.php + + - + message: '#^Method DishPlanner\\Dish\\Resources\\DishResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Dish/Resources/DishResource.php + + - + message: '#^Method DishPlanner\\Planner\\Actions\\CreatePlannerAction\:\:execute\(\) has parameter \$data with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: src/DishPlanner/Planner/Actions/CreatePlannerAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\ClearScheduleForMonthAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/ClearScheduleForMonthAction.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: \(App\\Models\\User\|null\), Closure\(App\\Models\\ScheduledUserDish\)\: \(App\\Models\\User\|null\) given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php + + - + message: '#^Using nullsafe property access on non\-nullable type App\\Models\\UserDish\. Use \-\> instead\.$#' + identifier: nullsafe.neverNull + count: 1 + path: src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php + + - + message: '#^Unable to resolve the template type TKey in call to function collect$#' + identifier: argument.templateType + count: 1 + path: src/DishPlanner/Schedule/Actions/DraftScheduleForPeriodAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:clearExistingSchedules\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:generateSchedulesForPeriod\(\) has parameter \$userDishesMap with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:generateSchedulesForPeriod\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:loadUserDishes\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\GenerateScheduleForMonthAction\:\:loadUserDishes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:each\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: mixed, Closure\(App\\Models\\User\)\: App\\Models\\ScheduledUserDish given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/Schedule/Actions/RegenerateScheduleDayAction.php + + - + message: '#^Parameter \$userDish of method DishPlanner\\ScheduledUserDish\\Actions\\CreateScheduledUserDishAction\:\:execute\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php + + - + message: '#^Right side of && is always true\.$#' + identifier: booleanAnd.rightAlwaysTrue + count: 1 + path: src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Schedule/Actions/RegenerateScheduleForDateForUsersAction.php + + - + message: '#^Method DishPlanner\\Schedule\\Actions\\RegenerateScheduleForDateForUsersAction\:\:execute\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Actions/RegenerateScheduleForDateForUsersAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:each\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: mixed, Closure\(App\\Models\\ScheduledUserDish\)\: bool given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/Schedule/Actions/UpdateScheduleAction.php + + - + message: '#^Access to an undefined property App\\Models\\UserDish\|Illuminate\\Database\\Eloquent\\Collection\\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php + + - + message: '#^Access to an undefined property App\\Models\\UserDish\|Illuminate\\Database\\Eloquent\\Collection\\:\:\$user\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php + + - + message: '#^Access to an undefined property App\\Models\\User\|Illuminate\\Database\\Eloquent\\Collection\\:\:\$id\.$#' + identifier: property.notFound + count: 3 + path: src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany\:\:forUser\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php + + - + message: '#^Method DishPlanner\\Schedule\\Requests\\CreateScheduleRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Requests/CreateScheduleRequest.php + + - + message: '#^Method DishPlanner\\Schedule\\Requests\\GenerateScheduleRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Requests/GenerateScheduleRequest.php + + - + message: '#^Method DishPlanner\\Schedule\\Requests\\ScheduleUserDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Requests/ScheduleUserDishRequest.php + + - + message: '#^Method DishPlanner\\Schedule\\Requests\\UpdateScheduleRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Requests/UpdateScheduleRequest.php + + - + message: '#^Method DishPlanner\\Schedule\\Resources\\ScheduleResource\:\:scheduledUserDishes\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Resources/ScheduleResource.php + + - + message: '#^Method DishPlanner\\Schedule\\Resources\\ScheduleResource\:\:toArray\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Resources/ScheduleResource.php + + - + message: '#^PHPDoc tag @property for property DishPlanner\\Schedule\\Resources\\ScheduleResource\:\:\$scheduledUserDishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/Schedule/Resources/ScheduleResource.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, user\: array\{id\: int, name\: string\}, skipped\: bool, user_dish\: App\\Http\\Resources\\UserDishResource\}, Closure\(App\\Models\\ScheduledUserDish\)\: array\{id\: int, user\: array\{id\: int, name\: string\}, skipped\: bool, user_dish\: App\\Http\\Resources\\UserDishResource\} given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/Schedule/Resources/ScheduleResource.php + + - + message: '#^Method DishPlanner\\Schedule\\Services\\ScheduleCalendarService\:\:buildCalendarDays\(\) has parameter \$schedules with generic class Illuminate\\Support\\Collection but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/Schedule/Services/ScheduleCalendarService.php + + - + message: '#^Method DishPlanner\\Schedule\\Services\\ScheduleCalendarService\:\:buildCalendarDays\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Services/ScheduleCalendarService.php + + - + message: '#^Method DishPlanner\\Schedule\\Services\\ScheduleCalendarService\:\:getCalendarDays\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/Schedule/Services/ScheduleCalendarService.php + + - + message: '#^Method DishPlanner\\Schedule\\Services\\ScheduleCalendarService\:\:loadSchedulesForMonth\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/Schedule/Services/ScheduleCalendarService.php + + - + message: '#^Using nullsafe property access "\?\-\>scheduledUserDishes" on left side of \?\? is unnecessary\. Use \-\> instead\.$#' + identifier: nullsafe.neverNull + count: 1 + path: src/DishPlanner/Schedule/Services/ScheduleCalendarService.php + + - + message: '#^Method DishPlanner\\ScheduledUserDish\\Requests\\UpdateScheduledUserDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/ScheduledUserDish/Requests/UpdateScheduledUserDishRequest.php + + - + message: '#^Method DishPlanner\\User\\Requests\\CreateUserRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/User/Requests/CreateUserRequest.php + + - + message: '#^Method DishPlanner\\User\\Requests\\UpdateUserRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/User/Requests/UpdateUserRequest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$recurrence\.$#' + identifier: property.notFound + count: 2 + path: src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:each\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: mixed, Closure\(App\\Models\\UserDishRecurrence\)\: \(bool\|null\) given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:filter\(\) expects \(callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: bool\)\|null, Closure\(App\\Models\\UserDishRecurrence\)\: bool given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Actions\\CreateUserDishAction\:\:addRecurrences\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Actions/CreateUserDishAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Actions\\CreateUserDishAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Actions/CreateUserDishAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Actions\\SyncRecurrencesForUserDishAction\:\:execute\(\) has parameter \$recurrences with generic class Illuminate\\Support\\Collection but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Builder\\:\:each\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\)\: mixed, Closure\(App\\Models\\UserDishRecurrence\)\: void given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Actions\\UpdateFixedRecurrenceAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Actions/UpdateFixedRecurrenceAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Actions\\UpdateMinimumRecurrenceAction\:\:execute\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Actions/UpdateMinimumRecurrenceAction.php + + - + message: '#^Method DishPlanner\\UserDish\\Controllers\\ListUserDishesController\:\:__invoke\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: src/DishPlanner/UserDish/Controllers/ListUserDishesController.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$recurrence\.$#' + identifier: property.notFound + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Method DishPlanner\\UserDish\\Repositories\\UserDishRepository\:\:findCandidatesForDate\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Method DishPlanner\\UserDish\\Repositories\\UserDishRepository\:\:findInterferingUserDishes\(\) return type with generic class Illuminate\\Database\\Eloquent\\Collection does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Method DishPlanner\\UserDish\\Repositories\\UserDishRepository\:\:getAllForPlanner\(\) return type with generic class Illuminate\\Database\\Eloquent\\Collection does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: App\\Models\\UserDish, Closure\(App\\Models\\ScheduledUserDish\)\: App\\Models\\UserDish given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:filter\(\) expects \(callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: bool\)\|null, Closure\(App\\Models\\ScheduledUserDish\)\: bool given\.$#' + identifier: argument.type + count: 3 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:filter\(\) expects \(callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: bool\)\|null, Closure\(App\\Models\\UserDish\)\: bool given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:reject\(\) expects bool\|\(callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: bool\)\|Illuminate\\Database\\Eloquent\\Model, Closure\(App\\Models\\UserDish\)\: bool given\.$#' + identifier: argument.type + count: 1 + path: src/DishPlanner/UserDish/Repositories/UserDishRepository.php + + - + message: '#^Method DishPlanner\\UserDish\\Requests\\CreateUserDishRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Requests/CreateUserDishRequest.php + + - + message: '#^Method DishPlanner\\UserDish\\Requests\\StoreUserDishRecurrenceRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Requests/StoreUserDishRecurrenceRequest.php + + - + message: '#^Method DishPlanner\\UserDish\\Requests\\UpdateUserDishFixedRecurrenceRequest\:\:rules\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php + + - + message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Auth/LoginTest.php + + - + message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Auth/LoginTest.php + + - + message: '#^Property Tests\\Browser\\Auth\\LoginTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Auth/LoginTest.php + + - + message: '#^Method Tests\\Browser\\Components\\DishModal\:\:selectUsers\(\) has parameter \$userIds with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/Browser/Components/DishModal.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$createDishFormValidationTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$createDishFormValidationTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishFormValidationTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishFormValidationTest.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$createDishSuccessTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$createDishSuccessTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishSuccessTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishSuccessTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$createDishTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$createDishTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\CreateDishTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/CreateDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$deleteDishTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DeleteDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$deleteDishTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DeleteDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DeleteDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DeleteDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DeleteDishTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DeleteDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$dishDeletionSafetyTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DishDeletionSafetyTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$dishDeletionSafetyTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DishDeletionSafetyTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DishDeletionSafetyTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DishDeletionSafetyTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\DishDeletionSafetyTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/DishDeletionSafetyTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$editDishTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/EditDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$editDishTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/EditDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/EditDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/EditDishTest.php + + - + message: '#^Property Tests\\Browser\\Dishes\\EditDishTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Dishes/EditDishTest.php + + - + message: '#^Method Tests\\Browser\\Pages\\SchedulePage\:\:elements\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/Browser/Pages/SchedulePage.php + + - + message: '#^Parameter \#2 \$value of method Laravel\\Dusk\\Browser\:\:select\(\) expects array\|string\|null, int given\.$#' + identifier: argument.type + count: 2 + path: tests/Browser/Pages/SchedulePage.php + + - + message: '#^Method Tests\\Browser\\RedirectTest\:\:test_login_page_loads\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Browser/RedirectTest.php + + - + message: '#^Method Tests\\Browser\\RedirectTest\:\:test_unauthenticated_redirects_to_login\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Browser/RedirectTest.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:clickGenerate\(\)\.$#' + identifier: method.notFound + count: 3 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$dish has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$email has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$password has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$planner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\GenerateScheduleTest\:\:\$user has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/GenerateScheduleTest.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:goToNextMonth\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:goToPreviousMonth\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$schedulePageTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$schedulePageTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Property Tests\\Browser\\Schedule\\SchedulePageTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Schedule/SchedulePageTest.php + + - + message: '#^Call to an undefined method Laravel\\Dusk\\Browser\:\:openCreateModal\(\)\.$#' + identifier: method.notFound + count: 4 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$createUserTestEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$createUserTestPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testEmail has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testPassword has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Property Tests\\Browser\\Users\\CreateUserTest\:\:\$testPlanner has no type specified\.$#' + identifier: missingType.property + count: 1 + path: tests/Browser/Users/CreateUserTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with string will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Feature/AuthenticationTest.php + + - + message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/AuthenticationTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\AddUsersToDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/AddUsersToDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/AddUsersToDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/AddUsersToDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\AddUsersToDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/AddUsersToDishTest.php + + - + message: '#^Called ''first'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 1 + path: tests/Feature/Dish/CreateDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\CreateDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/CreateDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\CreateDishTest\:\:invalidNameValues\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: tests/Feature/Dish/CreateDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\CreateDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/CreateDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\DeleteDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/DeleteDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/DeleteDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\DeleteDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/DeleteDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\ListDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/ListDishesTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/ListDishesTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\ListDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/ListDishesTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\RemoveUsersFromDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/RemoveUsersFromDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/RemoveUsersFromDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/RemoveUsersFromDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\RemoveUsersFromDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/RemoveUsersFromDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\ShowDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/ShowDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/ShowDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\ShowDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/ShowDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\SyncUsersForDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/SyncUsersForDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/SyncUsersForDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/SyncUsersForDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\SyncUsersForDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/SyncUsersForDishTest.php + + - + message: '#^Method Tests\\Feature\\Dish\\UpdateDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Dish/UpdateDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Dish/UpdateDishTest.php + + - + message: '#^Property Tests\\Feature\\Dish\\UpdateDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Dish/UpdateDishTest.php + + - + message: '#^Method Tests\\Feature\\RegistrationTest\:\:test_new_users_can_register\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Feature/RegistrationTest.php + + - + message: '#^Method Tests\\Feature\\RegistrationTest\:\:test_registration_fails_with_existing_email\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Feature/RegistrationTest.php + + - + message: '#^Method Tests\\Feature\\RegistrationTest\:\:test_registration_fails_with_password_mismatch\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Feature/RegistrationTest.php + + - + message: '#^Method Tests\\Feature\\RegistrationTest\:\:test_registration_screen_can_be_rendered\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: tests/Feature/RegistrationTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 4 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Method Tests\\Feature\\Schedule\\GenerateScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{user_dish_id\: int\}, Closure\(App\\Models\\ScheduledUserDish\)\: array\{user_dish_id\: int\} given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:filter\(\) expects \(callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: bool\)\|null, Closure\(App\\Models\\ScheduledUserDish\)\: bool given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Property Tests\\Feature\\Schedule\\GenerateScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Schedule/GenerateScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 2 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$user\.$#' + identifier: property.notFound + count: 2 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Method Tests\\Feature\\Schedule\\ListScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Property Tests\\Feature\\Schedule\\ListScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Schedule/ListScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$date\.$#' + identifier: property.notFound + count: 2 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Method Tests\\Feature\\Schedule\\ReadScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Property Tests\\Feature\\Schedule\\ReadScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Unable to resolve the template type TKey in call to function collect$#' + identifier: argument.templateType + count: 1 + path: tests/Feature/Schedule/ReadScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 5 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNull\(\) with int will always evaluate to false\.$#' + identifier: method.impossibleType + count: 1 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Method Tests\\Feature\\Schedule\\ScheduleEdgeCasesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Property Tests\\Feature\\Schedule\\ScheduleEdgeCasesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Schedule/ScheduleEdgeCasesTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleUserDishTest.php + + - + message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/ScheduleUserDishTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$dish_id\.$#' + identifier: property.notFound + count: 1 + path: tests/Feature/Schedule/UpdateScheduleTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$user_dish_id\.$#' + identifier: property.notFound + count: 1 + path: tests/Feature/Schedule/UpdateScheduleTest.php + + - + message: '#^Method Tests\\Feature\\Schedule\\UpdateScheduleTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/Schedule/UpdateScheduleTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/Schedule/UpdateScheduleTest.php + + - + message: '#^Property Tests\\Feature\\Schedule\\UpdateScheduleTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/Schedule/UpdateScheduleTest.php + + - + message: '#^Called ''first'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 1 + path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\ScheduledUserDish\\CreateScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php + + - + message: '#^Property Tests\\Feature\\ScheduledUserDish\\CreateScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\ScheduledUserDish\\DeleteScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php + + - + message: '#^Property Tests\\Feature\\ScheduledUserDish\\DeleteScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$user\.$#' + identifier: property.notFound + count: 2 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\ScheduledUserDish\\ReadScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Property Tests\\Feature\\ScheduledUserDish\\ReadScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$dish\.$#' + identifier: property.notFound + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 3 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$user\.$#' + identifier: property.notFound + count: 4 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateDishes\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:generateScheduledDishes\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model\|null given\.$#' + identifier: argument.type + count: 2 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Property Tests\\Feature\\ScheduledUserDish\\UpdateScheduledUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Unable to resolve the template type TKey in call to function collect$#' + identifier: argument.templateType + count: 1 + path: tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php + + - + message: '#^Method Tests\\Feature\\User\\CreateUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/CreateUserTest.php + + - + message: '#^Property Tests\\Feature\\User\\CreateUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/CreateUserTest.php + + - + message: '#^Method Tests\\Feature\\User\\DeleteUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/DeleteUserTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/DeleteUserTest.php + + - + message: '#^Property Tests\\Feature\\User\\DeleteUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/DeleteUserTest.php + + - + message: '#^Method Tests\\Feature\\User\\Dish\\ListUserDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/Dish/ListUserDishesTest.php + + - + message: '#^Parameter \#1 \$user of method Illuminate\\Foundation\\Testing\\TestCase\:\:actingAs\(\) expects Illuminate\\Contracts\\Auth\\Authenticatable, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/Dish/ListUserDishesTest.php + + - + message: '#^Property Tests\\Feature\\User\\Dish\\ListUserDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/Dish/ListUserDishesTest.php + + - + message: '#^Method Tests\\Feature\\User\\Dish\\RemoveDishesForUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/Dish/RemoveDishesForUserTest.php + + - + message: '#^Property Tests\\Feature\\User\\Dish\\RemoveDishesForUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/Dish/RemoveDishesForUserTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: tests/Feature/User/Dish/ShowUserDishTest.php + + - + message: '#^Method Tests\\Feature\\User\\Dish\\ShowUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/Dish/ShowUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/Dish/ShowUserDishTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/Dish/ShowUserDishTest.php + + - + message: '#^Property Tests\\Feature\\User\\Dish\\ShowUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/Dish/ShowUserDishTest.php + + - + message: '#^Method Tests\\Feature\\User\\Dish\\StoreRecurrenceForUserDishTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php + + - + message: '#^Property Tests\\Feature\\User\\Dish\\StoreRecurrenceForUserDishTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php + + - + message: '#^Method Tests\\Feature\\User\\ListUsersTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/ListUsersTest.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\}, Closure\(App\\Models\\UserDish\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\} given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/ListUsersTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/ListUsersTest.php + + - + message: '#^Property Tests\\Feature\\User\\ListUsersTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/ListUsersTest.php + + - + message: '#^Method Tests\\Feature\\User\\ShowUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/ShowUserTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/ShowUserTest.php + + - + message: '#^Property Tests\\Feature\\User\\ShowUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/ShowUserTest.php + + - + message: '#^Method Tests\\Feature\\User\\ShowUserWithDishesTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/ShowUserWithDishesTest.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\),Illuminate\\Database\\Eloquent\\Model\>\:\:map\(\) expects callable\(Illuminate\\Database\\Eloquent\\Model, int\|string\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\}, Closure\(App\\Models\\UserDish\)\: array\{id\: int, dish\: array\{id\: int, name\: string\}, recurrences\: array\{\}\} given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/ShowUserWithDishesTest.php + + - + message: '#^Property Tests\\Feature\\User\\ShowUserWithDishesTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/ShowUserWithDishesTest.php + + - + message: '#^Method Tests\\Feature\\User\\UpdateUserTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Feature/User/UpdateUserTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Feature/User/UpdateUserTest.php + + - + message: '#^Property Tests\\Feature\\User\\UpdateUserTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Feature/User/UpdateUserTest.php + + - + message: '#^Method Tests\\Unit\\Actions\\EditUserActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Actions/EditUserActionTest.php + + - + message: '#^Parameter \#1 \$user of method App\\Actions\\User\\EditUserAction\:\:execute\(\) expects App\\Models\\User, Mockery\\MockInterface given\.$#' + identifier: argument.type + count: 3 + path: tests/Unit/Actions/EditUserActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\EditUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 2 + path: tests/Unit/Actions/EditUserActionTest.php + + - + message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php + + - + message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\RegenerateScheduleDayActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayActionTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 2 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with App\\Models\\UserDish will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with int will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Called ''first'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 3 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Method Tests\\Unit\\Actions\\RegenerateScheduleDayForUserActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^PHPDoc tag @var for variable \$dishes contains generic class Illuminate\\Database\\Eloquent\\Collection but does not specify its types\: TKey, TModel$#' + identifier: missingType.generics + count: 2 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Parameter \#1 \$userDish of method Database\\Factories\\ScheduledUserDishFactory\:\:userDish\(\) expects App\\Models\\UserDish, Illuminate\\Database\\Eloquent\\Model given\.$#' + identifier: argument.type + count: 2 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\RegenerateScheduleDayForUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php + + - + message: '#^Call to an undefined static method Illuminate\\Support\\Facades\\Log\:\:shouldHaveReceived\(\)\.$#' + identifier: staticMethod.notFound + count: 3 + path: tests/Unit/Actions/User/CreateUserActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\User\\CreateUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Actions/User/CreateUserActionTest.php + + - + message: '#^Call to an undefined static method Illuminate\\Support\\Facades\\Log\:\:shouldHaveReceived\(\)\.$#' + identifier: staticMethod.notFound + count: 3 + path: tests/Unit/Actions/User/DeleteUserActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\User\\DeleteUserActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Actions/User/DeleteUserActionTest.php + + - + message: '#^Property Tests\\Unit\\Actions\\UserActionIntegrationTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Actions/UserActionIntegrationTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\ClearScheduleForMonthActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Actions\\ClearScheduleForMonthActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php + + - + message: '#^Called ''count'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Actions\\DraftScheduleForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertNotNull\(\) with App\\Models\\Planner will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php + + - + message: '#^Called ''count'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:generateDishes\(\) return type with generic class Illuminate\\Support\\Collection does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Actions\\DraftScheduleForPeriodActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\GenerateScheduleForMonthActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Actions\\GenerateScheduleForMonthActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Actions\\RegenerateScheduleForDateForUsersActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Actions\\RegenerateScheduleForDateForUsersActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$userDish\.$#' + identifier: property.notFound + count: 3 + path: tests/Unit/Schedule/ScheduleGeneratorTest.php + + - + message: '#^Called ''isNotEmpty'' on Laravel collection, but could have been retrieved as a query\.$#' + identifier: larastan.noUnnecessaryCollectionCall + count: 2 + path: tests/Unit/Schedule/ScheduleGeneratorTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\ScheduleGeneratorTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/ScheduleGeneratorTest.php + + - + message: '#^Parameter \#1 \$callback of method Illuminate\\Support\\Collection\\:\:reduce\(\) expects callable\(Illuminate\\Support\\Carbon\|null, Carbon\\Carbon, int\)\: Illuminate\\Support\\Carbon, Closure\(Illuminate\\Support\\Carbon\|null, Illuminate\\Support\\Carbon\)\: Illuminate\\Support\\Carbon given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/Schedule/ScheduleGeneratorTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\ScheduleGeneratorTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/ScheduleGeneratorTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 3 + path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php + + - + message: '#^Method Tests\\Unit\\Schedule\\Services\\ScheduleCalendarServiceTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php + + - + message: '#^Property Tests\\Unit\\Schedule\\Services\\ScheduleCalendarServiceTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php + + - + message: '#^Method Tests\\Unit\\ScheduleRepositoryTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/ScheduleRepositoryTest.php + + - + message: '#^Property Tests\\Unit\\ScheduleRepositoryTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/ScheduleRepositoryTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 4 + path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php + + - + message: '#^Method Tests\\Unit\\ScheduledUserDish\\Actions\\DeleteScheduledUserDishForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php + + - + message: '#^Property Tests\\Unit\\ScheduledUserDish\\Actions\\DeleteScheduledUserDishForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 3 + path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php + + - + message: '#^Method Tests\\Unit\\ScheduledUserDish\\Actions\\SkipScheduledUserDishForDateActionTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php + + - + message: '#^Property Tests\\Unit\\ScheduledUserDish\\Actions\\SkipScheduledUserDishForDateActionTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\DishFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 2 + path: tests/Unit/UpdateScheduledUserDishActionTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\ScheduleFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/UpdateScheduledUserDishActionTest.php + + - + message: '#^Parameter \#1 \$planner of method Database\\Factories\\UserFactory\:\:planner\(\) expects App\\Models\\Planner, App\\Models\\User given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/UpdateScheduledUserDishActionTest.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$id\.$#' + identifier: property.notFound + count: 1 + path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php + + - + message: '#^Method Tests\\Unit\\UserDish\\Repositories\\UserDishRepositoryTest\:\:createPlanner\(\) should return App\\Models\\Planner but returns App\\Models\\User\.$#' + identifier: return.type + count: 1 + path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php + + - + message: '#^Property Tests\\Unit\\UserDish\\Repositories\\UserDishRepositoryTest\:\:\$planner \(App\\Models\\Planner\) does not accept App\\Models\\User\.$#' + identifier: assign.propertyType + count: 1 + path: tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..91375f6 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,16 @@ +includes: + - vendor/larastan/larastan/extension.neon + - vendor/phpstan/phpstan-mockery/extension.neon + - phpstan-baseline.neon + +parameters: + level: 7 + paths: + - app/ + - src/ + - tests/ + + excludePaths: + - bootstrap/*.php + - storage/* + diff --git a/phpunit.xml b/phpunit.xml index 4eee333..ac5feb4 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -23,13 +23,6 @@ app/Providers - - - - - - - @@ -40,12 +33,8 @@ - - - - - - + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..93061b6 --- /dev/null +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "laravel" +} diff --git a/routes/api.php b/routes/api.php index 28b43b3..1ad6449 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,12 +5,12 @@ Route::group([ 'as' => 'api.', ], function () { - require __DIR__ . '/api/auth.php'; + require __DIR__.'/api/auth.php'; Route::middleware('auth:sanctum')->group(function () { - require __DIR__ . '/api/users.php'; - require __DIR__ . '/api/dishes.php'; - require __DIR__ . '/api/schedule.php'; - require __DIR__ . '/api/scheduledUserDishes.php'; + require __DIR__.'/api/users.php'; + require __DIR__.'/api/dishes.php'; + require __DIR__.'/api/schedule.php'; + require __DIR__.'/api/scheduledUserDishes.php'; }); }); diff --git a/routes/api/auth.php b/routes/api/auth.php index bd944e0..b91be18 100644 --- a/routes/api/auth.php +++ b/routes/api/auth.php @@ -18,4 +18,3 @@ ->json($request->user()) )->name('me'); }); - diff --git a/routes/web.php b/routes/web.php index c1e49da..a4031ae 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,8 +1,8 @@ route('dashboard'); diff --git a/shell.nix b/shell.nix index dfcd838..3373028 100644 --- a/shell.nix +++ b/shell.nix @@ -81,7 +81,7 @@ pkgs.mkShell { } dev-test() { - podman-compose exec -T app php -d memory_limit=512M vendor/bin/phpunit "$@" + podman-compose exec -T app env $(grep -vE '^\s*(#|$)' .env.testing) php -d memory_limit=512M vendor/bin/phpunit "$@" } dev-fix-permissions() { diff --git a/src/DishPlanner/Auth/Controllers/PlannerAuthController.php b/src/DishPlanner/Auth/Controllers/PlannerAuthController.php index db9b6d1..28ea11f 100644 --- a/src/DishPlanner/Auth/Controllers/PlannerAuthController.php +++ b/src/DishPlanner/Auth/Controllers/PlannerAuthController.php @@ -17,7 +17,7 @@ public function login(Request $request): JsonResponse 'password' => ['required'], ]); - if (!Auth::attempt($credentials)) { + if (! Auth::attempt($credentials)) { return response()->json([ 'message' => 'The provided credentials are incorrect.', ], 401); diff --git a/src/DishPlanner/Dish/Controllers/DishController.php b/src/DishPlanner/Dish/Controllers/DishController.php index 02e183c..e63457d 100644 --- a/src/DishPlanner/Dish/Controllers/DishController.php +++ b/src/DishPlanner/Dish/Controllers/DishController.php @@ -31,7 +31,7 @@ public function index(): JsonResponse public function store(StoreDishRequest $request): JsonResponse { - $dish = (new CreateDishAction())->execute($request->validated()); + $dish = (new CreateDishAction)->execute($request->validated()); return $this->success(['dish' => new DishResource($dish)]); } @@ -47,7 +47,7 @@ public function update(UpdateDishRequest $request, Dish $dish): JsonResponse { Gate::authorize('update', $dish); - $dish = (new UpdateDishAction())->execute($dish, $request->validated()); + $dish = (new UpdateDishAction)->execute($dish, $request->validated()); return $this->success(['dish' => new DishResource($dish)]); } @@ -56,14 +56,14 @@ public function destroy(Dish $dish): JsonResponse { Gate::authorize('delete', $dish); - (new DeleteDishAction())->execute($dish); + (new DeleteDishAction)->execute($dish); return $this->success(null); } public function syncUsers(SyncUsersRequest $request, Dish $dish): JsonResponse { - (new SyncUsersAction())->execute($dish, Arr::get($request->validated(), 'users', [])); + (new SyncUsersAction)->execute($dish, Arr::get($request->validated(), 'users', [])); return $this->success(['dish' => new DishResource($dish->refresh())]); } @@ -72,14 +72,14 @@ public function addUsers(AddUsersToDishRequest $request, Dish $dish): JsonRespon { Gate::authorize('update', $dish); - (new AddUsersToDishAction())->execute($dish, Arr::get($request->validated(), 'users', [])); + (new AddUsersToDishAction)->execute($dish, Arr::get($request->validated(), 'users', [])); return $this->success(['dish' => new DishResource($dish->refresh())]); } public function removeUsers(RemoveUsersFromDishRequest $request, Dish $dish): JsonResponse { - (new RemoveUsersFromDishAction())->execute($dish, Arr::get($request->validated(), 'users', [])); + (new RemoveUsersFromDishAction)->execute($dish, Arr::get($request->validated(), 'users', [])); return $this->success(['dish' => new DishResource($dish->refresh())]); } diff --git a/src/DishPlanner/Dish/Exceptions/InvalidDishException.php b/src/DishPlanner/Dish/Exceptions/InvalidDishException.php index 3ab82b6..805f684 100644 --- a/src/DishPlanner/Dish/Exceptions/InvalidDishException.php +++ b/src/DishPlanner/Dish/Exceptions/InvalidDishException.php @@ -7,5 +7,6 @@ class InvalidDishException extends CustomException { protected $message = 'INVALID_DISH'; + protected $code = 422; } diff --git a/src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php b/src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php index 1375ad5..77ea2ff 100644 --- a/src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php +++ b/src/DishPlanner/Schedule/Actions/DraftScheduleForDateAction.php @@ -11,7 +11,7 @@ class DraftScheduleForDateAction public function execute(Schedule $schedule): Schedule { User::all() - ->reject(fn($user) => $schedule + ->reject(fn ($user) => $schedule ->scheduledUserDishes ->map(fn (ScheduledUserDish $scheduledUserDish) => $scheduledUserDish->userDish?->user) ->filter() diff --git a/src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php b/src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php index 1f5937d..05e1c09 100644 --- a/src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php +++ b/src/DishPlanner/Schedule/Actions/GenerateScheduleForMonthAction.php @@ -7,7 +7,6 @@ use App\Models\ScheduledUserDish; use App\Models\User; use Carbon\Carbon; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; class GenerateScheduleForMonthAction @@ -84,7 +83,7 @@ private function generateSchedulesForPeriod( ); foreach ($userIds as $userId) { - if (!isset($userDishesMap[$userId]) || $userDishesMap[$userId]->isEmpty()) { + if (! isset($userDishesMap[$userId]) || $userDishesMap[$userId]->isEmpty()) { continue; } diff --git a/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php b/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php index a27de32..c952cd9 100644 --- a/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php +++ b/src/DishPlanner/Schedule/Actions/RegenerateScheduleDayForUserAction.php @@ -29,7 +29,7 @@ public function execute(Planner $planner, Schedule $schedule, User $user, bool $ ); } - if (!$overwrite && $scheduledUserDish->userDish) { + if (! $overwrite && $scheduledUserDish->userDish) { return $scheduledUserDish; } diff --git a/src/DishPlanner/Schedule/Controllers/ScheduleController.php b/src/DishPlanner/Schedule/Controllers/ScheduleController.php index 5dd9f6c..4adcaef 100644 --- a/src/DishPlanner/Schedule/Controllers/ScheduleController.php +++ b/src/DishPlanner/Schedule/Controllers/ScheduleController.php @@ -5,8 +5,6 @@ use App\Http\Controllers\Api\ApiController; use App\Models\Planner; use App\Models\Schedule; -use Carbon\CarbonPeriod; -use DishPlanner\Schedule\Actions\DraftScheduleForPeriodAction; use DishPlanner\Schedule\Actions\GenerateScheduleForPeriodAction; use DishPlanner\Schedule\Actions\UpdateScheduleAction; use DishPlanner\Schedule\Repositories\ScheduleRepository; @@ -83,7 +81,7 @@ public function generate(GenerateScheduleRequest $request): JsonResponse /** @var Planner $planner */ $planner = auth()->user(); - (new GenerateScheduleForPeriodAction())->execute($planner, $request->get('overwrite', false)); + (new GenerateScheduleForPeriodAction)->execute($planner, $request->get('overwrite', false)); return $this->success(null); } diff --git a/src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php b/src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php index f94082a..606c8dc 100644 --- a/src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php +++ b/src/DishPlanner/Schedule/Controllers/ScheduleUserDishController.php @@ -37,7 +37,7 @@ public function __invoke(ScheduleUserDishRequest $request, Carbon $date): JsonRe ->first(); if (! $scheduledUserDish) { - $scheduledUserDish = new ScheduledUserDish(); + $scheduledUserDish = new ScheduledUserDish; } abort_if( diff --git a/src/DishPlanner/Schedule/Requests/ScheduleUserDishRequest.php b/src/DishPlanner/Schedule/Requests/ScheduleUserDishRequest.php index 94f845a..040d2c7 100644 --- a/src/DishPlanner/Schedule/Requests/ScheduleUserDishRequest.php +++ b/src/DishPlanner/Schedule/Requests/ScheduleUserDishRequest.php @@ -12,7 +12,7 @@ public function rules(): array 'user_dish_id' => [ 'required_without:skipped', 'exists:user_dishes,id', - 'nullable' + 'nullable', ], 'user_id' => ['required', 'exists:users,id'], 'skipped' => ['required_if:user_dish_id,null', 'boolean'], diff --git a/src/DishPlanner/Schedule/Requests/UpdateScheduleRequest.php b/src/DishPlanner/Schedule/Requests/UpdateScheduleRequest.php index 895515e..2a57c98 100755 --- a/src/DishPlanner/Schedule/Requests/UpdateScheduleRequest.php +++ b/src/DishPlanner/Schedule/Requests/UpdateScheduleRequest.php @@ -5,7 +5,7 @@ use Illuminate\Foundation\Http\FormRequest; /** - * @property boolean $is_skipped + * @property bool $is_skipped */ class UpdateScheduleRequest extends FormRequest { diff --git a/src/DishPlanner/Schedule/Services/ScheduleCalendarService.php b/src/DishPlanner/Schedule/Services/ScheduleCalendarService.php index 8b64d3d..ca967e2 100644 --- a/src/DishPlanner/Schedule/Services/ScheduleCalendarService.php +++ b/src/DishPlanner/Schedule/Services/ScheduleCalendarService.php @@ -43,7 +43,7 @@ private function buildCalendarDays(int $year, int $month, int $daysInMonth, Coll 'date' => $date, 'isToday' => $date->isToday(), 'scheduledDishes' => $scheduledDishes, - 'isEmpty' => $scheduledDishes->isEmpty() + 'isEmpty' => $scheduledDishes->isEmpty(), ]; } else { $calendarDays[] = [ @@ -51,7 +51,7 @@ private function buildCalendarDays(int $year, int $month, int $daysInMonth, Coll 'date' => null, 'isToday' => false, 'scheduledDishes' => collect(), - 'isEmpty' => true + 'isEmpty' => true, ]; } } diff --git a/src/DishPlanner/Schedule/Services/ScheduleGenerator.php b/src/DishPlanner/Schedule/Services/ScheduleGenerator.php index 5e86f48..e9beed0 100644 --- a/src/DishPlanner/Schedule/Services/ScheduleGenerator.php +++ b/src/DishPlanner/Schedule/Services/ScheduleGenerator.php @@ -31,7 +31,7 @@ public function generate(Planner $planner): void $users->each(function (User $user) use ($date, $planner, $scheduleRepository, $userDishRepository) { $schedule = $scheduleRepository->findOrCreate($planner, $date); - (new CreateScheduledUserDishAction())->execute( + (new CreateScheduledUserDishAction)->execute( planner: $planner, schedule: $schedule, userDish: $userDishRepository->getRandomForDate($user, $date) diff --git a/src/DishPlanner/ScheduledUserDish/Actions/CreateScheduledUserDishAction.php b/src/DishPlanner/ScheduledUserDish/Actions/CreateScheduledUserDishAction.php index 9ab9994..dc79086 100644 --- a/src/DishPlanner/ScheduledUserDish/Actions/CreateScheduledUserDishAction.php +++ b/src/DishPlanner/ScheduledUserDish/Actions/CreateScheduledUserDishAction.php @@ -16,7 +16,7 @@ class CreateScheduledUserDishAction public function execute(Planner $planner, Schedule $schedule, UserDish $userDish): ScheduledUserDish { if ($userDish->dish->planner_id !== $planner->id || $userDish->user->planner_id !== $planner->id) { - throw new InvalidPlannerException(); + throw new InvalidPlannerException; } return ScheduledUserDish::create([ diff --git a/src/DishPlanner/ScheduledUserDish/Controllers/ScheduledUserDishController.php b/src/DishPlanner/ScheduledUserDish/Controllers/ScheduledUserDishController.php index db4b912..017f550 100644 --- a/src/DishPlanner/ScheduledUserDish/Controllers/ScheduledUserDishController.php +++ b/src/DishPlanner/ScheduledUserDish/Controllers/ScheduledUserDishController.php @@ -36,7 +36,7 @@ public function create(CreateScheduleRequest $request): JsonResponse $schedule = resolve(ScheduleRepository::class)->findOrCreate($planner, $date); try { - $scheduledUserDish = (new CreateScheduledUserDishAction())->execute( + $scheduledUserDish = (new CreateScheduledUserDishAction)->execute( planner: $planner, schedule: $schedule, userDish: $userDish, @@ -46,7 +46,7 @@ public function create(CreateScheduleRequest $request): JsonResponse } return $this->success([ - 'scheduled_user_dish' => new ScheduledUserDishResource($scheduledUserDish) + 'scheduled_user_dish' => new ScheduledUserDishResource($scheduledUserDish), ]); } @@ -63,7 +63,7 @@ public function update(UpdateScheduledUserDishRequest $request, ScheduledUserDis { Gate::authorize('update', $scheduledUserDish); - (new UpdateScheduledUserDishAction())->execute( + (new UpdateScheduledUserDishAction)->execute( scheduledUserDish: $scheduledUserDish, userDish: UserDish::find($request->user_dish_id), isSkipped: $request->is_skipped ?? null, @@ -78,7 +78,7 @@ public function delete(ScheduledUserDish $scheduledUserDish): JsonResponse { Gate::authorize('delete', $scheduledUserDish); - (new DeleteScheduledUserDishAction())->execute($scheduledUserDish); + (new DeleteScheduledUserDishAction)->execute($scheduledUserDish); return $this->success(null); } diff --git a/src/DishPlanner/ScheduledUserDish/Policies/ScheduledUserDishPolicy.php b/src/DishPlanner/ScheduledUserDish/Policies/ScheduledUserDishPolicy.php index dafe218..ce2c735 100644 --- a/src/DishPlanner/ScheduledUserDish/Policies/ScheduledUserDishPolicy.php +++ b/src/DishPlanner/ScheduledUserDish/Policies/ScheduledUserDishPolicy.php @@ -4,7 +4,6 @@ use App\Models\Planner; use App\Models\ScheduledUserDish; -use DishPlanner\UserDish\Policies\UserDishPolicy; use Illuminate\Support\Facades\Gate; class ScheduledUserDishPolicy diff --git a/src/DishPlanner/User/Actions/DeleteUserAction.php b/src/DishPlanner/User/Actions/DeleteUserAction.php index a18a120..1d8740b 100644 --- a/src/DishPlanner/User/Actions/DeleteUserAction.php +++ b/src/DishPlanner/User/Actions/DeleteUserAction.php @@ -2,7 +2,6 @@ namespace DishPlanner\User\Actions; -use App\Models\Planner; use App\Models\User; class DeleteUserAction diff --git a/src/DishPlanner/User/Actions/UpdateUserAction.php b/src/DishPlanner/User/Actions/UpdateUserAction.php index c59c77c..a0b3924 100644 --- a/src/DishPlanner/User/Actions/UpdateUserAction.php +++ b/src/DishPlanner/User/Actions/UpdateUserAction.php @@ -2,7 +2,6 @@ namespace DishPlanner\User\Actions; -use App\Models\Planner; use App\Models\User; class UpdateUserAction diff --git a/src/DishPlanner/User/Controllers/UserController.php b/src/DishPlanner/User/Controllers/UserController.php index 9890708..df0a776 100644 --- a/src/DishPlanner/User/Controllers/UserController.php +++ b/src/DishPlanner/User/Controllers/UserController.php @@ -33,7 +33,7 @@ public function create(CreateUserRequest $request): JsonResponse $requestData = $request->validated(); - $user = (new CreateUserAction()) + $user = (new CreateUserAction) ->execute($planner, Arr::get($requestData, 'name')); return $this->success(['user' => new UserResource($user)]); @@ -43,7 +43,7 @@ public function update(UpdateUserRequest $request, User $user): JsonResponse { Gate::authorize('update', $user); - $user = (new UpdateUserAction()) + $user = (new UpdateUserAction) ->execute($user, Arr::get($request->validated(), 'name')); return $this->success(['user' => new UserResource($user)]); @@ -53,7 +53,7 @@ public function delete(User $user): JsonResponse { Gate::authorize('delete', $user); - (new DeleteUserAction())->execute($user); + (new DeleteUserAction)->execute($user); return $this->success(null, 201); } diff --git a/src/DishPlanner/UserDish/Actions/CreateFixedRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/CreateFixedRecurrenceAction.php index 709e4df..3d149c2 100644 --- a/src/DishPlanner/UserDish/Actions/CreateFixedRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/CreateFixedRecurrenceAction.php @@ -17,8 +17,8 @@ class CreateFixedRecurrenceAction */ public function execute(UserDish $userDish, string $recurrenceType, int $value): void { - if (!in_array($recurrenceType, self::FIXED_RECURRENCES)) { - throw new InvalidRecurrenceTypeException(); + if (! in_array($recurrenceType, self::FIXED_RECURRENCES)) { + throw new InvalidRecurrenceTypeException; } $recurrence = $recurrenceType::create([ diff --git a/src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php index 18e0f19..e6d7df2 100644 --- a/src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/CreateMinimumRecurrenceAction.php @@ -15,7 +15,7 @@ class CreateMinimumRecurrenceAction public function execute(UserDish $userDish, string $recurrenceType, int $recurrenceValue): void { if ($recurrenceType !== MinimumRecurrence::class) { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } $existingRecurrenceForDay = $userDish diff --git a/src/DishPlanner/UserDish/Actions/CreateUserDishAction.php b/src/DishPlanner/UserDish/Actions/CreateUserDishAction.php index a21f2a6..1ead2e1 100644 --- a/src/DishPlanner/UserDish/Actions/CreateUserDishAction.php +++ b/src/DishPlanner/UserDish/Actions/CreateUserDishAction.php @@ -40,11 +40,11 @@ private function addRecurrences(UserDish $userDish, array $data): void } if ($recurrenceType === WeeklyRecurrence::class) { - (new CreateFixedRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue); + (new CreateFixedRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue); } elseif ($recurrenceType === MinimumRecurrence::class) { - (new CreateMinimumRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue); + (new CreateMinimumRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue); } else { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } } } diff --git a/src/DishPlanner/UserDish/Actions/DeleteFixedRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/DeleteFixedRecurrenceAction.php index a9d7701..063a2c6 100644 --- a/src/DishPlanner/UserDish/Actions/DeleteFixedRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/DeleteFixedRecurrenceAction.php @@ -14,7 +14,7 @@ class DeleteFixedRecurrenceAction public function execute(RecurrenceInterface $recurrence): void { if (! $recurrence instanceof WeeklyRecurrence) { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } $recurrence->delete(); diff --git a/src/DishPlanner/UserDish/Actions/DeleteMinimumRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/DeleteMinimumRecurrenceAction.php index cfc8044..12d89c6 100644 --- a/src/DishPlanner/UserDish/Actions/DeleteMinimumRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/DeleteMinimumRecurrenceAction.php @@ -15,7 +15,7 @@ class DeleteMinimumRecurrenceAction public function execute(RecurrenceInterface $recurrence): void { if (! $recurrence instanceof MinimumRecurrence) { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } UserDishRecurrence::query() diff --git a/src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php b/src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php index 2099712..dde4e41 100644 --- a/src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php +++ b/src/DishPlanner/UserDish/Actions/SyncRecurrencesForUserDishAction.php @@ -32,9 +32,9 @@ public function execute(UserDish $userDish, Collection $recurrences): UserDish } match ($recurrenceType) { - WeeklyRecurrence::class => (new CreateFixedRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue), - MinimumRecurrence::class => (new CreateMinimumRecurrenceAction())->execute($userDish, $recurrenceType, $recurrenceValue), - default => throw new InvalidRecurrenceTypeException(), + WeeklyRecurrence::class => (new CreateFixedRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue), + MinimumRecurrence::class => (new CreateMinimumRecurrenceAction)->execute($userDish, $recurrenceType, $recurrenceValue), + default => throw new InvalidRecurrenceTypeException, }; }); diff --git a/src/DishPlanner/UserDish/Actions/UpdateFixedRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/UpdateFixedRecurrenceAction.php index bd4d43b..b4bca06 100644 --- a/src/DishPlanner/UserDish/Actions/UpdateFixedRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/UpdateFixedRecurrenceAction.php @@ -15,7 +15,7 @@ class UpdateFixedRecurrenceAction public function execute(RecurrenceInterface $recurrence, array $data): RecurrenceInterface { if (! $recurrence instanceof WeeklyRecurrence) { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } $weekday = Arr::get($data, 'recurrence_data.weekday'); diff --git a/src/DishPlanner/UserDish/Actions/UpdateMinimumRecurrenceAction.php b/src/DishPlanner/UserDish/Actions/UpdateMinimumRecurrenceAction.php index 0142dad..99f8515 100644 --- a/src/DishPlanner/UserDish/Actions/UpdateMinimumRecurrenceAction.php +++ b/src/DishPlanner/UserDish/Actions/UpdateMinimumRecurrenceAction.php @@ -15,7 +15,7 @@ class UpdateMinimumRecurrenceAction public function execute(RecurrenceInterface $recurrence, array $data): RecurrenceInterface { if (! $recurrence instanceof MinimumRecurrence) { - throw new InvalidRecurrenceTypeException(); + throw new InvalidRecurrenceTypeException; } $days = Arr::get($data, 'recurrence_data.days'); diff --git a/src/DishPlanner/UserDish/Controllers/ListUserDishesController.php b/src/DishPlanner/UserDish/Controllers/ListUserDishesController.php index c515152..343fa6f 100644 --- a/src/DishPlanner/UserDish/Controllers/ListUserDishesController.php +++ b/src/DishPlanner/UserDish/Controllers/ListUserDishesController.php @@ -20,7 +20,7 @@ public function __invoke(Request $request) $userDishes = $userDishRepository->getAllForPlanner($planner); return $this->success([ - 'user_dishes' => UserDishResource::collection($userDishes)->collection->toArray() + 'user_dishes' => UserDishResource::collection($userDishes)->collection->toArray(), ]); } } diff --git a/src/DishPlanner/UserDish/Controllers/UserDishController.php b/src/DishPlanner/UserDish/Controllers/UserDishController.php index 23eea18..63b79dd 100644 --- a/src/DishPlanner/UserDish/Controllers/UserDishController.php +++ b/src/DishPlanner/UserDish/Controllers/UserDishController.php @@ -48,7 +48,7 @@ public function show(User $user, Dish $dish): JsonResponse */ public function store(CreateUserDishRequest $request, User $user, Dish $dish): JsonResponse { - $userDish = (new CreateUserDishAction())->execute($dish, $user, $request->validated()); + $userDish = (new CreateUserDishAction)->execute($dish, $user, $request->validated()); return $this->success([ 'user_dish' => new UserDishResource($userDish), @@ -57,7 +57,7 @@ public function store(CreateUserDishRequest $request, User $user, Dish $dish): J public function destroy(User $user, Dish $dish): JsonResponse { - (new DeleteUserDishAction())->execute($user, $dish); + (new DeleteUserDishAction)->execute($user, $dish); return $this->success(null); } diff --git a/src/DishPlanner/UserDish/Controllers/UserDishRecurrenceController.php b/src/DishPlanner/UserDish/Controllers/UserDishRecurrenceController.php index 425fba1..2398918 100644 --- a/src/DishPlanner/UserDish/Controllers/UserDishRecurrenceController.php +++ b/src/DishPlanner/UserDish/Controllers/UserDishRecurrenceController.php @@ -35,7 +35,7 @@ public function store(StoreUserDishRecurrenceRequest $request, User $user, Dish $recurrences = collect($request->validated()); - (new SyncRecurrencesForUserDishAction())->execute($userDish, $recurrences); + (new SyncRecurrencesForUserDishAction)->execute($userDish, $recurrences); return $this->success([ 'user_dish' => new UserDishResource($userDish->refresh()), @@ -51,9 +51,9 @@ public function update(UpdateUserDishFixedRecurrenceRequest $request, UserDish $ $recurrence = $recurrenceClass::findOrFail($recurrenceId); if ($recurrence instanceof WeeklyRecurrence) { - (new UpdateFixedRecurrenceAction())->execute($recurrence, $request->validated()); + (new UpdateFixedRecurrenceAction)->execute($recurrence, $request->validated()); } elseif ($recurrenceClass === MinimumRecurrence::class) { - (new UpdateMinimumRecurrenceAction())->execute($recurrence, $request->validated()); + (new UpdateMinimumRecurrenceAction)->execute($recurrence, $request->validated()); } else { return $this->error('invalid recurrence type'); } @@ -72,9 +72,9 @@ public function destroy(UserDish $userDish, string $recurrenceType, int $recurre $recurrence = $recurrenceClass::findOrFail($recurrenceId); if ($recurrence instanceof WeeklyRecurrence) { - (new DeleteFixedRecurrenceAction())->execute($recurrence); + (new DeleteFixedRecurrenceAction)->execute($recurrence); } elseif ($recurrenceClass === MinimumRecurrence::class) { - (new DeleteMinimumRecurrenceAction())->execute($recurrence); + (new DeleteMinimumRecurrenceAction)->execute($recurrence); } else { return $this->error('invalid recurrence type'); } diff --git a/src/DishPlanner/UserDish/Interfaces/FixedRecurrenceInterface.php b/src/DishPlanner/UserDish/Interfaces/FixedRecurrenceInterface.php index c8dd5dd..47c71b8 100644 --- a/src/DishPlanner/UserDish/Interfaces/FixedRecurrenceInterface.php +++ b/src/DishPlanner/UserDish/Interfaces/FixedRecurrenceInterface.php @@ -2,5 +2,4 @@ namespace DishPlanner\UserDish\Interfaces; -interface FixedRecurrenceInterface -{} +interface FixedRecurrenceInterface {} diff --git a/src/DishPlanner/UserDish/Interfaces/RecurrenceInterface.php b/src/DishPlanner/UserDish/Interfaces/RecurrenceInterface.php index 1776126..6c8ad9a 100644 --- a/src/DishPlanner/UserDish/Interfaces/RecurrenceInterface.php +++ b/src/DishPlanner/UserDish/Interfaces/RecurrenceInterface.php @@ -2,5 +2,4 @@ namespace DishPlanner\UserDish\Interfaces; -interface RecurrenceInterface -{} +interface RecurrenceInterface {} diff --git a/src/DishPlanner/UserDish/Repositories/UserDishRepository.php b/src/DishPlanner/UserDish/Repositories/UserDishRepository.php index 789c335..fb6badf 100644 --- a/src/DishPlanner/UserDish/Repositories/UserDishRepository.php +++ b/src/DishPlanner/UserDish/Repositories/UserDishRepository.php @@ -12,7 +12,6 @@ use App\Models\WeeklyRecurrence; use Carbon\Carbon; use Carbon\CarbonPeriod; -use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Collection as SupportCollection; @@ -70,7 +69,7 @@ public function findInterferingUserDishes(User $user, Carbon $date): Collection ->get() ->flatMap(fn (Schedule $schedule) => $schedule->scheduledUserDishes) ->filter(fn (ScheduledUserDish $scheduledUserDish) => $scheduledUserDish->userDish->user_id === $user->id) - ->filter(fn(ScheduledUserDish $scheduledUserDish) => $scheduledUserDish->userDish->recurrences->contains('recurrence_type', MinimumRecurrence::class)) + ->filter(fn (ScheduledUserDish $scheduledUserDish) => $scheduledUserDish->userDish->recurrences->contains('recurrence_type', MinimumRecurrence::class)) ->filter(function (ScheduledUserDish $scheduledUserDish) use ($date) { $minimum = $scheduledUserDish->userDish ->recurrences diff --git a/src/DishPlanner/UserDish/Requests/StoreUserDishRecurrenceRequest.php b/src/DishPlanner/UserDish/Requests/StoreUserDishRecurrenceRequest.php index 2c50204..e1190eb 100644 --- a/src/DishPlanner/UserDish/Requests/StoreUserDishRecurrenceRequest.php +++ b/src/DishPlanner/UserDish/Requests/StoreUserDishRecurrenceRequest.php @@ -19,7 +19,7 @@ public function rules(): array MinimumRecurrence::class, WeeklyRecurrence::class, ]), - 'required_with:*.recurrence_value' + 'required_with:*.recurrence_value', ], '*.value' => ['sometimes', 'integer', 'required_with:*.recurrence_type'], ]; diff --git a/src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php b/src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php index 49b23f9..a9f81ed 100644 --- a/src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php +++ b/src/DishPlanner/UserDish/Requests/UpdateUserDishFixedRecurrenceRequest.php @@ -14,14 +14,14 @@ public function rules(): array 'recurrence_type' => [ 'required', 'string', - 'in:' . implode(',', [ + 'in:'.implode(',', [ MinimumRecurrence::class, WeeklyRecurrence::class, ]), ], 'recurrence_data' => 'required|array', - 'recurrence_data.days' => 'required_if:recurrence_type,' . MinimumRecurrence::class . '|integer|min:1', - 'recurrence_data.weekday' => 'required_if:recurrence_type,' . WeeklyRecurrence::class . '|integer|between:0,6', + 'recurrence_data.days' => 'required_if:recurrence_type,'.MinimumRecurrence::class.'|integer|min:1', + 'recurrence_data.weekday' => 'required_if:recurrence_type,'.WeeklyRecurrence::class.'|integer|between:0,6', ]; } diff --git a/tests/Browser/Auth/LoginTest.php b/tests/Browser/Auth/LoginTest.php index 8eaa75b..fa56d89 100644 --- a/tests/Browser/Auth/LoginTest.php +++ b/tests/Browser/Auth/LoginTest.php @@ -2,15 +2,17 @@ namespace Tests\Browser\Auth; -use Laravel\Dusk\Browser; -use Tests\DuskTestCase; use App\Models\Planner; use Illuminate\Support\Facades\Hash; +use Laravel\Dusk\Browser; +use Tests\DuskTestCase; class LoginTest extends DuskTestCase { protected static $testPlanner = null; + protected static $testEmail = null; + protected static $testPassword = 'password'; protected function ensureTestPlannerExists(): void @@ -26,49 +28,49 @@ protected function ensureTestPlannerExists(): void } } - public function testSuccessfulLogin(): void + public function test_successful_login(): void { $this->ensureTestPlannerExists(); $this->browse(function (Browser $browser) { $browser->driver->manage()->deleteAllCookies(); $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$testPassword) - ->press('Login') - ->waitForLocation('/dashboard', self::TIMEOUT_MEDIUM) - ->assertPathIs('/dashboard'); + ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) + ->clear('input[id="email"]') + ->type('input[id="email"]', self::$testEmail) + ->clear('input[id="password"]') + ->type('input[id="password"]', self::$testPassword) + ->press('Login') + ->waitForLocation('/dashboard', self::TIMEOUT_MEDIUM) + ->assertPathIs('/dashboard'); }); } - public function testLoginWithWrongCredentials(): void + public function test_login_with_wrong_credentials(): void { $this->ensureTestPlannerExists(); $this->browse(function (Browser $browser) { $browser->driver->manage()->deleteAllCookies(); $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', 'wrongpassword') - ->press('Login') - ->pause(self::PAUSE_MEDIUM) - ->assertPathIs('/login') - ->assertSee('These credentials do not match our records'); + ->waitFor('input[id="email"]', self::TIMEOUT_SHORT) + ->clear('input[id="email"]') + ->type('input[id="email"]', self::$testEmail) + ->clear('input[id="password"]') + ->type('input[id="password"]', 'wrongpassword') + ->press('Login') + ->pause(self::PAUSE_MEDIUM) + ->assertPathIs('/login') + ->assertSee('These credentials do not match our records'); }); } - public function testLoginFormRequiredFields(): void + public function test_login_form_required_fields(): void { $this->browse(function (Browser $browser) { $browser->driver->manage()->deleteAllCookies(); $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', self::TIMEOUT_SHORT); + ->waitFor('input[id="email"]', self::TIMEOUT_SHORT); // Check that both fields have the required attribute $browser->assertAttribute('input[id="email"]', 'required', 'true'); @@ -82,8 +84,8 @@ public function testLoginFormRequiredFields(): void // Test that we stay on login page if we try to submit with empty fields $browser->press('Login') - ->pause(self::PAUSE_SHORT) - ->assertPathIs('/login'); + ->pause(self::PAUSE_SHORT) + ->assertPathIs('/login'); }); } } diff --git a/tests/Browser/Components/DishModal.php b/tests/Browser/Components/DishModal.php index 7baff18..1a31400 100644 --- a/tests/Browser/Components/DishModal.php +++ b/tests/Browser/Components/DishModal.php @@ -29,7 +29,7 @@ public function selector(): string public function assert(Browser $browser): void { $browser->assertVisible($this->selector()); - + if ($this->mode === 'create') { $browser->assertSee('Add New Dish'); } else { @@ -60,12 +60,12 @@ public function elements(): array public function fillForm(Browser $browser, string $name, ?string $description = null): void { $browser->waitFor('@name-input') - ->clear('@name-input') - ->type('@name-input', $name); - + ->clear('@name-input') + ->type('@name-input', $name); + if ($description !== null && $browser->element('@description-input')) { $browser->clear('@description-input') - ->type('@description-input', $description); + ->type('@description-input', $description); } } @@ -102,4 +102,4 @@ public function assertValidationError(Browser $browser, string $message = 'requi { $browser->assertSee($message); } -} \ No newline at end of file +} diff --git a/tests/Browser/Components/LoginForm.php b/tests/Browser/Components/LoginForm.php index f526ff3..2b817ee 100644 --- a/tests/Browser/Components/LoginForm.php +++ b/tests/Browser/Components/LoginForm.php @@ -21,9 +21,9 @@ public function selector(): string public function assert(Browser $browser): void { $browser->assertVisible($this->selector()) - ->assertVisible('@email') - ->assertVisible('@password') - ->assertVisible('@submit'); + ->assertVisible('@email') + ->assertVisible('@password') + ->assertVisible('@submit'); } /** @@ -48,7 +48,7 @@ public function elements(): array public function fillForm(Browser $browser, string $email, string $password): void { $browser->type('@email', $email) - ->type('@password', $password); + ->type('@password', $password); } /** @@ -74,9 +74,9 @@ public function loginWith(Browser $browser, string $email, string $password): vo public function assertFieldsRequired(Browser $browser): void { $browser->assertAttribute('@email', 'required', 'true') - ->assertAttribute('@password', 'required', 'true') - ->assertAttribute('@email', 'type', 'email') - ->assertAttribute('@password', 'type', 'password'); + ->assertAttribute('@password', 'required', 'true') + ->assertAttribute('@email', 'type', 'email') + ->assertAttribute('@password', 'type', 'password'); } /** @@ -86,4 +86,4 @@ public function assertHasErrors(Browser $browser): void { $browser->assertPresent('@error'); } -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/CreateDishFormValidationTest.php b/tests/Browser/Dishes/CreateDishFormValidationTest.php index a59c89c..318becf 100644 --- a/tests/Browser/Dishes/CreateDishFormValidationTest.php +++ b/tests/Browser/Dishes/CreateDishFormValidationTest.php @@ -3,18 +3,19 @@ namespace Tests\Browser\Dishes; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\Pages\DishesPage; use Tests\Browser\Components\DishModal; use Tests\Browser\LoginHelpers; +use Tests\Browser\Pages\DishesPage; +use Tests\DuskTestCase; class CreateDishFormValidationTest extends DuskTestCase { use LoginHelpers; - + protected static $createDishFormValidationTestPlanner = null; + protected static $createDishFormValidationTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -22,7 +23,7 @@ protected function setUp(): void self::$testPlanner = self::$createDishFormValidationTestPlanner; self::$testEmail = self::$createDishFormValidationTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -31,19 +32,19 @@ protected function tearDown(): void parent::tearDown(); } - public function testCreateDishFormValidation(): void + public function test_create_dish_form_validation(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser); - + $browser->on(new DishesPage) - ->openCreateModal() - ->within(new DishModal('create'), function ($browser) { - $browser->fillForm('', null) - ->submit() - ->pause(2000) - ->assertValidationError('required'); - }); + ->openCreateModal() + ->within(new DishModal('create'), function ($browser) { + $browser->fillForm('', null) + ->submit() + ->pause(2000) + ->assertValidationError('required'); + }); }); } -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/CreateDishSuccessTest.php b/tests/Browser/Dishes/CreateDishSuccessTest.php index 822dc77..aad8c7c 100644 --- a/tests/Browser/Dishes/CreateDishSuccessTest.php +++ b/tests/Browser/Dishes/CreateDishSuccessTest.php @@ -3,18 +3,19 @@ namespace Tests\Browser\Dishes; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\Pages\DishesPage; use Tests\Browser\Components\DishModal; use Tests\Browser\LoginHelpers; +use Tests\Browser\Pages\DishesPage; +use Tests\DuskTestCase; class CreateDishSuccessTest extends DuskTestCase { use LoginHelpers; - + protected static $createDishSuccessTestPlanner = null; + protected static $createDishSuccessTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -22,7 +23,7 @@ protected function setUp(): void self::$testPlanner = self::$createDishSuccessTestPlanner; self::$testEmail = self::$createDishSuccessTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -31,22 +32,22 @@ protected function tearDown(): void parent::tearDown(); } - public function testCanCreateDishSuccessfully(): void + public function test_can_create_dish_successfully(): void { $this->browse(function (Browser $browser) { - $dishName = 'Test Dish ' . uniqid(); - + $dishName = 'Test Dish '.uniqid(); + $this->loginAndGoToDishes($browser); - + $browser->on(new DishesPage) - ->openCreateModal() - ->within(new DishModal('create'), function ($browser) use ($dishName) { - $browser->fillForm($dishName) - ->submit(); - }) - ->pause(3000) - ->assertDishVisible($dishName) - ->assertSee('Dish created successfully'); + ->openCreateModal() + ->within(new DishModal('create'), function ($browser) use ($dishName) { + $browser->fillForm($dishName) + ->submit(); + }) + ->pause(3000) + ->assertDishVisible($dishName) + ->assertSee('Dish created successfully'); }); } -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/CreateDishTest.php b/tests/Browser/Dishes/CreateDishTest.php index 9a333e1..c20a99c 100644 --- a/tests/Browser/Dishes/CreateDishTest.php +++ b/tests/Browser/Dishes/CreateDishTest.php @@ -3,18 +3,18 @@ namespace Tests\Browser\Dishes; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\Pages\DishesPage; -use Tests\Browser\Components\DishModal; use Tests\Browser\LoginHelpers; +use Tests\Browser\Pages\DishesPage; +use Tests\DuskTestCase; class CreateDishTest extends DuskTestCase { use LoginHelpers; - + protected static $createDishTestPlanner = null; + protected static $createDishTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -22,7 +22,7 @@ protected function setUp(): void self::$testPlanner = self::$createDishTestPlanner; self::$testEmail = self::$createDishTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -30,18 +30,18 @@ protected function tearDown(): void self::$createDishTestEmail = self::$testEmail; parent::tearDown(); } - - public function testCanAccessDishesPage(): void + + public function test_can_access_dishes_page(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser); - + $browser->on(new DishesPage) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish'); }); } // TODO: Moved to separate single-method test files to avoid static planner issues // See: OpenCreateDishModalTest, CreateDishFormValidationTest, CancelDishCreationTest, CreateDishSuccessTest -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/DeleteDishTest.php b/tests/Browser/Dishes/DeleteDishTest.php index 51083f1..f0d7651 100644 --- a/tests/Browser/Dishes/DeleteDishTest.php +++ b/tests/Browser/Dishes/DeleteDishTest.php @@ -2,18 +2,19 @@ namespace Tests\Browser\Dishes; -use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\LoginHelpers; use App\Models\Planner; +use Laravel\Dusk\Browser; +use Tests\Browser\LoginHelpers; +use Tests\DuskTestCase; class DeleteDishTest extends DuskTestCase { use LoginHelpers; - + protected static $deleteDishTestPlanner = null; + protected static $deleteDishTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -21,7 +22,7 @@ protected function setUp(): void self::$testPlanner = self::$deleteDishTestPlanner; self::$testEmail = self::$deleteDishTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -29,14 +30,14 @@ protected function tearDown(): void self::$deleteDishTestEmail = self::$testEmail; parent::tearDown(); } - - public function testCanAccessDeleteFeature(): void + + public function test_can_access_delete_feature(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser) - ->assertPathIs('/dishes') - ->assertSee('MANAGE DISHES'); - + ->assertPathIs('/dishes') + ->assertSee('MANAGE DISHES'); + // Verify that delete functionality is available by looking for the text in the page source $pageSource = $browser->driver->getPageSource(); $this->assertStringContainsString('Delete', $pageSource); @@ -59,7 +60,7 @@ public function testDeletionSafetyFeatures(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser); - + // Check that Livewire component includes all CRUD features $pageSource = $browser->driver->getPageSource(); $this->assertStringContainsString('MANAGE DISHES', $pageSource); @@ -73,4 +74,4 @@ public function testDeletionSafetyFeatures(): void }); } */ -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/DishDeletionSafetyTest.php b/tests/Browser/Dishes/DishDeletionSafetyTest.php index ab5b1d2..70b030f 100644 --- a/tests/Browser/Dishes/DishDeletionSafetyTest.php +++ b/tests/Browser/Dishes/DishDeletionSafetyTest.php @@ -3,16 +3,17 @@ namespace Tests\Browser\Dishes; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; use Tests\Browser\LoginHelpers; +use Tests\DuskTestCase; class DishDeletionSafetyTest extends DuskTestCase { use LoginHelpers; - + protected static $dishDeletionSafetyTestPlanner = null; + protected static $dishDeletionSafetyTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -20,7 +21,7 @@ protected function setUp(): void self::$testPlanner = self::$dishDeletionSafetyTestPlanner; self::$testEmail = self::$dishDeletionSafetyTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -29,11 +30,11 @@ protected function tearDown(): void parent::tearDown(); } - public function testDeletionSafetyFeatures(): void + public function test_deletion_safety_features(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser); - + // Check that Livewire component includes all CRUD features $pageSource = $browser->driver->getPageSource(); $this->assertStringContainsString('MANAGE DISHES', $pageSource); @@ -46,4 +47,4 @@ public function testDeletionSafetyFeatures(): void } }); } -} \ No newline at end of file +} diff --git a/tests/Browser/Dishes/EditDishTest.php b/tests/Browser/Dishes/EditDishTest.php index af2d2f8..c27fec8 100644 --- a/tests/Browser/Dishes/EditDishTest.php +++ b/tests/Browser/Dishes/EditDishTest.php @@ -2,18 +2,19 @@ namespace Tests\Browser\Dishes; -use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\LoginHelpers; use App\Models\Planner; +use Laravel\Dusk\Browser; +use Tests\Browser\LoginHelpers; +use Tests\DuskTestCase; class EditDishTest extends DuskTestCase { use LoginHelpers; - + protected static $editDishTestPlanner = null; + protected static $editDishTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -21,7 +22,7 @@ protected function setUp(): void self::$testPlanner = self::$editDishTestPlanner; self::$testEmail = self::$editDishTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -29,36 +30,36 @@ protected function tearDown(): void self::$editDishTestEmail = self::$testEmail; parent::tearDown(); } - - public function testCanAccessEditFeature(): void + + public function test_can_access_edit_feature(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser) - ->assertPathIs('/dishes') - ->assertSee('MANAGE DISHES'); - + ->assertPathIs('/dishes') + ->assertSee('MANAGE DISHES'); + // Verify that edit functionality is available by looking for the text in the page source $pageSource = $browser->driver->getPageSource(); $this->assertStringContainsString('Edit', $pageSource); }); } - public function testEditModalComponents(): void + public function test_edit_modal_components(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish'); }); } - public function testDishesPageStructure(): void + public function test_dishes_page_structure(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToDishes($browser) - ->assertSee('MANAGE DISHES') - ->assertSee('Add Dish'); - + ->assertSee('MANAGE DISHES') + ->assertSee('Add Dish'); + // Check that the dishes CRUD structure is present $pageSource = $browser->driver->getPageSource(); // Either we have dishes with Edit/Delete buttons OR "No dishes found" message @@ -70,4 +71,4 @@ public function testDishesPageStructure(): void } }); } -} \ No newline at end of file +} diff --git a/tests/Browser/LoginHelpers.php b/tests/Browser/LoginHelpers.php index 2d4aac2..cfed692 100644 --- a/tests/Browser/LoginHelpers.php +++ b/tests/Browser/LoginHelpers.php @@ -2,25 +2,29 @@ namespace Tests\Browser; +use App\Models\Planner; +use Illuminate\Support\Facades\Hash; use Laravel\Dusk\Browser; use Tests\DuskTestCase; trait LoginHelpers { protected static $testPlanner = null; + protected static $testEmail = null; + protected static $testPassword = 'password'; protected function ensureTestPlannerExists(): void { // Always create a fresh planner for each test class to avoid session conflicts - if (self::$testPlanner === null || !self::$testPlanner->exists) { + if (self::$testPlanner === null || ! self::$testPlanner->exists) { // Generate unique email for this test run self::$testEmail = fake()->unique()->safeEmail(); - - self::$testPlanner = \App\Models\Planner::factory()->create([ + + self::$testPlanner = Planner::factory()->create([ 'email' => self::$testEmail, - 'password' => \Illuminate\Support\Facades\Hash::make(self::$testPassword), + 'password' => Hash::make(self::$testPassword), ]); } } @@ -28,21 +32,21 @@ protected function ensureTestPlannerExists(): void protected function loginAndNavigate(Browser $browser, string $page = '/dashboard'): Browser { $this->ensureTestPlannerExists(); - + // Clear browser session and cookies to start fresh $browser->driver->manage()->deleteAllCookies(); - + return $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$testEmail) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$testPassword) - ->press('Sign In') - ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) // Wait for successful login redirect - ->pause(DuskTestCase::PAUSE_SHORT) // Brief pause for any initialization - ->visit('http://dishplanner_app:8000' . $page) - ->pause(DuskTestCase::PAUSE_MEDIUM); // Let Livewire components initialize + ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) + ->clear('input[id="email"]') + ->type('input[id="email"]', self::$testEmail) + ->clear('input[id="password"]') + ->type('input[id="password"]', self::$testPassword) + ->press('Sign In') + ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) // Wait for successful login redirect + ->pause(DuskTestCase::PAUSE_SHORT) // Brief pause for any initialization + ->visit('http://dishplanner_app:8000'.$page) + ->pause(DuskTestCase::PAUSE_MEDIUM); // Let Livewire components initialize } protected function loginAndGoToDishes(Browser $browser): Browser diff --git a/tests/Browser/Pages/DishesPage.php b/tests/Browser/Pages/DishesPage.php index e8eeee5..ae5005f 100644 --- a/tests/Browser/Pages/DishesPage.php +++ b/tests/Browser/Pages/DishesPage.php @@ -20,7 +20,7 @@ public function url(): string public function assert(Browser $browser): void { $browser->assertPathIs($this->url()) - ->assertSee('MANAGE DISHES'); + ->assertSee('MANAGE DISHES'); } /** @@ -44,8 +44,8 @@ public function elements(): array public function openCreateModal(Browser $browser): void { $browser->waitFor('@add-button') - ->click('@add-button') - ->pause(1000); + ->click('@add-button') + ->pause(1000); } /** @@ -83,4 +83,4 @@ public function assertNoDishes(Browser $browser): void { $browser->assertSee('No dishes found'); } -} \ No newline at end of file +} diff --git a/tests/Browser/Pages/LoginPage.php b/tests/Browser/Pages/LoginPage.php index 6732270..d16642d 100644 --- a/tests/Browser/Pages/LoginPage.php +++ b/tests/Browser/Pages/LoginPage.php @@ -21,8 +21,8 @@ public function url(): string public function assert(Browser $browser): void { $browser->assertPathIs($this->url()) - ->assertSee('Login') - ->assertPresent((new LoginForm)->selector()); + ->assertSee('Login') + ->assertPresent((new LoginForm)->selector()); } /** @@ -44,4 +44,4 @@ public function goToRegistration(Browser $browser): void { $browser->click('@register-link'); } -} \ No newline at end of file +} diff --git a/tests/Browser/Pages/Page.php b/tests/Browser/Pages/Page.php index ecef801..54e0e22 100644 --- a/tests/Browser/Pages/Page.php +++ b/tests/Browser/Pages/Page.php @@ -18,4 +18,4 @@ public static function siteElements(): array '@alert' => '[role="alert"]', ]; } -} \ No newline at end of file +} diff --git a/tests/Browser/Pages/SchedulePage.php b/tests/Browser/Pages/SchedulePage.php index d2db379..bcd787f 100644 --- a/tests/Browser/Pages/SchedulePage.php +++ b/tests/Browser/Pages/SchedulePage.php @@ -14,7 +14,7 @@ public function url(): string public function assert(Browser $browser): void { $browser->assertPathIs($this->url()) - ->assertSee('SCHEDULE'); + ->assertSee('SCHEDULE'); } public function elements(): array @@ -34,49 +34,49 @@ public function elements(): array public function clickGenerate(Browser $browser): void { $browser->waitFor('@generate-button') - ->click('@generate-button') - ->pause(2000); // Wait for generation + ->click('@generate-button') + ->pause(2000); // Wait for generation } public function clickClearMonth(Browser $browser): void { $browser->waitFor('@clear-month-button') - ->click('@clear-month-button') - ->pause(1000); + ->click('@clear-month-button') + ->pause(1000); } public function goToPreviousMonth(Browser $browser): void { $browser->waitFor('@previous-month') - ->click('@previous-month') - ->pause(500); + ->click('@previous-month') + ->pause(500); } public function goToNextMonth(Browser $browser): void { $browser->waitFor('@next-month') - ->click('@next-month') - ->pause(500); + ->click('@next-month') + ->pause(500); } public function selectMonth(Browser $browser, int $month): void { $browser->waitFor('@month-select') - ->select('@month-select', $month) - ->pause(500); + ->select('@month-select', $month) + ->pause(500); } public function selectYear(Browser $browser, int $year): void { $browser->waitFor('@year-select') - ->select('@year-select', $year) - ->pause(500); + ->select('@year-select', $year) + ->pause(500); } public function toggleClearExisting(Browser $browser): void { $browser->waitFor('@clear-existing-checkbox') - ->click('@clear-existing-checkbox'); + ->click('@clear-existing-checkbox'); } public function selectUser(Browser $browser, string $userName): void @@ -84,7 +84,7 @@ public function selectUser(Browser $browser, string $userName): void $browser->check("input[type='checkbox'][value]", $userName); } - public function assertSuccessMessage(Browser $browser, string $message = null): void + public function assertSuccessMessage(Browser $browser, ?string $message = null): void { if ($message) { $browser->assertSee($message); diff --git a/tests/Browser/Pages/UsersPage.php b/tests/Browser/Pages/UsersPage.php index 71aed0a..7f0aa54 100644 --- a/tests/Browser/Pages/UsersPage.php +++ b/tests/Browser/Pages/UsersPage.php @@ -20,7 +20,7 @@ public function url(): string public function assert(Browser $browser): void { $browser->assertPathIs($this->url()) - ->assertSee('MANAGE USERS'); + ->assertSee('MANAGE USERS'); } /** @@ -43,8 +43,8 @@ public function elements(): array public function openCreateModal(Browser $browser): void { $browser->waitFor('@add-button') - ->click('@add-button') - ->pause(1000); + ->click('@add-button') + ->pause(1000); } /** @@ -63,8 +63,8 @@ public function clickDeleteForUser(Browser $browser, string $userName): void public function clickFirstDeleteButton(Browser $browser): void { $browser->waitFor('button.bg-danger', 5) - ->click('button.bg-danger') - ->pause(1000); + ->click('button.bg-danger') + ->pause(1000); } /** @@ -90,4 +90,4 @@ public function assertSuccessMessage(Browser $browser, string $message): void { $browser->assertSee($message); } -} \ No newline at end of file +} diff --git a/tests/Browser/RedirectTest.php b/tests/Browser/RedirectTest.php index 3c5954e..9b8913c 100644 --- a/tests/Browser/RedirectTest.php +++ b/tests/Browser/RedirectTest.php @@ -2,9 +2,9 @@ namespace Tests\Browser; +use Illuminate\Foundation\Testing\DatabaseTransactions; use Laravel\Dusk\Browser; use Tests\DuskTestCase; -use Illuminate\Foundation\Testing\DatabaseTransactions; class RedirectTest extends DuskTestCase { @@ -13,26 +13,26 @@ class RedirectTest extends DuskTestCase /** * Test that unauthenticated users are redirected to login */ - public function testUnauthenticatedRedirectsToLogin() + public function test_unauthenticated_redirects_to_login() { $this->browse(function (Browser $browser) { $browser->visit('http://dishplanner_app:8000/dashboard') - ->assertPathIs('/login') - ->assertSee('Login'); + ->assertPathIs('/login') + ->assertSee('Login'); }); } - + /** * Test that login page loads correctly */ - public function testLoginPageLoads() + public function test_login_page_loads() { $this->browse(function (Browser $browser) { $browser->visit('http://dishplanner_app:8000/login') - ->assertPathIs('/login') - ->assertSee('Login') - ->assertSee('Email') - ->assertSee('Password'); + ->assertPathIs('/login') + ->assertSee('Login') + ->assertSee('Email') + ->assertSee('Password'); }); } -} \ No newline at end of file +} diff --git a/tests/Browser/Schedule/GenerateScheduleTest.php b/tests/Browser/Schedule/GenerateScheduleTest.php index f3e945c..c2f1f71 100644 --- a/tests/Browser/Schedule/GenerateScheduleTest.php +++ b/tests/Browser/Schedule/GenerateScheduleTest.php @@ -7,15 +7,19 @@ use App\Models\User; use Illuminate\Support\Facades\Hash; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; use Tests\Browser\Pages\SchedulePage; +use Tests\DuskTestCase; class GenerateScheduleTest extends DuskTestCase { protected static $planner = null; + protected static $email = null; + protected static $password = 'password'; + protected static $user = null; + protected static $dish = null; protected function setUp(): void @@ -52,73 +56,73 @@ protected function loginAsPlanner(Browser $browser): Browser $browser->driver->manage()->deleteAllCookies(); return $browser->visit('http://dishplanner_app:8000/login') - ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) - ->clear('input[id="email"]') - ->type('input[id="email"]', self::$email) - ->clear('input[id="password"]') - ->type('input[id="password"]', self::$password) - ->press('Login') - ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) - ->pause(DuskTestCase::PAUSE_SHORT) - ->visit('http://dishplanner_app:8000/schedule') - ->pause(DuskTestCase::PAUSE_MEDIUM); + ->waitFor('input[id="email"]', DuskTestCase::TIMEOUT_SHORT) + ->clear('input[id="email"]') + ->type('input[id="email"]', self::$email) + ->clear('input[id="password"]') + ->type('input[id="password"]', self::$password) + ->press('Login') + ->waitForLocation('/dashboard', DuskTestCase::TIMEOUT_MEDIUM) + ->pause(DuskTestCase::PAUSE_SHORT) + ->visit('http://dishplanner_app:8000/schedule') + ->pause(DuskTestCase::PAUSE_MEDIUM); } - public function testCanGenerateScheduleWithUserAndDish(): void + public function test_can_generate_schedule_with_user_and_dish(): void { $this->browse(function (Browser $browser) { $this->loginAsPlanner($browser); $browser->on(new SchedulePage) - ->assertSee('Test User') // User should be in selection - ->clickGenerate() - ->pause(2000) + ->assertSee('Test User') // User should be in selection + ->clickGenerate() + ->pause(2000) // Verify schedule was generated by checking dish appears on calendar - ->assertSee('Test Dish'); + ->assertSee('Test Dish'); }); } - public function testGeneratedScheduleShowsDishOnCalendar(): void + public function test_generated_schedule_shows_dish_on_calendar(): void { $this->browse(function (Browser $browser) { $this->loginAsPlanner($browser); $browser->on(new SchedulePage) - ->clickGenerate() - ->pause(2000) + ->clickGenerate() + ->pause(2000) // The dish should appear somewhere on the calendar - ->assertSee('Test Dish'); + ->assertSee('Test Dish'); }); } - public function testCanClearMonthSchedule(): void + public function test_can_clear_month_schedule(): void { $this->browse(function (Browser $browser) { $this->loginAsPlanner($browser); $browser->on(new SchedulePage) // First generate a schedule - ->clickGenerate() - ->pause(2000) - ->assertSee('Test Dish') // Verify generated + ->clickGenerate() + ->pause(2000) + ->assertSee('Test Dish') // Verify generated // Then clear it - ->clickClearMonth() - ->pause(1000) + ->clickClearMonth() + ->pause(1000) // After clearing, should see "No dishes scheduled" on calendar days - ->assertSee('No dishes scheduled'); + ->assertSee('No dishes scheduled'); }); } - public function testUserSelectionAffectsGeneration(): void + public function test_user_selection_affects_generation(): void { $this->browse(function (Browser $browser) { $this->loginAsPlanner($browser); $browser->on(new SchedulePage) // Verify the user checkbox is present - ->assertSee('Test User') + ->assertSee('Test User') // User should be selected by default - ->assertChecked("input[value='" . self::$user->id . "']"); + ->assertChecked("input[value='".self::$user->id."']"); }); } } diff --git a/tests/Browser/Schedule/SchedulePageTest.php b/tests/Browser/Schedule/SchedulePageTest.php index a0a2ace..b4d59a0 100644 --- a/tests/Browser/Schedule/SchedulePageTest.php +++ b/tests/Browser/Schedule/SchedulePageTest.php @@ -3,15 +3,16 @@ namespace Tests\Browser\Schedule; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\Pages\SchedulePage; use Tests\Browser\LoginHelpers; +use Tests\Browser\Pages\SchedulePage; +use Tests\DuskTestCase; class SchedulePageTest extends DuskTestCase { use LoginHelpers; protected static $schedulePageTestPlanner = null; + protected static $schedulePageTestEmail = null; protected function setUp(): void @@ -28,30 +29,30 @@ protected function tearDown(): void parent::tearDown(); } - public function testCanAccessSchedulePage(): void + public function test_can_access_schedule_page(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); $browser->on(new SchedulePage) - ->assertSee('SCHEDULE') - ->assertSee('Generate Schedule'); + ->assertSee('SCHEDULE') + ->assertSee('Generate Schedule'); }); } - public function testSchedulePageHasMonthNavigation(): void + public function test_schedule_page_has_month_navigation(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); $browser->on(new SchedulePage) - ->assertPresent('@previous-month') - ->assertPresent('@next-month') - ->assertSee(now()->format('F Y')); + ->assertPresent('@previous-month') + ->assertPresent('@next-month') + ->assertSee(now()->format('F Y')); }); } - public function testCanNavigateToNextMonth(): void + public function test_can_navigate_to_next_month(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); @@ -59,12 +60,12 @@ public function testCanNavigateToNextMonth(): void $nextMonth = now()->addMonth(); $browser->on(new SchedulePage) - ->goToNextMonth() - ->assertSee($nextMonth->format('F Y')); + ->goToNextMonth() + ->assertSee($nextMonth->format('F Y')); }); } - public function testCanNavigateToPreviousMonth(): void + public function test_can_navigate_to_previous_month(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); @@ -72,36 +73,36 @@ public function testCanNavigateToPreviousMonth(): void $prevMonth = now()->subMonth(); $browser->on(new SchedulePage) - ->goToPreviousMonth() - ->assertSee($prevMonth->format('F Y')); + ->goToPreviousMonth() + ->assertSee($prevMonth->format('F Y')); }); } - public function testScheduleGeneratorShowsUserSelection(): void + public function test_schedule_generator_shows_user_selection(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); $browser->on(new SchedulePage) - ->assertSee('Select Users') - ->assertPresent('@generate-button') - ->assertPresent('@clear-month-button'); + ->assertSee('Select Users') + ->assertPresent('@generate-button') + ->assertPresent('@clear-month-button'); }); } - public function testCalendarDisplaysDaysOfWeek(): void + public function test_calendar_displays_days_of_week(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToSchedule($browser); $browser->on(new SchedulePage) - ->assertSee('Mon') - ->assertSee('Tue') - ->assertSee('Wed') - ->assertSee('Thu') - ->assertSee('Fri') - ->assertSee('Sat') - ->assertSee('Sun'); + ->assertSee('Mon') + ->assertSee('Tue') + ->assertSee('Wed') + ->assertSee('Thu') + ->assertSee('Fri') + ->assertSee('Sat') + ->assertSee('Sun'); }); } } diff --git a/tests/Browser/Users/CreateUserTest.php b/tests/Browser/Users/CreateUserTest.php index 56ebbb3..6817560 100644 --- a/tests/Browser/Users/CreateUserTest.php +++ b/tests/Browser/Users/CreateUserTest.php @@ -3,17 +3,18 @@ namespace Tests\Browser\Users; use Laravel\Dusk\Browser; -use Tests\DuskTestCase; -use Tests\Browser\Pages\UsersPage; use Tests\Browser\LoginHelpers; +use Tests\Browser\Pages\UsersPage; +use Tests\DuskTestCase; class CreateUserTest extends DuskTestCase { use LoginHelpers; - + protected static $createUserTestPlanner = null; + protected static $createUserTestEmail = null; - + protected function setUp(): void { parent::setUp(); @@ -21,7 +22,7 @@ protected function setUp(): void self::$testPlanner = self::$createUserTestPlanner; self::$testEmail = self::$createUserTestEmail; } - + protected function tearDown(): void { // Save the planner for next test method in this class @@ -30,74 +31,74 @@ protected function tearDown(): void parent::tearDown(); } - public function testCanAccessUsersPage(): void + public function test_can_access_users_page(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToUsers($browser); - + $browser->on(new UsersPage) - ->assertSee('MANAGE USERS') - ->assertSee('Add User'); + ->assertSee('MANAGE USERS') + ->assertSee('Add User'); }); } - public function testCanOpenCreateUserModal(): void + public function test_can_open_create_user_modal(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToUsers($browser); $browser->on(new UsersPage) - ->openCreateModal() - ->assertSee('Add New User') - ->assertSee('Name') - ->assertSee('Cancel') - ->assertSee('Create User'); + ->openCreateModal() + ->assertSee('Add New User') + ->assertSee('Name') + ->assertSee('Cancel') + ->assertSee('Create User'); }); } - public function testCreateUserFormValidation(): void + public function test_create_user_form_validation(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToUsers($browser); $browser->on(new UsersPage) - ->openCreateModal() - ->press('Create User') - ->pause(self::PAUSE_MEDIUM) - ->assertSee('The name field is required'); + ->openCreateModal() + ->press('Create User') + ->pause(self::PAUSE_MEDIUM) + ->assertSee('The name field is required'); }); } - public function testCanCreateUser(): void + public function test_can_create_user(): void { $this->browse(function (Browser $browser) { - $userName = 'TestCreate_' . uniqid(); + $userName = 'TestCreate_'.uniqid(); $this->loginAndGoToUsers($browser); $browser->on(new UsersPage) - ->openCreateModal() - ->type('input[wire\\:model="name"]', $userName) - ->press('Create User') - ->pause(self::PAUSE_MEDIUM) - ->assertSee('User created successfully') - ->assertSee($userName); + ->openCreateModal() + ->type('input[wire\\:model="name"]', $userName) + ->press('Create User') + ->pause(self::PAUSE_MEDIUM) + ->assertSee('User created successfully') + ->assertSee($userName); }); } - public function testCanCancelUserCreation(): void + public function test_can_cancel_user_creation(): void { $this->browse(function (Browser $browser) { $this->loginAndGoToUsers($browser); $browser->on(new UsersPage) - ->openCreateModal() - ->type('input[wire\\:model="name"]', 'Test Cancel User') - ->press('Cancel') - ->pause(self::PAUSE_SHORT) + ->openCreateModal() + ->type('input[wire\\:model="name"]', 'Test Cancel User') + ->press('Cancel') + ->pause(self::PAUSE_SHORT) // Modal should be closed, we should be back on users page - ->assertSee('MANAGE USERS') - ->assertDontSee('Add New User'); + ->assertSee('MANAGE USERS') + ->assertDontSee('Add New User'); }); } -} \ No newline at end of file +} diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php index ec8f947..075cf58 100644 --- a/tests/DuskTestCase.php +++ b/tests/DuskTestCase.php @@ -5,7 +5,6 @@ use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\RemoteWebDriver; -use Illuminate\Support\Collection; use Laravel\Dusk\TestCase as BaseTestCase; use PHPUnit\Framework\Attributes\BeforeClass; @@ -13,8 +12,11 @@ abstract class DuskTestCase extends BaseTestCase { // Timeout constants for consistent timing across all Dusk tests public const TIMEOUT_SHORT = 2; // 2 seconds max for most operations + public const TIMEOUT_MEDIUM = 3; // 3 seconds for slower operations + public const PAUSE_SHORT = 500; // 0.5 seconds for quick pauses + public const PAUSE_MEDIUM = 1000; // 1 second for medium pauses /** diff --git a/tests/Feature/AuthenticationTest.php b/tests/Feature/AuthenticationTest.php index e6486f4..ff3ebb7 100644 --- a/tests/Feature/AuthenticationTest.php +++ b/tests/Feature/AuthenticationTest.php @@ -2,10 +2,10 @@ namespace Tests\Feature; -use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; -use Tests\TestCase; use App\Models\Planner; +use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; class AuthenticationTest extends TestCase { @@ -61,10 +61,6 @@ public function test_session_is_created_on_login_page(): void // Check if CSRF token is generated $this->assertNotNull(csrf_token()); - // Check session driver - $sessionDriver = config('session.driver'); - $this->assertNotEquals('array', $sessionDriver, 'Session driver should not be array for authentication'); - $response->assertStatus(200); $response->assertSessionHasNoErrors(); } diff --git a/tests/Feature/Dish/CreateDishTest.php b/tests/Feature/Dish/CreateDishTest.php index 8733917..54a4e21 100755 --- a/tests/Feature/Dish/CreateDishTest.php +++ b/tests/Feature/Dish/CreateDishTest.php @@ -3,7 +3,6 @@ namespace Tests\Feature\Dish; use App\Models\Dish; -use App\Models\Planner; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Str; use Illuminate\Testing\Fluent\AssertableJson; diff --git a/tests/Feature/Dish/DeleteDishTest.php b/tests/Feature/Dish/DeleteDishTest.php index b773776..b07fde6 100644 --- a/tests/Feature/Dish/DeleteDishTest.php +++ b/tests/Feature/Dish/DeleteDishTest.php @@ -64,7 +64,6 @@ public function test_it_deletes_user_dishes_when_deleting_a_dish(): void $this->assertDatabaseEmpty(UserDish::class); } - public function test_planner_cannot_delete_dish_from_other_planner(): void { $planner = $this->planner; diff --git a/tests/Feature/RegistrationTest.php b/tests/Feature/RegistrationTest.php index b68a311..8908407 100644 --- a/tests/Feature/RegistrationTest.php +++ b/tests/Feature/RegistrationTest.php @@ -2,9 +2,9 @@ namespace Tests\Feature; -use Tests\TestCase; use App\Models\Planner; use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; class RegistrationTest extends TestCase { @@ -13,7 +13,7 @@ class RegistrationTest extends TestCase public function test_registration_screen_can_be_rendered() { $response = $this->get('/register'); - + $response->assertStatus(200); $response->assertViewIs('auth.register'); $response->assertSee('Register'); @@ -30,7 +30,7 @@ public function test_new_users_can_register() $this->assertAuthenticated(); $response->assertRedirect('/dashboard'); - + // Check user was created $this->assertDatabaseHas('planners', [ 'email' => 'test@example.com', @@ -68,10 +68,10 @@ public function test_registration_fails_with_password_mismatch() $response->assertRedirect(); $response->assertSessionHasErrors('password'); $this->assertGuest(); - + // Check user was not created $this->assertDatabaseMissing('planners', [ 'email' => 'test@example.com', ]); } -} \ No newline at end of file +} diff --git a/tests/Feature/Schedule/GenerateScheduleTest.php b/tests/Feature/Schedule/GenerateScheduleTest.php index 79bd6f6..d2fcd1e 100644 --- a/tests/Feature/Schedule/GenerateScheduleTest.php +++ b/tests/Feature/Schedule/GenerateScheduleTest.php @@ -3,12 +3,11 @@ namespace Tests\Feature\Schedule; use App\Models\Dish; -use App\Models\Planner; -use App\Models\UserDish; -use App\Models\UserDishRecurrence; use App\Models\Schedule; use App\Models\ScheduledUserDish; use App\Models\User; +use App\Models\UserDish; +use App\Models\UserDishRecurrence; use App\Models\WeeklyRecurrence; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Carbon; @@ -115,6 +114,7 @@ public function test_fresh_schedule_adheres_to_fixed_recurrences(): void $this->assertContains($targetUserDish->id, $targetScheduledUserDishes); } + public function test_schedule_can_be_overwritten(): void { $planner = $this->planner; @@ -132,8 +132,7 @@ public function test_schedule_can_be_overwritten(): void }); // Assert that every user has `UserDish` records - $users->each(fn (User $user) => - $this->assertNotEmpty($user->refresh()->userDishes) + $users->each(fn (User $user) => $this->assertNotEmpty($user->refresh()->userDishes) ); $scheduleDay = Schedule::factory() @@ -172,7 +171,7 @@ public function test_schedule_can_be_overwritten(): void $this->assertDatabaseCount(Schedule::class, 14); - $freshScheduleDay = Schedule::query()->where('date', $scheduleDay->date)->first(); + $freshScheduleDay = Schedule::query()->whereDate('date', $scheduleDay->date)->first(); $this->assertNotEquals( $originalUserDishes, $freshScheduleDay->scheduledUserDishes->map(fn (ScheduledUserDish $scheduledUserDish) => [ @@ -218,11 +217,11 @@ public function test_fixed_recurrence_takes_precedence_during_overwrite(): void $users ->map(fn (User $user) => ScheduledUserDish::factory() - ->create([ - 'schedule_id' => $scheduleDay->id, - 'user_dish_id' => $user->userDishes->random()->id, - 'user_id' => $user->id, - ])); + ->create([ + 'schedule_id' => $scheduleDay->id, + 'user_dish_id' => $user->userDishes->random()->id, + 'user_id' => $user->id, + ])); ScheduledUserDish::factory() ->schedule($scheduleDay) @@ -235,7 +234,6 @@ public function test_fixed_recurrence_takes_precedence_during_overwrite(): void $this->assertDatabaseCount(Schedule::class, 1); $this->assertDatabaseCount(ScheduledUserDish::class, 2); - $this ->actingAs($planner) ->post(route('api.schedule.generate'), [ diff --git a/tests/Feature/Schedule/ListScheduleTest.php b/tests/Feature/Schedule/ListScheduleTest.php index 4f24cb1..2168c6d 100644 --- a/tests/Feature/Schedule/ListScheduleTest.php +++ b/tests/Feature/Schedule/ListScheduleTest.php @@ -38,6 +38,7 @@ public function test_full_calendar_dishes_list_for_a_given_date_range(): void $schedule = Schedule::factory()->planner($planner)->date($date)->create(); $users->each(function (User $user) use ($schedule) { $randomUserDish = $user->userDishes->random(); + return $schedule->scheduledUserDishes()->create([ 'user_dish_id' => $randomUserDish->id, 'user_id' => $randomUserDish->user->id, @@ -80,6 +81,7 @@ public function test_it_does_not_show_dishes_of_other_planner(): void $schedule = Schedule::factory()->planner($otherPlanner)->date($date)->create(); $users->each(function (User $user) use ($schedule) { $randomUserDish = $user->userDishes->random(); + return $schedule->scheduledUserDishes()->create([ 'user_dish_id' => $randomUserDish->id, 'user_id' => $randomUserDish->user->id, diff --git a/tests/Feature/Schedule/UpdateScheduleTest.php b/tests/Feature/Schedule/UpdateScheduleTest.php index 1d770b6..d60e367 100644 --- a/tests/Feature/Schedule/UpdateScheduleTest.php +++ b/tests/Feature/Schedule/UpdateScheduleTest.php @@ -33,11 +33,11 @@ public function test_user_can_mark_day_as_skipped(): void $dishes->each(fn (Dish $dish) => $dish->users()->attach($users)); ScheduledUserDish::factory() ->schedule($schedule) - ->userDish($dishes->random()->userDishes->random()) + ->userDish($userOne->userDishes->firstOrFail()) ->create([]); ScheduledUserDish::factory() ->schedule($schedule) - ->userDish($dishes->random()->userDishes->random()) + ->userDish($userTwo->userDishes->firstOrFail()) ->create([]); $schedule->refresh(); diff --git a/tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php b/tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php index 67626de..04caad3 100644 --- a/tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php +++ b/tests/Feature/ScheduledUserDish/CreateScheduledUserDishTest.php @@ -75,7 +75,6 @@ public function test_planner_can_schedule_user_dishes(): void ->where('errors', null) ); - $this->assertDatabaseCount(Schedule::class, 1); $this->assertDatabaseHas(Schedule::class, [ 'date' => $scheduleDate, @@ -116,11 +115,10 @@ public function test_planner_cannot_schedule_user_dishes_from_other_planner(): v ->where('success', false) ->whereNull('payload') ->where('errors', [ - "This action is unauthorized." + 'This action is unauthorized.', ]) ); - $this->assertDatabaseEmpty(Schedule::class); $this->assertDatabaseEmpty(ScheduledUserDish::class); } diff --git a/tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php b/tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php index 4527623..ad6194f 100755 --- a/tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php +++ b/tests/Feature/ScheduledUserDish/DeleteScheduledUserDishTest.php @@ -79,7 +79,7 @@ public function test_planner_cannot_delete_a_scheduled_dish_of_another_planner() ->where('success', false) ->where('payload', null) ->where('errors', [ - "This action is unauthorized." + 'This action is unauthorized.', ]) ); diff --git a/tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php b/tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php index 5571d3f..6c7f5e4 100644 --- a/tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php +++ b/tests/Feature/ScheduledUserDish/ReadScheduledUserDishTest.php @@ -129,7 +129,7 @@ public function test_planner_cannot_read_scheduled_user_dish_from_other_planner( ->where('success', false) ->where('payload', null) ->where('errors', [ - "This action is unauthorized." + 'This action is unauthorized.', ]) ); } diff --git a/tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php b/tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php index 68953dc..6a00c5d 100644 --- a/tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php +++ b/tests/Feature/ScheduledUserDish/UpdateScheduledUserDishTest.php @@ -16,9 +16,9 @@ class UpdateScheduledUserDishTest extends TestCase { + use DishesTestTrait; use HasPlanner; use RefreshDatabase; - use DishesTestTrait; use ScheduledDishesTestTrait; protected function setUp(): void @@ -126,7 +126,7 @@ public function test_planner_cannot_update_dish_of_other_planner(): void ->where('success', false) ->where('payload', null) ->where('errors', [ - "This action is unauthorized." + 'This action is unauthorized.', ]) ); } diff --git a/tests/Feature/User/CreateUserTest.php b/tests/Feature/User/CreateUserTest.php index 76be687..7eaca58 100644 --- a/tests/Feature/User/CreateUserTest.php +++ b/tests/Feature/User/CreateUserTest.php @@ -2,7 +2,6 @@ namespace Tests\Feature\User; -use App\Models\Planner; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Testing\Fluent\AssertableJson; diff --git a/tests/Feature/User/DeleteUserTest.php b/tests/Feature/User/DeleteUserTest.php index b67e55d..7acfac6 100644 --- a/tests/Feature/User/DeleteUserTest.php +++ b/tests/Feature/User/DeleteUserTest.php @@ -52,7 +52,7 @@ public function test_planner_cannot_update_user_of_other_planner(): void ->assertJson(fn (AssertableJson $json) => $json ->where('success', false) ->where('payload', null) - ->where('errors', ["MODEL_NOT_FOUND"]) + ->where('errors', ['MODEL_NOT_FOUND']) ); } } diff --git a/tests/Feature/User/Dish/ListUserDishesTest.php b/tests/Feature/User/Dish/ListUserDishesTest.php index 8515193..a2dd542 100644 --- a/tests/Feature/User/Dish/ListUserDishesTest.php +++ b/tests/Feature/User/Dish/ListUserDishesTest.php @@ -5,7 +5,6 @@ use App\Models\Dish; use App\Models\Planner; use App\Models\User; -use App\Models\UserDish; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Testing\Fluent\AssertableJson; use Tests\TestCase; @@ -62,5 +61,4 @@ public function test_planner_cannot_see_user_dishes_from_other_planner(): void ->where('errors', null) ); } - } diff --git a/tests/Feature/User/Dish/RemoveDishesForUserTest.php b/tests/Feature/User/Dish/RemoveDishesForUserTest.php index 2f7e15a..6dce0f4 100755 --- a/tests/Feature/User/Dish/RemoveDishesForUserTest.php +++ b/tests/Feature/User/Dish/RemoveDishesForUserTest.php @@ -36,7 +36,7 @@ public function test_it_can_remove_dish_for_a_user(): void ->actingAs($planner) ->delete(route('api.users.dishes.destroy', [ 'dish' => $dish, - 'user' => $user + 'user' => $user, ]), []) ->assertStatus(200) ->assertJson(fn (AssertableJson $json) => $json diff --git a/tests/Feature/User/Dish/ShowUserDishTest.php b/tests/Feature/User/Dish/ShowUserDishTest.php index b1e294f..1c75afa 100644 --- a/tests/Feature/User/Dish/ShowUserDishTest.php +++ b/tests/Feature/User/Dish/ShowUserDishTest.php @@ -5,7 +5,6 @@ use App\Models\Dish; use App\Models\Planner; use App\Models\User; -use App\Models\UserDish; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Testing\Fluent\AssertableJson; use Tests\TestCase; diff --git a/tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php b/tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php index 205f634..673db96 100755 --- a/tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php +++ b/tests/Feature/User/Dish/StoreRecurrenceForUserDishTest.php @@ -62,7 +62,7 @@ public function test_it_adds_fixed_recurrence_to_user_dish(): void ->has('id') ->where('type', $recurrenceType) ->where('value', $recurrenceValue) - ) + ) ) ) ->where('errors', null) @@ -85,12 +85,12 @@ public function test_it_adds_minimum_recurrence_to_user_dish(): void ->actingAs($planner) ->post(route('api.users.dishes.recurrences.store', [ 'dish' => $dish, - 'user' => $user + 'user' => $user, ]), [ [ 'type' => $recurrenceType, 'value' => $recurrenceValue, - ] + ], ]) ->assertStatus(200) ->assertJson(fn (AssertableJson $json) => $json @@ -128,16 +128,16 @@ public function test_it_adds_multiple_recurrences_to_user_dish(): void ->actingAs($planner) ->post(route('api.users.dishes.recurrences.store', [ 'dish' => $dish, - 'user' => $user + 'user' => $user, ]), [ - [ - 'type' => MinimumRecurrence::class, - 'value' => 5, - ], - [ - 'type' => WeeklyRecurrence::class, - 'value' => WeekdaysEnum::Thursday->value, - ], + [ + 'type' => MinimumRecurrence::class, + 'value' => 5, + ], + [ + 'type' => WeeklyRecurrence::class, + 'value' => WeekdaysEnum::Thursday->value, + ], ]) ->assertStatus(200) ->assertJson(fn (AssertableJson $json) => $json @@ -187,7 +187,7 @@ public function test_it_removes_all_recurrences(): void ->actingAs($planner) ->post(route('api.users.dishes.recurrences.store', [ 'dish' => $dish, - 'user' => $user + 'user' => $user, ]), []) ->assertStatus(200) ->assertJson(fn (AssertableJson $json) => $json @@ -241,7 +241,7 @@ public function test_it_removes_other_recurrences_to_user_dish(): void ->actingAs($planner) ->post(route('api.users.dishes.recurrences.store', [ 'dish' => $dish, - 'user' => $user + 'user' => $user, ]), [ [ 'type' => WeeklyRecurrence::class, diff --git a/tests/Feature/User/UpdateUserTest.php b/tests/Feature/User/UpdateUserTest.php index 20c5e20..27f5f77 100644 --- a/tests/Feature/User/UpdateUserTest.php +++ b/tests/Feature/User/UpdateUserTest.php @@ -60,7 +60,7 @@ public function test_planner_cannot_update_user_of_other_planner(): void ->assertJson(fn (AssertableJson $json) => $json ->where('success', false) ->where('payload', null) - ->where('errors', ["MODEL_NOT_FOUND"]) + ->where('errors', ['MODEL_NOT_FOUND']) ); } } diff --git a/tests/Traits/DishesTestTrait.php b/tests/Traits/DishesTestTrait.php index 29d330d..a3475ee 100644 --- a/tests/Traits/DishesTestTrait.php +++ b/tests/Traits/DishesTestTrait.php @@ -9,7 +9,7 @@ trait DishesTestTrait { - public function generateDishes(Planner $planner, int $count = null): Collection + public function generateDishes(Planner $planner, ?int $count = null): Collection { if (is_null($count)) { $count = rand(15, 20); diff --git a/tests/Traits/ScheduledDishesTestTrait.php b/tests/Traits/ScheduledDishesTestTrait.php index 0690289..4a0026b 100644 --- a/tests/Traits/ScheduledDishesTestTrait.php +++ b/tests/Traits/ScheduledDishesTestTrait.php @@ -30,11 +30,11 @@ public function generateScheduledDishes(Planner $planner, ?CarbonPeriod $period $users ->each(fn (User $user) => ScheduledUserDish::factory() - ->schedule($schedule) - ->user($user) - ->userDish($user->userDishes->random()) - ->create() - ); + ->schedule($schedule) + ->user($user) + ->userDish($user->userDishes->random()) + ->create() + ); }); } } diff --git a/tests/Unit/Actions/EditUserActionTest.php b/tests/Unit/Actions/EditUserActionTest.php index 464cca9..6e0b543 100644 --- a/tests/Unit/Actions/EditUserActionTest.php +++ b/tests/Unit/Actions/EditUserActionTest.php @@ -25,7 +25,7 @@ protected function setUp(): void $planner = Planner::factory()->create(); $this->planner = $planner; - $this->action = new EditUserAction(); + $this->action = new EditUserAction; } public function test_successfully_updates_user_name(): void diff --git a/tests/Unit/Actions/RegenerateScheduleDayActionTest.php b/tests/Unit/Actions/RegenerateScheduleDayActionTest.php index c451b8c..d13898f 100644 --- a/tests/Unit/Actions/RegenerateScheduleDayActionTest.php +++ b/tests/Unit/Actions/RegenerateScheduleDayActionTest.php @@ -13,9 +13,9 @@ class RegenerateScheduleDayActionTest extends TestCase { + use DishesTestTrait; use HasPlanner; use RefreshDatabase; - use DishesTestTrait; protected function setUp(): void { @@ -36,7 +36,6 @@ public function test_it_regenerates_for_a_single_schedule(): void $mockAction = $this->mock(RegenerateScheduleDayForUserAction::class); $mockAction->shouldReceive('execute')->times(10); - resolve(RegenerateScheduleDayAction::class)->execute($planner, $schedule, true); } } diff --git a/tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php b/tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php index 6facf19..1a35c3b 100644 --- a/tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php +++ b/tests/Unit/Actions/RegenerateScheduleDayForUserActionTest.php @@ -34,10 +34,8 @@ public function test_it_creates(): void $this->assertEmpty($schedule->scheduledUserDishes); - resolve(RegenerateScheduleDayForUserAction::class)->execute($planner, $schedule, $user, true); - $expectedSchedule = Schedule::where('date', $date->format('Y-m-d'))->first(); $this->assertCount(1, $expectedSchedule->scheduledUserDishes); @@ -64,10 +62,8 @@ public function test_it_updates_if_overwrite_is_true(): void $this->assertCount(1, $schedule->refresh()->scheduledUserDishes); - resolve(RegenerateScheduleDayForUserAction::class)->execute($planner, $schedule, $user, true); - $schedule->refresh(); $this->assertCount(1, $schedule->scheduledUserDishes); @@ -95,10 +91,8 @@ public function test_it_does_not_update_if_overwrite_is_false(): void $this->assertCount(1, $schedule->refresh()->scheduledUserDishes); - resolve(RegenerateScheduleDayForUserAction::class)->execute($planner, $schedule, $user, false); - $schedule->refresh(); $this->assertCount(1, $schedule->scheduledUserDishes); diff --git a/tests/Unit/Actions/User/CreateUserActionTest.php b/tests/Unit/Actions/User/CreateUserActionTest.php index f899a58..d2247fa 100644 --- a/tests/Unit/Actions/User/CreateUserActionTest.php +++ b/tests/Unit/Actions/User/CreateUserActionTest.php @@ -14,13 +14,14 @@ class CreateUserActionTest extends TestCase use RefreshDatabase; private CreateUserAction $action; + private Planner $planner; protected function setUp(): void { parent::setUp(); - $this->action = new CreateUserAction(); - + $this->action = new CreateUserAction; + // Create a planner for testing $this->planner = Planner::factory()->create(); } @@ -58,9 +59,9 @@ public function test_it_throws_exception_when_name_is_empty(): void // Act & Assert $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Name is required'); - + $this->action->execute($userData); - + // Verify no user was created $this->assertDatabaseMissing('users', [ 'planner_id' => $this->planner->id, @@ -77,7 +78,7 @@ public function test_it_throws_exception_when_name_is_missing(): void // Act & Assert $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Name is required'); - + $this->action->execute($userData); } @@ -92,7 +93,7 @@ public function test_it_throws_exception_when_planner_id_is_empty(): void // Act & Assert $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Planner ID is required'); - + $this->action->execute($userData); } @@ -106,7 +107,7 @@ public function test_it_throws_exception_when_planner_id_is_missing(): void // Act & Assert $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Planner ID is required'); - + $this->action->execute($userData); } @@ -128,13 +129,19 @@ public function test_it_logs_creation_process(): void 'name' => 'Test User', 'planner_id' => $this->planner->id, ]); - + Log::shouldHaveReceived('info') ->with('CreateUserAction: User successfully created', [ 'user_id' => $user->id, 'name' => 'Test User', 'planner_id' => $this->planner->id, ]); + + $this->assertDatabaseHas('users', [ + 'id' => $user->id, + 'name' => 'Test User', + 'planner_id' => $this->planner->id, + ]); } public function test_it_handles_database_transaction_rollback_on_failure(): void @@ -147,7 +154,7 @@ public function test_it_handles_database_transaction_rollback_on_failure(): void // Act & Assert $this->expectException(\Exception::class); - + try { $this->action->execute($userData); } catch (\Exception $e) { @@ -174,15 +181,17 @@ public function test_it_logs_errors_on_failure(): void } catch (\Exception $e) { // Expected } - + // Assert Log::shouldHaveReceived('error') ->with('CreateUserAction: User creation failed', \Mockery::on(function ($data) { - return $data['name'] === 'Test User' && + return $data['name'] === 'Test User' && $data['planner_id'] === 999999 && isset($data['error']) && isset($data['trace']); })); + + $this->assertDatabaseMissing('users', ['name' => 'Test User']); } public function test_it_creates_user_with_whitespace_trimmed_name(): void @@ -212,7 +221,7 @@ public function test_it_can_create_multiple_users_with_same_planner(): void 'name' => 'User One', 'planner_id' => $this->planner->id, ]; - + $userData2 = [ 'name' => 'User Two', 'planner_id' => $this->planner->id, @@ -235,4 +244,4 @@ protected function tearDown(): void \Mockery::close(); parent::tearDown(); } -} \ No newline at end of file +} diff --git a/tests/Unit/Actions/User/DeleteUserActionTest.php b/tests/Unit/Actions/User/DeleteUserActionTest.php index 1363176..9fd46aa 100644 --- a/tests/Unit/Actions/User/DeleteUserActionTest.php +++ b/tests/Unit/Actions/User/DeleteUserActionTest.php @@ -16,13 +16,14 @@ class DeleteUserActionTest extends TestCase use RefreshDatabase; private DeleteUserAction $action; + private Planner $planner; protected function setUp(): void { parent::setUp(); - $this->action = new DeleteUserAction(); - + $this->action = new DeleteUserAction; + // Create a planner for testing $this->planner = Planner::factory()->create(); } @@ -32,7 +33,7 @@ public function test_it_can_delete_a_user_successfully(): void // Arrange $user = User::factory()->create([ 'planner_id' => $this->planner->id, - 'name' => 'Test User' + 'name' => 'Test User', ]); $userId = $user->id; @@ -49,19 +50,19 @@ public function test_it_can_delete_a_user_with_associated_dishes(): void // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); $dish = Dish::factory()->create(['planner_id' => $this->planner->id]); - + // Associate user with dish UserDish::create([ 'user_id' => $user->id, - 'dish_id' => $dish->id + 'dish_id' => $dish->id, ]); - + $userId = $user->id; - + // Verify the association exists $this->assertDatabaseHas('user_dishes', [ 'user_id' => $userId, - 'dish_id' => $dish->id + 'dish_id' => $dish->id, ]); // Act @@ -94,19 +95,21 @@ public function test_it_logs_deletion_process(): void 'user_name' => $userName, 'planner_id' => $this->planner->id, ]); - + Log::shouldHaveReceived('info') ->with('DeleteUserAction: User successfully deleted', [ 'user_id' => $userId, 'user_name' => $userName, ]); + + $this->assertDatabaseMissing('users', ['id' => $userId]); } public function test_it_handles_database_transaction_rollback_on_failure(): void { // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); - + // Mock the user to throw an exception during deletion $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); @@ -119,9 +122,9 @@ public function test_it_handles_database_transaction_rollback_on_failure(): void // Act & Assert $this->expectException(\Exception::class); $this->expectExceptionMessage('Database error'); - + $this->action->execute($mockUser); - + // Verify original user still exists (transaction rolled back) $this->assertDatabaseHas('users', ['id' => $user->id]); } @@ -130,7 +133,7 @@ public function test_it_throws_exception_when_deletion_returns_false(): void { // Arrange $user = User::factory()->create(['planner_id' => $this->planner->id]); - + // Mock the user to return false on delete $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); @@ -143,13 +146,13 @@ public function test_it_throws_exception_when_deletion_returns_false(): void // Act & Assert $this->expectException(\Exception::class); $this->expectExceptionMessage('User deletion returned false'); - + $this->action->execute($mockUser); } public function test_it_throws_exception_when_deletion_does_not_persist(): void { - // This test is tricky to implement realistically since we can't easily + // This test is tricky to implement realistically since we can't easily // mock the User::find() call in a way that makes sense. // We'll skip this edge case for now, but in a real scenario you might // want to test database connection issues, etc. @@ -161,7 +164,7 @@ public function test_it_logs_errors_on_failure(): void // Arrange Log::spy(); $user = User::factory()->create(['planner_id' => $this->planner->id]); - + // Mock the user to throw an exception during deletion $mockUser = \Mockery::mock(User::class); $mockUser->shouldReceive('getAttribute')->with('id')->andReturn($user->id); @@ -177,14 +180,16 @@ public function test_it_logs_errors_on_failure(): void } catch (\Exception $e) { // Expected } - + // Assert Log::shouldHaveReceived('error') ->with('DeleteUserAction: User deletion failed', \Mockery::on(function ($data) use ($user) { - return $data['user_id'] === $user->id && + return $data['user_id'] === $user->id && $data['error'] === 'Test error' && isset($data['trace']); })); + + $this->assertDatabaseHas('users', ['id' => $user->id]); } protected function tearDown(): void @@ -192,4 +197,4 @@ protected function tearDown(): void \Mockery::close(); parent::tearDown(); } -} \ No newline at end of file +} diff --git a/tests/Unit/Actions/UserActionIntegrationTest.php b/tests/Unit/Actions/UserActionIntegrationTest.php index d5daa2d..f4aef83 100644 --- a/tests/Unit/Actions/UserActionIntegrationTest.php +++ b/tests/Unit/Actions/UserActionIntegrationTest.php @@ -4,6 +4,7 @@ use App\Actions\User\CreateUserAction; use App\Actions\User\DeleteUserAction; +use App\Models\Dish; use App\Models\Planner; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -14,16 +15,18 @@ class UserActionIntegrationTest extends TestCase use RefreshDatabase; private Planner $planner; + private CreateUserAction $createAction; + private DeleteUserAction $deleteAction; protected function setUp(): void { parent::setUp(); - + $this->planner = Planner::factory()->create(); - $this->createAction = new CreateUserAction(); - $this->deleteAction = new DeleteUserAction(); + $this->createAction = new CreateUserAction; + $this->deleteAction = new DeleteUserAction; } public function test_complete_user_lifecycle_with_actions(): void @@ -33,9 +36,9 @@ public function test_complete_user_lifecycle_with_actions(): void 'name' => 'Integration Test User', 'planner_id' => $this->planner->id, ]; - + $user = $this->createAction->execute($userData); - + $this->assertInstanceOf(User::class, $user); $this->assertEquals('Integration Test User', $user->name); $this->assertEquals($this->planner->id, $user->planner_id); @@ -44,11 +47,11 @@ public function test_complete_user_lifecycle_with_actions(): void 'name' => 'Integration Test User', 'planner_id' => $this->planner->id, ]); - + // Test deletion $userId = $user->id; $result = $this->deleteAction->execute($user); - + $this->assertTrue($result); $this->assertDatabaseMissing('users', ['id' => $userId]); } @@ -60,28 +63,28 @@ public function test_creating_and_deleting_user_with_relationships(): void 'name' => 'User With Relationships', 'planner_id' => $this->planner->id, ]); - + // Create a dish and associate it with the user - $dish = \App\Models\Dish::factory()->create(['planner_id' => $this->planner->id]); + $dish = Dish::factory()->create(['planner_id' => $this->planner->id]); $user->dishes()->attach($dish->id); - + // Verify the relationship exists $this->assertEquals(1, $user->dishes()->count()); $this->assertDatabaseHas('user_dishes', [ 'user_id' => $user->id, 'dish_id' => $dish->id, ]); - + // Delete the user $userId = $user->id; $result = $this->deleteAction->execute($user); - + // Verify deletion and cascade $this->assertTrue($result); $this->assertDatabaseMissing('users', ['id' => $userId]); $this->assertDatabaseMissing('user_dishes', ['user_id' => $userId]); - + // Dish should still exist $this->assertDatabaseHas('dishes', ['id' => $dish->id]); } -} \ No newline at end of file +} diff --git a/tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php b/tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php index f3faa44..a41ef24 100644 --- a/tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php +++ b/tests/Unit/Schedule/Actions/ClearScheduleForMonthActionTest.php @@ -24,7 +24,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->action = new ClearScheduleForMonthAction(); + $this->action = new ClearScheduleForMonthAction; } public function test_clears_scheduled_user_dishes_for_month(): void @@ -38,7 +38,7 @@ public function test_clears_scheduled_user_dishes_for_month(): void $year = 2026; $daysInMonth = Carbon::createFromDate($year, $month, 1)->daysInMonth; - (new GenerateScheduleForMonthAction())->execute($planner, $month, $year, [$user->id]); + (new GenerateScheduleForMonthAction)->execute($planner, $month, $year, [$user->id]); $this->assertEquals($daysInMonth, ScheduledUserDish::where('user_id', $user->id)->count()); @@ -60,7 +60,7 @@ public function test_only_clears_specified_users(): void $year = 2026; $daysInMonth = Carbon::createFromDate($year, $month, 1)->daysInMonth; - (new GenerateScheduleForMonthAction())->execute($planner, $month, $year, [$user1->id, $user2->id]); + (new GenerateScheduleForMonthAction)->execute($planner, $month, $year, [$user1->id, $user2->id]); $this->assertEquals($daysInMonth * 2, ScheduledUserDish::whereIn('user_id', [$user1->id, $user2->id])->count()); $this->action->execute($planner, $month, $year, [$user1->id]); @@ -80,8 +80,8 @@ public function test_does_not_affect_other_months(): void $janDays = Carbon::createFromDate($year, 1, 1)->daysInMonth; $febDays = Carbon::createFromDate($year, 2, 1)->daysInMonth; - (new GenerateScheduleForMonthAction())->execute($planner, 1, $year, [$user->id]); - (new GenerateScheduleForMonthAction())->execute($planner, 2, $year, [$user->id]); + (new GenerateScheduleForMonthAction)->execute($planner, 1, $year, [$user->id]); + (new GenerateScheduleForMonthAction)->execute($planner, 2, $year, [$user->id]); $this->assertEquals($janDays + $febDays, ScheduledUserDish::where('user_id', $user->id)->count()); diff --git a/tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php b/tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php index 7635ec8..7c55a84 100644 --- a/tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php +++ b/tests/Unit/Schedule/Actions/DraftScheduleForDateActionTest.php @@ -35,12 +35,11 @@ public function test_user_can_draft_schedule(): void $schedule = Schedule::create([ 'planner_id' => $planner->id, - 'date' => now()->addDay() + 'date' => now()->addDay(), ]); resolve(DraftScheduleForDateAction::class)->execute($schedule); - $this->assertDatabaseCount(Schedule::class, $expectedScheduleCount); $this->assertDatabaseCount(ScheduledUserDish::class, $expectedScheduleCount * User::all()->count()); } diff --git a/tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php b/tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php index e30758e..ddedb87 100644 --- a/tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php +++ b/tests/Unit/Schedule/Actions/DraftScheduleForPeriodActionTest.php @@ -14,9 +14,9 @@ class DraftScheduleForPeriodActionTest extends TestCase { + use DishesTestTrait; use HasPlanner; use RefreshDatabase; - use DishesTestTrait; protected function setUp(): void { @@ -35,11 +35,9 @@ public function test_user_can_generate_schedule(): void $this->assertDatabaseCount(Schedule::class, 0); - resolve(DraftScheduleForPeriodAction::class) ->execute($planner, CarbonPeriod::create(now()->addDay(), now()->addDays($expectedPeriodScheduleCount))); - $this->assertDatabaseCount(Schedule::class, $expectedPeriodScheduleCount); $this->assertDatabaseCount(ScheduledUserDish::class, $expectedPeriodScheduleCount * User::all()->count()); } diff --git a/tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php b/tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php index 16714fe..77b5fd6 100644 --- a/tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php +++ b/tests/Unit/Schedule/Actions/GenerateScheduleForMonthActionTest.php @@ -23,7 +23,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->action = new GenerateScheduleForMonthAction(); + $this->action = new GenerateScheduleForMonthAction; } public function test_generates_schedule_for_entire_month(): void diff --git a/tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php b/tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php index 89e0fbf..e297016 100644 --- a/tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php +++ b/tests/Unit/Schedule/Actions/RegenerateScheduleForDateForUsersActionTest.php @@ -23,7 +23,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->action = new RegenerateScheduleForDateForUsersAction(); + $this->action = new RegenerateScheduleForDateForUsersAction; } public function test_regenerates_schedule_for_single_date(): void diff --git a/tests/Unit/Schedule/ScheduleGeneratorTest.php b/tests/Unit/Schedule/ScheduleGeneratorTest.php index db24196..fa2d65d 100644 --- a/tests/Unit/Schedule/ScheduleGeneratorTest.php +++ b/tests/Unit/Schedule/ScheduleGeneratorTest.php @@ -4,7 +4,6 @@ use App\Models\Dish; use App\Models\MinimumRecurrence; -use App\Models\Planner; use App\Models\Schedule; use App\Models\User; use App\Models\UserDish; @@ -40,7 +39,7 @@ public function test_it_fills_up_the_next_2_weeks(): void $this->assertDatabaseEmpty(Schedule::class); - (new ScheduleGenerator())->generate($planner); + (new ScheduleGenerator)->generate($planner); $schedules = Schedule::all(); $this->assertTrue($schedules->isNotEmpty()); @@ -81,9 +80,7 @@ public function test_it_takes_weekly_recurrences_into_account(): void $this->assertDatabaseEmpty(Schedule::class); - - (new ScheduleGenerator())->generate($planner); - + (new ScheduleGenerator)->generate($planner); $this->assertTrue(Schedule::all()->isNotEmpty()); @@ -127,9 +124,7 @@ public function test_it_takes_minimum_recurrences_into_account(): void $this->assertDatabaseEmpty(Schedule::class); - - (new ScheduleGenerator())->generate($planner); - + (new ScheduleGenerator)->generate($planner); $this->assertTrue(Schedule::all()->isNotEmpty()); diff --git a/tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php b/tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php index 6e6c841..6c11d28 100644 --- a/tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php +++ b/tests/Unit/Schedule/Services/ScheduleCalendarServiceTest.php @@ -23,7 +23,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->service = new ScheduleCalendarService(); + $this->service = new ScheduleCalendarService; } public function test_returns_31_calendar_days(): void diff --git a/tests/Unit/ScheduleRepositoryTest.php b/tests/Unit/ScheduleRepositoryTest.php index b0f93c5..3e151d4 100644 --- a/tests/Unit/ScheduleRepositoryTest.php +++ b/tests/Unit/ScheduleRepositoryTest.php @@ -28,7 +28,7 @@ public function test_find_or_create_finds_existing_model(): void $this->assertDatabaseCount(Schedule::class, 1); - $schedule = (new ScheduleRepository())->findOrCreate($planner, $date); + $schedule = (new ScheduleRepository)->findOrCreate($planner, $date); $this->assertDatabaseCount(Schedule::class, 1); $this->assertEquals($date, $schedule->date); @@ -41,7 +41,7 @@ public function test_find_or_create_creates_new_schedule_for_date(): void $this->assertDatabaseEmpty(Schedule::class); - $schedule = (new ScheduleRepository())->findOrCreate($planner, $date); + $schedule = (new ScheduleRepository)->findOrCreate($planner, $date); $this->assertDatabaseCount(Schedule::class, 1); $this->assertEquals($date, $schedule->date); diff --git a/tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php b/tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php index c452a01..2dd0e2a 100644 --- a/tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php +++ b/tests/Unit/ScheduledUserDish/Actions/DeleteScheduledUserDishForDateActionTest.php @@ -23,7 +23,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->action = new DeleteScheduledUserDishForDateAction(); + $this->action = new DeleteScheduledUserDishForDateAction; } public function test_deletes_scheduled_user_dish(): void diff --git a/tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php b/tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php index 2a592c6..ab45dc1 100644 --- a/tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php +++ b/tests/Unit/ScheduledUserDish/Actions/SkipScheduledUserDishForDateActionTest.php @@ -23,7 +23,7 @@ protected function setUp(): void { parent::setUp(); $this->setUpHasPlanner(); - $this->action = new SkipScheduledUserDishForDateAction(); + $this->action = new SkipScheduledUserDishForDateAction; } public function test_skips_scheduled_user_dish(): void diff --git a/tests/Unit/UpdateScheduledUserDishActionTest.php b/tests/Unit/UpdateScheduledUserDishActionTest.php index b87f351..0d41f72 100644 --- a/tests/Unit/UpdateScheduledUserDishActionTest.php +++ b/tests/Unit/UpdateScheduledUserDishActionTest.php @@ -27,7 +27,7 @@ public function test_dish_of_scheduled_user_dish_can_be_updated(): void $schedule = Schedule::factory()->planner($planner)->create(); $scheduledUserDish = ScheduledUserDish::factory()->schedule($schedule)->userDish($userDish)->create(); - (new UpdateScheduledUserDishAction())->execute($scheduledUserDish, $otherUserDish); + (new UpdateScheduledUserDishAction)->execute($scheduledUserDish, $otherUserDish); $scheduledUserDish->refresh(); $this->assertEquals($otherUserDish->id, $scheduledUserDish->user_dish_id); diff --git a/tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php b/tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php index 3e904e0..18b0011 100644 --- a/tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php +++ b/tests/Unit/UserDish/Repositories/UserDishRepositoryTest.php @@ -45,12 +45,10 @@ public function test_find_interfering_dishes(): void ScheduledUserDish::factory()->userDish($userDishRecurring)->schedule($schedule)->create(); $this->actingAs($planner); - /** UserDishRepository $userDishRepository */ $userDishRepository = resolve(UserDishRepository::class); $userDishes = $userDishRepository->findInterferingUserDishes($user, $date); - $this->assertCount(1, $userDishes); $this->assertEquals($userDishRecurring->id, $userDishes->first()->id); } @@ -75,12 +73,10 @@ public function test_find_candidates_for_date(): void ScheduledUserDish::factory()->userDish($userDishRecurring)->schedule($schedule)->create(); $this->actingAs($planner); - /** UserDishRepository $userDishRepository */ $userDishRepository = resolve(UserDishRepository::class); $userDishes = $userDishRepository->findCandidatesForDate($user, $date); - $this->assertEquals($userDishes->pluck('id')->toArray(), [$userDishPlain->id]); } } From abac8509cb64c7a13362ddacc03cfb0d42d5814a Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 14:00:02 +0200 Subject: [PATCH 03/10] 46 - Add CI image and app image build workflows --- .forgejo/workflows/build.yml | 42 +++++++++++++++++++++++++++++++++ .forgejo/workflows/images.yml | 44 +++++++++++++++++++++++++++++++++++ .gitattributes | 2 -- docker/build/Dockerfile.ci | 30 ++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .forgejo/workflows/build.yml create mode 100644 .forgejo/workflows/images.yml create mode 100644 docker/build/Dockerfile.ci diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml new file mode 100644 index 0000000..ee637de --- /dev/null +++ b/.forgejo/workflows/build.yml @@ -0,0 +1,42 @@ +name: Build and Push Docker Image + +on: + push: + branches: [main] + tags: ['v*'] + +jobs: + build: + runs-on: docker + container: + image: catthehacker/ubuntu:act-latest + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Set up Docker Buildx + uses: https://data.forgejo.org/docker/setup-buildx-action@v3 + + - name: Login to Forgejo Registry + uses: https://data.forgejo.org/docker/login-action@v3 + with: + registry: forge.lvl0.xyz + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Determine tags + id: meta + run: | + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + TAG="${{ github.ref_name }}" + echo "tags=forge.lvl0.xyz/lvl0/dishplanner:${TAG},forge.lvl0.xyz/lvl0/dishplanner:latest" >> $GITHUB_OUTPUT + else + echo "tags=forge.lvl0.xyz/lvl0/dishplanner:latest" >> $GITHUB_OUTPUT + fi + + - name: Build and push + uses: https://data.forgejo.org/docker/build-push-action@v5 + with: + context: . + file: Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} diff --git a/.forgejo/workflows/images.yml b/.forgejo/workflows/images.yml new file mode 100644 index 0000000..37ec313 --- /dev/null +++ b/.forgejo/workflows/images.yml @@ -0,0 +1,44 @@ +name: Build and Push Base Images + +on: + push: + branches: [main] + paths: + - 'docker/build/**' + - '.forgejo/workflows/images.yml' + workflow_dispatch: + +jobs: + images: + runs-on: docker + container: + image: catthehacker/ubuntu:act-latest + strategy: + matrix: + include: + - name: dishplanner-ci + file: docker/build/Dockerfile.ci + version: php8.3-1 + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Set up Docker Buildx + uses: https://data.forgejo.org/docker/setup-buildx-action@v3 + + - name: Login to Forgejo Registry + uses: https://data.forgejo.org/docker/login-action@v3 + with: + registry: forge.lvl0.xyz + username: ${{ github.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build and push + uses: https://data.forgejo.org/docker/build-push-action@v5 + with: + context: . + file: ${{ matrix.file }} + push: true + tags: | + forge.lvl0.xyz/lvl0/${{ matrix.name }}:${{ matrix.version }} + forge.lvl0.xyz/lvl0/${{ matrix.name }}:latest + forge.lvl0.xyz/lvl0/${{ matrix.name }}:${{ github.sha }} diff --git a/.gitattributes b/.gitattributes index fcb21d3..1d7f82b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,6 +6,4 @@ *.md diff=markdown *.php diff=php -/.github export-ignore CHANGELOG.md export-ignore -.styleci.yml export-ignore diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci new file mode 100644 index 0000000..3c3c5ec --- /dev/null +++ b/docker/build/Dockerfile.ci @@ -0,0 +1,30 @@ +# CI image: PHP + Composer only, no runtime server or frontend toolchain. +# Tests run against SQLite in memory (see .env.testing), so no database client +# or cache extension is needed. +# +# Published as dishplanner-ci:php8.3-. Bump the revision in the tag +# and in .forgejo/workflows/ci.yml whenever this file changes (runners cache +# mutable tags and will not re-pull them). +# +# Debian-based rather than Alpine to avoid the DNS resolution timeouts against +# codeload.github.com that the Alpine base hit during composer install. + +FROM php:8.3-cli + +COPY --from=mlocati/php-extension-installer:2 /usr/bin/install-php-extensions /usr/local/bin/ + +RUN install-php-extensions \ + pdo_sqlite \ + mbstring \ + dom \ + xml \ + fileinfo \ + pcntl + +# git is needed by the checkout action; nodejs runs the Forgejo JavaScript +# actions (checkout, cache); unzip lets Composer extract dist archives. +RUN apt-get update \ + && apt-get install -y --no-install-recommends git unzip nodejs \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer From 1b6effb5d0f7d7da32a98be2631ceeec1127aac2 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 14:09:36 +0200 Subject: [PATCH 04/10] 47 - Professionalize documentation --- .gitignore | 1 - CONTRIBUTING.md | 97 + README.md | 12 +- composer.lock | 8923 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 9031 insertions(+), 2 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index ceebbb0..cc770c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/composer.lock /.phpunit.cache /coverage /node_modules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..908749c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,97 @@ +# Contributing + +Thanks for your interest in DishPlanner. It's a small self-hosted project — +issues and pull requests are both welcome. + +## Reporting issues + +Use [Issues](https://forge.lvl0.xyz/lvl0/dishplanner/issues). + +For bugs, include what you expected, what happened, and enough detail to +reproduce it. Relevant log output helps; `dev-logs` follows the application log. + +## Development setup + +Requires PHP 8.2+ and a container runtime (Podman or Docker). The development +environment runs in containers defined by `docker-compose.yml`. + +On NixOS, or anywhere with Nix installed: + +```bash +git clone https://forge.lvl0.xyz/lvl0/dishplanner.git +cd dishplanner +nix-shell +``` + +The shell prints the available commands on entry and can start the containers +for you: + +| Command | Description | +|---------|-------------| +| `dev-up` | Start the development environment | +| `dev-down` | Stop the development environment | +| `dev-restart` | Restart the containers | +| `dev-rebuild` | Full rebuild (removes volumes) | +| `dev-rebuild-quick` | Quick rebuild (keeps volumes) | +| `dev-logs [service]` | Follow logs | +| `dev-logs-db` | Tail database logs | +| `dev-shell` | Enter the app container | +| `dev-artisan ` | Run an artisan command | +| `dev-test [path]` | Run the PHPUnit suite the CI way | +| `dev-fix-permissions` | Fix Docker-created file permissions | + +Once running: + +| Service | URL | +|---------|-----| +| App | http://localhost:8000 | +| Vite | http://localhost:5173 | +| Mailhog | http://localhost:8025 | +| MariaDB | localhost:3306 | + +Without Nix, start the same containers directly from `docker-compose.yml`. +Contributions improving the setup instructions for other platforms are welcome. + +## Before opening a pull request + +Three checks run in CI, and all three must pass. Run them locally first, from +inside the app container or anywhere the project's dependencies are available: + +```bash +vendor/bin/pint --test # code style, Laravel preset +vendor/bin/phpstan analyse --memory-limit=1G # static analysis, level 7 +php -d memory_limit=512M vendor/bin/phpunit # PHPUnit on SQLite in memory +``` + +Or run the test suite the CI way with `dev-test`. + +Some conventions: + +- **Static analysis.** PHPStan runs at level 7 with a baseline + (`phpstan-baseline.neon`) covering pre-existing findings. Don't add baseline + entries to silence errors in code you're writing — fix the cause instead. The + baseline is for cases where the analyzer or an upstream docblock is wrong, + not real bugs. +- **Tests.** New behaviour needs a test. Tests run against SQLite in memory and + must not reach for the network. +- **Dependencies.** `composer.lock` is committed. If you change dependencies, + commit the updated lockfile alongside `composer.json`. + +## Commits + +One commit does one thing. Keep each commit passing all three checks so history +stays bisectable. Separate renames from behaviour changes, and mechanical edits +from logic. + +Commit messages are a single line, referencing the ticket they belong to: + +``` +45 - Add Forgejo CI workflow +``` + +No body, no trailers. + +## License + +By contributing, you agree that your contributions are licensed under the +[GNU AGPL-3.0](LICENSE.md), the same license as the project. diff --git a/README.md b/README.md index a233b72..e43934d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # 🍽️ Dish Planner +[![CI](https://forge.lvl0.xyz/lvl0/dishplanner/badges/workflows/ci.yml/badge.svg)](https://forge.lvl0.xyz/lvl0/dishplanner/actions) +[![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE.md) + A Laravel-based meal planning application that helps households organize and schedule their dishes among multiple users. Built with Laravel, Livewire, and FrankenPHP for a modern, single-container deployment. ## ✨ Features @@ -13,7 +16,7 @@ ## ✨ Features ## 🚀 Self-hosting -The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`. +The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`. See [CHANGELOG.md](CHANGELOG.md) before upgrading. ### docker-compose.yml @@ -102,8 +105,10 @@ #### Available Commands | `dev-rebuild` | Full rebuild (removes volumes) | | `dev-rebuild-quick` | Quick rebuild (keeps volumes) | | `dev-logs [service]` | Follow logs | +| `dev-logs-db` | Tail database logs | | `dev-shell` | Enter app container | | `dev-artisan ` | Run artisan commands | +| `dev-test [path]` | Run the PHPUnit suite the CI way | | `dev-fix-permissions` | Fix Docker-created file permissions | #### Services @@ -119,6 +124,11 @@ ### Other Platforms Contributions welcome for development setup instructions on other platforms. +## 🤝 Contributing + +Issues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for +the development setup, the checks that run in CI, and the commit conventions. + ## 📄 License This project is open-source software licensed under the [AGPL-3.0 license](LICENSE.md). diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..2a576e1 --- /dev/null +++ b/composer.lock @@ -0,0 +1,8923 @@ +{ + "_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": "2d432ae148aec8fd888017ec1261a7a3", + "packages": [ + { + "name": "brick/math", + "version": "0.14.8", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.14.8" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2026-02-10T14:33:43+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "shasum": "" + }, + "require": { + "php": "^8.2|^8.3|^8.4|^8.5" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2025-10-31T18:51:33+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" + }, + "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.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:43:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.15.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.5.2", + "guzzlehttp/psr7": "^2.13", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.15.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2026-08-05T19:48:21+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.5.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2823687acff28b2dbe67b2508a6b300e2c3fa4ce", + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.5.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2026-08-05T19:30:54+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.13.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-07-16T22:23:49+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.10", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/f6c24c21f42b990e9a58912b332d0874df6ba839", + "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.25" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.10" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2026-07-17T13:53:03+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.66.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "82a53323c701a668f9054cbeb1d6b6cdbb6a5e10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/82a53323c701a668f9054cbeb1d6b6cdbb6a5e10", + "reference": "82a53323c701a668f9054cbeb1d6b6cdbb6a5e10", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12|^0.13|^0.14", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.8.1", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.34", + "symfony/polyfill-php85": "^1.34", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.9.0", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.1.41", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0|^1.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2026-08-11T13:59:27+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.22", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/02b89b39e8972a998db4d5d4ad4719239dd4aee4", + "reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0|^8.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.22" + }, + "time": "2026-08-04T14:50:50+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v4.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "fee27a573d1a013af3721d86153a65e0b11927e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/fee27a573d1a013af3721d86153a65e0b11927e6", + "reference": "fee27a573d1a013af3721d86153a65e0b11927e6", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/contracts": "^11.0|^12.0|^13.0", + "illuminate/database": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "php": "^8.2", + "symfony/console": "^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2026-06-23T18:26:55+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.15", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661", + "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-07-21T16:49:22+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.11.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.11.1" + }, + "time": "2026-02-06T14:12:35+00:00" + }, + { + "name": "league/commonmark", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d2d1aa8b35e072966c89bc0c66cf926e56767dc4", + "reference": "d2d1aa8b35e072966c89bc0c66cf926e56767dc4", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^2.0.0", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.11-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2026-08-11T16:06:25+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.35.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "b277b5dc3d56650b68904117124e79c851e12376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376", + "reference": "b277b5dc3d56650b68904117124e79c851e12376", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.35.2" + }, + "time": "2026-07-06T14:42:07+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76", + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2026-07-09T11:49:27+00:00" + }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-08T20:05:35+00:00" + }, + { + "name": "livewire/livewire", + "version": "v3.8.4", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "bc06b755058e2253cc4153d95c8720c585f6254c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/bc06b755058e2253cc4153d95c8720c585f6254c", + "reference": "bc06b755058e2253cc4153d95c8720c585f6254c", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/routing": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^10.0|^11.0|^12.0|^13.0", + "laravel/prompts": "^0.1.24|^0.2|^0.3", + "league/mime-type-detection": "^1.9", + "php": "^8.1", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/http-kernel": "^6.2|^7.0|^8.0" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^10.15.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^8.21.0|^9.0|^10.0|^11.0", + "orchestra/testbench-dusk": "^8.24|^9.1|^10.0|^11.0", + "phpunit/phpunit": "^10.4|^11.5|^12.5", + "psy/psysh": "^0.11.22|^0.12" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Livewire": "Livewire\\Livewire" + }, + "providers": [ + "Livewire\\LivewireServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v3.8.4" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2026-08-10T15:24:05+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8 || ^2.0", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2026-01-02T08:56:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.13.2", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/a1c54919f5fff9800cd03c32bd01defd5a4061cb", + "reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbonphp.github.io/carbon/", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2026-08-08T11:40:35+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.5" + }, + "time": "2026-02-23T03:47:12+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.5", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/b043439dbdf954e6c28b5ea7e34b0100f83165e0", + "reference": "b043439dbdf954e6c28b5ea7e34b0100f83165e0", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::chr(), ord() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.5" + }, + "time": "2026-07-17T23:02:45+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.8.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" + }, + "time": "2026-07-04T14:30:18+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "require-dev": { + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "It's like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2026-02-16T23:10:27+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "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.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://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/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.24", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1", + "reference": "ca0fdcf8a7617afa3adfdf1b5fef573dffb69ca1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" + }, + "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.24" + }, + "time": "2026-06-29T15:41:09+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8", + "shasum": "" + }, + "require": { + "brick/math": ">=0.8.16 <=0.18", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.3" + }, + "time": "2026-06-18T03:57:49+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-24T13:12:05+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/f4c69c9aed03abf933b294257d618bdd9b30a06d", + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.16" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-31T12:37:14+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b75663ed96cf4756e28e3105476f220f92886cc4", + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-18T13:18:21+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "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": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/d49f6a19f326db41ae7103bdc38e3eb35a791261", + "reference": "d49f6a19f326db41ae7103bdc38e3eb35a791261", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-21T15:13:06+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/336e7f3b9e95aba04f93ea9143920c2186abfbb9", + "reference": "336e7f3b9e95aba04f93ea9143920c2186abfbb9", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-21T15:13:06+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "13b38720174286f55d1761152b575a8d1436fc25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/13b38720174286f55d1761152b575a8d1436fc25", + "reference": "13b38720174286f55d1761152b575a8d1436fc25", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.14" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-27T08:31:18+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "b676451bb638e99a7d34d8a2be90406822e301eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b676451bb638e99a7d34d8a2be90406822e301eb", + "reference": "b676451bb638e99a7d34d8a2be90406822e301eb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.4.16" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-08-07T11:50:27+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "f5e728670fa2218ae8be8ea91f2b44b7d6e5304c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f5e728670fa2218ae8be8ea91f2b44b7d6e5304c", + "reference": "f5e728670fa2218ae8be8ea91f2b44b7d6e5304c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12|^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.4.16" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-08-07T18:00:13+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "68c1f27c97edd0222eb8d440a6c8c4da5354ab46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/68c1f27c97edd0222eb8d440a6c8c4da5354ab46", + "reference": "68c1f27c97edd0222eb8d440a6c8c4da5354ab46", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-28T07:33:02+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "20094b76a7106dbe978d31cd3bd7aa1ed248d0e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/20094b76a7106dbe978d31cd3bd7aa1ed248d0e5", + "reference": "20094b76a7106dbe978d31cd3bd7aa1ed248d0e5", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.4.16" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-08-07T14:56:57+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "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.37.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": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "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 intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.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": "2026-07-28T08:25:59+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "dc21118016c039a66235cf93d96b435ffb282412" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T15:22:23+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.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": "2026-05-25T13:48:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "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.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "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.37.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": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/5ea99087fb99c273a9b9236ed4c31e78b16103c6", + "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6", + "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\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.41.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": "2026-07-01T12:47:55+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "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\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T12:51:13+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", + "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\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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 backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.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": "2026-07-01T12:47:55+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.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": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f5804be144caceb570f6747519999636b664f24c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-23T16:05:06+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/80c0a93d3f8e7499f716204a1fb38ead942a7a2b", + "reference": "80c0a93d3f8e7499f716204a1fb38ead942a7a2b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-21T15:13:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-16T09:55:08+00:00" + }, + { + "name": "symfony/string", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-28T07:33:02+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "501e0ff4553c744209ca1a68790d8a4541563710" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/501e0ff4553c744209ca1a68790d8a4541563710", + "reference": "501e0ff4553c744209ca1a68790d8a4541563710", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5.3|^3.3" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.4.16" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-30T12:37:26+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621", + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2676b524340abcfe4d6151ec698463cebafee439" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439", + "reference": "2676b524340abcfe4d6151ec698463cebafee439", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-30T15:19:22+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", + "reference": "04ba4add636a95ff437af3a5a9499bb1d6c6d4bd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12|^4.0" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-21T15:13:06+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" + }, + "time": "2025-12-02T11:56:42+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.4", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/416df702837983f8d5ff48c9c3fee4f5f57b980b", + "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.4", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" + }, + "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.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2026-07-06T19:11:50+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2026-04-26T05:33:54+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b61cd040da1a4925bc90a51c074f5297e7c0fa52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b61cd040da1a4925bc90a51c074f5297e7c0fa52", + "reference": "b61cd040da1a4925bc90a51c074f5297e7c0fa52", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v3.0.0" + }, + "time": "2026-03-17T11:56:53+00:00" + }, + { + "name": "iamcal/sql-parser", + "version": "v0.7", + "source": { + "type": "git", + "url": "https://github.com/iamcal/SQLParser.git", + "reference": "610392f38de49a44dab08dc1659960a29874c4b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8", + "reference": "610392f38de49a44dab08dc1659960a29874c4b8", + "shasum": "" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^1.0", + "phpunit/phpunit": "^5|^6|^7|^8|^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "iamcal\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Cal Henderson", + "email": "cal@iamcal.com" + } + ], + "description": "MySQL schema parser", + "support": { + "issues": "https://github.com/iamcal/SQLParser/issues", + "source": "https://github.com/iamcal/SQLParser/tree/v0.7" + }, + "time": "2026-01-28T22:20:33+00:00" + }, + { + "name": "larastan/larastan", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/larastan/larastan.git", + "reference": "2970f83398154178a739609c244577267c7ee8eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/larastan/larastan/zipball/2970f83398154178a739609c244577267c7ee8eb", + "reference": "2970f83398154178a739609c244577267c7ee8eb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "iamcal/sql-parser": "^0.7.0", + "illuminate/console": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/container": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/database": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/http": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13", + "illuminate/support": "^11.44.2 || ^12.4.1 || ^13", + "php": "^8.2", + "phpstan/phpstan": "^2.2.0" + }, + "require-dev": { + "doctrine/coding-standard": "^14", + "laravel/framework": "^11.44.2 || ^12.7.2 || ^13", + "mockery/mockery": "^1.6.12", + "nikic/php-parser": "^5.4", + "orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11", + "orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8 || ^13.1.8" + }, + "suggest": { + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench", + "phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Larastan\\Larastan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Can Vural", + "email": "can9119@gmail.com" + } + ], + "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "larastan", + "laravel", + "package", + "php", + "static analysis" + ], + "support": { + "issues": "https://github.com/larastan/larastan/issues", + "source": "https://github.com/larastan/larastan/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://github.com/canvural", + "type": "github" + } + ], + "time": "2026-05-28T08:00:58+00:00" + }, + { + "name": "laravel/dusk", + "version": "v8.6.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "e7fd48762c6a82ad2cd311db07587aa2a97ce143" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/e7fd48762c6a82ad2cd311db07587aa2a97ce143", + "reference": "e7fd48762c6a82ad2cd311db07587aa2a97ce143", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-zip": "*", + "guzzlehttp/guzzle": "^7.5", + "illuminate/console": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "php-webdriver/webdriver": "^1.15.2", + "symfony/console": "^6.2|^7.0|^8.0", + "symfony/finder": "^6.2|^7.0|^8.0", + "symfony/process": "^6.2|^7.0|^8.0", + "vlucas/phpdotenv": "^5.2" + }, + "require-dev": { + "laravel/framework": "^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.6", + "orchestra/testbench-core": "^8.19|^9.17|^10.8|^11.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.1|^11.0|^12.0.1", + "psy/psysh": "^0.11.12|^0.12", + "symfony/yaml": "^6.2|^7.0|^8.0" + }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/laravel/dusk/issues", + "source": "https://github.com/laravel/dusk/tree/v8.6.0" + }, + "time": "2026-04-15T14:50:40+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/2f7d27dada8effc48b8c424445a69cca7007daaa", + "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2026-05-20T22:24:57+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.30.5", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "fe4148c503a0e266353d61396b79bbf7f35122df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/fe4148c503a0e266353d61396b79bbf7f35122df", + "reference": "fe4148c503a0e266353d61396b79bbf7f35122df", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.3.0" + }, + "require-dev": { + "composer/semver": "^3.4.4", + "friendsofphp/php-cs-fixer": "^3.95.18", + "illuminate/view": "^13.24.0", + "larastan/larastan": "^3.10.0", + "laravel-zero/framework": "^13.0.0", + "laravel/agent-detector": "^2.0.2", + "laravel/prompts": "^0.3.22", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^4.7.8" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2026-08-10T15:35:50+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.66.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "ebf286104306c50c8fd060cbc57de35b9dffa779" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/ebf286104306c50c8fd060cbc57de35b9dffa779", + "reference": "ebf286104306c50c8fd060cbc57de35b9dffa779", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2026-08-10T13:09:27+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.13", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "9cb54414cdcd2ec5ca292e7ba19dba3a3444885d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/9cb54414cdcd2ec5ca292e7ba19dba3a3444885d", + "reference": "9cb54414cdcd2ec5ca292e7ba19dba3a3444885d", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0 || ^3.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6.36", + "symplify/easy-coding-standard": "^13.2.17" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2026-08-15T03:07:32+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2026-08-11T10:17:44+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.5", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "fb53eacd509a1d303858e2d20cfebf2d630254ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/fb53eacd509a1d303858e2d20cfebf2d630254ec", + "reference": "fb53eacd509a1d303858e2d20cfebf2d630254ec", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.14 || ^8.1.1" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.10.0", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.20.0", + "laravel/pint": "^1.29.3", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.3.5", + "pestphp/pest": "^3.8.5 || ^4.7.5 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.1.2 || ^9.3.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-07-15T19:09:14+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-webdriver/webdriver", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "ac0662863aa120b4f645869f584013e4c4dba46a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/ac0662863aa120b4f645869f584013e4c4dba46a", + "reference": "ac0662863aa120b4f645869f584013e4c4dba46a", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^7.3 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^5.0 || ^6.0 || ^7.0 || ^8.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.20.0", + "ondram/ci-detector": "^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0 || ^6.0 || ^7.0 || ^8.0" + }, + "suggest": { + "ext-simplexml": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.16.0" + }, + "time": "2025-12-28T23:57:40+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.2.8", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e285254e60f33c21902efef4a926ca0987c06804", + "reference": "e285254e60f33c21902efef4a926ca0987c06804", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-08-04T22:21:45+00:00" + }, + { + "name": "phpstan/phpstan-mockery", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-mockery.git", + "reference": "89a949d0ac64298e88b7c7fa00caee565c198394" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/89a949d0ac64298e88b7c7fa00caee565c198394", + "reference": "89a949d0ac64298e88b7c7fa00caee565c198394", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "require-dev": { + "mockery/mockery": "^1.6.11", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan Mockery extension", + "support": { + "issues": "https://github.com/phpstan/phpstan-mockery/issues", + "source": "https://github.com/phpstan/phpstan-mockery/tree/2.0.0" + }, + "time": "2024-10-14T03:18:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.46" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-12-24T07:01:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T13:52:54+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.56", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256", + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-07-06T14:52:39+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:26:40+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.4.15", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e101850ded5d2c0d44bf32abb8996404afec2dec", + "reference": "e101850ded5d2c0d44bf32abb8996404afec2dec", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.4.15" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-21T15:13:06+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} From 0988f40dacb930755215fc5e5cd83105be6969d5 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 14:58:10 +0200 Subject: [PATCH 05/10] 46 - Add zip extension to CI image --- .forgejo/workflows/ci.yml | 2 +- .forgejo/workflows/images.yml | 2 +- docker/build/Dockerfile.ci | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index b51e604..e6bdde5 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: ci: runs-on: docker container: - image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-1 + image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-2 steps: - uses: https://data.forgejo.org/actions/checkout@v4 diff --git a/.forgejo/workflows/images.yml b/.forgejo/workflows/images.yml index 37ec313..f120e93 100644 --- a/.forgejo/workflows/images.yml +++ b/.forgejo/workflows/images.yml @@ -18,7 +18,7 @@ jobs: include: - name: dishplanner-ci file: docker/build/Dockerfile.ci - version: php8.3-1 + version: php8.3-2 steps: - uses: https://data.forgejo.org/actions/checkout@v4 diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci index 3c3c5ec..8fd3972 100644 --- a/docker/build/Dockerfile.ci +++ b/docker/build/Dockerfile.ci @@ -3,8 +3,8 @@ # or cache extension is needed. # # Published as dishplanner-ci:php8.3-. Bump the revision in the tag -# and in .forgejo/workflows/ci.yml whenever this file changes (runners cache -# mutable tags and will not re-pull them). +# (.forgejo/workflows/images.yml) and in .forgejo/workflows/ci.yml whenever +# this file changes (runners cache mutable tags and will not re-pull them). # # Debian-based rather than Alpine to avoid the DNS resolution timeouts against # codeload.github.com that the Alpine base hit during composer install. @@ -19,7 +19,8 @@ RUN install-php-extensions \ dom \ xml \ fileinfo \ - pcntl + pcntl \ + zip # git is needed by the checkout action; nodejs runs the Forgejo JavaScript # actions (checkout, cache); unzip lets Composer extract dist archives. From e38d7006120e188642b30f24ccfa8f353e1797d9 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 17:33:18 +0200 Subject: [PATCH 06/10] 46 - Retry composer install on transient 429 --- .forgejo/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index e6bdde5..40c92e0 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -22,7 +22,14 @@ jobs: restore-keys: composer- - name: Install dependencies - run: composer install --no-interaction --prefer-dist + run: | + for attempt in 1 2 3 4 5; do + echo "composer install attempt $attempt/5" + composer install --no-interaction --prefer-dist && exit 0 + echo "composer install failed (attempt $attempt/5), retrying in 30s..." + sleep 30 + done + exit 1 - name: Prepare environment run: cp .env.testing .env From 42563511b462df0f5c7d866e87460c749c02bbee Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 18:04:30 +0200 Subject: [PATCH 07/10] 50 - Avoid codeload 429 via prefer-source and baked phpstan --- .forgejo/workflows/ci.yml | 6 +++--- .forgejo/workflows/images.yml | 2 +- docker/build/Dockerfile.ci | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 40c92e0..2f504a4 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -10,14 +10,14 @@ jobs: ci: runs-on: docker container: - image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-2 + image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-3 steps: - uses: https://data.forgejo.org/actions/checkout@v4 - name: Cache Composer dependencies uses: https://data.forgejo.org/actions/cache@v4 with: - path: ~/.cache/composer + path: ~/.composer/cache key: composer-${{ hashFiles('composer.lock') }} restore-keys: composer- @@ -25,7 +25,7 @@ jobs: run: | for attempt in 1 2 3 4 5; do echo "composer install attempt $attempt/5" - composer install --no-interaction --prefer-dist && exit 0 + composer install --no-interaction --prefer-source && exit 0 echo "composer install failed (attempt $attempt/5), retrying in 30s..." sleep 30 done diff --git a/.forgejo/workflows/images.yml b/.forgejo/workflows/images.yml index f120e93..c644391 100644 --- a/.forgejo/workflows/images.yml +++ b/.forgejo/workflows/images.yml @@ -18,7 +18,7 @@ jobs: include: - name: dishplanner-ci file: docker/build/Dockerfile.ci - version: php8.3-2 + version: php8.3-3 steps: - uses: https://data.forgejo.org/actions/checkout@v4 diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci index 8fd3972..548205d 100644 --- a/docker/build/Dockerfile.ci +++ b/docker/build/Dockerfile.ci @@ -29,3 +29,12 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +# phpstan/phpstan is dist-only (no VCS source), so pre-warm its Composer cache +# here to avoid codeload.github.com rate limits during CI's composer install. +# Keep the version in sync with composer.lock. Build this image on a network +# that isn't codeload-rate-limited (e.g. locally) until #50 is resolved. +RUN mkdir -p /tmp/warm && cd /tmp/warm \ + && printf '{"require":{"phpstan/phpstan":"2.2.8"}}' > composer.json \ + && composer install --no-interaction --no-progress \ + && rm -rf /tmp/warm From 08d86d92902fedc294a131d45dedf4fbc3d84ad6 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 20:45:45 +0200 Subject: [PATCH 08/10] 50 - Fix tests failing on missing Vite manifest --- tests/TestCase.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index fe1ffc2..19a9d2a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,5 +6,11 @@ abstract class TestCase extends BaseTestCase { - // + protected function setUp(): void + { + parent::setUp(); + + // Skip Vite asset resolution in tests: no build manifest exists. + $this->withoutVite(); + } } From 7954d9499dd76cb54ac8d4eb0cd5c0574a391160 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 21:18:01 +0200 Subject: [PATCH 09/10] 46 - Pre-load PHP dependencies into the CI image --- .forgejo/workflows/ci.yml | 24 ++++++------------------ .forgejo/workflows/images.yml | 4 +++- docker/build/Dockerfile.ci | 20 ++++++++++++-------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 2f504a4..6046793 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -10,30 +10,18 @@ jobs: ci: runs-on: docker container: - image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-3 + image: forge.lvl0.xyz/lvl0/dishplanner-ci:php8.3-4 steps: - uses: https://data.forgejo.org/actions/checkout@v4 - - name: Cache Composer dependencies - uses: https://data.forgejo.org/actions/cache@v4 - with: - path: ~/.composer/cache - key: composer-${{ hashFiles('composer.lock') }} - restore-keys: composer- - - - name: Install dependencies - run: | - for attempt in 1 2 3 4 5; do - echo "composer install attempt $attempt/5" - composer install --no-interaction --prefer-source && exit 0 - echo "composer install failed (attempt $attempt/5), retrying in 30s..." - sleep 30 - done - exit 1 - - name: Prepare environment run: cp .env.testing .env + - name: Restore dependencies + run: | + cp -a /opt/deps/vendor ./vendor + composer install --no-interaction --no-progress --prefer-source + - name: Lint run: vendor/bin/pint --test diff --git a/.forgejo/workflows/images.yml b/.forgejo/workflows/images.yml index c644391..0ebc9e5 100644 --- a/.forgejo/workflows/images.yml +++ b/.forgejo/workflows/images.yml @@ -5,6 +5,8 @@ on: branches: [main] paths: - 'docker/build/**' + - 'composer.json' + - 'composer.lock' - '.forgejo/workflows/images.yml' workflow_dispatch: @@ -18,7 +20,7 @@ jobs: include: - name: dishplanner-ci file: docker/build/Dockerfile.ci - version: php8.3-3 + version: php8.3-4 steps: - uses: https://data.forgejo.org/actions/checkout@v4 diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci index 548205d..f3270c8 100644 --- a/docker/build/Dockerfile.ci +++ b/docker/build/Dockerfile.ci @@ -30,11 +30,15 @@ RUN apt-get update \ COPY --from=composer:2 /usr/bin/composer /usr/bin/composer -# phpstan/phpstan is dist-only (no VCS source), so pre-warm its Composer cache -# here to avoid codeload.github.com rate limits during CI's composer install. -# Keep the version in sync with composer.lock. Build this image on a network -# that isn't codeload-rate-limited (e.g. locally) until #50 is resolved. -RUN mkdir -p /tmp/warm && cd /tmp/warm \ - && printf '{"require":{"phpstan/phpstan":"2.2.8"}}' > composer.json \ - && composer install --no-interaction --no-progress \ - && rm -rf /tmp/warm +# Bake the project's PHP dependencies (dev included) into the image so CI +# restores them with a local copy instead of paying a per-run composer install +# over the network. Build this image on a network that isn't +# codeload-rate-limited (e.g. locally) — the Forgejo runner hits codeload 429 +# under --prefer-dist. +# +# --no-scripts skips `php artisan package:discover` (the app isn't present +# here). CI runs `composer install` after restoring vendor, which regenerates +# bootstrap/cache and tops up any lockfile drift between main and release/*. +WORKDIR /opt/deps +COPY composer.json composer.lock ./ +RUN composer install --no-interaction --no-progress --prefer-dist --no-scripts From e95932d01ccedda412f26f34437f747703d2b359 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 17 Aug 2026 21:38:10 +0200 Subject: [PATCH 10/10] 47 - Document v0.8.0 in CHANGELOG --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c98d309..c902ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,30 @@ # Changelog The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.0] - 2026-08-17 + +### Added + +- **Forgejo CI pipeline** (#45) — added a CI workflow that runs Laravel Pint code-style checks, PHPStan static analysis, and the PHPUnit suite against SQLite in memory. +- **Static analysis** — added PHPStan via `larastan/larastan` and `phpstan/phpstan-mockery`, with `phpstan.neon` and a baseline. +- **Code style** — added Laravel Pint configuration (`pint.json`). +- **CI and app image build workflows** (#46) — automated Docker image builds for the app and the CI base image, published to the Forgejo registry. +- **CI base image** (#46) — added `docker/build/Dockerfile.ci`, a Debian-based PHP 8.3 CLI image with the project's PHP dependencies pre-loaded so CI doesn't pay a per-run Composer install. +- **Contributing guide** (#47) — added `CONTRIBUTING.md`. +- **Nix shell completion** (#44) — finished the `nix-shell` development commands. + +### Changed + +- **Dependencies** — `composer.lock` is now committed to the repository. +- **Code style** — reformatted the codebase with Laravel Pint. +- **Documentation** (#47) — professionalized the `README.md`. +- **Git hygiene** — dropped stale `.github`/StyleCI export rules from `.gitattributes`. + +### Fixed + +- **CI dependency installation** (#46, #50) — retried `composer install` on transient HTTP 429s, switched to `--prefer-source` and pre-loaded dependencies to avoid `codeload.github.com` rate limits, and added the missing `zip` extension to the CI image. +- **Test suite** (#50) — fixed feature tests failing on a missing Vite manifest by disabling Vite resolution during tests. + ## [0.7.0] - 2026-08-17 ### Removed