#[AsCommand(name: 'breeze:install')]
class InstallCommand extends Command implements PromptsForMissingInput
{
    use InstallsApiStack, InstallsBladeStack, InstallsInertiaStacks, InstallsLivewireStack;

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'breeze:install {stack : The development stack that should be installed (blade,livewire,livewire-functional,react,vue,api)}
                            {--dark : Indicate that dark mode support should be installed}
                            {--pest : Indicate that Pest should be installed}
                            {--ssr : Indicates if Inertia SSR support should be installed}
                            {--typescript : Indicates if TypeScript is preferred for the Inertia stack}
                            {--eslint : Indicates if ESLint with Prettier should be installed}
                            {--composer=global : Absolute path to the Composer binary which should be used to install packages}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Install the Breeze controllers and resources';

    /**
     * Execute the console command.
     *
     * @return int|null
     */
    public function handle()
    {
        if ($this->argument('stack') === 'vue') {
            return $this->installInertiaVueStack();
        } elseif ($this->argument('stack') === 'react') {
            return $this->installInertiaReactStack();
        } elseif ($this->argument('stack') === 'api') {
            return $this->installApiStack();
        } elseif ($this->argument('stack') === 'blade') {
            return $this->installBladeStack();
        } elseif ($this->argument('stack') === 'livewire') {
            return $this->installLivewireStack();
        } elseif ($this->argument('stack') === 'livewire-functional') {
            return $this->installLivewireStack(true);
        }

        $this->components->error('Invalid stack. Supported stacks are [blade], [livewire], [livewire-functional], [react], [vue], and [api].');

        return 1;
    }

    /**
     * Install Breeze's tests.
     *
     * @return bool
     */
    protected function installTests()
    {
        (new Filesystem)->ensureDirectoryExists(base_path('tests/Feature'));

        $stubStack = match ($this->argument('stack')) {
            'api' => 'api',
            'livewire' => 'livewire-common',
            'livewire-functional' => 'livewire-common',
            default => 'default',
        };

        if ($this->option('pest') || $this->isUsingPest()) {
            if ($this->hasComposerPackage('phpunit/phpunit')) {
                $this->removeComposerPackages(['phpunit/phpunit'], true);
            }

            if (! $this->requireComposerPackages(['pestphp/pest', 'pestphp/pest-plugin-laravel'], true)) {
                return false;
            }

            (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Feature', base_path('tests/Feature'));
            (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Unit', base_path('tests/Unit'));
            (new Filesystem)->copy(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Pest.php', base_path('tests/Pest.php'));
        } else {
            (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/tests/Feature', base_path('tests/Feature'));
        }

        return true;
    }

    /**
     * Install the given middleware names into the application.
     *
     * @param  array|string  $name
     * @param  string  $group
     * @param  string  $modifier
     * @return void
     */
    protected function installMiddleware($names, $group = 'web', $modifier = 'append')
    {
        $bootstrapApp = file_get_contents(base_path('bootstrap/app.php'));

        $names = collect(Arr::wrap($names))
            ->filter(fn ($name) => ! Str::contains($bootstrapApp, $name))
            ->whenNotEmpty(function ($names) use ($bootstrapApp, $group, $modifier) {
                $names = $names->map(fn ($name) => "$name")->implode(','.PHP_EOL.'            ');

                $bootstrapApp = str_replace(
                    '->withMiddleware(function (Middleware $middleware) {',
                    '->withMiddleware(function (Middleware $middleware) {'
                        .PHP_EOL."        \$middleware->$group($modifier: ["
                        .PHP_EOL."            $names,"
                        .PHP_EOL.'        ]);'
                        .PHP_EOL,
                    $bootstrapApp,
                );

                file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp);
            });
    }

    /**
     * Install the given middleware aliases into the application.
     *
     * @param  array  $aliases
     * @return void
     */
    protected function installMiddlewareAliases($aliases)
    {
        $bootstrapApp = file_get_contents(base_path('bootstrap/app.php'));

        $aliases = collect($aliases)
            ->filter(fn ($alias) => ! Str::contains($bootstrapApp, $alias))
            ->whenNotEmpty(function ($aliases) use ($bootstrapApp) {
                $aliases = $aliases->map(fn ($name, $alias) => "'$alias' => $name")->implode(','.PHP_EOL.'            ');

                $bootstrapApp = str_replace(
                    '->withMiddleware(function (Middleware $middleware) {',
                    '->withMiddleware(function (Middleware $middleware) {'
                        .PHP_EOL.'        $middleware->alias(['
                        .PHP_EOL."            $aliases,"
                        .PHP_EOL.'        ]);'
                        .PHP_EOL,
                    $bootstrapApp,
                );

                file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp);
            });
    }

    /**
     * Determine if the given Composer package is installed.
     *
     * @param  string  $package
     * @return bool
     */
    protected function hasComposerPackage($package)
    {
        $packages = json_decode(file_get_contents(base_path('composer.json')), true);

        return array_key_exists($package, $packages['require'] ?? [])
            || array_key_exists($package, $packages['require-dev'] ?? []);
    }

    /**
     * Installs the given Composer Packages into the application.
     *
     * @param  bool  $asDev
     * @return bool
     */
    protected function requireComposerPackages(array $packages, $asDev = false)
    {
        $composer = $this->option('composer');

        if ($composer !== 'global') {
            $command = ['php', $composer, 'require'];
        }

        $command = array_merge(
            $command ?? ['composer', 'require'],
            $packages,
            $asDev ? ['--dev'] : [],
        );

        return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1']))
            ->setTimeout(null)
            ->run(function ($type, $output) {
                $this->output->write($output);
            }) === 0;
    }

    /**
     * Removes the given Composer Packages from the application.
     *
     * @param  bool  $asDev
     * @return bool
     */
    protected function removeComposerPackages(array $packages, $asDev = false)
    {
        $composer = $this->option('composer');

        if ($composer !== 'global') {
            $command = ['php', $composer, 'remove'];
        }

        $command = array_merge(
            $command ?? ['composer', 'remove'],
            $packages,
            $asDev ? ['--dev'] : [],
        );

        return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1']))
            ->setTimeout(null)
            ->run(function ($type, $output) {
                $this->output->write($output);
            }) === 0;
    }

    /**
     * Update the dependencies in the "package.json" file.
     *
     * @param  bool  $dev
     * @return void
     */
    protected static function updateNodePackages(callable $callback, $dev = true)
    {
        if (! file_exists(base_path('package.json'))) {
            return;
        }

        $configurationKey = $dev ? 'devDependencies' : 'dependencies';

        $packages = json_decode(file_get_contents(base_path('package.json')), true);

        $packages[$configurationKey] = $callback(
            array_key_exists($configurationKey, $packages) ? $packages[$configurationKey] : [],
            $configurationKey
        );

        ksort($packages[$configurationKey]);

        file_put_contents(
            base_path('package.json'),
            json_encode($packages, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT).PHP_EOL
        );
    }

    /**
     * Update the scripts in the "package.json" file.
     *
     * @return void
     */


<?php

namespace LegacyTests;

use Illuminate\Foundation\Http\Kernel;

class HttpKernel extends Kernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \Orchestra\Testbench\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

    /**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}


<?php

namespace LegacyTests\Unit;

use Orchestra\Testbench\TestCase as BaseTestCase;
use Livewire\LivewireServiceProvider;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Artisan;

class TestCase extends BaseTestCase
{
    public function setUp(): void
    {
        $this->afterApplicationCreated(function () {
            $this->makeACleanSlate();
        });

        $this->beforeApplicationDestroyed(function () {
            $this->makeACleanSlate();
        });

        parent::setUp();
    }

    public function makeACleanSlate()
    {
        Artisan::call('view:clear');

        File::deleteDirectory($this->livewireViewsPath());
        File::deleteDirectory($this->livewireClassesPath());
        File::deleteDirectory($this->livewireTestsPath());
        File::delete(app()->bootstrapPath('cache/livewire-components.php'));
    }

    protected function getPackageProviders($app)
    {
        return [
            LivewireServiceProvider::class,
        ];
    }

    protected function getEnvironmentSetUp($app)
    {
        $app['config']->set('view.paths', [
            __DIR__.'/views',
            resource_path('views'),
        ]);

        $app['config']->set('app.key', 'base64:Hupx3yAySikrM2/edkZQNQHslgDWYfiBfCuSThJ5SK8=');

        $app['config']->set('database.default', 'testbench');
        $app['config']->set('database.connections.testbench', [
            'driver'   => 'sqlite',
            'database' => ':memory:',
            'prefix'   => '',
        ]);

        $app['config']->set('filesystems.disks.unit-downloads', [
            'driver' => 'local',
            'root' => __DIR__.'/fixtures',
        ]);
    }

    protected function resolveApplicationHttpKernel($app)
    {
        $app->singleton('Illuminate\Contracts\Http\Kernel', 'LegacyTests\HttpKernel');
    }

    protected function livewireClassesPath($path = '')
    {
        return app_path('Livewire'.($path ? '/'.$path : ''));
    }

    protected function livewireViewsPath($path = '')
    {
        return resource_path('views').'/livewire'.($path ? '/'.$path : '');
    }

    protected function livewireTestsPath($path = '')
    {
        return base_path('tests/Feature/Livewire'.($path ? '/'.$path : ''));
    }
}


    @if (session('status'))

            {{ session('status') }}

    @endif


    @foreach ($models as $model)
        {{ $model->title }}
    @endforeach


class TestCase extends BaseTestCase
{
    use SupportsSafari;

    public static $useSafari = false;
    public static $useAlpineV3 = false;

    function visitLivewireComponent($browser, $classes, $queryString = '')
    {
        $classes = (array) $classes;

        $this->registerComponentForNextTest($classes);

        $url = '/livewire-dusk/'.urlencode(head($classes)).$queryString;

        return $browser->visit($url)->waitForLivewireToLoad();
    }

    function registerComponentForNextTest($components)
    {
        $tmp = __DIR__ . '/_runtime_components.json';

        file_put_contents($tmp, json_encode($components, JSON_PRETTY_PRINT));
    }

    function wipeRuntimeComponentRegistration()
    {
        $tmp = __DIR__ . '/_runtime_components.json';

        file_exists($tmp) && unlink($tmp);
    }

    public function setUp(): void
    {
        if (isset($_SERVER['CI'])) {
            DuskOptions::withoutUI();
        }

        Browser::mixin(new \Livewire\Features\SupportTesting\DuskBrowserMacros);

        $this->afterApplicationCreated(function () {
            $this->makeACleanSlate();
        });

        $this->beforeApplicationDestroyed(function () {
            $this->makeACleanSlate();
        });

        parent::setUp();

        // $thing = get_class($this);

        $isUsingAlpineV3 = static::$useAlpineV3;

        $this->tweakApplication(function () use ($isUsingAlpineV3) {
            $tmp = __DIR__ . '/_runtime_components.json';
            if (file_exists($tmp)) {
                // We can't just "require" this file because of race conditions...
                $components = json_decode(file_get_contents($tmp));

                foreach ($components as $name => $class) {
                    if (is_numeric($name)) {
                        app('livewire')->component($class);
                    } else {
                        app('livewire')->component($name, $class);
                    }
                }
            }

            // // Autoload all Livewire components in this test suite.
            // collect(File::allFiles(__DIR__))
            //     ->map(function ($file) {
            //         return 'Tests\\Browser\\'.str($file->getRelativePathname())->before('.php')->replace('/', '\\');
            //     })
            //     ->filter(function ($computedClassName) {
            //         return class_exists($computedClassName);
            //     })
            //     ->filter(function ($class) {
            //         return is_subclass_of($class, Component::class);
            //     })->each(function ($componentClass) {
            //         app('livewire')->component($componentClass);
            //     });

            // Route::get(
            //     '/livewire-dusk/tests/browser/sync-history-without-mount/{id}',
            //     \LegacyTests\Browser\SyncHistory\ComponentWithMount::class
            // )->middleware('web')->name('sync-history-without-mount');

            // // This needs to be registered for Dusk to test the route-parameter binding
            // // See: \LegacyTests\Browser\SyncHistory\Test.php
            // Route::get(
            //     '/livewire-dusk/tests/browser/sync-history/{step}',
            //     \LegacyTests\Browser\SyncHistory\Component::class
            // )->middleware('web')->name('sync-history');

            // Route::get(
            //     '/livewire-dusk/tests/browser/sync-history-without-query-string/{step}',
            //     \LegacyTests\Browser\SyncHistory\ComponentWithoutQueryString::class
            // )->middleware('web')->name('sync-history-without-query-string');

            // Route::get(
            //     '/livewire-dusk/tests/browser/sync-history-with-optional-parameter/{step?}',
            //     \LegacyTests\Browser\SyncHistory\ComponentWithOptionalParameter::class
            // )->middleware('web')->name('sync-history-with-optional-parameter');

            // // The following two routes belong together. The first one serves a view which in return
            // // loads and renders a component dynamically. There may not be a POST route for the first one.
            // Route::get('/livewire-dusk/tests/browser/load-dynamic-component', function () {
            //     return View::file(__DIR__ . '/DynamicComponentLoading/view-load-dynamic-component.blade.php');
            // })->middleware('web')->name('load-dynamic-component');

            // Route::post('/livewire-dusk/tests/browser/dynamic-component', function () {
            //     return View::file(__DIR__ . '/DynamicComponentLoading/view-dynamic-component.blade.php');
            // })->middleware('web')->name('dynamic-component');

            Route::get('/livewire-dusk/{component}', ShowDuskComponent::class)->middleware('web');

            Route::middleware('web')->get('/entangle-turbo', function () {
                return view('turbo', [
                    'link' => '/livewire-dusk/' . urlencode(\LegacyTests\Browser\Alpine\Entangle\ToggleEntangledTurbo::class),
                ]);
            })->name('entangle-turbo');

            // app('session')->put('_token', 'this-is-a-hack-because-something-about-validating-the-csrf-token-is-broken');

            // app('config')->set('view.paths', [
            //     __DIR__.'/views',
            //     resource_path('views'),
            // ]);

            config()->set('app.debug', true);
        });
    }

    protected function tearDown(): void
    {
        $this->wipeRuntimeComponentRegistration();

        $this->removeApplicationTweaks();

        parent::tearDown();
    }

    // We don't want to deal with screenshots or console logs.
    protected function storeConsoleLogsFor($browsers) {}
    protected function captureFailuresFor($browsers) {}

    public function makeACleanSlate()
    {
        Artisan::call('view:clear');

        File::deleteDirectory($this->livewireViewsPath());
        File::cleanDirectory(__DIR__.'/downloads');
        File::deleteDirectory($this->livewireClassesPath());
        File::delete(app()->bootstrapPath('cache/livewire-components.php'));
    }

    protected function getPackageProviders($app)
    {
        return [
            LivewireServiceProvider::class,
        ];
    }

    protected function getEnvironmentSetUp($app)
    {
        $app['config']->set('view.paths', [
            __DIR__.'/views',
            resource_path('views'),
        ]);

        $app['config']->set('app.key', 'base64:Hupx3yAySikrM2/edkZQNQHslgDWYfiBfCuSThJ5SK8=');

        $app['config']->set('database.default', 'testbench');
        $app['config']->set('database.connections.testbench', [
            'driver'   => 'sqlite',
            'database' => ':memory:',
            'prefix'   => '',
        ]);

        $app['config']->set('auth.providers.users.model', User::class);

        $app['config']->set('filesystems.disks.dusk-downloads', [
            'driver' => 'local',
            'root' => __DIR__.'/downloads',
        ]);
    }

    protected function resolveApplicationHttpKernel($app)
    {
        $app->singleton('Illuminate\Contracts\Http\Kernel', 'LegacyTests\HttpKernel');
    }

    protected function livewireClassesPath($path = '')
    {
        return app_path('Livewire'.($path ? '/'.$path : ''));
    }

    protected function livewireViewsPath($path = '')
    {
        return resource_path('views').'/livewire'.($path ? '/'.$path : '');
    }

    protected function driver(): RemoteWebDriver
    {
        $options = DuskOptions::getChromeOptions();

        $options->setExperimentalOption('prefs', [
            'download.default_directory' => __DIR__.'/downloads',
        ]);

        // $options->addArguments([
        //     'auto-open-devtools-for-tabs',
        // ]);

        return static::$useSafari
            ? RemoteWebDriver::create(
                'http://localhost:9515', DesiredCapabilities::safari()
            )
            : RemoteWebDriver::create(
                'http://localhost:9515',
                DesiredCapabilities::chrome()->setCapability(
                    ChromeOptions::CAPABILITY,
                    $options
                )
            );
    }

    public function browse(Closure $callback)
    {
        parent::browse(function (...$browsers) use ($callback) {
            try {
                $callback(...$browsers);
            } catch (Exception|Throwable $e) {
                if (DuskOptions::hasUI()) $this->breakIntoATinkerShell($browsers, $e);

                throw $e;
            }
        });
    }

    public function breakIntoATinkerShell($browsers, $e)
    {
        $sh = new Shell();

        $sh->add(new DuskCommand($this, $e));

        $sh->setScopeVariables([
            'browsers' => $browsers,
        ]);

        $sh->addInput('dusk');

        $sh->setBoundObject($this);

        $sh->run();

        return $sh->getScopeVariables(false);
    }
}


    <input dusk="foo.bar" type="radio" wire:model="foo" value="bar" name="foo">
    <input dusk="foo.baz" type="radio" wire:model="foo" value="baz" name="foo">


    <button wire:click="$refresh" dusk="refresh">Refresh</button>
    <button wire:click="flashMessage" dusk="flash">Flash</button>
    <button wire:click="redirectWithFlash" dusk="redirect-with-flash">Redirect With Flash</button>

    <button wire:click="redirectPage" dusk="redirect.button">Redirect Page</button>
    {{ $message }}

    <button wire:click="redirectPageWithModel" dusk="redirect-with-model.button">Redirect PageWithModel</button>
    {{ $foo }}

        @if (session()->has('message'))

{{ session()->get('message') }}

        @endif


    {{ $baz }}

    <input wire:model.live="baz" type="text" dusk="baz-input">


    <input wire:model.live="search" type="text" dusk="search">

    @foreach ($posts as $post)

id }}">{{ $post->title }}

    @endforeach

    {{ $posts->links() }}


    {{--

        <input type="text" x-model="count">

 --}}

    {{ $foo }}
    {{ $bar }}

    {{ $qux['hyphen'] }}
    {{ $qux['comma'] }}
    {{ $qux['ampersand'] }}
    {{ $qux['space'] }}
    {{ json_encode($qux['array']) }}

    <input wire:model.live="foo" type="text" dusk="input">
    <input wire:model.live="bar" type="text" dusk="bar-input">

    <button wire:click="$set('showNestedComponent', true)" dusk="show-nested">Show Nested Component</button>

    <button wire:click="modifyBob" dusk="bob.modify">Modify Bob (Array Property)</button>
    @json($bob)

    @if ($showNestedComponent)
        @livewire('nested')
    @endif


    <input wire:model.live="foo" dusk="foo.input">
    <button wire:click="changeFoo" dusk="foo.button">Change Foo</button>

    <input wire:model.live="bar.baz" dusk="bar.input">
    <button wire:click="resetBar" dusk="bar.button">Change BarBaz</button>


    <input type="text" wire:model.live="foo" dusk="foo">{{ $foo }}
    <button wire:click="updateFooTo('changed')" dusk="foo.change">Change Foo</button>

    <input type="text" wire:model.live="bar.baz.bob" dusk="bar">@json($bar)

    <input type="text" wire:model.lazy="baz" dusk="baz">{{ $baz }}

    <input type="text" wire:model="bob" dusk="bob">{{ $bob }}
    <button type="button" wire:click="$refresh" dusk="refresh">Refresh</button>


    <textarea wire:model.live="foo" dusk="foo" class="{{ $showFooClass ? 'foo' : '' }}"></textarea>{{ $foo }}
    <button wire:click="updateFooTo('changed')" dusk="foo.change">Change Foo</button>
    <button wire:click="$set('showFooClass', true)" dusk="foo.add-class">Add Class</button>

    <textarea wire:model.blur="baz" dusk="baz"></textarea>{{ $baz }}

    <textarea wire:model="bob" dusk="bob"></textarea>{{ $bob }}
    <button type="button" wire:click="$refresh" dusk="refresh">Refresh</button>


<?php

namespace LegacyTests\Browser\DataBinding\Lazy;

use Livewire\Component as BaseComponent;

class LazyInputsWithUpdatesDisplayedComponent extends BaseComponent
{
    public $name;
    public $description;

    public $is_active = false;

    public $updates = [];

    public function updated()
    {
        $this->updateUpdates();
    }

    public function submit()
    {
        $this->updateUpdates();
    }

    function updateUpdates()
    {
        // To keep the test from V2, I'm going to massage the V3 schema update data
        // back into the V2 schema here...
        $this->updates = [];

        foreach (request('components.0.updates') as $key => $value) {
            $this->updates[] = ['type' => 'syncInput', 'payload' => ['name' => $key]];
        }

        foreach (request('components.0.calls') as $call) {
            $this->updates[] = ['type' => 'callMethod', 'payload' => ['method' => $call['method']]];
        }
    }

    public function render()
    {
        return
<<<'HTML'

    <input dusk="name" wire:model.lazy="name">
    <input dusk="description" wire:model.lazy="description">

    <input dusk="is_active" type="checkbox" wire:model.live="is_active">

{{ count($updates) }}

        @foreach($updates as $update)

                @if($update['type'] == 'syncInput')
                    {{ $update['type'] . ' - ' . $update['payload']['name'] }}
                @elseif($update['type'] == 'callMethod')
                    {{ $update['type'] . ' - ' . $update['payload']['method'] }}
                @endif

        @endforeach

    <button dusk="submit" type="button" wire:click="submit">Submit</button>

HTML;
    }
}


<html>
<head>
    @livewireStyles
    <meta name="csrf-token" content="{{ csrf_token() }}">

</head>
<body>
    {{ $slot }}

    @livewireScripts
    <script type="module">
        import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';
    </script>
    <script src="https://cdn.jsdelivr.net/gh/livewire/turbolinks@v0.1.4/dist/livewire-turbolinks.js" data-turbolinks-eval="false" data-turbo-eval="false"></script>
    <script data-turbo-eval="false">
        document.addEventListener('turbo:before-render', () => {
            let permanents = document.querySelectorAll('[data-turbo-permanent]')

            let undos = Array.from(permanents).map(el => {
                el._x_ignore = true

                return () => {
                    delete el._x_ignore
                }
            })

            document.addEventListener('turbo:render', function handler() {
                while(undos.length) undos.shift()()

                document.removeEventListener('turbo:render', handler)
            })
        })
    </script>
    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
    @yield('content')

    @stack('scripts')
</body>
</html>


var UploadManager = class {
    constructor(component) {
      this.component = component;
      this.uploadBag = new MessageBag();
      this.removeBag = new MessageBag();
    }
    registerListeners() {
      this.component.$wire.$on("upload:generatedSignedUrl", ({ name, url }) => {
        setUploadLoading(this.component, name);
        this.handleSignedUrl(name, url);
      });
      this.component.$wire.$on("upload:generatedSignedUrlForS3", ({ name, payload }) => {
        setUploadLoading(this.component, name);
        this.handleS3PreSignedUrl(name, payload);
      });
      this.component.$wire.$on("upload:finished", ({ name, tmpFilenames }) => this.markUploadFinished(name, tmpFilenames));
      this.component.$wire.$on("upload:errored", ({ name }) => this.markUploadErrored(name));
      this.component.$wire.$on("upload:removed", ({ name, tmpFilename }) => this.removeBag.shift(name).finishCallback(tmpFilename));
    }
    upload(name, file, finishCallback, errorCallback, progressCallback, cancelledCallback) {
      this.setUpload(name, {
        files: [file],
        multiple: false,
        finishCallback,
        errorCallback,
        progressCallback,
        cancelledCallback
      });
    }
    uploadMultiple(name, files, finishCallback, errorCallback, progressCallback, cancelledCallback) {
      this.setUpload(name, {
        files: Array.from(files),
        multiple: true,
        finishCallback,
        errorCallback,
        progressCallback,
        cancelledCallback
      });
    }
    removeUpload(name, tmpFilename, finishCallback) {
      this.removeBag.push(name, {
        tmpFilename,
        finishCallback
      });
      this.component.$wire.call("_removeUpload", name, tmpFilename);
    }
    setUpload(name, uploadObject) {
      this.uploadBag.add(name, uploadObject);
      if (this.uploadBag.get(name).length === 1) {
        this.startUpload(name, uploadObject);
      }
    }
    handleSignedUrl(name, url) {
      let formData = new FormData();
      Array.from(this.uploadBag.first(name).files).forEach((file) => formData.append("files[]", file, file.name));
      let headers = {
        "Accept": "application/json"
      };
      let csrfToken = getCsrfToken();
      if (csrfToken)
        headers["X-CSRF-TOKEN"] = csrfToken;
      this.makeRequest(name, formData, "post", url, headers, (response) => {
        return response.paths;
      });
    }
    handleS3PreSignedUrl(name, payload) {
      let formData = this.uploadBag.first(name).files[0];
      let headers = payload.headers;
      if ("Host" in headers)
        delete headers.Host;
      let url = payload.url;
      this.makeRequest(name, formData, "put", url, headers, (response) => {
        return [payload.path];
      });
    }
    makeRequest(name, formData, method, url, headers, retrievePaths) {
      let request = new XMLHttpRequest();
      request.open(method, url);
      Object.entries(headers).forEach(([key, value]) => {
        request.setRequestHeader(key, value);
      });
      request.upload.addEventListener("progress", (e) => {
        e.detail = {};
        e.detail.progress = Math.floor(e.loaded * 100 / e.total);
        this.uploadBag.first(name).progressCallback(e);
      });
      request.addEventListener("load", () => {
        if ((request.status + "")[0] === "2") {
          let paths = retrievePaths(request.response && JSON.parse(request.response));
          this.component.$wire.call("_finishUpload", name, paths, this.uploadBag.first(name).multiple);
          return;
        }
        let errors = null;
        if (request.status === 422) {
          errors = request.response;
        }
        this.component.$wire.call("_uploadErrored", name, errors, this.uploadBag.first(name).multiple);
      });
      this.uploadBag.first(name).request = request;
      request.send(formData);
    }
    startUpload(name, uploadObject) {
      let fileInfos = uploadObject.files.map((file) => {
        return { name: file.name, size: file.size, type: file.type };
      });
      this.component.$wire.call("_startUpload", name, fileInfos, uploadObject.multiple);
      setUploadLoading(this.component, name);
    }
    markUploadFinished(name, tmpFilenames) {
      unsetUploadLoading(this.component);
      let uploadObject = this.uploadBag.shift(name);
      uploadObject.finishCallback(uploadObject.multiple ? tmpFilenames : tmpFilenames[0]);
      if (this.uploadBag.get(name).length > 0)
        this.startUpload(name, this.uploadBag.last(name));
    }
    markUploadErrored(name) {
      unsetUploadLoading(this.component);
      this.uploadBag.shift(name).errorCallback();
      if (this.uploadBag.get(name).length > 0)
        this.startUpload(name, this.uploadBag.last(name));
    }
    cancelUpload(name, cancelledCallback = null) {
      unsetUploadLoading(this.component);
      let uploadItem = this.uploadBag.first(name);
      if (uploadItem) {
        if (uploadItem.request) {
          uploadItem.request.abort();
        }
        this.uploadBag.shift(name).cancelledCallback();
        if (cancelledCallback)
          cancelledCallback();
      }
    }
  };
  var MessageBag = class {
    constructor() {
      this.bag = {};
    }
    add(name, thing) {
      if (!this.bag[name]) {
        this.bag[name] = [];
      }
      this.bag[name].push(thing);
    }
    push(name, thing) {
      this.add(name, thing);
    }
    first(name) {
      if (!this.bag[name])
        return null;
      return this.bag[name][0];
    }
    last(name) {
      return this.bag[name].slice(-1)[0];
    }
    get(name) {
      return this.bag[name];
    }
    shift(name) {
      return this.bag[name].shift();
    }
    call(name, ...params) {
      (this.listeners[name] || []).forEach((callback) => {
        callback(...params);
      });
    }
    has(name) {
      return Object.keys(this.listeners).includes(name);
    }
  };
  function setUploadLoading() {
  }
  function unsetUploadLoading() {
  }
  function upload(component, name, file, finishCallback = () => {
  }, errorCallback = () => {
  }, progressCallback = () => {
  }, cancelledCallback = () => {
  }) {
    let uploadManager = getUploadManager(component);
    uploadManager.upload(name, file, finishCallback, errorCallback, progressCallback, cancelledCallback);
  }
  function uploadMultiple(component, name, files, finishCallback = () => {
  }, errorCallback = () => {
  }, progressCallback = () => {
  }, cancelledCallback = () => {
  }) {
    let uploadManager = getUploadManager(component);
    uploadManager.uploadMultiple(name, files, finishCallback, errorCallback, progressCallback, cancelledCallback);
  }
  function removeUpload(component, name, tmpFilename, finishCallback = () => {
  }, errorCallback = () => {
  }) {
    let uploadManager = getUploadManager(component);
    uploadManager.removeUpload(name, tmpFilename, finishCallback, errorCallback);
  }
  function cancelUpload(component, name, cancelledCallback = () => {
  }) {
    let uploadManager = getUploadManager(component);
    uploadManager.cancelUpload(name, cancelledCallback);
  }

  // ../alpine/packages/alpinejs/dist/module.esm.js
  var flushPending = false;
  var flushing = false;
  var queue = [];
  var lastFlushedIndex = -1;
  function scheduler(callback) {
    queueJob(callback);
  }
  function queueJob(job) {
    if (!queue.includes(job))
      queue.push(job);
    queueFlush();
  }
  function dequeueJob(job) {
    let index = queue.indexOf(job);
    if (index !== -1 && index > lastFlushedIndex)
      queue.splice(index, 1);
  }
  function queueFlush() {
    if (!flushing && !flushPending) {
      flushPending = true;
      queueMicrotask(flushJobs);
    }
  }
  function flushJobs() {
    flushPending = false;
    flushing = true;
    for (let i = 0; i < queue.length; i++) {
      queue[i]();
      lastFlushedIndex = i;
    }
    queue.length = 0;
    lastFlushedIndex = -1;
    flushing = false;
  }
  var reactive;
  var effect;
  var release;
  var raw;
  var shouldSchedule = true;
  function disableEffectScheduling(callback) {
    shouldSchedule = false;
    callback();
    shouldSchedule = true;
  }
  function setReactivityEngine(engine) {
    reactive = engine.reactive;
    release = engine.release;
    effect = (callback) => engine.effect(callback, { scheduler: (task) => {
      if (shouldSchedule) {
        scheduler(task);
      } else {
        task();
      }
    } });
    raw = engine.raw;
  }
  function overrideEffect(override) {
    effect = override;
  }


function track2(name, initialSeedValue, alwaysShow = false, except = null) {
    let { has: has2, get: get3, set: set3, remove } = queryStringUtils();
    let url = new URL(window.location.href);
    let isInitiallyPresentInUrl = has2(url, name);
    let initialValue = isInitiallyPresentInUrl ? get3(url, name) : initialSeedValue;
    let initialValueMemo = JSON.stringify(initialValue);
    let exceptValueMemo = [false, null, void 0].includes(except) ? initialSeedValue : JSON.stringify(except);
    let hasReturnedToInitialValue = (newValue) => JSON.stringify(newValue) === initialValueMemo;
    let hasReturnedToExceptValue = (newValue) => JSON.stringify(newValue) === exceptValueMemo;
    if (alwaysShow)
      url = set3(url, name, initialValue);
    replace(url, name, { value: initialValue });
    let lock = false;
    let update = (strategy, newValue) => {
      if (lock)
        return;
      let url2 = new URL(window.location.href);
      if (!alwaysShow && !isInitiallyPresentInUrl && hasReturnedToInitialValue(newValue)) {
        url2 = remove(url2, name);
      } else if (newValue === void 0) {
        url2 = remove(url2, name);
      } else if (!alwaysShow && hasReturnedToExceptValue(newValue)) {
        url2 = remove(url2, name);
      } else {
        url2 = set3(url2, name, newValue);
      }
      strategy(url2, name, { value: newValue });
    };
    return {
      initial: initialValue,
      replace(newValue) {
        update(replace, newValue);
      },
      push(newValue) {
        update(push, newValue);
      },
      pop(receiver) {
        let handler4 = (e) => {
          if (!e.state || !e.state.alpine)
            return;
          Object.entries(e.state.alpine).forEach(([iName, { value: newValue }]) => {
            if (iName !== name)
              return;
            lock = true;
            let result = receiver(newValue);
            if (result instanceof Promise) {
              result.finally(() => lock = false);
            } else {
              lock = false;
            }
          });
        };
        window.addEventListener("popstate", handler4);
        return () => window.removeEventListener("popstate", handler4);
      }
    };
  }
  function replace(url, key, object) {
    let state = window.history.state || {};
    if (!state.alpine)
      state.alpine = {};
    state.alpine[key] = unwrap(object);
    window.history.replaceState(state, "", url.toString());
  }
  function push(url, key, object) {
    let state = window.history.state || {};
    if (!state.alpine)
      state.alpine = {};
    state = { alpine: { ...state.alpine, ...{ [key]: unwrap(object) } } };
    window.history.pushState(state, "", url.toString());
  }
  function unwrap(object) {
    if (object === void 0)
      return void 0;
    return JSON.parse(JSON.stringify(object));
  }
  function queryStringUtils() {
    return {
      has(url, key) {
        let search = url.search;
        if (!search)
          return false;
        let data2 = fromQueryString(search);
        return Object.keys(data2).includes(key);
      },
      get(url, key) {
        let search = url.search;
        if (!search)
          return false;
        let data2 = fromQueryString(search);
        return data2[key];
      },
      set(url, key, value) {
        let data2 = fromQueryString(url.search);
        data2[key] = stripNulls(unwrap(value));
        url.search = toQueryString(data2);
        return url;
      },
      remove(url, key) {
        let data2 = fromQueryString(url.search);
        delete data2[key];
        url.search = toQueryString(data2);
        return url;
      }
    };
  }
  function stripNulls(value) {
    if (!isObjecty(value))
      return value;
    for (let key in value) {
      if (value[key] === null)
        delete value[key];
      else
        value[key] = stripNulls(value[key]);
    }
    return value;
  }
  function toQueryString(data2) {
    let isObjecty2 = (subject) => typeof subject === "object" && subject !== null;
    let buildQueryStringEntries = (data3, entries2 = {}, baseKey = "") => {
      Object.entries(data3).forEach(([iKey, iValue]) => {
        let key = baseKey === "" ? iKey : `${baseKey}[${iKey}]`;
        if (iValue === null) {
          entries2[key] = "";
        } else if (!isObjecty2(iValue)) {
          entries2[key] = encodeURIComponent(iValue).replaceAll("%20", "+").replaceAll("%2C", ",");
        } else {
          entries2 = { ...entries2, ...buildQueryStringEntries(iValue, entries2, key) };
        }
      });
      return entries2;
    };
    let entries = buildQueryStringEntries(data2);
    return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join("&");
  }
  function fromQueryString(search) {
    search = search.replace("?", "");
    if (search === "")
      return {};
    let insertDotNotatedValueIntoData = (key, value, data3) => {
      let [first2, second, ...rest] = key.split(".");
      if (!second)
        return data3[key] = value;
      if (data3[first2] === void 0) {
        data3[first2] = isNaN(second) ? {} : [];
      }
      insertDotNotatedValueIntoData([second, ...rest].join("."), value, data3[first2]);
    };
    let entries = search.split("&").map((i) => i.split("="));
    let data2 = /* @__PURE__ */ Object.create(null);
    entries.forEach(([key, value]) => {
      if (typeof value == "undefined")
        return;
      value = decodeURIComponent(value.replaceAll("+", "%20"));
      if (!key.includes("[")) {
        data2[key] = value;
      } else {
        let dotNotatedKey = key.replaceAll("[", ".").replaceAll("]", "");
        insertDotNotatedValueIntoData(dotNotatedKey, value, data2);
      }
    });
    return data2;
  }

  // ../alpine/packages/morph/dist/module.esm.js
  function morph(from, toHtml, options)


function ye(e){let t=ci(e)?e[0]:e,r=ci(e)?e[1]:void 0;return St(t)&&Object.entries(t).forEach(([n,i])=>{t[n]=ye(i)}),t}function ci(e){return Array.isArray(e)&&e.length===2&&typeof e[1]=="object"&&Object.keys(e[1]).includes("s")}function Et(){if(document.querySelector('meta[name="csrf-token"]'))return document.querySelector('meta[name="csrf-token"]').getAttribute("content");if(document.querySelector("[data-csrf]"))return document.querySelector("[data-csrf]").getAttribute("data-csrf");if(window.livewireScriptConfig.csrf??!1)return window.livewireScriptConfig.csrf;throw"Livewire: No CSRF token detected"}var Ie;function fi(){if(Ie)return Ie;if(window.livewireScriptConfig&&(window.livewireScriptConfig.nonce??!1))return Ie=window.livewireScriptConfig.nonce,Ie;let e=document.querySelector("style[data-livewire-style][nonce]");return e?(Ie=e.nonce,Ie):null}function di(){return document.querySelector("[data-update-uri]")?.getAttribute("data-update-uri")??window.livewireScriptConfig.uri??null}function At(e){return!!e.match(/<script>Sfdump\(".+"\)<\/script>/)}function pi(e){let t=e.match(/.*<script>Sfdump\(".+"\)<\/script>/s);return[t,e.replace(t,"")]}var yr=new WeakMap;function tt(e){if(!yr.has(e)){let t=new xr(e);yr.set(e,t),t.registerListeners()}return yr.get(e)}function hi(e,t,r,n){let i=tt(r),o=()=>e.dispatchEvent(new CustomEvent("livewire-upload-start",{bubbles:!0,detail:{id:r.id,property:t}})),s=()=>e.dispatchEvent(new CustomEvent("livewire-upload-finish",{bubbles:!0,detail:{id:r.id,property:t}})),a=()=>e.dispatchEvent(new CustomEvent("livewire-upload-error",{bubbles:!0,detail:{id:r.id,property:t}})),l=()=>e.dispatchEvent(new CustomEvent("livewire-upload-cancel",{bubbles:!0,detail:{id:r.id,property:t}})),u=c=>{var d=Math.round(c.loaded*100/c.total);e.dispatchEvent(new CustomEvent("livewire-upload-progress",{bubbles:!0,detail:{progress:d}}))},f=c=>{c.target.files.length!==0&&(o(),c.target.multiple?i.uploadMultiple(t,c.target.files,s,a,u,l):i.upload(t,c.target.files[0],s,a,u,l))};e.addEventListener("change",f),r.$wire.$watch(t,c=>{!e.isConnected||(c===null||c==="")&&(e.value="")});let p=()=>{e.value=null};e.addEventListener("click",p),e.addEventListener("livewire-upload-cancel",p),n(()=>{e.removeEventListener("change",f),e.removeEventListener("click",p)})}var xr=class{constructor(t){this.component=t,this.uploadBag=new et,this.removeBag=new et}registerListeners(){this.component.$wire.$on("upload:generatedSignedUrl",({name:t,url:r})=>{this.component,this.handleSignedUrl(t,r)}),this.component.$wire.$on("upload:generatedSignedUrlForS3",({name:t,payload:r})=>{this.component,this.handleS3PreSignedUrl(t,r)}),this.component.$wire.$on("upload:finished",({name:t,tmpFilenames:r})=>this.markUploadFinished(t,r)),this.component.$wire.$on("upload:errored",({name:t})=>this.markUploadErrored(t)),this.component.$wire.$on("upload:removed",({name:t,tmpFilename:r})=>this.removeBag.shift(t).finishCallback(r))}upload(t,r,n,i,o,s){this.setUpload(t,{files:[r],multiple:!1,finishCallback:n,errorCallback:i,progressCallback:o,cancelledCallback:s})}uploadMultiple(t,r,n,i,o,s){this.setUpload(t,{files:Array.from(r),multiple:!0,finishCallback:n,errorCallback:i,progressCallback:o,cancelledCallback:s})}removeUpload(t,r,n){this.removeBag.push(t,{tmpFilename:r,finishCallback:n}),this.component.$wire.call("_removeUpload",t,r)}setUpload(t,r){this.uploadBag.add(t,r),this.uploadBag.get(t).length===1&&this.startUpload(t,r)}handleSignedUrl(t,r){let n=new FormData;Array.from(this.uploadBag.first(t).files).forEach(s=>n.append("files[]",s,s.name));let i={Accept:"application/json"},o=Et();o&&(i["X-CSRF-TOKEN"]=o),this.makeRequest(t,n,"post",r,i,s=>s.paths)}handleS3PreSignedUrl(t,r){let n=this.uploadBag.first(t).files[0],i=r.headers;"Host"in i&&delete i.Host;let o=r.url;this.makeRequest(t,n,"put",o,i,s=>[r.path])}makeRequest(t,r,n,i,o,s){let a=new XMLHttpRequest;a.open(n,i),Object.entries(o).forEach(([l,u])=>{a.setRequestHeader(l,u)}),a.upload.addEventListener("progress",l=>{l.detail={},l.detail.progress=Math.floor(l.loaded*100/l.total),this.uploadBag.first(t).progressCallback(l)}),a.addEventListener("load",()=>{if((a.status+"")[0]==="2"){let u=s(a.response&&JSON.parse(a.response));this.component.$wire.call("_finishUpload",t,u,this.uploadBag.first(t).multiple);return}let l=null;a.status===422&&(l=a.response),this.component.$wire.call("_uploadErrored",t,l,this.uploadBag.first(t).multiple)}),this.uploadBag.first(t).request=a,a.send(r)}startUpload(t,r){let n=r.files.map(i=>({name:i.name,size:i.size,type:i.type}));this.component.$wire.call("_startUpload",t,n,r.multiple),this.component}markUploadFinished(t,r){this.component;let n=this.uploadBag.shift(t);n.finishCallback(n.multiple?r:r[0]),this.uploadBag.get(t).length>0&&this.startUpload(t,this.uploadBag.last(t))}markUploadErrored(t){this.component,this.uploadBag.shift(t).errorCallback(),this.uploadBag.get(t).length>0&&this.startUpload(t,this.uploadBag.last(t))}cancelUpload(t,r=null){this.component;let n=this.uploadBag.first(t);n&&(n.request&&n.request.abort(),this.uploadBag.shift(t).cancelledCallback(),r&&r())}},et=class{constructor(){this.bag={}}add(t,r){this.bag[t]||(this.bag[t]=[]),this.bag[t].push(r)}push(t,r){this.add(t,r)}first(t){return this.bag[t]?this.bag[t][0]:null}last(t){return this.bag[t].slice(-1)[0]}get(t){return this.bag[t]}shift(t){return this.bag[t].shift()}call(t,...r){(this.listeners[t]||[]).forEach(n=>{n(...r)})}has(t){return Object.keys(this.listeners).includes(t)}};function mi(e,t,r,n=()=>{},i=()=>{},o=()=>{},s=()=>{}){tt(e).upload(t,r,n,i,o,s)}function gi(e,t,r,n=()=>{},i=()=>{},o=()=>{},s=()=>{}){tt(e).uploadMultiple(t,r,n,i,o,s)}function vi(e,t,r,n=()=>{},i=()=>{}){tt(e).removeUpload(t,r,n,i)}function bi(e,t,r=()=>{}){tt(e).cancelUpload(t,r)}var Or=!1,Tr=!1,Se=[],kr=-1;function ml(e){gl(e)}function gl(e){Se.includes(e)||Se.push(e),bl()}function vl(e){let t=Se.indexOf(e);t!==-1&&t>kr&&Se.splice(t,1)}function bl(){!Tr&&!Or&&(Or=!0,queueMicrotask(wl))}function wl(){Or=!1,Tr=!0;for(let e=0;e<Se.length;e++)Se[e](),kr=e;Se.length=0,kr=-1,Tr=!1}var Be,Te,Ue,Ri,Lr=!0;function yl(e){Lr=!1,e(),Lr=!0}function xl(e){Be=e.reactive,Ue=e.release,Te=t=>e.effect(t,{scheduler:r=>{Lr?ml(r):r()}}),Ri=e.raw}function wi(e){Te=e}function _l(e){let t=()=>{};return[n=>{let i=Te(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),Ue(i))},i},()=>{t()}]}


var UploadManager = class {
  constructor(component) {
    this.component = component;
    this.uploadBag = new MessageBag();
    this.removeBag = new MessageBag();
  }
  registerListeners() {
    this.component.$wire.$on("upload:generatedSignedUrl", ({ name, url }) => {
      setUploadLoading(this.component, name);
      this.handleSignedUrl(name, url);
    });
    this.component.$wire.$on("upload:generatedSignedUrlForS3", ({ name, payload }) => {
      setUploadLoading(this.component, name);
      this.handleS3PreSignedUrl(name, payload);
    });
    this.component.$wire.$on("upload:finished", ({ name, tmpFilenames }) => this.markUploadFinished(name, tmpFilenames));
    this.component.$wire.$on("upload:errored", ({ name }) => this.markUploadErrored(name));
    this.component.$wire.$on("upload:removed", ({ name, tmpFilename }) => this.removeBag.shift(name).finishCallback(tmpFilename));
  }
  upload(name, file, finishCallback, errorCallback, progressCallback, cancelledCallback) {
    this.setUpload(name, {
      files: [file],
      multiple: false,
      finishCallback,
      errorCallback,
      progressCallback,
      cancelledCallback
    });
  }
  uploadMultiple(name, files, finishCallback, errorCallback, progressCallback, cancelledCallback) {
    this.setUpload(name, {
      files: Array.from(files),
      multiple: true,
      finishCallback,
      errorCallback,
      progressCallback,
      cancelledCallback
    });
  }
  removeUpload(name, tmpFilename, finishCallback) {
    this.removeBag.push(name, {
      tmpFilename,
      finishCallback
    });
    this.component.$wire.call("_removeUpload", name, tmpFilename);
  }
  setUpload(name, uploadObject) {
    this.uploadBag.add(name, uploadObject);
    if (this.uploadBag.get(name).length === 1) {
      this.startUpload(name, uploadObject);
    }
  }
  handleSignedUrl(name, url) {
    let formData = new FormData();
    Array.from(this.uploadBag.first(name).files).forEach((file) => formData.append("files[]", file, file.name));
    let headers = {
      "Accept": "application/json"
    };
    let csrfToken = getCsrfToken();
    if (csrfToken)
      headers["X-CSRF-TOKEN"] = csrfToken;
    this.makeRequest(name, formData, "post", url, headers, (response) => {
      return response.paths;
    });
  }
  handleS3PreSignedUrl(name, payload) {
    let formData = this.uploadBag.first(name).files[0];
    let headers = payload.headers;
    if ("Host" in headers)
      delete headers.Host;
    let url = payload.url;
    this.makeRequest(name, formData, "put", url, headers, (response) => {
      return [payload.path];
    });
  }
  makeRequest(name, formData, method, url, headers, retrievePaths) {
    let request = new XMLHttpRequest();
    request.open(method, url);
    Object.entries(headers).forEach(([key, value]) => {
      request.setRequestHeader(key, value);
    });
    request.upload.addEventListener("progress", (e) => {
      e.detail = {};
      e.detail.progress = Math.floor(e.loaded * 100 / e.total);
      this.uploadBag.first(name).progressCallback(e);
    });
    request.addEventListener("load", () => {
      if ((request.status + "")[0] === "2") {
        let paths = retrievePaths(request.response && JSON.parse(request.response));
        this.component.$wire.call("_finishUpload", name, paths, this.uploadBag.first(name).multiple);
        return;
      }
      let errors = null;
      if (request.status === 422) {
        errors = request.response;
      }
      this.component.$wire.call("_uploadErrored", name, errors, this.uploadBag.first(name).multiple);
    });
    this.uploadBag.first(name).request = request;
    request.send(formData);
  }
  startUpload(name, uploadObject) {
    let fileInfos = uploadObject.files.map((file) => {
      return { name: file.name, size: file.size, type: file.type };
    });
    this.component.$wire.call("_startUpload", name, fileInfos, uploadObject.multiple);
    setUploadLoading(this.component, name);
  }
  markUploadFinished(name, tmpFilenames) {
    unsetUploadLoading(this.component);
    let uploadObject = this.uploadBag.shift(name);
    uploadObject.finishCallback(uploadObject.multiple ? tmpFilenames : tmpFilenames[0]);
    if (this.uploadBag.get(name).length > 0)
      this.startUpload(name, this.uploadBag.last(name));
  }
  markUploadErrored(name) {
    unsetUploadLoading(this.component);
    this.uploadBag.shift(name).errorCallback();
    if (this.uploadBag.get(name).length > 0)
      this.startUpload(name, this.uploadBag.last(name));
  }
  cancelUpload(name, cancelledCallback = null) {
    unsetUploadLoading(this.component);
    let uploadItem = this.uploadBag.first(name);
    if (uploadItem) {
      if (uploadItem.request) {
        uploadItem.request.abort();
      }
      this.uploadBag.shift(name).cancelledCallback();
      if (cancelledCallback)
        cancelledCallback();
    }
  }
};
var MessageBag = class {
  constructor() {
    this.bag = {};
  }
  add(name, thing) {
    if (!this.bag[name]) {
      this.bag[name] = [];
    }
    this.bag[name].push(thing);
  }
  push(name, thing) {
    this.add(name, thing);
  }
  first(name) {
    if (!this.bag[name])
      return null;
    return this.bag[name][0];
  }
  last(name) {
    return this.bag[name].slice(-1)[0];
  }
  get(name) {
    return this.bag[name];
  }
  shift(name) {
    return this.bag[name].shift();
  }
  call(name, ...params) {
    (this.listeners[name] || []).forEach((callback) => {
      callback(...params);
    });
  }
  has(name) {
    return Object.keys(this.listeners).includes(name);
  }
};
function setUploadLoading() {
}
function unsetUploadLoading() {
}
function upload(component, name, file, finishCallback = () => {
}, errorCallback = () => {
}, progressCallback = () => {
}, cancelledCallback = () => {
}) {
  let uploadManager = getUploadManager(component);
  uploadManager.upload(name, file, finishCallback, errorCallback, progressCallback, cancelledCallback);
}
function uploadMultiple(component, name, files, finishCallback = () => {
}, errorCallback = () => {
}, progressCallback = () => {
}, cancelledCallback = () => {
}) {
  let uploadManager = getUploadManager(component);
  uploadManager.uploadMultiple(name, files, finishCallback, errorCallback, progressCallback, cancelledCallback);
}
function removeUpload(component, name, tmpFilename, finishCallback = () => {
}, errorCallback = () => {
}) {
  let uploadManager = getUploadManager(component);
  uploadManager.removeUpload(name, tmpFilename, finishCallback, errorCallback);
}
function cancelUpload(component, name, cancelledCallback = () => {
}) {
  let uploadManager = getUploadManager(component);
  uploadManager.cancelUpload(name, cancelledCallback);
}

// js/features/supportEntangle.js
var import_alpinejs = __toESM(require_module_cjs());
function generateEntangleFunction(component, cleanup) {
  if (!cleanup)
    cleanup = () => {
    };
  return (name, live = false) => {
    let isLive = live;
    let livewireProperty = name;
    let livewireComponent = component.$wire;
    let livewirePropertyValue = livewireComponent.get(livewireProperty);
    let interceptor = import_alpinejs.default.interceptor((initialValue, getter, setter, path, key) => {
      if (typeof livewirePropertyValue === "undefined") {
        console.error(`Livewire Entangle Error: Livewire property ['${livewireProperty}'] cannot be found on component: ['${component.name}']`);
        return;
      }
      let release = import_alpinejs.default.entangle({
        get() {
          return livewireComponent.get(name);
        },
        set(value) {
          livewireComponent.set(name, value, isLive);
        }
      }, {
        get() {
          return getter();
        },
        set(value) {
          setter(value);
        }
      });
      cleanup(() => release());
      return cloneIfObject(livewireComponent.get(name));
    }, (obj) => {
      Object.defineProperty(obj, "live", {
        get() {
          isLive = true;
          return obj;
        }
      });
    });
    return interceptor(livewirePropertyValue);
  };
}
function cloneIfObject(value) {
  return typeof value === "object" ? JSON.parse(JSON.stringify(value)) : value;
}

// js/hooks.js
var listeners = [];
function on(name, callback) {
  if (!listeners[name])
    listeners[name] = [];
  listeners[name].push(callback);
  return () => {
    listeners[name] = listeners[name].filter((i) => i !== callback);
  };
}


function track(name, initialSeedValue, alwaysShow = false, except = null) {
  let { has, get, set, remove } = queryStringUtils();
  let url = new URL(window.location.href);
  let isInitiallyPresentInUrl = has(url, name);
  let initialValue = isInitiallyPresentInUrl ? get(url, name) : initialSeedValue;
  let initialValueMemo = JSON.stringify(initialValue);
  let exceptValueMemo = [false, null, void 0].includes(except) ? initialSeedValue : JSON.stringify(except);
  let hasReturnedToInitialValue = (newValue) => JSON.stringify(newValue) === initialValueMemo;
  let hasReturnedToExceptValue = (newValue) => JSON.stringify(newValue) === exceptValueMemo;
  if (alwaysShow)
    url = set(url, name, initialValue);
  replace(url, name, { value: initialValue });
  let lock = false;
  let update = (strategy, newValue) => {
    if (lock)
      return;
    let url2 = new URL(window.location.href);
    if (!alwaysShow && !isInitiallyPresentInUrl && hasReturnedToInitialValue(newValue)) {
      url2 = remove(url2, name);
    } else if (newValue === void 0) {
      url2 = remove(url2, name);
    } else if (!alwaysShow && hasReturnedToExceptValue(newValue)) {
      url2 = remove(url2, name);
    } else {
      url2 = set(url2, name, newValue);
    }
    strategy(url2, name, { value: newValue });
  };
  return {
    initial: initialValue,
    replace(newValue) {
      update(replace, newValue);
    },
    push(newValue) {
      update(push, newValue);
    },
    pop(receiver) {
      let handler = (e) => {
        if (!e.state || !e.state.alpine)
          return;
        Object.entries(e.state.alpine).forEach(([iName, { value: newValue }]) => {
          if (iName !== name)
            return;
          lock = true;
          let result = receiver(newValue);
          if (result instanceof Promise) {
            result.finally(() => lock = false);
          } else {
            lock = false;
          }
        });
      };
      window.addEventListener("popstate", handler);
      return () => window.removeEventListener("popstate", handler);
    }
  };
}
function replace(url, key, object) {
  let state = window.history.state || {};
  if (!state.alpine)
    state.alpine = {};
  state.alpine[key] = unwrap(object);
  window.history.replaceState(state, "", url.toString());
}
function push(url, key, object) {
  let state = window.history.state || {};
  if (!state.alpine)
    state.alpine = {};
  state = { alpine: { ...state.alpine, ...{ [key]: unwrap(object) } } };
  window.history.pushState(state, "", url.toString());
}
function unwrap(object) {
  if (object === void 0)
    return void 0;
  return JSON.parse(JSON.stringify(object));
}
function queryStringUtils() {
  return {
    has(url, key) {
      let search = url.search;
      if (!search)
        return false;
      let data = fromQueryString(search);
      return Object.keys(data).includes(key);
    },
    get(url, key) {
      let search = url.search;
      if (!search)
        return false;
      let data = fromQueryString(search);
      return data[key];
    },
    set(url, key, value) {
      let data = fromQueryString(url.search);
      data[key] = stripNulls(unwrap(value));
      url.search = toQueryString(data);
      return url;
    },
    remove(url, key) {
      let data = fromQueryString(url.search);
      delete data[key];
      url.search = toQueryString(data);
      return url;
    }
  };
}
function stripNulls(value) {
  if (!isObjecty(value))
    return value;
  for (let key in value) {
    if (value[key] === null)
      delete value[key];
    else
      value[key] = stripNulls(value[key]);
  }
  return value;
}
function toQueryString(data) {
  let isObjecty2 = (subject) => typeof subject === "object" && subject !== null;
  let buildQueryStringEntries = (data2, entries2 = {}, baseKey = "") => {
    Object.entries(data2).forEach(([iKey, iValue]) => {
      let key = baseKey === "" ? iKey : `${baseKey}[${iKey}]`;
      if (iValue === null) {
        entries2[key] = "";
      } else if (!isObjecty2(iValue)) {
        entries2[key] = encodeURIComponent(iValue).replaceAll("%20", "+").replaceAll("%2C", ",");
      } else {
        entries2 = { ...entries2, ...buildQueryStringEntries(iValue, entries2, key) };
      }
    });
    return entries2;
  };
  let entries = buildQueryStringEntries(data);
  return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join("&");
}
function fromQueryString(search) {
  search = search.replace("?", "");
  if (search === "")
    return {};
  let insertDotNotatedValueIntoData = (key, value, data2) => {
    let [first2, second, ...rest] = key.split(".");
    if (!second)
      return data2[key] = value;
    if (data2[first2] === void 0) {
      data2[first2] = isNaN(second) ? {} : [];
    }
    insertDotNotatedValueIntoData([second, ...rest].join("."), value, data2[first2]);
  };
  let entries = search.split("&").map((i) => i.split("="));
  let data = /* @__PURE__ */ Object.create(null);
  entries.forEach(([key, value]) => {
    if (typeof value == "undefined")
      return;
    value = decodeURIComponent(value.replaceAll("+", "%20"));
    if (!key.includes("[")) {
      data[key] = value;
    } else {
      let dotNotatedKey = key.replaceAll("[", ".").replaceAll("]", "");
      insertDotNotatedValueIntoData(dotNotatedKey, value, data);
    }
  });
  return data;
}

// js/lifecycle.js
var import_morph = __toESM(require_module_cjs8());
var import_mask = __toESM(require_module_cjs9());
var import_alpinejs5 = __toESM(require_module_cjs());
function start() {
  setTimeout(() => ensureLivewireScriptIsntMisplaced());
  dispatch(document, "livewire:init");
  dispatch(document, "livewire:initializing");
  import_alpinejs5.default.plugin(import_morph.default);
  import_alpinejs5.default.plugin(history2);
  import_alpinejs5.default.plugin(import_intersect.default);
  import_alpinejs5.default.plugin(import_resize.default);
  import_alpinejs5.default.plugin(import_collapse.default);
  import_alpinejs5.default.plugin(import_anchor.default);
  import_alpinejs5.default.plugin(import_focus.default);
  import_alpinejs5.default.plugin(import_persist2.default);
  import_alpinejs5.default.plugin(navigate_default);
  import_alpinejs5.default.plugin(import_mask.default);
  import_alpinejs5.default.addRootSelector(() => "[wire\\:id]");
  import_alpinejs5.default.onAttributesAdded((el, attributes) => {
    if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
      return;
    let component = closestComponent(el, false);
    if (!component)
      return;
    attributes.forEach((attribute) => {
      if (!matchesForLivewireDirective(attribute.name))
        return;
      let directive2 = extractDirective(el, attribute.name);
      trigger("directive.init", { el, component, directive: directive2, cleanup: (callback) => {
        import_alpinejs5.default.onAttributeRemoved(el, directive2.raw, callback);
      } });
    });
  });
  import_alpinejs5.default.interceptInit(import_alpinejs5.default.skipDuringClone((el) => {
    if (!Array.from(el.attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
      return;
    if (el.hasAttribute("wire:id")) {
      let component2 = initComponent(el);
      import_alpinejs5.default.onAttributeRemoved(el, "wire:id", () => {
        destroyComponent(component2.id);
      });
    }
    let component = closestComponent(el, false);
    if (component) {
      trigger("element.init", { el, component });
      let directives = Array.from(el.getAttributeNames()).filter((name) => matchesForLivewireDirective(name)).map((name) => extractDirective(el, name));
      directives.forEach((directive2) => {
        trigger("directive.init", { el, component, directive: directive2, cleanup: (callback) => {
          import_alpinejs5.default.onAttributeRemoved(el, directive2.raw, callback);
        } });
      });
    }
  }));
  import_alpinejs5.default.start();
  setTimeout(() => window.Livewire.initialRenderIsFinished = true);
  dispatch(document, "livewire:initialized");
}


<?php

return [

    /*
    |---------------------------------------------------------------------------
    | Class Namespace
    |---------------------------------------------------------------------------
    |
    | This value sets the root class namespace for Livewire component classes in
    | your application. This value will change where component auto-discovery
    | finds components. It's also referenced by the file creation commands.
    |
    */

    'class_namespace' => 'App\\Livewire',

    /*
    |---------------------------------------------------------------------------
    | View Path
    |---------------------------------------------------------------------------
    |
    | This value is used to specify where Livewire component Blade templates are
    | stored when running file creation commands like `artisan make:livewire`.
    | It is also used if you choose to omit a component's render() method.
    |
    */

    'view_path' => resource_path('views/livewire'),

    /*
    |---------------------------------------------------------------------------
    | Layout
    |---------------------------------------------------------------------------
    | The view that will be used as the layout when rendering a single component
    | as an entire page via `Route::get('/post/create', CreatePost::class);`.
    | In this case, the view returned by CreatePost will render into $slot.
    |
    */

    'layout' => 'components.layouts.app',

    /*
    |---------------------------------------------------------------------------
    | Lazy Loading Placeholder
    |---------------------------------------------------------------------------
    | Livewire allows you to lazy load components that would otherwise slow down
    | the initial page load. Every component can have a custom placeholder or
    | you can define the default placeholder view for all components below.
    |
    */

    'lazy_placeholder' => null,

    /*
    |---------------------------------------------------------------------------
    | Temporary File Uploads
    |---------------------------------------------------------------------------
    |
    | Livewire handles file uploads by storing uploads in a temporary directory
    | before the file is stored permanently. All file uploads are directed to
    | a global endpoint for temporary storage. You may configure this below:
    |
    */

    'temporary_file_upload' => [
        'disk' => null,        // Example: 'local', 's3'              | Default: 'default'
        'rules' => null,       // Example: ['file', 'mimes:png,jpg']  | Default: ['required', 'file', 'max:12288'] (12MB)
        'directory' => null,   // Example: 'tmp'                      | Default: 'livewire-tmp'
        'middleware' => null,  // Example: 'throttle:5,1'             | Default: 'throttle:60,1'
        'preview_mimes' => [   // Supported file types for temporary pre-signed file URLs...
            'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
            'mov', 'avi', 'wmv', 'mp3', 'm4a',
            'jpg', 'jpeg', 'mpga', 'webp', 'wma',
        ],
        'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
        'cleanup' => true, // Should cleanup temporary uploads older than 24 hrs...
    ],

    /*
    |---------------------------------------------------------------------------
    | Render On Redirect
    |---------------------------------------------------------------------------
    |
    | This value determines if Livewire will run a component's `render()` method
    | after a redirect has been triggered using something like `redirect(...)`
    | Setting this to true will render the view once more before redirecting
    |
    */

    'render_on_redirect' => false,

    /*
    |---------------------------------------------------------------------------
    | Eloquent Model Binding
    |---------------------------------------------------------------------------
    |
    | Previous versions of Livewire supported binding directly to eloquent model
    | properties using wire:model by default. However, this behavior has been
    | deemed too "magical" and has therefore been put under a feature flag.
    |
    */

    'legacy_model_binding' => false,

    /*
    |---------------------------------------------------------------------------
    | Auto-inject Frontend Assets
    |---------------------------------------------------------------------------
    |
    | By default, Livewire automatically injects its JavaScript and CSS into the
    | <head> and <body> of pages containing Livewire components. By disabling
    | this behavior, you need to use @livewireStyles and @livewireScripts.
    |
    */

    'inject_assets' => true,

    /*
    |---------------------------------------------------------------------------
    | Navigate (SPA mode)
    |---------------------------------------------------------------------------
    |
    | By adding `wire:navigate` to links in your Livewire application, Livewire
    | will prevent the default link handling and instead request those pages
    | via AJAX, creating an SPA-like effect. Configure this behavior here.
    |
    */

    'navigate' => [
        'show_progress_bar' => true,
        'progress_bar_color' => '#2299dd',
    ],

    /*
    |---------------------------------------------------------------------------
    | HTML Morph Markers
    |---------------------------------------------------------------------------
    |
    | Livewire intelligently "morphs" existing HTML into the newly rendered HTML
    | after each update. To make this process more reliable, Livewire injects
    | "markers" into the rendered Blade surrounding @if, @class & @foreach.
    |
    */

    'inject_morph_markers' => true,

    /*
    |---------------------------------------------------------------------------
    | Pagination Theme
    |---------------------------------------------------------------------------
    |
    | When enabling Livewire's pagination feature by using the `WithPagination`
    | trait, Livewire will use Tailwind templates to render pagination views
    | on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
    |
    */

    'pagination_theme' => 'tailwind',
];


import { isObjecty } from "@/utils"

export default function history(Alpine) {
    Alpine.magic('queryString', (el, { interceptor }) =>  {
        let alias
        let alwaysShow = false
        let usePush = false

        return interceptor((initialSeedValue, getter, setter, path, key) => {
            let queryKey = alias || path

            let { initial, replace, push, pop } = track(queryKey, initialSeedValue, alwaysShow)

            setter(initial)

            if (! usePush) {
                Alpine.effect(() => replace(getter()))
            } else {
                Alpine.effect(() => push(getter()))

                pop(async newValue => {
                    setter(newValue)

                    let tillTheEndOfTheMicrotaskQueue = () => Promise.resolve()

                    await tillTheEndOfTheMicrotaskQueue() // ...so that we preserve the internal lock...
                })
            }

            return initial
        }, func => {
            func.alwaysShow = () => { alwaysShow = true; return func }
            func.usePush = () => { usePush = true; return func }
            func.as = key => { alias = key; return func }
        })
    })

    Alpine.history = { track }
}

export function track(name, initialSeedValue, alwaysShow = false, except = null) {
    let { has, get, set, remove } = queryStringUtils()

    let url = new URL(window.location.href)
    let isInitiallyPresentInUrl = has(url, name)
    let initialValue = isInitiallyPresentInUrl ? get(url, name) : initialSeedValue
    let initialValueMemo = JSON.stringify(initialValue)
    let exceptValueMemo = [false, null, undefined].includes(except) ? initialSeedValue : JSON.stringify(except)

    let hasReturnedToInitialValue = (newValue) => JSON.stringify(newValue) === initialValueMemo
    let hasReturnedToExceptValue = (newValue) =>  JSON.stringify(newValue) === exceptValueMemo

    if (alwaysShow) url = set(url, name, initialValue)

    replace(url, name, { value: initialValue })

    let lock = false

    let update = (strategy, newValue) => {
        if (lock) return

        let url = new URL(window.location.href)

        // This block of code is what needs to be changed for this failing test to pass:
        if (! alwaysShow && ! isInitiallyPresentInUrl && hasReturnedToInitialValue(newValue)) {
            url = remove(url, name)
        // This is so that when deeply nested values are tracked, but their parent array/object
        // is removed, we can handle it gracefully by removing the entry from the URL instead
        // of letting it get set to `?someKey=undefined` which causes issues on refresh...
        } else if (newValue === undefined) {
            url = remove(url, name)
        } else if (! alwaysShow && hasReturnedToExceptValue(newValue)) {
            url = remove(url, name)
        } else {
            url = set(url, name, newValue)
        }

        // Right now, the above block, checks a few conditions and updates/removes an entry from the query string.
        // The new strategy needs to be something like:
        // - If "alwaysShow" is toggled on, then just "set" the whole thing with no deep diff
        // - Otherwise, run a deep comparison callback (given the original value and new value).
        //   - The callback recieves two params (leaf name and value)
        //   - Check leaf name and value for existance in the original URL from page load. If it's there, just call "set"
        //   - Check leaf name and value for equivelance to original name and value, if equal, call "remove", otherwise, "set"

        // That code will look something like this:

        // if (alwaysShow) {
        //     set(url, name, newValue)
        // } else {
        //     deepCompare(name, newValue, originalValue, (leafName, leafValue) => {
        //         // ....
        //     })
        // }

        strategy(url, name, { value: newValue})
    }

    return {
        initial: initialValue,

        replace(newValue) { // Update via replaceState...
            update(replace, newValue)
        },

        push(newValue) { // Update via pushState...
            update(push, newValue)
        },

        pop(receiver) { // "popstate" handler...
            let handler = (e) => {
                if (! e.state || ! e.state.alpine) return

                Object.entries(e.state.alpine).forEach(([iName, { value: newValue }]) => {
                    if (iName !== name) return

                    lock = true

                    // Allow the "receiver" to be an async function in case a non-syncronous
                    // operation (like an ajax) requests needs to happen while preserving
                    // the "locking" mechanism ("lock = true" in this case)...
                    let result = receiver(newValue)

                    if (result instanceof Promise) {
                        result.finally(() => lock = false)
                    } else {
                        lock = false
                    }
                })
            }

            window.addEventListener('popstate', handler)

            return () => window.removeEventListener('popstate', handler)
        }
    }
}

function replace(url, key, object) {
    let state = window.history.state || {}

    if (! state.alpine) state.alpine = {}

    state.alpine[key] = unwrap(object)

    window.history.replaceState(state, '', url.toString())
}

function push(url, key, object) {
    let state = window.history.state || {}

    if (! state.alpine) state.alpine = {}

    state = { alpine: {...state.alpine, ...{[key]: unwrap(object)}} }

    window.history.pushState(state, '', url.toString())
}

function unwrap(object) {
    if (object === undefined) return undefined

    return JSON.parse(JSON.stringify(object))
}

function queryStringUtils() {
    return {
        has(url, key) {
            let search = url.search

            if (! search) return false

            let data = fromQueryString(search)

            return Object.keys(data).includes(key)
        },
        get(url, key) {
            let search = url.search

            if (! search) return false

            let data = fromQueryString(search)

            return data[key]
        },
        set(url, key, value) {
            let data = fromQueryString(url.search)

            data[key] = stripNulls(unwrap(value))

            url.search = toQueryString(data)

            return url
        },
        remove(url, key) {
            let data = fromQueryString(url.search)

            delete data[key]

            url.search = toQueryString(data)

            return url
        },
    }
}

function stripNulls(value) {
    if (! isObjecty(value)) return value

    for (let key in value) {
        if (value[key] === null) delete value[key]
        else value[key] = stripNulls(value[key])
    }

    return value
}

// This function converts JavaScript data to bracketed query string notation...
// { items: [['foo']] } -> "items[0][0]=foo"
function toQueryString(data) {
    let isObjecty = (subject) => typeof subject === 'object' && subject !== null

    let buildQueryStringEntries = (data, entries = {}, baseKey = '') => {
        Object.entries(data).forEach(([iKey, iValue]) => {
            let key = baseKey === '' ? iKey : `${baseKey}[${iKey}]`

            if (iValue === null) {
                entries[key] = '';
            } else if (! isObjecty(iValue)) {
                entries[key] = encodeURIComponent(iValue)
                    .replaceAll('%20', '+') // Conform to RFC1738
                    .replaceAll('%2C', ',')
            } else {
                entries = {...entries, ...buildQueryStringEntries(iValue, entries, key)}
            }
        })

        return entries
    }

    let entries = buildQueryStringEntries(data)

    return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join('&')
}

// This function converts bracketed query string notation back to JS data...
// "items[0][0]=foo" -> { items: [['foo']] }


function fromQueryString(search) {
    search = search.replace('?', '')

    if (search === '') return {}

    let insertDotNotatedValueIntoData = (key, value, data) => {
        let [first, second, ...rest] = key.split('.')

        // We're at a leaf node, let's make the assigment...
        if (! second) return data[key] = value

        // This is where we fill in empty arrays/objects allong the way to the assigment...
        if (data[first] === undefined) {
            data[first] = isNaN(second) ? {} : []
        }

        // Keep deferring assignment until the full key is built up...
        insertDotNotatedValueIntoData([second, ...rest].join('.'), value, data[first])
    }

    let entries = search.split('&').map(i => i.split('='))

    // let data = {} creates a security (XSS) vulnerability here. We need to use
    // Object.create(null) instead so that we have a "pure" object that doesnt
    // inherit Object.prototype and expose the js internals to manipulation.
    let data = Object.create(null)

    entries.forEach(([key, value]) => {
        // Query string params don't always have values... (`?foo=`)
        if ( typeof value == 'undefined' ) return;

        value = decodeURIComponent(value.replaceAll('+', '%20'))

        if (! key.includes('[')) {
            data[key] = value
        } else {
            // Convert to dot notation because it's easier...
            let dotNotatedKey = key.replaceAll('[', '.').replaceAll(']', '')

            insertDotNotatedValueIntoData(dotNotatedKey, value, data)
        }
    })

    return data
}


export function hasQueryParam(param) {
    let queryParams = new URLSearchParams(window.location.search);

    return queryParams.has(param)
}

export function getQueryParam(param) {
    let queryParams = new URLSearchParams(window.location.search);

    return queryParams.get(param)
}

export function setQueryParam(param, value) {
    let queryParams = new URLSearchParams(window.location.search);

    queryParams.set(param, value)

    let url = urlFromQueryParams(queryParams)

    history.replaceState(history.state, '', url)
}

function urlFromParams(params = {}) {
    let queryParams = new URLSearchParams(window.location.search);

    Object.entries(params).forEach(([key, value]) => {
        queryParams.set(key, value)
    })

    let queryString = Array.from(queryParams.entries()).length > 0
        ? '?'+params.toString()
        : ''

    return window.location.origin + window.location.pathname + '?'+queryString + window.location.hash
}


<?php

namespace Tests;

use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Artisan;

class TestCase extends \Orchestra\Testbench\Dusk\TestCase
{
    public function setUp(): void
    {
        $this->afterApplicationCreated(function () {
            $this->makeACleanSlate();
        });

        $this->beforeApplicationDestroyed(function () {
            $this->makeACleanSlate();
        });

        parent::setUp();
    }

    public function makeACleanSlate()
    {
        Artisan::call('view:clear');

        File::deleteDirectory($this->livewireViewsPath());
        File::deleteDirectory($this->livewireClassesPath());
        File::deleteDirectory($this->livewireTestsPath());
        File::delete(app()->bootstrapPath('cache/livewire-components.php'));
    }

    protected function getPackageProviders($app)
    {
        return [
            \Livewire\LivewireServiceProvider::class,
        ];
    }

    protected function defineEnvironment($app)
    {
        $app['config']->set('view.paths', [
            __DIR__.'/views',
            resource_path('views'),
        ]);

        $app['config']->set('app.key', 'base64:Hupx3yAySikrM2/edkZQNQHslgDWYfiBfCuSThJ5SK8=');

        $app['config']->set('database.default', 'testbench');
        $app['config']->set('database.connections.testbench', [
            'driver'   => 'sqlite',
            'database' => ':memory:',
            'prefix'   => '',
        ]);

        $app['config']->set('filesystems.disks.unit-downloads', [
            'driver' => 'local',
            'root' => __DIR__.'/fixtures',
        ]);
    }

    protected function livewireClassesPath($path = '')
    {
        return app_path('Livewire'.($path ? '/'.$path : ''));
    }

    protected function livewireViewsPath($path = '')
    {
        return resource_path('views').'/livewire'.($path ? '/'.$path : '');
    }

    protected function livewireTestsPath($path = '')
    {
        return base_path('tests/Feature/Livewire'.($path ? '/'.$path : ''));
    }
}


    @foreach ($children as $child)
        @livewire('child', ['name' => $child], key($child))
    @endforeach


    <meta name="csrf-token" content="{{ csrf_token() }}">

This is a custom layout

    {{ $slot }}

    @stack('scripts')


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
    {{ $header ?? 'No Header' }}

    {{ $slot }}

    {{ $footer ?? 'No Footer' }}
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <style>
        .show {
            display: block;
        }
    </style>
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    @stack('styles')
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


Because forms are the backbone of most web applications, Livewire provides loads of helpful utilities for building them. From handling simple input elements to complex things like real-time validation or file uploading, Livewire has simple, well-documented tools to make your life easier and delight your users.

Let's dive in.

## Submitting a form

Let's start by looking at a very simple form in a `CreatePost` component. This form will have two simple text inputs and a submit button, as well as some code on the backend to manage the form's state and submission:

```php
<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;

class CreatePost extends Component
{
    public $title = '';

    public $content = '';

    public function save()
    {
        Post::create(
            $this->only(['title', 'content'])
        );

        session()->flash('status', 'Post successfully updated.');

        return $this->redirect('/posts');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}
```

```blade
<form wire:submit="save">
    <input type="text" wire:model="title">

    <input type="text" wire:model="content">

    <button type="submit">Save</button>
</form>
```

As you can see, we are "binding" the public `$title` and `$content` properties in the form above using `wire:model`. This is one of the most commonly used and powerful features of Livewire.

In addition to binding `$title` and `$content`, we are using `wire:submit` to capture the `submit` event when the "Save" button is clicked and invoking the `save()` action. This action will persist the form input to the database.

After the new post is created in the database, we redirect the user to the `ShowPosts` component page and show them a "flash" message that the new post was created.

### Adding validation

To avoid storing incomplete or dangerous user input, most forms need some sort of input validation.

Livewire makes validating your forms as simple as adding `#[Validate]` attributes above the properties you want to be validated.

Once a property has a `#[Validate]` attribute attached to it, the validation rule will be applied to the property's value any time it's updated server-side.

Let's add some basic validation rules to the `$title` and `$content` properties in our `CreatePost` component:

```php
<?php

namespace App\Livewire;

use Livewire\Attributes\Validate; // [tl! highlight]
use Livewire\Component;
use App\Models\Post;

class CreatePost extends Component
{
    #[Validate('required')] // [tl! highlight]
    public $title = '';

    #[Validate('required')] // [tl! highlight]
    public $content = '';

    public function save()
    {
        $this->validate(); // [tl! highlight]

        Post::create(
            $this->only(['title', 'content'])
        );

        return $this->redirect('/posts');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}
```

We'll also modify our Blade template to show any validation errors on the page.

```blade
<form wire:submit="save">
    <input type="text" wire:model="title">
    <div>
        @error('title') <span class="error">{{ $message }}</span> @enderror <!-- [tl! highlight] -->
    </div>

    <input type="text" wire:model="content">
    <div>
        @error('content') <span class="error">{{ $message }}</span> @enderror <!-- [tl! highlight] -->
    </div>

    <button type="submit">Save</button>
</form>
```

Now, if the user tries to submit the form without filling in any of the fields, they will see validation messages telling them which fields are required before saving the post.

Livewire has a lot more validation features to offer. For more information, visit our [dedicated documentation page on Validation](/docs/validation).

### Extracting a form object

If you are working with a large form and prefer to extract all of its properties, validation logic, etc., into a separate class, Livewire offers form objects.

Form objects allow you to re-use form logic across components and provide a nice way to keep your component class cleaner by grouping all form-related code into a separate class.

You can either create a form class by hand or use the convenient artisan command:

```shell
php artisan livewire:form PostForm
```

The above command will create a file called `app/Livewire/Forms/PostForm.php`.

Let's rewrite the `CreatePost` component to use a `PostForm` class:

```php
<?php

namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use Livewire\Form;

class PostForm extends Form
{
    #[Validate('required|min:5')]
    public $title = '';

    #[Validate('required|min:5')]
    public $content = '';
}
```

```php
<?php

namespace App\Livewire;

use App\Livewire\Forms\PostForm;
use Livewire\Component;
use App\Models\Post;

class CreatePost extends Component
{
    public PostForm $form; // [tl! highlight]

    public function save()
    {
        $this->validate();

        Post::create(
            $this->form->all() // [tl! highlight]
        );

        return $this->redirect('/posts');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}
```

```blade
<form wire:submit="save">
    <input type="text" wire:model="form.title">
    <div>
        @error('form.title') <span class="error">{{ $message }}</span> @enderror
    </div>

    <input type="text" wire:model="form.content">
    <div>
        @error('form.content') <span class="error">{{ $message }}</span> @enderror
    </div>

    <button type="submit">Save</button>
</form>
```

If you'd like, you can also extract the post creation logic into the form object like so:

```php
<?php

namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use App\Models\Post;
use Livewire\Form;

class PostForm extends Form
{
    #[Validate('required|min:5')]
    public $title = '';

    #[Validate('required|min:5')]
    public $content = '';

    public function store() // [tl! highlight:5]
    {
        $this->validate();

        Post::create($this->all());
    }
}
```

Now you can call `$this->form->store()` from the component:

```php
class CreatePost extends Component
{
    public PostForm $form;

    public function save()
    {
        $this->form->store(); // [tl! highlight]

        return $this->redirect('/posts');
    }

    // ...
}
```

If you want to use this form object for both a create and update form, you can easily adapt it to handle both use cases.

Here's what it would look like to use this same form object for an `UpdatePost` component and fill it with initial data:

```php
<?php

namespace App\Livewire;

use App\Livewire\Forms\PostForm;
use Livewire\Component;
use App\Models\Post;

class UpdatePost extends Component
{
    public PostForm $form;

    public function mount(Post $post)
    {
        $this->form->setPost($post);
    }

    public function save()
    {
        $this->form->update();

        return $this->redirect('/posts');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}
```

```php
<?php

namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use Livewire\Form;
use App\Models\Post;

class PostForm extends Form
{
    public ?Post $post;

    #[Validate('required|min:5')]
    public $title = '';

    #[Validate('required|min:5')]
    public $content = '';

    public function setPost(Post $post)
    {
        $this->post = $post;

        $this->title = $post->title;

        $this->content = $post->content;
    }

    public function store()
    {
        $this->validate();

        Post::create($this->only(['title', 'content']));
    }

    public function update()
    {
        $this->validate();

        $this->post->update(
            $this->all()
        );
    }
}
```

As you can see, we've added a `setPost()` method to the `PostForm` object to optionally allow for filling the form with existing data as well as storing the post on the form object for later use. We've also added an `update()` method for updating the existing post.

Form objects are not required when working with Livewire, but they do offer a nice abstraction for keeping your components free of repetitive boilerplate.