chore: Update composer packages and laravel boost (#150)
This commit is contained in:
parent
255033999d
commit
545cc66024
|
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
name: developing-with-fortify
|
||||
description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
|
||||
---
|
||||
|
||||
# Laravel Fortify Development
|
||||
|
||||
Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Laravel Fortify patterns and documentation.
|
||||
|
||||
## Usage
|
||||
|
||||
- **Routes**: Use `list-routes` with `only_vendor: true` and `action: "Fortify"` to see all registered endpoints
|
||||
- **Actions**: Check `app/Actions/Fortify/` for customizable business logic (user creation, password validation, etc.)
|
||||
- **Config**: See `config/fortify.php` for all options including features, guards, rate limiters, and username field
|
||||
- **Contracts**: Look in `Laravel\Fortify\Contracts\` for overridable response classes (`LoginResponse`, `LogoutResponse`, etc.)
|
||||
- **Views**: All view callbacks are set in `FortifyServiceProvider::boot()` using `Fortify::loginView()`, `Fortify::registerView()`, etc.
|
||||
|
||||
## Available Features
|
||||
|
||||
Enable in `config/fortify.php` features array:
|
||||
|
||||
- `Features::registration()` - User registration
|
||||
- `Features::resetPasswords()` - Password reset via email
|
||||
- `Features::emailVerification()` - Requires User to implement `MustVerifyEmail`
|
||||
- `Features::updateProfileInformation()` - Profile updates
|
||||
- `Features::updatePasswords()` - Password changes
|
||||
- `Features::twoFactorAuthentication()` - 2FA with QR codes and recovery codes
|
||||
|
||||
> Use `search-docs` for feature configuration options and customization patterns.
|
||||
|
||||
## Setup Workflows
|
||||
|
||||
### Two-Factor Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Add TwoFactorAuthenticatable trait to User model
|
||||
- [ ] Enable feature in config/fortify.php
|
||||
- [ ] If the `*_add_two_factor_columns_to_users_table.php` migration is missing, publish via `php artisan vendor:publish --tag=fortify-migrations` and migrate
|
||||
- [ ] Set up view callbacks in FortifyServiceProvider
|
||||
- [ ] Create 2FA management UI
|
||||
- [ ] Test QR code and recovery codes
|
||||
```
|
||||
|
||||
> Use `search-docs` for TOTP implementation and recovery code handling patterns.
|
||||
|
||||
### Email Verification Setup
|
||||
|
||||
```
|
||||
- [ ] Enable emailVerification feature in config
|
||||
- [ ] Implement MustVerifyEmail interface on User model
|
||||
- [ ] Set up verifyEmailView callback
|
||||
- [ ] Add verified middleware to protected routes
|
||||
- [ ] Test verification email flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for MustVerifyEmail implementation patterns.
|
||||
|
||||
### Password Reset Setup
|
||||
|
||||
```
|
||||
- [ ] Enable resetPasswords feature in config
|
||||
- [ ] Set up requestPasswordResetLinkView callback
|
||||
- [ ] Set up resetPasswordView callback
|
||||
- [ ] Define password.reset named route (if views disabled)
|
||||
- [ ] Test reset email and link flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for custom password reset flow patterns.
|
||||
|
||||
### SPA Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Set 'views' => false in config/fortify.php
|
||||
- [ ] Install and configure Laravel Sanctum for session-based SPA authentication
|
||||
- [ ] Use the 'web' guard in config/fortify.php (required for session-based authentication)
|
||||
- [ ] Set up CSRF token handling
|
||||
- [ ] Test XHR authentication flows
|
||||
```
|
||||
|
||||
> Use `search-docs` for integration and SPA authentication patterns.
|
||||
|
||||
#### Two-Factor Authentication in SPA Mode
|
||||
|
||||
When `views` is set to `false`, Fortify returns JSON responses instead of redirects.
|
||||
|
||||
If a user attempts to log in and two-factor authentication is enabled, the login request will return a JSON response indicating that a two-factor challenge is required:
|
||||
|
||||
```json
|
||||
{
|
||||
"two_factor": true
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Custom Authentication Logic
|
||||
|
||||
Override authentication behavior using `Fortify::authenticateUsing()` for custom user retrieval or `Fortify::authenticateThrough()` to customize the authentication pipeline. Override response contracts in `AppServiceProvider` for custom redirects.
|
||||
|
||||
### Registration Customization
|
||||
|
||||
Modify `app/Actions/Fortify/CreateNewUser.php` to customize user creation logic, validation rules, and additional fields.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Configure via `fortify.limiters.login` in config. Default configuration throttles by username + IP combination.
|
||||
|
||||
## Key Endpoints
|
||||
|
||||
| Feature | Method | Endpoint |
|
||||
|------------------------|----------|---------------------------------------------|
|
||||
| Login | POST | `/login` |
|
||||
| Logout | POST | `/logout` |
|
||||
| Register | POST | `/register` |
|
||||
| Password Reset Request | POST | `/forgot-password` |
|
||||
| Password Reset | POST | `/reset-password` |
|
||||
| Email Verify Notice | GET | `/email/verify` |
|
||||
| Resend Verification | POST | `/email/verification-notification` |
|
||||
| Password Confirm | POST | `/user/confirm-password` |
|
||||
| Enable 2FA | POST | `/user/two-factor-authentication` |
|
||||
| Confirm 2FA | POST | `/user/confirmed-two-factor-authentication` |
|
||||
| 2FA Challenge | POST | `/two-factor-challenge` |
|
||||
| Get QR Code | GET | `/user/two-factor-qr-code` |
|
||||
| Recovery Codes | GET/POST | `/user/two-factor-recovery-codes` |
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
---
|
||||
name: inertia-react-development
|
||||
description: "Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Inertia React Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating or modifying React page components for Inertia
|
||||
- Working with forms in React (using `<Form>` or `useForm`)
|
||||
- Implementing client-side navigation with `<Link>` or `router`
|
||||
- Using v2 features: deferred props, prefetching, or polling
|
||||
- Building React-specific features with the Inertia protocol
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v2 React patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
React page components should be placed in the `resources/js/pages` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
<!-- Basic React Page Component -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{users.map(user => <li key={user.id}>{user.name}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
<!-- Inertia React Navigation -->
|
||||
```react
|
||||
import { Link, router } from '@inertiajs/react'
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
```
|
||||
|
||||
### Link with Method
|
||||
|
||||
<!-- Link with POST Method -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
```
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
<!-- Prefetch on Hover -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/users" prefetch>
|
||||
Users
|
||||
</Link>
|
||||
```
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
<!-- Router Visit -->
|
||||
```react
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
```
|
||||
|
||||
## Form Handling
|
||||
|
||||
### Form Component (Recommended)
|
||||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
<!-- Form Component Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
return (
|
||||
<Form action="/users" method="post">
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input type="email" name="email" />
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Creating...' : 'Create User'}
|
||||
</button>
|
||||
|
||||
{wasSuccessful && <div>User created!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Form Component With All Props
|
||||
|
||||
<!-- Form Component Full Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form action="/users" method="post">
|
||||
{({
|
||||
errors,
|
||||
hasErrors,
|
||||
processing,
|
||||
progress,
|
||||
wasSuccessful,
|
||||
recentlySuccessful,
|
||||
clearErrors,
|
||||
resetAndClearErrors,
|
||||
defaults,
|
||||
isDirty,
|
||||
reset,
|
||||
submit
|
||||
}) => (
|
||||
<>
|
||||
<input type="text" name="name" defaultValue={defaults.name} />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
|
||||
{progress && (
|
||||
<progress value={progress.percentage} max="100">
|
||||
{progress.percentage}%
|
||||
</progress>
|
||||
)}
|
||||
|
||||
{wasSuccessful && <div>Saved!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
```
|
||||
|
||||
### Form Component Reset Props
|
||||
|
||||
The `<Form>` component supports automatic resetting:
|
||||
|
||||
- `resetOnError` - Reset form data when the request fails
|
||||
- `resetOnSuccess` - Reset form data when the request succeeds
|
||||
- `setDefaultsOnSuccess` - Update default values on success
|
||||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
<!-- Form with Reset Props -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
resetOnSuccess
|
||||
setDefaultsOnSuccess
|
||||
>
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Submit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
```
|
||||
|
||||
Forms can also be built using the `useForm` helper for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
### `useForm` Hook
|
||||
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
||||
|
||||
<!-- useForm Hook Example -->
|
||||
```react
|
||||
import { useForm } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault()
|
||||
post('/users', {
|
||||
onSuccess: () => reset('password'),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<input
|
||||
type="text"
|
||||
value={data.name}
|
||||
onChange={e => setData('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={e => setData('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<input
|
||||
type="password"
|
||||
value={data.password}
|
||||
onChange={e => setData('password', e.target.value)}
|
||||
/>
|
||||
{errors.password && <div>{errors.password}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
### Deferred Props
|
||||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
<!-- Deferred Props with Empty State -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
// users will be undefined initially, then populated
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
{!users ? (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
) : (
|
||||
<ul>
|
||||
{users.map(user => (
|
||||
<li key={user.id}>{user.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Polling
|
||||
|
||||
Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
<!-- Basic Polling -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
usePoll(5000)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Polling With Request Options and Manual Control -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
<button onClick={start}>Start Polling</button>
|
||||
<button onClick={stop}>Stop Polling</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
### WhenVisible (Infinite Scroll)
|
||||
|
||||
Load more data when user scrolls to a specific element:
|
||||
|
||||
<!-- Infinite Scroll with WhenVisible -->
|
||||
```react
|
||||
import { WhenVisible } from '@inertiajs/react'
|
||||
|
||||
export default function UsersList({ users }) {
|
||||
return (
|
||||
<div>
|
||||
{users.data.map(user => (
|
||||
<div key={user.id}>{user.name}</div>
|
||||
))}
|
||||
|
||||
{users.next_page_url && (
|
||||
<WhenVisible
|
||||
data="users"
|
||||
params={{ page: users.current_page + 1 }}
|
||||
fallback={<div>Loading more...</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Forgetting to add loading states (skeleton screens) when using deferred props
|
||||
- Not handling the `undefined` state of deferred props before data loads
|
||||
- Using `<form>` without preventing default submission (use `<Form>` component or `e.preventDefault()`)
|
||||
- Forgetting to check if `<Form>` component is available in your Inertia version
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
name: pennant-development
|
||||
description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pennant Features
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating or checking feature flags
|
||||
- Managing feature rollouts
|
||||
- Implementing A/B testing
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pennant patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Defining Features
|
||||
|
||||
<!-- Defining Features -->
|
||||
```php
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
Feature::define('new-dashboard', function (User $user) {
|
||||
return $user->isAdmin();
|
||||
});
|
||||
```
|
||||
|
||||
### Checking Features
|
||||
|
||||
<!-- Checking Features -->
|
||||
```php
|
||||
if (Feature::active('new-dashboard')) {
|
||||
// Feature is active
|
||||
}
|
||||
|
||||
// With scope
|
||||
if (Feature::for($user)->active('new-dashboard')) {
|
||||
// Feature is active for this user
|
||||
}
|
||||
```
|
||||
|
||||
### Blade Directive
|
||||
|
||||
<!-- Blade Directive -->
|
||||
```blade
|
||||
@feature('new-dashboard')
|
||||
<x-new-dashboard />
|
||||
@else
|
||||
<x-old-dashboard />
|
||||
@endfeature
|
||||
```
|
||||
|
||||
### Activating / Deactivating
|
||||
|
||||
<!-- Activating Features -->
|
||||
```php
|
||||
Feature::activate('new-dashboard');
|
||||
Feature::for($user)->activate('new-dashboard');
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check feature flag is defined
|
||||
2. Test with different scopes/users
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Forgetting to scope features for specific users/entities
|
||||
- Not following existing naming conventions
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
name: pest-testing
|
||||
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pest Testing 4
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating new tests (unit, feature, or browser)
|
||||
- Modifying existing tests
|
||||
- Debugging test failures
|
||||
- Working with browser testing or smoke testing
|
||||
- Writing architecture tests or visual regression tests
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pest 4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Tests
|
||||
|
||||
All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
|
||||
- Browser tests: `tests/Browser/` directory.
|
||||
- Do NOT remove tests without approval - these are core application code.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
<!-- Basic Pest Test Example -->
|
||||
```php
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
|
||||
- Run all tests: `php artisan test --compact`.
|
||||
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
|
||||
|
||||
## Assertions
|
||||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
<!-- Pest Response Assertion -->
|
||||
```php
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
```
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
| `assertSuccessful()` | `assertStatus(200)` |
|
||||
| `assertNotFound()` | `assertStatus(404)` |
|
||||
| `assertForbidden()` | `assertStatus(403)` |
|
||||
|
||||
## Mocking
|
||||
|
||||
Import mock function before use: `use function Pest\Laravel\mock;`
|
||||
|
||||
## Datasets
|
||||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
<!-- Pest Dataset Example -->
|
||||
```php
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
```
|
||||
|
||||
## Pest 4 Features
|
||||
|
||||
| Feature | Purpose |
|
||||
|---------|---------|
|
||||
| Browser Testing | Full integration tests in real browsers |
|
||||
| Smoke Testing | Validate multiple pages quickly |
|
||||
| Visual Regression | Compare screenshots for visual changes |
|
||||
| Test Sharding | Parallel CI runs |
|
||||
| Architecture Testing | Enforce code conventions |
|
||||
|
||||
### Browser Test Example
|
||||
|
||||
Browser tests run in real browsers for full integration testing:
|
||||
|
||||
- Browser tests live in `tests/Browser/`.
|
||||
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
|
||||
- Use `RefreshDatabase` for clean state per test.
|
||||
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
|
||||
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
|
||||
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging.
|
||||
|
||||
<!-- Pest Browser Test Example -->
|
||||
```php
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in');
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavaScriptErrors()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!');
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
```
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Quickly validate multiple pages have no JavaScript errors:
|
||||
|
||||
<!-- Pest Smoke Testing Example -->
|
||||
```php
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
|
||||
```
|
||||
|
||||
### Visual Regression Testing
|
||||
|
||||
Capture and compare screenshots to detect visual changes.
|
||||
|
||||
### Test Sharding
|
||||
|
||||
Split tests across parallel processes for faster CI runs.
|
||||
|
||||
### Architecture Testing
|
||||
|
||||
Pest 4 includes architecture testing (from Pest 3):
|
||||
|
||||
<!-- Architecture Test Example -->
|
||||
```php
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not importing `use function Pest\Laravel\mock;` before using mock
|
||||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Forgetting `assertNoJavaScriptErrors()` in browser tests
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
name: tailwindcss-development
|
||||
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Tailwind CSS Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Adding styles to components or pages
|
||||
- Working with responsive design
|
||||
- Implementing dark mode
|
||||
- Extracting repeated patterns into components
|
||||
- Debugging spacing or layout issues
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
|
||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||
|
||||
## Tailwind CSS v4 Specifics
|
||||
|
||||
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||
- `corePlugins` is not supported in Tailwind v4.
|
||||
|
||||
### CSS-First Configuration
|
||||
|
||||
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||
|
||||
<!-- CSS-First Config -->
|
||||
```css
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
```
|
||||
|
||||
### Import Syntax
|
||||
|
||||
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||
|
||||
<!-- v4 Import Syntax -->
|
||||
```diff
|
||||
- @tailwind base;
|
||||
- @tailwind components;
|
||||
- @tailwind utilities;
|
||||
+ @import "tailwindcss";
|
||||
```
|
||||
|
||||
### Replaced Utilities
|
||||
|
||||
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
|
||||
|
||||
| Deprecated | Replacement |
|
||||
|------------|-------------|
|
||||
| bg-opacity-* | bg-black/* |
|
||||
| text-opacity-* | text-black/* |
|
||||
| border-opacity-* | border-black/* |
|
||||
| divide-opacity-* | divide-black/* |
|
||||
| ring-opacity-* | ring-black/* |
|
||||
| placeholder-opacity-* | placeholder-black/* |
|
||||
| flex-shrink-* | shrink-* |
|
||||
| flex-grow-* | grow-* |
|
||||
| overflow-ellipsis | text-ellipsis |
|
||||
| decoration-slice | box-decoration-slice |
|
||||
| decoration-clone | box-decoration-clone |
|
||||
|
||||
## Spacing
|
||||
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
|
||||
- Using `@tailwind` directives instead of `@import "tailwindcss"`
|
||||
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
|
||||
- Using margins for spacing between siblings instead of gap utilities
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
name: wayfinder-development
|
||||
description: "Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Wayfinder Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate whenever referencing backend routes in frontend components:
|
||||
- Importing from `@/actions/` or `@/routes/`
|
||||
- Calling Laravel routes from TypeScript/JavaScript
|
||||
- Creating links or navigation to backend endpoints
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Wayfinder patterns and documentation.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Generate Routes
|
||||
|
||||
Run after route changes if Vite plugin isn't installed:
|
||||
```bash
|
||||
php artisan wayfinder:generate --no-interaction
|
||||
```
|
||||
For form helpers, use `--with-form` flag:
|
||||
```bash
|
||||
php artisan wayfinder:generate --with-form --no-interaction
|
||||
```
|
||||
|
||||
### Import Patterns
|
||||
|
||||
<!-- Controller Action Imports -->
|
||||
```typescript
|
||||
// Named imports for tree-shaking (preferred)...
|
||||
import { show, store, update } from '@/actions/App/Http/Controllers/PostController'
|
||||
|
||||
// Named route imports...
|
||||
import { show as postShow } from '@/routes/post'
|
||||
```
|
||||
|
||||
### Common Methods
|
||||
|
||||
<!-- Wayfinder Methods -->
|
||||
```typescript
|
||||
// Get route object...
|
||||
show(1) // { url: "/posts/1", method: "get" }
|
||||
|
||||
// Get URL string...
|
||||
show.url(1) // "/posts/1"
|
||||
|
||||
// Specific HTTP methods...
|
||||
show.get(1)
|
||||
store.post()
|
||||
update.patch(1)
|
||||
destroy.delete(1)
|
||||
|
||||
// Form attributes for HTML forms...
|
||||
store.form() // { action: "/posts", method: "post" }
|
||||
|
||||
// Query parameters...
|
||||
show(1, { query: { page: 1 } }) // "/posts/1?page=1"
|
||||
```
|
||||
|
||||
## Wayfinder + Inertia
|
||||
|
||||
Use Wayfinder with the `<Form>` component:
|
||||
<!-- Wayfinder Form (React) -->
|
||||
```typescript
|
||||
<Form {...store.form()}><input name="title" /></Form>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Run `php artisan wayfinder:generate` to regenerate routes if Vite plugin isn't installed
|
||||
2. Check TypeScript imports resolve correctly
|
||||
3. Verify route URLs match expected paths
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using default imports instead of named imports (breaks tree-shaking)
|
||||
- Forgetting to regenerate after route changes
|
||||
- Not using type-safe parameter objects for route model binding
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
name: developing-with-fortify
|
||||
description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
|
||||
---
|
||||
|
||||
# Laravel Fortify Development
|
||||
|
||||
Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Laravel Fortify patterns and documentation.
|
||||
|
||||
## Usage
|
||||
|
||||
- **Routes**: Use `list-routes` with `only_vendor: true` and `action: "Fortify"` to see all registered endpoints
|
||||
- **Actions**: Check `app/Actions/Fortify/` for customizable business logic (user creation, password validation, etc.)
|
||||
- **Config**: See `config/fortify.php` for all options including features, guards, rate limiters, and username field
|
||||
- **Contracts**: Look in `Laravel\Fortify\Contracts\` for overridable response classes (`LoginResponse`, `LogoutResponse`, etc.)
|
||||
- **Views**: All view callbacks are set in `FortifyServiceProvider::boot()` using `Fortify::loginView()`, `Fortify::registerView()`, etc.
|
||||
|
||||
## Available Features
|
||||
|
||||
Enable in `config/fortify.php` features array:
|
||||
|
||||
- `Features::registration()` - User registration
|
||||
- `Features::resetPasswords()` - Password reset via email
|
||||
- `Features::emailVerification()` - Requires User to implement `MustVerifyEmail`
|
||||
- `Features::updateProfileInformation()` - Profile updates
|
||||
- `Features::updatePasswords()` - Password changes
|
||||
- `Features::twoFactorAuthentication()` - 2FA with QR codes and recovery codes
|
||||
|
||||
> Use `search-docs` for feature configuration options and customization patterns.
|
||||
|
||||
## Setup Workflows
|
||||
|
||||
### Two-Factor Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Add TwoFactorAuthenticatable trait to User model
|
||||
- [ ] Enable feature in config/fortify.php
|
||||
- [ ] If the `*_add_two_factor_columns_to_users_table.php` migration is missing, publish via `php artisan vendor:publish --tag=fortify-migrations` and migrate
|
||||
- [ ] Set up view callbacks in FortifyServiceProvider
|
||||
- [ ] Create 2FA management UI
|
||||
- [ ] Test QR code and recovery codes
|
||||
```
|
||||
|
||||
> Use `search-docs` for TOTP implementation and recovery code handling patterns.
|
||||
|
||||
### Email Verification Setup
|
||||
|
||||
```
|
||||
- [ ] Enable emailVerification feature in config
|
||||
- [ ] Implement MustVerifyEmail interface on User model
|
||||
- [ ] Set up verifyEmailView callback
|
||||
- [ ] Add verified middleware to protected routes
|
||||
- [ ] Test verification email flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for MustVerifyEmail implementation patterns.
|
||||
|
||||
### Password Reset Setup
|
||||
|
||||
```
|
||||
- [ ] Enable resetPasswords feature in config
|
||||
- [ ] Set up requestPasswordResetLinkView callback
|
||||
- [ ] Set up resetPasswordView callback
|
||||
- [ ] Define password.reset named route (if views disabled)
|
||||
- [ ] Test reset email and link flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for custom password reset flow patterns.
|
||||
|
||||
### SPA Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Set 'views' => false in config/fortify.php
|
||||
- [ ] Install and configure Laravel Sanctum for session-based SPA authentication
|
||||
- [ ] Use the 'web' guard in config/fortify.php (required for session-based authentication)
|
||||
- [ ] Set up CSRF token handling
|
||||
- [ ] Test XHR authentication flows
|
||||
```
|
||||
|
||||
> Use `search-docs` for integration and SPA authentication patterns.
|
||||
|
||||
#### Two-Factor Authentication in SPA Mode
|
||||
|
||||
When `views` is set to `false`, Fortify returns JSON responses instead of redirects.
|
||||
|
||||
If a user attempts to log in and two-factor authentication is enabled, the login request will return a JSON response indicating that a two-factor challenge is required:
|
||||
|
||||
```json
|
||||
{
|
||||
"two_factor": true
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Custom Authentication Logic
|
||||
|
||||
Override authentication behavior using `Fortify::authenticateUsing()` for custom user retrieval or `Fortify::authenticateThrough()` to customize the authentication pipeline. Override response contracts in `AppServiceProvider` for custom redirects.
|
||||
|
||||
### Registration Customization
|
||||
|
||||
Modify `app/Actions/Fortify/CreateNewUser.php` to customize user creation logic, validation rules, and additional fields.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Configure via `fortify.limiters.login` in config. Default configuration throttles by username + IP combination.
|
||||
|
||||
## Key Endpoints
|
||||
|
||||
| Feature | Method | Endpoint |
|
||||
|------------------------|----------|---------------------------------------------|
|
||||
| Login | POST | `/login` |
|
||||
| Logout | POST | `/logout` |
|
||||
| Register | POST | `/register` |
|
||||
| Password Reset Request | POST | `/forgot-password` |
|
||||
| Password Reset | POST | `/reset-password` |
|
||||
| Email Verify Notice | GET | `/email/verify` |
|
||||
| Resend Verification | POST | `/email/verification-notification` |
|
||||
| Password Confirm | POST | `/user/confirm-password` |
|
||||
| Enable 2FA | POST | `/user/two-factor-authentication` |
|
||||
| Confirm 2FA | POST | `/user/confirmed-two-factor-authentication` |
|
||||
| 2FA Challenge | POST | `/two-factor-challenge` |
|
||||
| Get QR Code | GET | `/user/two-factor-qr-code` |
|
||||
| Recovery Codes | GET/POST | `/user/two-factor-recovery-codes` |
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
name: inertia-react-development
|
||||
description: >-
|
||||
Develops Inertia.js v2 React client-side applications. Activates when creating
|
||||
React pages, forms, or navigation; using <Link>, <Form>, useForm, or router;
|
||||
working with deferred props, prefetching, or polling; or when user mentions
|
||||
React with Inertia, React pages, React forms, or React navigation.
|
||||
description: "Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Inertia React Development
|
||||
|
|
@ -27,12 +26,12 @@ Use `search-docs` for detailed Inertia v2 React patterns and documentation.
|
|||
|
||||
### Page Components Location
|
||||
|
||||
React page components should be placed in the `resources/js/Pages` directory.
|
||||
React page components should be placed in the `resources/js/pages` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
<code-snippet name="Basic React Page Component" lang="react">
|
||||
|
||||
<!-- Basic React Page Component -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -43,8 +42,7 @@ export default function UsersIndex({ users }) {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
|
|
@ -52,46 +50,43 @@ export default function UsersIndex({ users }) {
|
|||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
<code-snippet name="Inertia React Navigation" lang="react">
|
||||
|
||||
<!-- Inertia React Navigation -->
|
||||
```react
|
||||
import { Link, router } from '@inertiajs/react'
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Link with Method
|
||||
|
||||
<code-snippet name="Link with POST Method" lang="react">
|
||||
|
||||
<!-- Link with POST Method -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
<code-snippet name="Prefetch on Hover" lang="react">
|
||||
|
||||
<!-- Prefetch on Hover -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/users" prefetch>
|
||||
Users
|
||||
</Link>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
<code-snippet name="Router Visit" lang="react">
|
||||
|
||||
<!-- Router Visit -->
|
||||
```react
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
function handleClick() {
|
||||
|
|
@ -104,8 +99,7 @@ router.visit('/users', {
|
|||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Form Handling
|
||||
|
||||
|
|
@ -113,8 +107,8 @@ router.visit('/users', {
|
|||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
<code-snippet name="Form Component Example" lang="react">
|
||||
|
||||
<!-- Form Component Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
|
|
@ -138,13 +132,12 @@ export default function CreateUser() {
|
|||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Form Component With All Props
|
||||
|
||||
<code-snippet name="Form Component Full Example" lang="react">
|
||||
|
||||
<!-- Form Component Full Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form action="/users" method="post">
|
||||
|
|
@ -180,8 +173,7 @@ import { Form } from '@inertiajs/react'
|
|||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Form Component Reset Props
|
||||
|
||||
|
|
@ -193,8 +185,8 @@ The `<Form>` component supports automatic resetting:
|
|||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
<code-snippet name="Form with Reset Props" lang="react">
|
||||
|
||||
<!-- Form with Reset Props -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form
|
||||
|
|
@ -214,8 +206,7 @@ import { Form } from '@inertiajs/react'
|
|||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
Forms can also be built using the `useForm` helper for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
|
|
@ -223,8 +214,8 @@ Forms can also be built using the `useForm` helper for more programmatic control
|
|||
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
||||
|
||||
<code-snippet name="useForm Hook Example" lang="react">
|
||||
|
||||
<!-- useForm Hook Example -->
|
||||
```react
|
||||
import { useForm } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
|
|
@ -270,8 +261,7 @@ export default function CreateUser() {
|
|||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
|
|
@ -279,8 +269,8 @@ export default function CreateUser() {
|
|||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
<code-snippet name="Deferred Props with Empty State" lang="react">
|
||||
|
||||
<!-- Deferred Props with Empty State -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
// users will be undefined initially, then populated
|
||||
return (
|
||||
|
|
@ -301,26 +291,18 @@ export default function UsersIndex({ users }) {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Polling
|
||||
|
||||
Automatically refresh data at intervals:
|
||||
Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
<code-snippet name="Polling Example" lang="react">
|
||||
|
||||
import { router } from '@inertiajs/react'
|
||||
import { useEffect } from 'react'
|
||||
<!-- Basic Polling -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
router.reload({ only: ['stats'] })
|
||||
}, 5000) // Poll every 5 seconds
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
usePoll(5000)
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -329,15 +311,46 @@ export default function Dashboard({ stats }) {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</code-snippet>
|
||||
<!-- Polling With Request Options and Manual Control -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
<button onClick={start}>Start Polling</button>
|
||||
<button onClick={stop}>Stop Polling</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
### WhenVisible (Infinite Scroll)
|
||||
|
||||
Load more data when user scrolls to a specific element:
|
||||
|
||||
<code-snippet name="Infinite Scroll with WhenVisible" lang="react">
|
||||
|
||||
<!-- Infinite Scroll with WhenVisible -->
|
||||
```react
|
||||
import { WhenVisible } from '@inertiajs/react'
|
||||
|
||||
export default function UsersList({ users }) {
|
||||
|
|
@ -357,8 +370,7 @@ export default function UsersList({ users }) {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
name: pennant-development
|
||||
description: >-
|
||||
Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling
|
||||
feature flags; showing or hiding features conditionally; implementing A/B testing; working with
|
||||
@feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional
|
||||
features, rollouts, or gradually enabling features.
|
||||
description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pennant Features
|
||||
|
|
@ -25,17 +24,19 @@ Use `search-docs` for detailed Pennant patterns and documentation.
|
|||
|
||||
### Defining Features
|
||||
|
||||
<code-snippet name="Defining Features" lang="php">
|
||||
<!-- Defining Features -->
|
||||
```php
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
Feature::define('new-dashboard', function (User $user) {
|
||||
return $user->isAdmin();
|
||||
});
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Checking Features
|
||||
|
||||
<code-snippet name="Checking Features" lang="php">
|
||||
<!-- Checking Features -->
|
||||
```php
|
||||
if (Feature::active('new-dashboard')) {
|
||||
// Feature is active
|
||||
}
|
||||
|
|
@ -44,24 +45,26 @@ if (Feature::active('new-dashboard')) {
|
|||
if (Feature::for($user)->active('new-dashboard')) {
|
||||
// Feature is active for this user
|
||||
}
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Blade Directive
|
||||
|
||||
<code-snippet name="Blade Directive" lang="blade">
|
||||
<!-- Blade Directive -->
|
||||
```blade
|
||||
@feature('new-dashboard')
|
||||
<x-new-dashboard />
|
||||
@else
|
||||
<x-old-dashboard />
|
||||
@endfeature
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Activating / Deactivating
|
||||
|
||||
<code-snippet name="Activating Features" lang="php">
|
||||
<!-- Activating Features -->
|
||||
```php
|
||||
Feature::activate('new-dashboard');
|
||||
Feature::for($user)->activate('new-dashboard');
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
name: pest-testing
|
||||
description: >-
|
||||
Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature
|
||||
tests, adding assertions, testing Livewire components, browser testing, debugging test failures,
|
||||
working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion,
|
||||
coverage, or needs to verify functionality works.
|
||||
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pest Testing 4
|
||||
|
|
@ -37,13 +36,12 @@ All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
|
|||
|
||||
### Basic Test Structure
|
||||
|
||||
<code-snippet name="Basic Pest Test Example" lang="php">
|
||||
|
||||
<!-- Basic Pest Test Example -->
|
||||
```php
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
|
|
@ -55,13 +53,12 @@ it('is true', function () {
|
|||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
<code-snippet name="Pest Response Assertion" lang="php">
|
||||
|
||||
<!-- Pest Response Assertion -->
|
||||
```php
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
|
|
@ -77,16 +74,15 @@ Import mock function before use: `use function Pest\Laravel\mock;`
|
|||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
<code-snippet name="Pest Dataset Example" lang="php">
|
||||
|
||||
<!-- Pest Dataset Example -->
|
||||
```php
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Pest 4 Features
|
||||
|
||||
|
|
@ -111,8 +107,8 @@ Browser tests run in real browsers for full integration testing:
|
|||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging.
|
||||
|
||||
<code-snippet name="Pest Browser Test Example" lang="php">
|
||||
|
||||
<!-- Pest Browser Test Example -->
|
||||
```php
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
|
|
@ -121,7 +117,7 @@ it('may reset the password', function () {
|
|||
$page = visit('/sign-in');
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavascriptErrors()
|
||||
->assertNoJavaScriptErrors()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
|
|
@ -129,20 +125,18 @@ it('may reset the password', function () {
|
|||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Quickly validate multiple pages have no JavaScript errors:
|
||||
|
||||
<code-snippet name="Pest Smoke Testing Example" lang="php">
|
||||
|
||||
<!-- Pest Smoke Testing Example -->
|
||||
```php
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
|
||||
|
||||
</code-snippet>
|
||||
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
|
||||
```
|
||||
|
||||
### Visual Regression Testing
|
||||
|
||||
|
|
@ -156,14 +150,13 @@ Split tests across parallel processes for faster CI runs.
|
|||
|
||||
Pest 4 includes architecture testing (from Pest 3):
|
||||
|
||||
<code-snippet name="Architecture Test Example" lang="php">
|
||||
|
||||
<!-- Architecture Test Example -->
|
||||
```php
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
|
@ -171,4 +164,4 @@ arch('controllers')
|
|||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Forgetting `assertNoJavascriptErrors()` in browser tests
|
||||
- Forgetting `assertNoJavaScriptErrors()` in browser tests
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
---
|
||||
name: tailwindcss-development
|
||||
description: >-
|
||||
Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components,
|
||||
working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors,
|
||||
typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle,
|
||||
hero section, cards, buttons, or any visual/UI changes.
|
||||
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Tailwind CSS Development
|
||||
|
|
@ -38,22 +37,24 @@ Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
|||
|
||||
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||
|
||||
<code-snippet name="CSS-First Config" lang="css">
|
||||
<!-- CSS-First Config -->
|
||||
```css
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Import Syntax
|
||||
|
||||
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||
|
||||
<code-snippet name="v4 Import Syntax" lang="diff">
|
||||
<!-- v4 Import Syntax -->
|
||||
```diff
|
||||
- @tailwind base;
|
||||
- @tailwind components;
|
||||
- @tailwind utilities;
|
||||
+ @import "tailwindcss";
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Replaced Utilities
|
||||
|
||||
|
|
@ -77,43 +78,47 @@ Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opac
|
|||
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<code-snippet name="Gap Utilities" lang="html">
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<code-snippet name="Dark Mode" lang="html">
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<code-snippet name="Flexbox Layout" lang="html">
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<code-snippet name="Grid Layout" lang="html">
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
name: wayfinder-development
|
||||
description: >-
|
||||
Activates whenever referencing backend routes in frontend components. Use when
|
||||
importing from @/actions or @/routes, calling Laravel routes from TypeScript,
|
||||
or working with Wayfinder route functions.
|
||||
description: "Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Wayfinder Development
|
||||
|
|
@ -24,29 +24,29 @@ Use `search-docs` for detailed Wayfinder patterns and documentation.
|
|||
### Generate Routes
|
||||
|
||||
Run after route changes if Vite plugin isn't installed:
|
||||
|
||||
```bash
|
||||
php artisan wayfinder:generate --no-interaction
|
||||
|
||||
```
|
||||
For form helpers, use `--with-form` flag:
|
||||
|
||||
```bash
|
||||
php artisan wayfinder:generate --with-form --no-interaction
|
||||
```
|
||||
|
||||
### Import Patterns
|
||||
|
||||
<code-snippet name="Controller Action Imports" lang="typescript">
|
||||
|
||||
<!-- Controller Action Imports -->
|
||||
```typescript
|
||||
// Named imports for tree-shaking (preferred)...
|
||||
import { show, store, update } from '@/actions/App/Http/Controllers/PostController'
|
||||
|
||||
// Named route imports...
|
||||
import { show as postShow } from '@/routes/post'
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
### Common Methods
|
||||
|
||||
<code-snippet name="Wayfinder Methods" lang="typescript">
|
||||
|
||||
<!-- Wayfinder Methods -->
|
||||
```typescript
|
||||
// Get route object...
|
||||
show(1) // { url: "/posts/1", method: "get" }
|
||||
|
||||
|
|
@ -64,17 +64,15 @@ store.form() // { action: "/posts", method: "post" }
|
|||
|
||||
// Query parameters...
|
||||
show(1, { query: { page: 1 } }) // "/posts/1?page=1"
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Wayfinder + Inertia
|
||||
|
||||
Use Wayfinder with the `<Form>` component:
|
||||
<code-snippet name="Wayfinder Form (React)" lang="typescript">
|
||||
|
||||
<!-- Wayfinder Form (React) -->
|
||||
```typescript
|
||||
<Form {...store.form()}><input name="title" /></Form>
|
||||
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
name: developing-with-fortify
|
||||
description: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
|
||||
---
|
||||
|
||||
# Laravel Fortify Development
|
||||
|
||||
Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Laravel Fortify patterns and documentation.
|
||||
|
||||
## Usage
|
||||
|
||||
- **Routes**: Use `list-routes` with `only_vendor: true` and `action: "Fortify"` to see all registered endpoints
|
||||
- **Actions**: Check `app/Actions/Fortify/` for customizable business logic (user creation, password validation, etc.)
|
||||
- **Config**: See `config/fortify.php` for all options including features, guards, rate limiters, and username field
|
||||
- **Contracts**: Look in `Laravel\Fortify\Contracts\` for overridable response classes (`LoginResponse`, `LogoutResponse`, etc.)
|
||||
- **Views**: All view callbacks are set in `FortifyServiceProvider::boot()` using `Fortify::loginView()`, `Fortify::registerView()`, etc.
|
||||
|
||||
## Available Features
|
||||
|
||||
Enable in `config/fortify.php` features array:
|
||||
|
||||
- `Features::registration()` - User registration
|
||||
- `Features::resetPasswords()` - Password reset via email
|
||||
- `Features::emailVerification()` - Requires User to implement `MustVerifyEmail`
|
||||
- `Features::updateProfileInformation()` - Profile updates
|
||||
- `Features::updatePasswords()` - Password changes
|
||||
- `Features::twoFactorAuthentication()` - 2FA with QR codes and recovery codes
|
||||
|
||||
> Use `search-docs` for feature configuration options and customization patterns.
|
||||
|
||||
## Setup Workflows
|
||||
|
||||
### Two-Factor Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Add TwoFactorAuthenticatable trait to User model
|
||||
- [ ] Enable feature in config/fortify.php
|
||||
- [ ] If the `*_add_two_factor_columns_to_users_table.php` migration is missing, publish via `php artisan vendor:publish --tag=fortify-migrations` and migrate
|
||||
- [ ] Set up view callbacks in FortifyServiceProvider
|
||||
- [ ] Create 2FA management UI
|
||||
- [ ] Test QR code and recovery codes
|
||||
```
|
||||
|
||||
> Use `search-docs` for TOTP implementation and recovery code handling patterns.
|
||||
|
||||
### Email Verification Setup
|
||||
|
||||
```
|
||||
- [ ] Enable emailVerification feature in config
|
||||
- [ ] Implement MustVerifyEmail interface on User model
|
||||
- [ ] Set up verifyEmailView callback
|
||||
- [ ] Add verified middleware to protected routes
|
||||
- [ ] Test verification email flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for MustVerifyEmail implementation patterns.
|
||||
|
||||
### Password Reset Setup
|
||||
|
||||
```
|
||||
- [ ] Enable resetPasswords feature in config
|
||||
- [ ] Set up requestPasswordResetLinkView callback
|
||||
- [ ] Set up resetPasswordView callback
|
||||
- [ ] Define password.reset named route (if views disabled)
|
||||
- [ ] Test reset email and link flow
|
||||
```
|
||||
|
||||
> Use `search-docs` for custom password reset flow patterns.
|
||||
|
||||
### SPA Authentication Setup
|
||||
|
||||
```
|
||||
- [ ] Set 'views' => false in config/fortify.php
|
||||
- [ ] Install and configure Laravel Sanctum for session-based SPA authentication
|
||||
- [ ] Use the 'web' guard in config/fortify.php (required for session-based authentication)
|
||||
- [ ] Set up CSRF token handling
|
||||
- [ ] Test XHR authentication flows
|
||||
```
|
||||
|
||||
> Use `search-docs` for integration and SPA authentication patterns.
|
||||
|
||||
#### Two-Factor Authentication in SPA Mode
|
||||
|
||||
When `views` is set to `false`, Fortify returns JSON responses instead of redirects.
|
||||
|
||||
If a user attempts to log in and two-factor authentication is enabled, the login request will return a JSON response indicating that a two-factor challenge is required:
|
||||
|
||||
```json
|
||||
{
|
||||
"two_factor": true
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Custom Authentication Logic
|
||||
|
||||
Override authentication behavior using `Fortify::authenticateUsing()` for custom user retrieval or `Fortify::authenticateThrough()` to customize the authentication pipeline. Override response contracts in `AppServiceProvider` for custom redirects.
|
||||
|
||||
### Registration Customization
|
||||
|
||||
Modify `app/Actions/Fortify/CreateNewUser.php` to customize user creation logic, validation rules, and additional fields.
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
Configure via `fortify.limiters.login` in config. Default configuration throttles by username + IP combination.
|
||||
|
||||
## Key Endpoints
|
||||
|
||||
| Feature | Method | Endpoint |
|
||||
|------------------------|----------|---------------------------------------------|
|
||||
| Login | POST | `/login` |
|
||||
| Logout | POST | `/logout` |
|
||||
| Register | POST | `/register` |
|
||||
| Password Reset Request | POST | `/forgot-password` |
|
||||
| Password Reset | POST | `/reset-password` |
|
||||
| Email Verify Notice | GET | `/email/verify` |
|
||||
| Resend Verification | POST | `/email/verification-notification` |
|
||||
| Password Confirm | POST | `/user/confirm-password` |
|
||||
| Enable 2FA | POST | `/user/two-factor-authentication` |
|
||||
| Confirm 2FA | POST | `/user/confirmed-two-factor-authentication` |
|
||||
| 2FA Challenge | POST | `/two-factor-challenge` |
|
||||
| Get QR Code | GET | `/user/two-factor-qr-code` |
|
||||
| Recovery Codes | GET/POST | `/user/two-factor-recovery-codes` |
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
---
|
||||
name: inertia-react-development
|
||||
description: "Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Inertia React Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating or modifying React page components for Inertia
|
||||
- Working with forms in React (using `<Form>` or `useForm`)
|
||||
- Implementing client-side navigation with `<Link>` or `router`
|
||||
- Using v2 features: deferred props, prefetching, or polling
|
||||
- Building React-specific features with the Inertia protocol
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v2 React patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
React page components should be placed in the `resources/js/pages` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
<!-- Basic React Page Component -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{users.map(user => <li key={user.id}>{user.name}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
<!-- Inertia React Navigation -->
|
||||
```react
|
||||
import { Link, router } from '@inertiajs/react'
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
```
|
||||
|
||||
### Link with Method
|
||||
|
||||
<!-- Link with POST Method -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
```
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
<!-- Prefetch on Hover -->
|
||||
```react
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/users" prefetch>
|
||||
Users
|
||||
</Link>
|
||||
```
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
<!-- Router Visit -->
|
||||
```react
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
```
|
||||
|
||||
## Form Handling
|
||||
|
||||
### Form Component (Recommended)
|
||||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
<!-- Form Component Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
return (
|
||||
<Form action="/users" method="post">
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input type="email" name="email" />
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Creating...' : 'Create User'}
|
||||
</button>
|
||||
|
||||
{wasSuccessful && <div>User created!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Form Component With All Props
|
||||
|
||||
<!-- Form Component Full Example -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form action="/users" method="post">
|
||||
{({
|
||||
errors,
|
||||
hasErrors,
|
||||
processing,
|
||||
progress,
|
||||
wasSuccessful,
|
||||
recentlySuccessful,
|
||||
clearErrors,
|
||||
resetAndClearErrors,
|
||||
defaults,
|
||||
isDirty,
|
||||
reset,
|
||||
submit
|
||||
}) => (
|
||||
<>
|
||||
<input type="text" name="name" defaultValue={defaults.name} />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
|
||||
{progress && (
|
||||
<progress value={progress.percentage} max="100">
|
||||
{progress.percentage}%
|
||||
</progress>
|
||||
)}
|
||||
|
||||
{wasSuccessful && <div>Saved!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
```
|
||||
|
||||
### Form Component Reset Props
|
||||
|
||||
The `<Form>` component supports automatic resetting:
|
||||
|
||||
- `resetOnError` - Reset form data when the request fails
|
||||
- `resetOnSuccess` - Reset form data when the request succeeds
|
||||
- `setDefaultsOnSuccess` - Update default values on success
|
||||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
<!-- Form with Reset Props -->
|
||||
```react
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
resetOnSuccess
|
||||
setDefaultsOnSuccess
|
||||
>
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Submit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
```
|
||||
|
||||
Forms can also be built using the `useForm` helper for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
### `useForm` Hook
|
||||
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
||||
|
||||
<!-- useForm Hook Example -->
|
||||
```react
|
||||
import { useForm } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault()
|
||||
post('/users', {
|
||||
onSuccess: () => reset('password'),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<input
|
||||
type="text"
|
||||
value={data.name}
|
||||
onChange={e => setData('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={e => setData('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<input
|
||||
type="password"
|
||||
value={data.password}
|
||||
onChange={e => setData('password', e.target.value)}
|
||||
/>
|
||||
{errors.password && <div>{errors.password}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
### Deferred Props
|
||||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
<!-- Deferred Props with Empty State -->
|
||||
```react
|
||||
export default function UsersIndex({ users }) {
|
||||
// users will be undefined initially, then populated
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
{!users ? (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
) : (
|
||||
<ul>
|
||||
{users.map(user => (
|
||||
<li key={user.id}>{user.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Polling
|
||||
|
||||
Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
<!-- Basic Polling -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
usePoll(5000)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Polling With Request Options and Manual Control -->
|
||||
```react
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
<button onClick={start}>Start Polling</button>
|
||||
<button onClick={stop}>Stop Polling</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
### WhenVisible (Infinite Scroll)
|
||||
|
||||
Load more data when user scrolls to a specific element:
|
||||
|
||||
<!-- Infinite Scroll with WhenVisible -->
|
||||
```react
|
||||
import { WhenVisible } from '@inertiajs/react'
|
||||
|
||||
export default function UsersList({ users }) {
|
||||
return (
|
||||
<div>
|
||||
{users.data.map(user => (
|
||||
<div key={user.id}>{user.name}</div>
|
||||
))}
|
||||
|
||||
{users.next_page_url && (
|
||||
<WhenVisible
|
||||
data="users"
|
||||
params={{ page: users.current_page + 1 }}
|
||||
fallback={<div>Loading more...</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Forgetting to add loading states (skeleton screens) when using deferred props
|
||||
- Not handling the `undefined` state of deferred props before data loads
|
||||
- Using `<form>` without preventing default submission (use `<Form>` component or `e.preventDefault()`)
|
||||
- Forgetting to check if `<Form>` component is available in your Inertia version
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
name: pennant-development
|
||||
description: "Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pennant Features
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating or checking feature flags
|
||||
- Managing feature rollouts
|
||||
- Implementing A/B testing
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pennant patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Defining Features
|
||||
|
||||
<!-- Defining Features -->
|
||||
```php
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
Feature::define('new-dashboard', function (User $user) {
|
||||
return $user->isAdmin();
|
||||
});
|
||||
```
|
||||
|
||||
### Checking Features
|
||||
|
||||
<!-- Checking Features -->
|
||||
```php
|
||||
if (Feature::active('new-dashboard')) {
|
||||
// Feature is active
|
||||
}
|
||||
|
||||
// With scope
|
||||
if (Feature::for($user)->active('new-dashboard')) {
|
||||
// Feature is active for this user
|
||||
}
|
||||
```
|
||||
|
||||
### Blade Directive
|
||||
|
||||
<!-- Blade Directive -->
|
||||
```blade
|
||||
@feature('new-dashboard')
|
||||
<x-new-dashboard />
|
||||
@else
|
||||
<x-old-dashboard />
|
||||
@endfeature
|
||||
```
|
||||
|
||||
### Activating / Deactivating
|
||||
|
||||
<!-- Activating Features -->
|
||||
```php
|
||||
Feature::activate('new-dashboard');
|
||||
Feature::for($user)->activate('new-dashboard');
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check feature flag is defined
|
||||
2. Test with different scopes/users
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Forgetting to scope features for specific users/entities
|
||||
- Not following existing naming conventions
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
name: pest-testing
|
||||
description: "Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Pest Testing 4
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Creating new tests (unit, feature, or browser)
|
||||
- Modifying existing tests
|
||||
- Debugging test failures
|
||||
- Working with browser testing or smoke testing
|
||||
- Writing architecture tests or visual regression tests
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pest 4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Tests
|
||||
|
||||
All tests must be written using Pest. Use `php artisan make:test --pest {name}`.
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
|
||||
- Browser tests: `tests/Browser/` directory.
|
||||
- Do NOT remove tests without approval - these are core application code.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
<!-- Basic Pest Test Example -->
|
||||
```php
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
- Run minimal tests with filter before finalizing: `php artisan test --compact --filter=testName`.
|
||||
- Run all tests: `php artisan test --compact`.
|
||||
- Run file: `php artisan test --compact tests/Feature/ExampleTest.php`.
|
||||
|
||||
## Assertions
|
||||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
<!-- Pest Response Assertion -->
|
||||
```php
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
```
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
| `assertSuccessful()` | `assertStatus(200)` |
|
||||
| `assertNotFound()` | `assertStatus(404)` |
|
||||
| `assertForbidden()` | `assertStatus(403)` |
|
||||
|
||||
## Mocking
|
||||
|
||||
Import mock function before use: `use function Pest\Laravel\mock;`
|
||||
|
||||
## Datasets
|
||||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
<!-- Pest Dataset Example -->
|
||||
```php
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
```
|
||||
|
||||
## Pest 4 Features
|
||||
|
||||
| Feature | Purpose |
|
||||
|---------|---------|
|
||||
| Browser Testing | Full integration tests in real browsers |
|
||||
| Smoke Testing | Validate multiple pages quickly |
|
||||
| Visual Regression | Compare screenshots for visual changes |
|
||||
| Test Sharding | Parallel CI runs |
|
||||
| Architecture Testing | Enforce code conventions |
|
||||
|
||||
### Browser Test Example
|
||||
|
||||
Browser tests run in real browsers for full integration testing:
|
||||
|
||||
- Browser tests live in `tests/Browser/`.
|
||||
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
|
||||
- Use `RefreshDatabase` for clean state per test.
|
||||
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
|
||||
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
|
||||
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging.
|
||||
|
||||
<!-- Pest Browser Test Example -->
|
||||
```php
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in');
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavaScriptErrors()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!');
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
```
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Quickly validate multiple pages have no JavaScript errors:
|
||||
|
||||
<!-- Pest Smoke Testing Example -->
|
||||
```php
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
|
||||
```
|
||||
|
||||
### Visual Regression Testing
|
||||
|
||||
Capture and compare screenshots to detect visual changes.
|
||||
|
||||
### Test Sharding
|
||||
|
||||
Split tests across parallel processes for faster CI runs.
|
||||
|
||||
### Architecture Testing
|
||||
|
||||
Pest 4 includes architecture testing (from Pest 3):
|
||||
|
||||
<!-- Architecture Test Example -->
|
||||
```php
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not importing `use function Pest\Laravel\mock;` before using mock
|
||||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Forgetting `assertNoJavaScriptErrors()` in browser tests
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
name: tailwindcss-development
|
||||
description: "Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Tailwind CSS Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate this skill when:
|
||||
|
||||
- Adding styles to components or pages
|
||||
- Working with responsive design
|
||||
- Implementing dark mode
|
||||
- Extracting repeated patterns into components
|
||||
- Debugging spacing or layout issues
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
|
||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||
|
||||
## Tailwind CSS v4 Specifics
|
||||
|
||||
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||
- `corePlugins` is not supported in Tailwind v4.
|
||||
|
||||
### CSS-First Configuration
|
||||
|
||||
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||
|
||||
<!-- CSS-First Config -->
|
||||
```css
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
```
|
||||
|
||||
### Import Syntax
|
||||
|
||||
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||
|
||||
<!-- v4 Import Syntax -->
|
||||
```diff
|
||||
- @tailwind base;
|
||||
- @tailwind components;
|
||||
- @tailwind utilities;
|
||||
+ @import "tailwindcss";
|
||||
```
|
||||
|
||||
### Replaced Utilities
|
||||
|
||||
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
|
||||
|
||||
| Deprecated | Replacement |
|
||||
|------------|-------------|
|
||||
| bg-opacity-* | bg-black/* |
|
||||
| text-opacity-* | text-black/* |
|
||||
| border-opacity-* | border-black/* |
|
||||
| divide-opacity-* | divide-black/* |
|
||||
| ring-opacity-* | ring-black/* |
|
||||
| placeholder-opacity-* | placeholder-black/* |
|
||||
| flex-shrink-* | shrink-* |
|
||||
| flex-grow-* | grow-* |
|
||||
| overflow-ellipsis | text-ellipsis |
|
||||
| decoration-slice | box-decoration-slice |
|
||||
| decoration-clone | box-decoration-clone |
|
||||
|
||||
## Spacing
|
||||
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
|
||||
- Using `@tailwind` directives instead of `@import "tailwindcss"`
|
||||
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
|
||||
- Using margins for spacing between siblings instead of gap utilities
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
name: wayfinder-development
|
||||
description: "Activates whenever referencing backend routes in frontend components. Use when importing from @/actions or @/routes, calling Laravel routes from TypeScript, or working with Wayfinder route functions."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Wayfinder Development
|
||||
|
||||
## When to Apply
|
||||
|
||||
Activate whenever referencing backend routes in frontend components:
|
||||
- Importing from `@/actions/` or `@/routes/`
|
||||
- Calling Laravel routes from TypeScript/JavaScript
|
||||
- Creating links or navigation to backend endpoints
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Wayfinder patterns and documentation.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Generate Routes
|
||||
|
||||
Run after route changes if Vite plugin isn't installed:
|
||||
```bash
|
||||
php artisan wayfinder:generate --no-interaction
|
||||
```
|
||||
For form helpers, use `--with-form` flag:
|
||||
```bash
|
||||
php artisan wayfinder:generate --with-form --no-interaction
|
||||
```
|
||||
|
||||
### Import Patterns
|
||||
|
||||
<!-- Controller Action Imports -->
|
||||
```typescript
|
||||
// Named imports for tree-shaking (preferred)...
|
||||
import { show, store, update } from '@/actions/App/Http/Controllers/PostController'
|
||||
|
||||
// Named route imports...
|
||||
import { show as postShow } from '@/routes/post'
|
||||
```
|
||||
|
||||
### Common Methods
|
||||
|
||||
<!-- Wayfinder Methods -->
|
||||
```typescript
|
||||
// Get route object...
|
||||
show(1) // { url: "/posts/1", method: "get" }
|
||||
|
||||
// Get URL string...
|
||||
show.url(1) // "/posts/1"
|
||||
|
||||
// Specific HTTP methods...
|
||||
show.get(1)
|
||||
store.post()
|
||||
update.patch(1)
|
||||
destroy.delete(1)
|
||||
|
||||
// Form attributes for HTML forms...
|
||||
store.form() // { action: "/posts", method: "post" }
|
||||
|
||||
// Query parameters...
|
||||
show(1, { query: { page: 1 } }) // "/posts/1?page=1"
|
||||
```
|
||||
|
||||
## Wayfinder + Inertia
|
||||
|
||||
Use Wayfinder with the `<Form>` component:
|
||||
<!-- Wayfinder Form (React) -->
|
||||
```typescript
|
||||
<Form {...store.form()}><input name="title" /></Form>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Run `php artisan wayfinder:generate` to regenerate routes if Vite plugin isn't installed
|
||||
2. Check TypeScript imports resolve correctly
|
||||
3. Verify route URLs match expected paths
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using default imports instead of named imports (breaks tree-shaking)
|
||||
- Forgetting to regenerate after route changes
|
||||
- Not using type-safe parameter objects for route model binding
|
||||
57
AGENTS.md
57
AGENTS.md
|
|
@ -10,22 +10,24 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for
|
|||
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
|
||||
|
||||
- php - 8.4.17
|
||||
- inertiajs/inertia-laravel (INERTIA) - v2
|
||||
- inertiajs/inertia-laravel (INERTIA_LARAVEL) - v2
|
||||
- laravel/cashier (CASHIER) - v16
|
||||
- laravel/fortify (FORTIFY) - v1
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/pennant (PENNANT) - v1
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/wayfinder (WAYFINDER) - v0
|
||||
- laravel/boost (BOOST) - v2
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/pail (PAIL) - v1
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
- pestphp/pest (PEST) - v4
|
||||
- phpunit/phpunit (PHPUNIT) - v12
|
||||
- @inertiajs/react (INERTIA) - v2
|
||||
- @inertiajs/react (INERTIA_REACT) - v2
|
||||
- react (REACT) - v19
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
- @laravel/vite-plugin-wayfinder (WAYFINDER) - v0
|
||||
- @laravel/vite-plugin-wayfinder (WAYFINDER_VITE) - v0
|
||||
- eslint (ESLINT) - v9
|
||||
- prettier (PRETTIER) - v3
|
||||
|
||||
|
|
@ -38,6 +40,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
|
||||
- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation.
|
||||
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
|
||||
- `developing-with-fortify` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
|
@ -84,6 +87,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
|
||||
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
|
||||
- Use the `database-query` tool when you only need to read from the database.
|
||||
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
|
||||
|
||||
## Reading Browser Logs With the `browser-logs` Tool
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
## Constructors
|
||||
|
||||
- Use PHP 8 constructor property promotion in `__construct()`.
|
||||
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
|
||||
- `public function __construct(public GitHub $github) { }`
|
||||
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
|
||||
|
||||
## Type Declarations
|
||||
|
|
@ -122,12 +126,13 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
- Always use explicit return type declarations for methods and functions.
|
||||
- Use appropriate PHP type hints for method parameters.
|
||||
|
||||
<code-snippet name="Explicit Return Types and Method Params" lang="php">
|
||||
<!-- Explicit Return Types and Method Params -->
|
||||
```php
|
||||
protected function isAccessible(User $user, ?string $path = null): bool
|
||||
{
|
||||
...
|
||||
}
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
|
|
@ -148,17 +153,15 @@ protected function isAccessible(User $user, ?string $path = null): bool
|
|||
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
|
||||
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter.
|
||||
|
||||
=== inertia-laravel/core rules ===
|
||||
=== inertia-laravel/v2 rules ===
|
||||
|
||||
# Inertia
|
||||
|
||||
- Inertia creates fully client-side rendered SPAs without modern SPA complexity, leveraging existing server-side patterns.
|
||||
- Components live in `resources/js/Pages` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
|
||||
- Components live in `resources/js/pages` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
|
||||
- ALWAYS use `search-docs` tool for version-specific Inertia documentation and updated code examples.
|
||||
- IMPORTANT: Activate `inertia-react-development` when working with Inertia client-side patterns.
|
||||
|
||||
=== inertia-laravel/v2 rules ===
|
||||
|
||||
# Inertia v2
|
||||
|
||||
- Use all Inertia features from v1 and v2. Check the documentation before making changes to ensure the correct approach.
|
||||
|
|
@ -269,8 +272,8 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti
|
|||
|
||||
# Laravel Pint Code Formatter
|
||||
|
||||
- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues.
|
||||
- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.
|
||||
|
||||
=== pest/core rules ===
|
||||
|
||||
|
|
@ -298,32 +301,10 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti
|
|||
|
||||
=== laravel/fortify rules ===
|
||||
|
||||
## Laravel Fortify
|
||||
# Laravel Fortify
|
||||
|
||||
Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
- Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
- IMPORTANT: Always use the `search-docs` tool for detailed Laravel Fortify patterns and documentation.
|
||||
- IMPORTANT: Activate `developing-with-fortify` skill when working with Fortify authentication features.
|
||||
|
||||
**Before implementing any authentication features, use the `search-docs` tool to get the latest docs for that specific feature.**
|
||||
|
||||
### Configuration & Setup
|
||||
|
||||
- Check `config/fortify.php` to see what's enabled. Use `search-docs` for detailed information on specific features.
|
||||
- Enable features by adding them to the `'features' => []` array: `Features::registration()`, `Features::resetPasswords()`, etc.
|
||||
- To see the all Fortify registered routes, use the `list-routes` tool with the `only_vendor: true` and `action: "Fortify"` parameters.
|
||||
- Fortify includes view routes by default (login, register). Set `'views' => false` in the configuration file to disable them if you're handling views yourself.
|
||||
|
||||
### Customization
|
||||
|
||||
- Views can be customized in `FortifyServiceProvider`'s `boot()` method using `Fortify::loginView()`, `Fortify::registerView()`, etc.
|
||||
- Customize authentication logic with `Fortify::authenticateUsing()` for custom user retrieval / validation.
|
||||
- Actions in `app/Actions/Fortify/` handle business logic (user creation, password reset, etc.). They're fully customizable, so you can modify them to change feature behavior.
|
||||
|
||||
## Available Features
|
||||
|
||||
- `Features::registration()` for user registration.
|
||||
- `Features::emailVerification()` to verify new user emails.
|
||||
- `Features::twoFactorAuthentication()` for 2FA with QR codes and recovery codes.
|
||||
- Add options: `['confirmPassword' => true, 'confirm' => true]` to require password confirmation and OTP confirmation before enabling 2FA.
|
||||
- `Features::updateProfileInformation()` to let users update their profile.
|
||||
- `Features::updatePasswords()` to let users change their passwords.
|
||||
- `Features::resetPasswords()` for password reset via email.
|
||||
</laravel-boost-guidelines>
|
||||
|
|
|
|||
57
CLAUDE.md
57
CLAUDE.md
|
|
@ -106,22 +106,24 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for
|
|||
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
|
||||
|
||||
- php - 8.4.17
|
||||
- inertiajs/inertia-laravel (INERTIA) - v2
|
||||
- inertiajs/inertia-laravel (INERTIA_LARAVEL) - v2
|
||||
- laravel/cashier (CASHIER) - v16
|
||||
- laravel/fortify (FORTIFY) - v1
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/pennant (PENNANT) - v1
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- laravel/wayfinder (WAYFINDER) - v0
|
||||
- laravel/boost (BOOST) - v2
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/pail (PAIL) - v1
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
- pestphp/pest (PEST) - v4
|
||||
- phpunit/phpunit (PHPUNIT) - v12
|
||||
- @inertiajs/react (INERTIA) - v2
|
||||
- @inertiajs/react (INERTIA_REACT) - v2
|
||||
- react (REACT) - v19
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
- @laravel/vite-plugin-wayfinder (WAYFINDER) - v0
|
||||
- @laravel/vite-plugin-wayfinder (WAYFINDER_VITE) - v0
|
||||
- eslint (ESLINT) - v9
|
||||
- prettier (PRETTIER) - v3
|
||||
|
||||
|
|
@ -134,6 +136,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
- `pest-testing` — Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
|
||||
- `inertia-react-development` — Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation.
|
||||
- `tailwindcss-development` — Styles applications using Tailwind CSS v4 utilities. Activates when adding styles, restyling components, working with gradients, spacing, layout, flex, grid, responsive design, dark mode, colors, typography, or borders; or when the user mentions CSS, styling, classes, Tailwind, restyle, hero section, cards, buttons, or any visual/UI changes.
|
||||
- `developing-with-fortify` — Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
|
@ -180,6 +183,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
|
||||
- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly.
|
||||
- Use the `database-query` tool when you only need to read from the database.
|
||||
- Use the `database-schema` tool to inspect table structure before writing migrations or models.
|
||||
|
||||
## Reading Browser Logs With the `browser-logs` Tool
|
||||
|
||||
|
|
@ -210,7 +214,7 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
## Constructors
|
||||
|
||||
- Use PHP 8 constructor property promotion in `__construct()`.
|
||||
- <code-snippet>public function __construct(public GitHub $github) { }</code-snippet>
|
||||
- `public function __construct(public GitHub $github) { }`
|
||||
- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private.
|
||||
|
||||
## Type Declarations
|
||||
|
|
@ -218,12 +222,13 @@ This project has domain-specific skills available. You MUST activate the relevan
|
|||
- Always use explicit return type declarations for methods and functions.
|
||||
- Use appropriate PHP type hints for method parameters.
|
||||
|
||||
<code-snippet name="Explicit Return Types and Method Params" lang="php">
|
||||
<!-- Explicit Return Types and Method Params -->
|
||||
```php
|
||||
protected function isAccessible(User $user, ?string $path = null): bool
|
||||
{
|
||||
...
|
||||
}
|
||||
</code-snippet>
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
|
|
@ -244,17 +249,15 @@ protected function isAccessible(User $user, ?string $path = null): bool
|
|||
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
|
||||
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter.
|
||||
|
||||
=== inertia-laravel/core rules ===
|
||||
=== inertia-laravel/v2 rules ===
|
||||
|
||||
# Inertia
|
||||
|
||||
- Inertia creates fully client-side rendered SPAs without modern SPA complexity, leveraging existing server-side patterns.
|
||||
- Components live in `resources/js/Pages` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
|
||||
- Components live in `resources/js/pages` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
|
||||
- ALWAYS use `search-docs` tool for version-specific Inertia documentation and updated code examples.
|
||||
- IMPORTANT: Activate `inertia-react-development` when working with Inertia client-side patterns.
|
||||
|
||||
=== inertia-laravel/v2 rules ===
|
||||
|
||||
# Inertia v2
|
||||
|
||||
- Use all Inertia features from v1 and v2. Check the documentation before making changes to ensure the correct approach.
|
||||
|
|
@ -365,8 +368,8 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti
|
|||
|
||||
# Laravel Pint Code Formatter
|
||||
|
||||
- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues.
|
||||
- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.
|
||||
|
||||
=== pest/core rules ===
|
||||
|
||||
|
|
@ -394,32 +397,10 @@ Wayfinder generates TypeScript functions for Laravel routes. Import from `@/acti
|
|||
|
||||
=== laravel/fortify rules ===
|
||||
|
||||
## Laravel Fortify
|
||||
# Laravel Fortify
|
||||
|
||||
Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
- Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
|
||||
- IMPORTANT: Always use the `search-docs` tool for detailed Laravel Fortify patterns and documentation.
|
||||
- IMPORTANT: Activate `developing-with-fortify` skill when working with Fortify authentication features.
|
||||
|
||||
**Before implementing any authentication features, use the `search-docs` tool to get the latest docs for that specific feature.**
|
||||
|
||||
### Configuration & Setup
|
||||
|
||||
- Check `config/fortify.php` to see what's enabled. Use `search-docs` for detailed information on specific features.
|
||||
- Enable features by adding them to the `'features' => []` array: `Features::registration()`, `Features::resetPasswords()`, etc.
|
||||
- To see the all Fortify registered routes, use the `list-routes` tool with the `only_vendor: true` and `action: "Fortify"` parameters.
|
||||
- Fortify includes view routes by default (login, register). Set `'views' => false` in the configuration file to disable them if you're handling views yourself.
|
||||
|
||||
### Customization
|
||||
|
||||
- Views can be customized in `FortifyServiceProvider`'s `boot()` method using `Fortify::loginView()`, `Fortify::registerView()`, etc.
|
||||
- Customize authentication logic with `Fortify::authenticateUsing()` for custom user retrieval / validation.
|
||||
- Actions in `app/Actions/Fortify/` handle business logic (user creation, password reset, etc.). They're fully customizable, so you can modify them to change feature behavior.
|
||||
|
||||
## Available Features
|
||||
|
||||
- `Features::registration()` for user registration.
|
||||
- `Features::emailVerification()` to verify new user emails.
|
||||
- `Features::twoFactorAuthentication()` for 2FA with QR codes and recovery codes.
|
||||
- Add options: `['confirmPassword' => true, 'confirm' => true]` to require password confirmation and OTP confirmation before enabling 2FA.
|
||||
- `Features::updateProfileInformation()` to let users update their profile.
|
||||
- `Features::updatePasswords()` to let users change their passwords.
|
||||
- `Features::resetPasswords()` for password reset via email.
|
||||
</laravel-boost-guidelines>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"guidelines": true,
|
||||
"herd_mcp": false,
|
||||
"mcp": true,
|
||||
"nightwatch_mcp": false,
|
||||
"packages": [
|
||||
"laravel/fortify"
|
||||
],
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
"wayfinder-development",
|
||||
"pest-testing",
|
||||
"inertia-react-development",
|
||||
"tailwindcss-development"
|
||||
"tailwindcss-development",
|
||||
"developing-with-fortify"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue