Output:
Chunk ID: 2f3cee
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 2050
Output:
<?php

use Laravel\Fortify\Features;

return [

    /*
    |--------------------------------------------------------------------------
    | Fortify Guard
    |--------------------------------------------------------------------------
    |
    | Here you may specify which authentication guard Fortify will use while
    | authenticating users. This value should correspond with one of your
    | guards that is already present in your "auth" configuration file.
    |
    */

    'guard' => 'web',

    /*
    |--------------------------------------------------------------------------
    | Fortify Password Broker
    |--------------------------------------------------------------------------
    |
    | Here you may specify which password broker Fortify can use when a user
    | is resetting their password. This configured value should match one
    | of your password brokers setup in your "auth" configuration file.
    |
    */

    'passwords' => 'users',

    /*
    |--------------------------------------------------------------------------
    | Username / Email
    |--------------------------------------------------------------------------
    |
    | This value defines which model attribute should be considered as your
    | application's "username" field. Typically, this might be the email
    | address of the users but you are free to change this value here.
    |
    | Out of the box, Fortify expects forgot password and reset password
    | requests to have a field named 'email'. If the application uses
    | another name for the field you may define it below as needed.
    |
    */

    'username' => 'email',

    'email' => 'email',

    /*
    |--------------------------------------------------------------------------
    | Lowercase Usernames
    |--------------------------------------------------------------------------
    |
    | This value defines whether usernames should be lowercased before saving
    | them in the database, as some database system string fields are case
    | sensitive. You may disable this for your application if necessary.
    |
    */

    'lowercase_usernames' => true,

    /*
    |--------------------------------------------------------------------------
    | Home Path
    |--------------------------------------------------------------------------
    |
    | Here you may configure the path where users will get redirected during
    | authentication or password reset when the operations are successful
    | and the user is authenticated. You are free to change this value.
    |
    */

    'home' => '/admin',

    /*
    |--------------------------------------------------------------------------
    | Fortify Routes Prefix / Subdomain
    |--------------------------------------------------------------------------
    |
    | Here you may specify which prefix Fortify will assign to all the routes
    | that it registers with the application. If necessary, you may change
    | subdomain under which all of the Fortify routes will be available.
    |
    */

    'prefix' => '',

    'domain' => null,

    /*
    |--------------------------------------------------------------------------
    | Fortify Routes Middleware
    |--------------------------------------------------------------------------
    |
    | Here you may specify which middleware Fortify will assign to the routes
    | that it registers with the application. If necessary, you may change
    | these middleware but typically this provided default is preferred.
    |
    */

    'middleware' => ['web'],

    /*
    |--------------------------------------------------------------------------
    | Rate Limiting
    |--------------------------------------------------------------------------
    |
    | By default, Fortify will throttle logins to five requests per minute for
    | every email and IP address combination. However, if you would like to
    | specify a custom rate limiter to call then you may specify it here.
    |
    */

    'limiters' => [
        'login' => 'login',
        'two-factor' => 'two-factor',
    ],

    /*
    |--------------------------------------------------------------------------
    | Register View Routes
    |--------------------------------------------------------------------------
    |
    | Here you may specify if the routes returning views should be disabled as
    | you may not need them when building your own application. This may be
    | especially true if you're writing a custom single-page application.
    |
    */

    'views' => true,

    /*
    |--------------------------------------------------------------------------
    | Features
    |--------------------------------------------------------------------------
    |
    | Some of the Fortify features are optional. You may disable the features
    | by removing them from this array. You're free to only remove some of
    | these features, or you can even remove all of these if you need to.
    |
    */

    'features' => [
        Features::registration(),
        Features::resetPasswords(),
        Features::emailVerification(),
        Features::twoFactorAuthentication([
            'confirm' => true,
            'confirmPassword' => true,
            // 'window' => 0
        ]),
    ],

];
<?php

use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;

uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);

test('reset password link screen can be rendered', function () {
    $response = $this->get(route('password.request'));

    $response->assertOk();
});

test('reset password link can be requested', function () {
    Notification::fake();

    $user = User::factory()->create();

    $this->post(route('password.request'), ['email' => $user->email]);

    Notification::assertSentTo($user, ResetPassword::class);
});

test('reset password screen can be rendered', function () {
    Notification::fake();

    $user = User::factory()->create();

    $this->post(route('password.request'), ['email' => $user->email]);

    Notification::assertSentTo($user, ResetPassword::class, function ($notification) {
        $response = $this->get(route('password.reset', $notification->token));
        $response->assertOk();

        return true;
    });
});

test('password can be reset with valid token', function () {
    Notification::fake();

    $user = User::factory()->create();

    $this->post(route('password.request'), ['email' => $user->email]);

    Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
        $response = $this->post(route('password.update'), [
            'token' => $notification->token,
            'email' => $user->email,
            'password' => 'password',
            'password_confirmation' => 'password',
        ]);

        $response
            ->assertSessionHasNoErrors()
            ->assertRedirect(route('login', absolute: false));

        return true;
    });
});<x-layouts::auth>

        <x-auth-header :title="__('Forgot password')" :description="__('Enter your email to receive a password reset link')" />

        <x-auth-session-status class="text-center" :status="session('status')" />

        <form method="POST" action="{{ route('password.email') }}" class="flex flex-col gap-6">
            @csrf

            <flux:input
                name="email"
                :label="__('Email Address')"
                type="email"
                required
                autofocus
                placeholder="email@example.com"
            />

            <flux:button variant="primary" type="submit" class="w-full" data-test="email-password-reset-link-button">
                {{ __('Email password reset link') }}
            </flux:button>
        </form>

            {{ __('Or, return to') }}
            <flux:link :href="route('login')" wire:navigate>{{ __('log in') }}</flux:link>

</x-layouts::auth>


php artisan route:list | rg " login|register|verification|confirm-password|two-factor|user/|profile|settings"


Output:
Chunk ID: 544318
Wall time: 0.1969 seconds
Process exited with code 0
Original token count: 651
Output:
  GET|HEAD  account/register account.register › Livewire\Mechanisms › Livewir…
  GET|HEAD  admin/search/settings admin.search.settings › Livewire\Mechanisms…
  GET|HEAD  admin/settings admin.settings.index › Livewire\Mechanisms › Livew…
  GET|HEAD  admin/settings/checkout admin.settings.checkout › Livewire\Mechan…
  GET|HEAD  admin/settings/notifications admin.settings.notifications › Livew…
  GET|HEAD  admin/settings/shipping admin.settings.shipping › Livewire\Mechan…
  GET|HEAD  admin/settings/taxes admin.settings.taxes › Livewire\Mechanisms  …
  POST      email/verification-notification verification.send › Laravel\Forti…
  GET|HEAD  email/verify verification.notice › Laravel\Fortify › EmailVerific…
  GET|HEAD  email/verify/{id}/{hash} verification.verify › Laravel\Fortify › …
  GET|HEAD  login login › Laravel\Fortify › AuthenticatedSessionController@cr…
  POST      login login.store › Laravel\Fortify › AuthenticatedSessionControl…
  GET|HEAD  register register › Laravel\Fortify › RegisteredUserController@cr…
  POST      register register.store › Laravel\Fortify › RegisteredUserControl…
  ANY       settings ................. Illuminate\Routing › RedirectController
  GET|HEAD  settings/appearance appearance.edit › Livewire\Mechanisms › Livew…
  GET|HEAD  settings/password user-password.edit › Livewire\Mechanisms › Live…
  GET|HEAD  settings/profile profile.edit › Livewire\Mechanisms › LivewirePag…
  GET|HEAD  settings/two-factor two-factor.show › Livewire\Mechanisms › Livew…
  GET|HEAD  two-factor-challenge two-factor.login › Laravel\Fortify › TwoFact…
  POST      two-factor-challenge two-factor.login.store › Laravel\Fortify › T…
  GET|HEAD  user/confirm-password password.confirm › Laravel\Fortify › Confir…
  POST      user/confirm-password password.confirm.store › Laravel\Fortify › …
  GET|HEAD  user/confirmed-password-status password.confirmation › Laravel\Fo…
  POST      user/confirmed-two-factor-authentication two-factor.confirm › Lar…
  POST      user/two-factor-authentication two-factor.enable › Laravel\Fortif…
  DELETE    user/two-factor-authentication two-factor.disable › Laravel\Forti…
  GET|HEAD  user/two-factor-qr-code two-factor.qr-code › Laravel\Fortify › Tw…
  GET|HEAD  user/two-factor-recovery-codes two-factor.recovery-codes › Larave…
  POST      user/two-factor-recovery-codes two-factor.regenerate-recovery-cod…
  GET|HEAD  user/two-factor-secret-key two-factor.secret-key › Laravel\Fortif…


php artisan route:list | rg " login|register|verification|confirm-password|two-factor|user/|profile|settings"

  GET|HEAD  account/register account.register › Livewire\Mechanisms › Livewir…
  GET|HEAD  admin/search/settings admin.search.settings › Livewire\Mechanisms…
  GET|HEAD  admin/settings admin.settings.index › Livewire\Mechanisms › Livew…
  GET|HEAD  admin/settings/checkout admin.settings.checkout › Livewire\Mechan…
  GET|HEAD  admin/settings/notifications admin.settings.notifications › Livew…
  GET|HEAD  admin/settings/shipping admin.settings.shipping › Livewire\Mechan…
  GET|HEAD  admin/settings/taxes admin.settings.taxes › Livewire\Mechanisms  …
  POST      email/verification-notification verification.send › Laravel\Forti…
  GET|HEAD  email/verify verification.notice › Laravel\Fortify › EmailVerific…
  GET|HEAD  email/verify/{id}/{hash} verification.verify › Laravel\Fortify › …
  GET|HEAD  login login › Laravel\Fortify › AuthenticatedSessionController@cr…
  POST      login login.store › Laravel\Fortify › AuthenticatedSessionControl…
  GET|HEAD  register register › Laravel\Fortify › RegisteredUserController@cr…
  POST      register register.store › Laravel\Fortify › RegisteredUserControl…
  ANY       settings ................. Illuminate\Routing › RedirectController
  GET|HEAD  settings/appearance appearance.edit › Livewire\Mechanisms › Livew…
  GET|HEAD  settings/password user-password.edit › Livewire\Mechanisms › Live…
  GET|HEAD  settings/profile profile.edit › Livewire\Mechanisms › LivewirePag…
  GET|HEAD  settings/two-factor two-factor.show › Livewire\Mechanisms › Livew…
  GET|HEAD  two-factor-challenge two-factor.login › Laravel\Fortify › TwoFact…
  POST      two-factor-challenge two-factor.login.store › Laravel\Fortify › T…
  GET|HEAD  user/confirm-password password.confirm › Laravel\Fortify › Confir…
  POST      user/confirm-password password.confirm.store › Laravel\Fortify › …
  GET|HEAD  user/confirmed-password-status password.confirmation › Laravel\Fo…
  POST      user/confirmed-two-factor-authentication two-factor.confirm › Lar…
  POST      user/two-factor-authentication two-factor.enable › Laravel\Fortif…
  DELETE    user/two-factor-authentication two-factor.disable › Laravel\Forti…
  GET|HEAD  user/two-factor-qr-code two-factor.qr-code › Laravel\Fortify › Tw…
  GET|HEAD  user/two-factor-recovery-codes two-factor.recovery-codes › Larave…
  POST      user/two-factor-recovery-codes two-factor.regenerate-recovery-cod…
  GET|HEAD  user/two-factor-secret-key two-factor.secret-key › Laravel\Fortif…


Browser smoke coverage is committed. I’m moving back to the broader admin REST gap and will add another focused surface rather than mixing many resource types in one commit. Collections are a good next slice because they are store-scoped and have clear CRUD endpoints in the spec.


sed -n '1738,1855p' specs/02-API-ROUTES.md


sed -n '1,240p' app/Models/Collection.php && sed -n '1,220p' database/factories/CollectionFactory.php


rg -n "CollectionService|collections" app/Livewire/Admin/Collections app/Services tests/Feature/Catalog/CatalogUiTest.php


sed -n '1738,1855p' specs/02-API-ROUTES.md

#### Admin Collections Endpoint Summary

| Method | Path | Auth Scope | Description | Rate Limit |
|--------|------|------------|-------------|------------|
| GET | `/api/admin/v1/stores/{storeId}/collections` | `read-collections` | List collections | `api.admin` (60/min) |
| POST | `/api/admin/v1/stores/{storeId}/collections` | `write-collections` | Create a collection | `api.admin` (60/min) |
| PUT | `/api/admin/v1/stores/{storeId}/collections/{collectionId}` | `write-collections` | Update a collection | `api.admin` (60/min) |
| DELETE | `/api/admin/v1/stores/{storeId}/collections/{collectionId}` | `write-collections` | Delete a collection | `api.admin` (60/min) |

#### GET /api/admin/v1/stores/{storeId}/collections

List collections.

**Query parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by status: `draft`, `active`, `archived`. |
| `query` | string | No | Search by title. |
| `page` | integer | No | Default: 1. |
| `per_page` | integer | No | Default: 25, max: 100. |

**Success response:** `200 OK`

```json
{
  "data": [
    {
      "id": 5,
      "store_id": 1,
      "title": "Summer Collection",
      "handle": "summer-collection",
      "description_html": "<p>Best items for summer.</p>",
      "type": "manual",
      "status": "active",
      "products_count": 15,
      "created_at": "2026-02-01T10:00:00Z",
      "updated_at": "2026-02-10T14:00:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total": 5,
    "last_page": 1
  }
}
```

---

#### POST /api/admin/v1/stores/{storeId}/collections

Create a collection.

**Request body:**

```json
{
  "title": "Winter Collection",
  "handle": "winter-collection",
  "description_html": "<p>Cozy items for winter.</p>",
  "type": "manual",
  "status": "active",
  "product_ids": [10, 11]
}
```

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| `title` | string | Yes | Max: 255 |
| `handle` | string | No | Auto-generated from title. Unique per store. |
| `description_html` | string | No | Max: 65535 |
| `type` | string | Yes | One of: `manual`, `automated`. |
| `status` | string | No | One of: `draft`, `active`, `archived`. Default: `active`. |
| `product_ids` | array of integers | No | Products to add to the collection. |

**Success response:** `201 Created`

Returns the full collection resource.

**Error responses:**
- `403` - Insufficient permissions
- `422` - Validation errors

---

#### PUT /api/admin/v1/stores/{storeId}/collections/{collectionId}

Update a collection.

Same fields as POST (all optional). Supports partial updates. Use `product_ids` to replace the full product set, or `add_product_ids` / `remove_product_ids` for incremental changes.

**Success response:** `200 OK`

**Error responses:**
- `403` - Insufficient permissions
- `404` - Collection not found
- `422` - Validation errors

---

#### DELETE /api/admin/v1/stores/{storeId}/collections/{collectionId}

Delete a collection.

**Authentication:** Bearer token with `write-collections` ability.

**Success response:** `200 OK`

```json
{
  "message": "Collection deleted"
}
```

**Error responses:**
- `403` - Insufficient permissions


Output:
Chunk ID: 7dc1b6
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 814
Output:
#### Admin Collections Endpoint Summary

| Method | Path | Auth Scope | Description | Rate Limit |
|--------|------|------------|-------------|------------|
| GET | `/api/admin/v1/stores/{storeId}/collections` | `read-collections` | List collections | `api.admin` (60/min) |
| POST | `/api/admin/v1/stores/{storeId}/collections` | `write-collections` | Create a collection | `api.admin` (60/min) |
| PUT | `/api/admin/v1/stores/{storeId}/collections/{collectionId}` | `write-collections` | Update a collection | `api.admin` (60/min) |
| DELETE | `/api/admin/v1/stores/{storeId}/collections/{collectionId}` | `write-collections` | Delete a collection | `api.admin` (60/min) |

#### GET /api/admin/v1/stores/{storeId}/collections

List collections.

**Query parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by status: `draft`, `active`, `archived`. |
| `query` | string | No | Search by title. |
| `page` | integer | No | Default: 1. |
| `per_page` | integer | No | Default: 25, max: 100. |

**Success response:** `200 OK`

```json
{
  "data": [
    {
      "id": 5,
      "store_id": 1,
      "title": "Summer Collection",
      "handle": "summer-collection",
      "description_html": "<p>Best items for summer.</p>",
      "type": "manual",
      "status": "active",
      "products_count": 15,
      "created_at": "2026-02-01T10:00:00Z",
      "updated_at": "2026-02-10T14:00:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 25,
    "total": 5,
    "last_page": 1
  }
}
```

---

#### POST /api/admin/v1/stores/{storeId}/collections

Create a collection.

**Request body:**

```json
{
  "title": "Winter Collection",
  "handle": "winter-collection",
  "description_html": "<p>Cozy items for winter.</p>",
  "type": "manual",
  "status": "active",
  "product_ids": [10, 11]
}
```

| Field | Type | Required | Validation |
|-------|------|----------|------------|
| `title` | string | Yes | Max: 255 |
| `handle` | string | No | Auto-generated from title. Unique per store. |
| `description_html` | string | No | Max: 65535 |
| `type` | string | Yes | One of: `manual`, `automated`. |
| `status` | string | No | One of: `draft`, `active`, `archived`. Default: `active`. |
| `product_ids` | array of integers | No | Products to add to the collection. |

**Success response:** `201 Created`

Returns the full collection resource.

**Error responses:**
- `403` - Insufficient permissions
- `422` - Validation errors

---

#### PUT /api/admin/v1/stores/{storeId}/collections/{collectionId}

Update a collection.

Same fields as POST (all optional). Supports partial updates. Use `product_ids` to replace the full product set, or `add_product_ids` / `remove_product_ids` for incremental changes.

**Success response:** `200 OK`

**Error responses:**
- `403` - Insufficient permissions
- `404` - Collection not found
- `422` - Validation errors

---

#### DELETE /api/admin/v1/stores/{storeId}/collections/{collectionId}

Delete a collection.

**Authentication:** Bearer token with `write-collections` ability.

**Success response:** `200 OK`

```json
{
  "message": "Collection deleted"
}
```

**Error responses:**
- `403` - Insufficient permissions


sed -n '1,240p' app/Models/Collection.php && sed -n '1,220p' database/factories/CollectionFactory.php

CollectionFactory.php

<?php

namespace App\Models;

use App\Enums\CollectionStatus;
use App\Enums\CollectionType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Collection extends Model
{
    /** @use HasFactory<\Database\Factories\CollectionFactory> */
    use BelongsToStore, HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'title',
        'handle',
        'description_html',
        'type',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'type' => 'manual',
        'status' => 'active',
    ];

    /**
     * @return BelongsTo<Store, $this>
     */
    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

    /**
     * @return BelongsToMany<Product, $this>
     */
    public function products(): BelongsToMany
    {
        return $this->belongsToMany(Product::class, 'collection_products')
            ->withPivot('position')
            ->orderByPivot('position');
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'type' => CollectionType::class,
            'status' => CollectionStatus::class,
        ];
    }
}
<?php

namespace Database\Factories;

use App\Enums\CollectionStatus;
use App\Enums\CollectionType;
use App\Models\Store;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Collection>
 */
class CollectionFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        $title = Str::title(fake()->words(2, true));

        return [
            'store_id' => Store::factory(),
            'title' => $title,
            'handle' => Str::slug($title),
            'description_html' => '<p>'.fake()->sentence().'</p>',
            'type' => CollectionType::Manual,
            'status' => CollectionStatus::Active,
        ];
    }

    public function draft(): static
    {
        return $this->state(fn (array $attributes): array => [
            'status' => CollectionStatus::Draft,
        ]);
    }

    public function automated(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => CollectionType::Automated,
        ]);
    }
}


Output:
Chunk ID: 6836d1
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 668
Output:
<?php

namespace App\Models;

use App\Enums\CollectionStatus;
use App\Enums\CollectionType;
use App\Models\Concerns\BelongsToStore;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Collection extends Model
{
    /** @use HasFactory<\Database\Factories\CollectionFactory> */
    use BelongsToStore, HasFactory;

    /**
     * @var list<string>
     */
    protected $fillable = [
        'store_id',
        'title',
        'handle',
        'description_html',
        'type',
        'status',
    ];

    /**
     * @var array<string, mixed>
     */
    protected $attributes = [
        'type' => 'manual',
        'status' => 'active',
    ];

    /**
     * @return BelongsTo<Store, $this>
     */
    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

    /**
     * @return BelongsToMany<Product, $this>
     */
    public function products(): BelongsToMany
    {
        return $this->belongsToMany(Product::class, 'collection_products')
            ->withPivot('position')
            ->orderByPivot('position');
    }

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'type' => CollectionType::class,
            'status' => CollectionStatus::class,
        ];
    }
}
<?php

namespace Database\Factories;

use App\Enums\CollectionStatus;
use App\Enums\CollectionType;
use App\Models\Store;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Collection>
 */
class CollectionFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        $title = Str::title(fake()->words(2, true));

        return [
            'store_id' => Store::factory(),
            'title' => $title,
            'handle' => Str::slug($title),
            'description_html' => '<p>'.fake()->sentence().'</p>',
            'type' => CollectionType::Manual,
            'status' => CollectionStatus::Active,
        ];
    }

    public function draft(): static
    {
        return $this->state(fn (array $attributes): array => [
            'status' => CollectionStatus::Draft,
        ]);
    }

    public function automated(): static
    {
        return $this->state(fn (array $attributes): array => [
            'type' => CollectionType::Automated,
        ]);
    }
}


rg -n "CollectionService|collections" app/Livewire/Admin/Collections app/Services tests/Feature/Catalog/CatalogUiTest.php

rg -n 'CollectionService|collections' app/Livewire/Admin/Collections app/Services tests/Feature/Catalog/CatalogUiTest.php

Question: CollectionService|collections

app/Livewire/Admin/Collections/Form.php:80:                Rule::unique('collections', 'handle')
app/Livewire/Admin/Collections/Form.php:145:        return view('livewire.admin.collections.form', [
tests/Feature/Catalog/CatalogUiTest.php:31:        ->get('/collections/t-shirts')
tests/Feature/Catalog/CatalogUiTest.php:66:        ->get('/admin/collections')
app/Livewire/Admin/Collections/Index.php:36:    public function collections(): LengthAwarePaginator
app/Livewire/Admin/Collections/Index.php:50:        return view('livewire.admin.collections.index', [
app/Livewire/Admin/Collections/Index.php:51:            'collections' => $this->collections(),
app/Services/SearchService.php:86:        $collections = $remaining > 0
app/Services/SearchService.php:102:        return $products->concat($collections)->values();
app/Services/DiscountService.php:190:                ->with(['product' => fn ($query) => $query->withoutGlobalScopes()->with('collections')])
app/Services/DiscountService.php:201:            return $variant->product->collections
app/Services/ThemeSettingsService.php:71:                    'primary_url' => '/collections/new-arrivals',
app/Services/ThemeSettingsService.php:72:                    'secondary_label' => 'View collections',
app/Services/ThemeSettingsService.php:73:                    'secondary_url' => '/collections',
app/Services/NavigationService.php:104:        return $collection ? route('collections.show', $collection->handle, false) : '#';


Output:
Chunk ID: 423214
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 371
Output:
app/Livewire/Admin/Collections/Form.php:80:                Rule::unique('collections', 'handle')
app/Livewire/Admin/Collections/Form.php:145:        return view('livewire.admin.collections.form', [
tests/Feature/Catalog/CatalogUiTest.php:31:        ->get('/collections/t-shirts')
tests/Feature/Catalog/CatalogUiTest.php:66:        ->get('/admin/collections')
app/Livewire/Admin/Collections/Index.php:36:    public function collections(): LengthAwarePaginator
app/Livewire/Admin/Collections/Index.php:50:        return view('livewire.admin.collections.index', [
app/Livewire/Admin/Collections/Index.php:51:            'collections' => $this->collections(),
app/Services/SearchService.php:86:        $collections = $remaining > 0
app/Services/SearchService.php:102:        return $products->concat($collections)->values();
app/Services/DiscountService.php:190:                ->with(['product' => fn ($query) => $query->withoutGlobalScopes()->with('collections')])
app/Services/DiscountService.php:201:            return $variant->product->collections
app/Services/ThemeSettingsService.php:71:                    'primary_url' => '/collections/new-arrivals',
app/Services/ThemeSettingsService.php:72:                    'secondary_label' => 'View collections',
app/Services/ThemeSettingsService.php:73:                    'secondary_url' => '/collections',
app/Services/NavigationService.php:104:        return $collection ? route('collections.show', $collection->handle, false) : '#';


sed -n '1,240p' app/Livewire/Admin/Collections/Form.php


sed -n '1,120p' app/Enums/CollectionStatus.php && sed -n '1,120p' app/Enums/CollectionType.php


api resource collection pagination create update delete controller validation

many to many sync with pivot values validation rule exists scoped

route middleware ability group api tests json


sed -n '1,240p' app/Livewire/Admin/Collections/Form.php

<?php

namespace App\Livewire\Admin\Collections;

use App\Models\Collection;
use App\Models\Product;
use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as SupportCollection;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Component;

class Form extends Component
{
    public ?Collection $collection = null;

    public string $title = '';

    public string $handle = '';

    public string $descriptionHtml = '';

    public string $status = 'active';

    public string $productSearch = '';

    /**
     * @var array<int, int>
     */
    public array $assignedProductIds = [];

    public function mount(?Collection $collection = null): void
    {
        if ($collection?->exists) {
            $store = app('current_store');

            abort_unless($store instanceof Store && (int) $collection->store_id === $store->getKey(), 404);

            $this->collection = $collection->load('products');
            $this->fillFromCollection($this->collection);
        }
    }

    public function updatedTitle(): void
    {
        if ($this->collection === null && $this->handle === '') {
            $this->handle = Str::slug($this->title);
        }
    }

    public function addProduct(int $productId): void
    {
        if (! in_array($productId, $this->assignedProductIds, true)) {
            $this->assignedProductIds[] = $productId;
        }

        $this->productSearch = '';
    }

    public function removeProduct(int $productId): void
    {
        $this->assignedProductIds = array_values(array_filter(
            $this->assignedProductIds,
            fn (int $assignedProductId): bool => $assignedProductId !== $productId,
        ));
    }

    public function save(): void
    {
        $store = app('current_store');
        abort_unless($store instanceof Store, 404);

        $this->validate([
            'title' => ['required', 'string', 'max:255'],
            'handle' => [
                'required',
                'string',
                'max:255',
                Rule::unique('collections', 'handle')
                    ->where('store_id', $store->getKey())
                    ->ignore($this->collection?->getKey()),
            ],
            'descriptionHtml' => ['nullable', 'string', 'max:65535'],
            'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
        ]);

        $attributes = [
            'store_id' => $store->getKey(),
            'title' => $this->title,
            'handle' => Str::slug($this->handle),
            'description_html' => $this->descriptionHtml ?: null,
            'type' => 'manual',
            'status' => $this->status,
        ];

        $collection = $this->collection instanceof Collection
            ? tap($this->collection)->update($attributes)
            : Collection::query()->create($attributes);

        $collection->products()->sync(collect($this->assignedProductIds)
            ->values()
            ->mapWithKeys(fn (int $productId, int $position): array => [$productId => ['position' => $position]])
            ->all());

        $this->collection = $collection->refresh()->load('products');
        $this->fillFromCollection($this->collection);

        session()->flash('status', 'Collection saved');
        $this->dispatch('toast', type: 'success', message: __('Collection saved'));
    }

    public function searchResults(): SupportCollection
    {
        if (trim($this->productSearch) === '') {
            return collect();
        }

        return Product::query()
            ->whereNotIn('id', $this->assignedProductIds)
            ->where(function (Builder $query): void {
                $query
                    ->where('title', 'like', '%'.$this->productSearch.'%')
                    ->orWhere('handle', 'like', '%'.$this->productSearch.'%');
            })
            ->limit(5)
            ->get();
    }

    public function assignedProducts(): SupportCollection
    {
        if ($this->assignedProductIds === []) {
            return collect();
        }

        return Product::query()
            ->whereIn('id', $this->assignedProductIds)
            ->get()
            ->sortBy(fn (Product $product): int => array_search($product->getKey(), $this->assignedProductIds, true))
            ->values();
    }

    public function render(): mixed
    {
        return view('livewire.admin.collections.form', [
            'searchResults' => $this->searchResults(),
            'assignedProducts' => $this->assignedProducts(),
            'isEditing' => $this->collection !== null,
        ])->layout('layouts.app', [
            'title' => $this->collection ? $this->collection->title : __('Add collection'),
        ]);
    }

    private function fillFromCollection(Collection $collection): void
    {
        $this->title = $collection->title;
        $this->handle = $collection->handle;
        $this->descriptionHtml = (string) $collection->description_html;
        $this->status = $collection->status->value;
        $this->assignedProductIds = $collection->products->pluck('id')->map(fn (int $id): int => $id)->all();
    }
}


sed -n '1,120p' app/Enums/CollectionStatus.php && sed -n '1,120p' app/Enums/CollectionType.php

CollectionType.php

<?php

namespace App\Enums;

enum CollectionStatus: string
{
    case Draft = 'draft';
    case Active = 'active';
    case Archived = 'archived';
}
<?php

namespace App\Enums;

enum CollectionType: string
{
    case Manual = 'manual';
    case Automated = 'automated';
}


Output:
Chunk ID: f871b2
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1293
Output:
<?php

namespace App\Livewire\Admin\Collections;

use App\Models\Collection;
use App\Models\Product;
use App\Models\Store;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection as SupportCollection;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Livewire\Component;

class Form extends Component
{
    public ?Collection $collection = null;

    public string $title = '';

    public string $handle = '';

    public string $descriptionHtml = '';

    public string $status = 'active';

    public string $productSearch = '';

    /**
     * @var array<int, int>
     */
    public array $assignedProductIds = [];

    public function mount(?Collection $collection = null): void
    {
        if ($collection?->exists) {
            $store = app('current_store');

            abort_unless($store instanceof Store && (int) $collection->store_id === $store->getKey(), 404);

            $this->collection = $collection->load('products');
            $this->fillFromCollection($this->collection);
        }
    }

    public function updatedTitle(): void
    {
        if ($this->collection === null && $this->handle === '') {
            $this->handle = Str::slug($this->title);
        }
    }

    public function addProduct(int $productId): void
    {
        if (! in_array($productId, $this->assignedProductIds, true)) {
            $this->assignedProductIds[] = $productId;
        }

        $this->productSearch = '';
    }

    public function removeProduct(int $productId): void
    {
        $this->assignedProductIds = array_values(array_filter(
            $this->assignedProductIds,
            fn (int $assignedProductId): bool => $assignedProductId !== $productId,
        ));
    }

    public function save(): void
    {
        $store = app('current_store');
        abort_unless($store instanceof Store, 404);

        $this->validate([
            'title' => ['required', 'string', 'max:255'],
            'handle' => [
                'required',
                'string',
                'max:255',
                Rule::unique('collections', 'handle')
                    ->where('store_id', $store->getKey())
                    ->ignore($this->collection?->getKey()),
            ],
            'descriptionHtml' => ['nullable', 'string', 'max:65535'],
            'status' => ['required', Rule::in(['draft', 'active', 'archived'])],
        ]);

        $attributes = [
            'store_id' => $store->getKey(),
            'title' => $this->title,
            'handle' => Str::slug($this->handle),
            'description_html' => $this->descriptionHtml ?: null,
            'type' => 'manual',
            'status' => $this->status,
        ];

        $collection = $this->collection instanceof Collection
            ? tap($this->collection)->update($attributes)
            : Collection::query()->create($attributes);

        $collection->products()->sync(collect($this->assignedProductIds)
            ->values()
            ->mapWithKeys(fn (int $productId, int $position): array => [$productId => ['position' => $position]])
            ->all());

        $this->collection = $collection->refresh()->load('products');
        $this->fillFromCollection($this->collection);

        session()->flash('status', 'Collection saved');
        $this->dispatch('toast', type: 'success', message: __('Collection saved'));
    }

    public function searchResults(): SupportCollection
    {
        if (trim($this->productSearch) === '') {
            return collect();
        }

        return Product::query()
            ->whereNotIn('id', $this->assignedProductIds)
            ->where(function (Builder $query): void {
                $query
                    ->where('title', 'like', '%'.$this->productSearch.'%')
                    ->orWhere('handle', 'like', '%'.$this->productSearch.'%');
            })
            ->limit(5)
            ->get();
    }

    public function assignedProducts(): SupportCollection
    {
        if ($this->assignedProductIds === []) {
            return collect();
        }

        return Product::query()
            ->whereIn('id', $this->assignedProductIds)
            ->get()
            ->sortBy(fn (Product $product): int => array_search($product->getKey(), $this->assignedProductIds, true))
            ->values();
    }

    public function render(): mixed
    {
        return view('livewire.admin.collections.form', [
            'searchResults' => $this->searchResults(),
            'assignedProducts' => $this->assignedProducts(),
            'isEditing' => $this->collection !== null,
        ])->layout('layouts.app', [
            'title' => $this->collection ? $this->collection->title : __('Add collection'),
        ]);
    }

    private function fillFromCollection(Collection $collection): void
    {
        $this->title = $collection->title;
        $this->handle = $collection->handle;
        $this->descriptionHtml = (string) $collection->description_html;
        $this->status = $collection->status->value;
        $this->assignedProductIds = $collection->products->pluck('id')->map(fn (int $id): int => $id)->all();
    }
}


Output:
Chunk ID: efaae5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 68
Output:
<?php

namespace App\Enums;

enum CollectionStatus: string
{
    case Draft = 'draft';
    case Active = 'active';
    case Archived = 'archived';
}
<?php

namespace App\Enums;

enum CollectionType: string
{
    case Manual = 'manual';
    case Automated = 'automated';
}


# Search Results

## Helpers Other Utilities URI Manipulating Query Strings
Helpers | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/helpers.md

#### Manipulating Query Strings

The `Uri` class offers several methods that may be used to manipulate a URI's query string. The `withQuery` method may be used to merge additional query string parameters into the existing query string:

```php
$uri = $uri->withQuery(['sort' => 'name']);
```

The `withQueryIfMissing` method may be used to merge additional query string parameters into the existing query string if the given keys do not already exist in the query string:

```php
$uri = $uri->withQueryIfMissing(['page' => 1]);
```

The `replaceQuery` method may be used to complete replace the existing query string with a new one:

```php
$uri = $uri->replaceQuery(['page' => 1]);
```

The `pushOntoQuery` method may be used to push additional parameters onto a query string parameter that has an array value:

```php
$uri = $uri->pushOntoQuery('filter', ['active', 'pending']);
```

The `withoutQuery` method may be used to remove parameters from the query string:

```php
$uri = $uri->withoutQuery(['page']);
```

---

## Pest3 Now Available New Configuration API
Pest3 Now Available | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/pest3-now-available.md

## New Configuration API

Pest 1 / Pest 2's configuration API was a little bit confusing, the `uses()` function that was originally made only for having the `$this` variable within closure bound to the test case instance, ended up being used for pretty much everything.

In Pest 3, we've introduced a new configuration API that is more intuitive and easier to use. The new configuration API is based on the `pest()` function, which allows you to configure Pest using a fluent and expressive API.

> Note: the `uses()` function is still available in Pest 3, and we don't have plans to remove it. However, we recommend using the new configuration API for new projects.

```diff
-uses(TestCase::class)->in(__DIR__);
+pest()->extends(TestCase::class);

-uses(TestCase::class, RefreshDatabase::class)->in('Features');
+pest()->extends(TestCase::class)->use(RefreshDatabase::class)->in('Features');

-uses()->compact();
+pest()->printer()->compact();
```

And of course, any method that was available on the `uses()` API, like `->beforeEach()` or `->group()` is still available on the new `pest()` configuration API; we've just made it more intuitive and easier to use.

---

## Vite Working With Blade and Routes Aliases
Vite | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/vite.md

### Aliases

It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Facades\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers):

```php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));
}
```

Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`:

```blade
<img src="{{ Vite::image('logo.png') }}" alt="Laravel Logo">
```

---

## Cli Api Reference
Cli Api Reference | pestphp/pest@4.x | Source: https://github.com/pestphp/docs/blob/4.x/cli-api-reference.md

---
title: CLI API Reference
description: In the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.
---

# CLI API Reference

In the preceding chapters of the Pest documentation, we have covered numerous CLI options that are available in Pest. Nevertheless, Pest provides many other options that could prove beneficial. The complete CLI API Reference is provided below for your convenience.

## Configuration

- `--init`: Initialize a standard Pest configuration.
- `--bootstrap <file>` A PHP script that is included before the tests run.
- `-c|--configuration <file>`: Read configuration from XML file.
- `--no-configuration`: Ignore default configuration file (phpunit.xml).
- `--extension <class>`: Register test runner extension with bootstrap <class>.
- `--no-extensions`: Do not load PHPUnit extensions.
- `--include-path <path(s)>`: Prepend PHP's include_path with given path(s).
- `-d <key[=value]>`: Set a php.ini value.
- `--cache-directory <dir>`: Specify cache directory.
- `--generate-configuration`: Generate configuration file with suggested settings.
- `--migrate-configuration`: Migrate configuration file to current format.
- `--generate-baseline <file>`: Generate baseline for issues.
- `--use-baseline <file>`: Use baseline to ignore issues.
- `--ignore-baseline`: Do not use baseline to ignore issues.
- `--test-directory`: Specify test directory containing Pest.php, TestCase.php, helpers and your tests. Default: tests

## Selection

- `--bail`: Stop execution upon first not-passed test.
- `--ci`: Ignore focused tests using `->only()` and run the entire test suite.
- `--todos`: Output to standard output the list of todos.
- `--notes`: Output to standard output tests with notes.
- `--issue`: Output to standard output tests with the given issue number.
- `--pr`: Output to standard output tests with the given pull request number.
- `--pull-request`: Output to standard output tests with the given pull request number (alias for `--pr`).
- `--flaky`: Output to standard output tests marked as flaky.
- `--retry`: Run non-passing tests first and stop execution upon first error or failure.
- `--list-suites` List available test suites.
- `--testsuite <name>`: Only run tests from the specified test suite(s).
- `--exclude-testsuite <name>`: Exclude tests from the specified test suite(s).
- `--list-groups`: List available test groups.
- `--group <name>`: Only run tests from the specified group(s).
- `--exclude-group <name>`: Exclude tests from the specified group(s).
- `--covers <name>`: Only run tests that intend to cover `<name>`.
- `--uses <name>`: Only run tests that intend to use `<name>`.
- `--requires-php-extension <name>`: Only run tests that require PHP extension <name>.
- `--list-test-files`: List available test files.
- `--list-tests`: List available tests.
- `--list-tests-xml <file>`: List available tests in XML format.
- `--filter <pattern>`: Filter which tests to run
- `--exclude-filter <pattern>`: Exclude tests for the specified filter pattern.
- `--test-suffix <suffixes>`: Only search for test in files with specified suffix(es). Default: Test.php,.phpt

## Execution

- `--parallel` Run tests in parallel.
- `--shard <index/total>`: Run only the given shard of tests (e.g. `--shard=1/5`). Uses time-balanced distribution when `tests/.pest/shards.json` exists.
- `--update-shards`: Update `tests/.pest/shards.json` with test timing data for time-balanced sharding. Can be combined with `--parallel`.
- `--update-snapshots`: Update snapshots for tests using the "toMatchSnapshot" expectation.
- `--globals-backup`: Backup and restore $GLOBALS for each test.
- `--static-backup`: Backup and restore static properties for each test.
- `--strict-coverage`: Be strict about code coverage metadata.
- `--strict-global-state`: Be strict about changes to global state.
- `--disallow-test-output`: Be strict about output during tests.
- `--enforce-time-limit`: Enforce time limit based on test size.
- `--default-time-limit <sec>`: Timeout in seconds for tests that have no declared size.
- `--dont-report-useless-tests`: Do not report tests that do not test anything.
- `--stop-on-defect`: Stop execution upon first not-passed test.
- `--stop-on-error`: Stop execution upon first error.
- `--stop-on-failure`: Stop execution upon first error or failure.
- `--stop-on-warning`: Stop execution upon first warning.
- `--stop-on-risky`: Stop execution upon first risky test.
- `--stop-on-deprecation`: Stop after first test that triggered a deprecation.
- `--stop-on-notice`: Stop after first test that triggered a notice.
- `--stop-on-skipped`: Stop execution upon first skipped test.
- `--stop-on-incomplete`: Stop execution upon first incomplete test.
- `--fail-on-empty-test-suite`: Signal failure using shell exit code when no tests were run.
- `--fail-on-warning`: Treat tests with warnings as failures.
- `--fail-on-risky`: Treat risky tests as failures.
- `--fail-on-deprecation`: Signal failure using shell exit code when a deprecation was triggered.
- `--fail-on-phpunit-deprecation`: Signal failure using shell exit code when a PHPUnit deprecation was triggered.
- `--fail-on-notice`: Signal failure using shell exit code when a notice was triggered.
- `--fail-on-skipped`: Treat skipped tests as failures.
- `--fail-on-incomplete`: Signal failure using shell exit code when a test was marked incomplete.
- `--cache-result`: Write test results to cache file.
- `--do-not-cache-result`: Do not write test results to cache file
- `--order-by <order>`: Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size.
- `--random-order-seed <N>`: Use the specified random seed when running tests in random order

## Reporting

- `--colors <flag>`: Use colors in output ("never", "auto" or "always").
- `--columns <n>`: Number of columns to use for progress output.
- `--columns max`: Use maximum number of columns for progress output.
- `--stderr`: Write to STDERR instead of STDOUT.
- `--no-progress`: Disable output of test execution progress.
- `--no-results`: Disable output of test results.
- `--no-output`: Disable all output.
- `--display-incomplete`: Display details for incomplete tests.
- `--display-skipped`: Display details for skipped tests.
- `--display-deprecations`: Display details for deprecations triggered by tests.
- `--display-phpunit-deprecations`: Display details for PHPUnit deprecations.
- `--display-errors`: Display details for errors triggered by tests.
- `--display-notices`: Display details for notices triggered by tests.
- `--display-warnings`: Display details for warnings triggered by tests.
- `--reverse-list`: Print defects in reverse order.
- `--teamcity`: Replace default progress and result output with TeamCity format.
- `--testdox`: Replace default result output with TestDox format
- `--testdox-summary`: Repeat TestDox output for tests with errors, failures, or issues.
- `--debug`: Replace default progress and result output with debugging information.
- `--compact`:  Replace default result output with Compact format

## Logging

- `--log-junit <file>`: Write test results in JUnit XML format to file.
- `--log-teamcity <file>`: Write test results in TeamCity format to file.
- `--testdox-html <file>`: Write test results in TestDox format (HTML) to file.
- `--testdox-text <file>`: Write test results in TestDox format (plain text) to file.
- `--log-events-text <file>`: Stream events as plain text to file.
- `--log-events-verbose-text <file>`: Stream events as plain text (with telemetry information) to file.
- `--no-logging`: Ignore logging configured in the XML configuration file

## Code Coverage

- `--coverage`: Generate code coverage report and output to standard output.
- `--coverage --min=<value>`: Set the minimum required coverage percentage, and fail if not met.
- `--coverage --exactly=<value>`: Set the exact required coverage percentage, and fail if not met.
- `--coverage --only-covered`: Hide files with 0% coverage from the code coverage report.
- `--coverage-clover <file>`: Write code coverage report in Clover XML format to file.
- `--coverage-cobertura <file>`: Write code coverage report in Cobertura XML format to file.
- `--coverage-crap4j <file>`: Write code coverage report in Crap4J XML format to file.
- `--coverage-html <dir>`: Write code coverage report in HTML format to directory.
- `--coverage-php <file>`: Write serialized code coverage data to file.
- `--coverage-text=<file>`: Write code coverage report in text format to file [default: standard output].
- `--only-summary-for-coverage-text`: Option for code coverage report in text format: only show summary.
- `--show-uncovered-for-coverage-text`: Option for code coverage report in text format: show uncovered files.
- `--coverage-xml <dir>`: Write code coverage report in XML format to directory.
- `--warm-coverage-cache`: Warm static analysis cache.
- `--coverage-filter <dir>`: Include `<dir>`: in code coverage reporting.
- `--path-coverage`: Report path coverage in addition to line coverage.
- `--disable-coverage-ignore`: Disable metadata for ignoring code coverage.
- `--no-coverage`: Ignore code coverage reporting configured in the XML configuration file

## Mutation Testing

- `--mutate`: Runs mutation testing, to understand the quality of your tests.
- `--mutate --parallel`: Runs mutation testing in parallel.
- `--mutate --min`: Set the minimum required mutation score, and fail if not met.
- `--mutate --id`:  Run only the mutation with the given ID. But E.g. --id=ecb35ab30ffd3491. Note, you need to provide the same options as the original run.
- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.
- `--mutate --bail`: Stop mutation testing execution upon first untested or uncovered mutation.
- `--mutate --class`: Generate mutations for the given class(es). E.g. --class=App\\Models.
- `--mutate --ignore`: Ignore the given class(es) when generating mutations. E.g. --ignore=App\\Http\\Requests.
- `--mutate --clear-cache`: Clear the mutation cache.
- `--mutate --no-cache`: Clear the mutation cache.
- `--mutate --ignore-min-score-on-zero-mutations`: Ignore the minimum score requirement when there are no mutations.
- `--mutate --covered-only`: Only generate mutations for classes that are covered by tests.
- `--mutate --everything`: Generate mutations for all classes, even if they are not covered by tests.
- `--mutate --profile`: Output to standard output the top ten slowest mutations.
- `--mutate --retry`: Run untested or uncovered mutations first and stop execution upon first error or failure.
- `--mutate --stop-on-uncovered`: Stop mutation testing execution upon first untested mutation.
- `--mutate --stop-on-untested`: Stop mutation testing execution upon first untested mutation.

## Profiling

- `--profile`: Output to standard output the top ten slowest tests

---

In this chapter, you found a complete list of CLI options provided by Pest. In the subsequent documentation, we will explore the topic of test dependencies: [Test Dependencies](/docs/test-dependencies)

---

## Vite Working With Blade and Routes Refreshing on Save
Vite | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/vite.md

### Refreshing on Save

When your application is built using traditional server-side rendering with Blade, Vite can improve your development workflow by automatically refreshing the browser when you make changes to view files in your application. To get started, you can simply specify the `refresh` option as `true`.

```js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: true,
        }),
    ],
});
```

When the `refresh` option is `true`, saving files in the following directories will trigger the browser to perform a full page refresh while you are running `npm run dev`:

- `app/Livewire/**`
- `app/View/Components/**`
- `lang/**`
- `resources/lang/**`
- `resources/views/**`
- `routes/**`

Watching the `routes/**` directory is useful if you are utilizing [Ziggy](https://github.com/tighten/ziggy) to generate route links within your application's frontend.

If these default paths do not suit your needs, you can specify your own list of paths to watch:

```js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: ['resources/views/**'],
        }),
    ],
});
```

Under the hood, the Laravel Vite plugin uses the [vite-plugin-full-reload](https://github.com/ElMassimo/vite-plugin-full-reload) package, which offers some advanced configuration options to fine-tune this feature's behavior. If you need this level of customization, you may provide a `config` definition:

```js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: [{
                paths: ['path/to/watch/**'],
                config: { delay: 300 }
            }],
        }),
    ],
});
```

---

## Controllers Resource Controllers Partial Resource Routes API Resource Routes
Controllers | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/controllers.md

#### API Resource Routes

When declaring resource routes that will be consumed by APIs, you will commonly want to exclude routes that present HTML templates such as `create` and `edit`. For convenience, you may use the `apiResource` method to automatically exclude these two routes:

```php
use App\Http\Controllers\PhotoController;

Route::apiResource('photos', PhotoController::class);
```

You may register many API resource controllers at once by passing an array to the `apiResources` method:

```php
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\PostController;

Route::apiResources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);
```

To quickly generate an API resource controller that does not include the `create` or `edit` methods, use the `--api` switch when executing the `make:controller` command:

```shell
php artisan make:controller PhotoController --api
```

---

## Routing Route Groups Middleware
Routing | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/routing.md

### Middleware

To assign [middleware](/docs/{{version}}/middleware) to all routes within a group, you may use the `middleware` method before defining the group. Middleware are executed in the order they are listed in the array:

```php
Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second middleware...
    });

    Route::get('/user/profile', function () {
        // Uses first & second middleware...
    });
});
```

---

## Sanctum Mobile Application Authentication Issuing API Tokens
Sanctum | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/sanctum.md

### Issuing API Tokens

To get started, create a route that accepts the user's email / username, password, and device name, then exchanges those credentials for a new Sanctum token. The "device name" given to this endpoint is for informational purposes and may be any value you wish. In general, the device name value should be a name the user would recognize, such as "Nuno's iPhone 17".

Typically, you will make a request to the token endpoint from your mobile application's "login" screen. The endpoint will return the plain-text API token which may then be stored on the mobile device and used to make additional API requests:

```php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

Route::post('/sanctum/token', function (Request $request) {
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device_name' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

    return $user->createToken($request->device_name)->plainTextToken;
});
```

When the mobile application uses the token to make an API request to your application, it should pass the token in the `Authorization` header as a `Bearer` token.

> [!NOTE]
> When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities).

---

## Vite Working With Blade and Routes Processing Static Assets With Vite
Vite | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/vite.md

### Processing Static Assets With Vite

When referencing assets in your JavaScript or CSS, Vite automatically processes and versions them. In addition, when building Blade based applications, Vite can also process and version static assets that you reference solely in Blade templates.

However, in order to accomplish this, you need to make Vite aware of your assets by importing the static assets into the application's entry point. For example, if you want to process and version all images stored in `resources/images` and all fonts stored in `resources/fonts`, you should add the following in your application's `resources/js/app.js` entry point:

```js
import.meta.glob([
  '../images/**',
  '../fonts/**',
]);
```

These assets will now be processed by Vite when running `npm run build`. You can then reference these assets in Blade templates using the `Vite::asset` method, which will return the versioned URL for a given asset:

```blade
<img src="{{ Vite::asset('resources/images/logo.png') }}">
```

---

## Vite Working With Blade and Routes
Vite | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/vite.md

## Working With Blade and Routes

---

## Sanctum API Token Authentication Token Expiration
Sanctum | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/sanctum.md

### Token Expiration

By default, Sanctum tokens never expire and may only be invalidated by [revoking the token](#revoking-tokens). However, if you would like to configure an expiration time for your application's API tokens, you may do so via the `expiration` configuration option defined in your application's `sanctum` configuration file. This configuration option defines the number of minutes until an issued token will be considered expired:

```php
'expiration' => 525600,
```

If you would like to specify the expiration time of each token independently, you may do so by providing the expiration time as the third argument to the `createToken` method:

```php
return $user->createToken(
    'token-name', ['*'], now()->plus(weeks: 1)
)->plainTextToken;
```

If you have configured a token expiration time for your application, you may also wish to [schedule a task](/docs/{{version}}/scheduling) to prune your application's expired tokens. Thankfully, Sanctum includes a `sanctum:prune-expired` Artisan command that you may use to accomplish this. For example, you may configure a scheduled task to delete all expired token database records that have been expired for at least 24 hours:

```php
use Illuminate\Support\Facades\Schedule;

Schedule::command('sanctum:prune-expired --hours=24')->daily();
```

---

## Controllers Resource Controllers Singleton Resource Controllers API Singleton Resources
Controllers | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/controllers.md

#### API Singleton Resources

The `apiSingleton` method may be used to register a singleton resource that will be manipulated via an API, thus rendering the `create` and `edit` routes unnecessary:

```php
Route::apiSingleton('profile', ProfileController::class);
```

Of course, API singleton resources may also be `creatable`, which will register `store` and `destroy` routes for the resource:

```php
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
```

---

## Sanctum API Token Authentication Protecting Routes
Sanctum | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/sanctum.md

### Protecting Routes

To protect routes so that all incoming requests must be authenticated, you should attach the `sanctum` authentication guard to your protected routes within your `routes/web.php` and `routes/api.php` route files. This guard will ensure that incoming requests are authenticated as either stateful, cookie authenticated requests or contain a valid API token header if the request is from a third party.

You may be wondering why we suggest that you authenticate the routes within your application's `routes/web.php` file using the `sanctum` guard. Remember, Sanctum will first attempt to authenticate incoming requests using Laravel's typical session authentication cookie. If that cookie is not present then Sanctum will attempt to authenticate the request using a token in the request's `Authorization` header. In addition, authenticating all requests using Sanctum ensures that we may always call the `tokenCan` method on the currently authenticated user instance:

```php
use Illuminate\Http\Request;

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');
```

---

## Routing Route Groups
Routing | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/routing.md

## Route Groups

Route groups allow you to share route attributes, such as middleware, across a large number of routes without needing to define those attributes on each individual route.

Nested groups attempt to intelligently "merge" attributes with their parent group. Middleware and `where` conditions are merged while names and prefixes are appended. Namespace delimiters and slashes in URI prefixes are automatically added where appropriate.

---

## Verification Routing Protecting Routes
Verification | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/verification.md

### Protecting Routes

[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel includes a `verified` [middleware alias](/docs/{{version}}/middleware#middleware-aliases), which is an alias for the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` middleware class. Since this alias is already automatically registered by Laravel, all you need to do is attach the `verified` middleware to a route definition. Typically, this middleware is paired with the `auth` middleware:

```php
Route::get('/profile', function () {
    // Only verified users may access this route...
})->middleware(['auth', 'verified']);
```

If an unverified user attempts to access a route that has been assigned this middleware, they will automatically be redirected to the `verification.notice` [named route](/docs/{{version}}/routing#named-routes).

---

## Sanctum API Token Authentication Revoking Tokens
Sanctum | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/sanctum.md

### Revoking Tokens

You may "revoke" tokens by deleting them from your database using the `tokens` relationship that is provided by the `Laravel\Sanctum\HasApiTokens` trait:

```php
// Revoke all tokens...
$user->tokens()->delete();

// Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();
```

---

## Http Tests Testing JSON APIs Fluent JSON Testing Scoping JSON Collection Assertions
Http Tests | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/http-tests.md

#### Scoping JSON Collection Assertions

Sometimes, your application's routes will return JSON collections that are assigned named keys:

```php
Route::get('/users', function () {
    return [
        'meta' => [...],
        'users' => User::all(),
    ];
})
```

When testing these routes, you may use the `has` method to assert against the number of items in the collection. In addition, you may use the `has` method to scope a chain of assertions:

```php
$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3)
            ->has('users.0', fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                    ->missing('password')
                    ->etc()
            )
    );
```

However, instead of making two separate calls to the `has` method to assert against the `users` collection, you may make a single call which provides a closure as its third parameter. When doing so, the closure will automatically be invoked and scoped to the first item in the collection:

```php
$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
            ->has('users', 3, fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                    ->missing('password')
                    ->etc()
            )
    );
```

---

## Verification Routing Resending the Verification Email
Verification | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/verification.md

### Resending the Verification Email

Sometimes a user may misplace or accidentally delete the email address verification email. To accommodate this, you may wish to define a route to allow the user to request that the verification email be resent. You may then make a request to this route by placing a simple form submission button within your [verification notice view](#the-email-verification-notice):

```php
use Illuminate\Http\Request;

Route::post('/email/verification-notification', function (Request $request) {
    $request->user()->sendEmailVerificationNotification();

    return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
```

---

## Sanctum API Token Authentication Token Abilities First-Party UI Initiated Requests
Sanctum | laravel/framework@12.x | Source: https://github.com/laravel/docs/blob/12.x/sanctum.md

#### First-Party UI Initiated Requests

For convenience, the `tokenCan` method will always return `true` if the incoming authenticated request was from your first-party SPA and you are using Sanctum's built-in [SPA authentication](#spa-authentication).

However, this does not necessarily mean that your application has to allow the user to perform the action. Typically, your application's [authorization policies](/docs/{{version}}/authorization#creating-policies) will determine if the token has been granted the permission to perform the abilities as well as check that the user instance itself should be allowed to perform the action.

For example, if we imagine an application that manages servers, this might mean checking that the token is authorized to update servers **and** that the server belongs to the user:

```php
return $request->user()->id === $server->user_id &&
       $request->user()->tokenCan('server:update')
```

At first, allowing the `tokenCan` method to be called and always return `true` for first-party UI initiated requests may seem strange; however, it is convenient to be able to always assume an API token is available and can be inspected via the `tokenCan` method. By taking this approach, you may always call the `tokenCan` method within your application's authorization policies without worrying about whether the request was triggered from your application's UI or was initiated by one of your API's third-party consumers.

_[Truncated]_