diff --git a/composer.json b/composer.json index 726a8af2..3051f751 100644 --- a/composer.json +++ b/composer.json @@ -60,6 +60,7 @@ "illuminate/queue": "^11.0", "illuminate/routing": "^11.0", "illuminate/support": "^11.0", + "illuminate/testing": "^11.0", "illuminate/validation": "^11.0", "illuminate/view": "^11.0", "laravel/prompts": "^0.1.17", diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 41a3b206..47d8ec45 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.1.1'; + const VERSION = '11.15.0'; /** * The base path for the Laravel installation. @@ -99,7 +99,7 @@ class Application extends Container implements ApplicationContract, CachesConfig /** * All of the registered service providers. * - * @var \Illuminate\Support\ServiceProvider[] + * @var array */ protected $serviceProviders = []; @@ -194,6 +194,13 @@ class Application extends Container implements ApplicationContract, CachesConfig */ protected $namespace; + /** + * Indicates if the framework's base configuration should be merged. + * + * @var bool + */ + protected $mergeFrameworkConfiguration = true; + /** * The prefixes of absolute cache paths for use during normalization. * @@ -224,7 +231,7 @@ public function __construct($basePath = null) * @param string|null $basePath * @return \Illuminate\Foundation\Configuration\ApplicationBuilder */ - public static function configure(string $basePath = null) + public static function configure(?string $basePath = null) { $basePath = match (true) { is_string($basePath) => $basePath, @@ -591,6 +598,10 @@ public function storagePath($path = '') return $this->joinPaths($this->storagePath ?: $_ENV['LARAVEL_STORAGE_PATH'], $path); } + if (isset($_SERVER['LARAVEL_STORAGE_PATH'])) { + return $this->joinPaths($this->storagePath ?: $_SERVER['LARAVEL_STORAGE_PATH'], $path); + } + return $this->joinPaths($this->storagePath ?: $this->basePath('storage'), $path); } @@ -893,7 +904,9 @@ public function register($provider, $force = false) */ public function getProvider($provider) { - return array_values($this->getProviders($provider))[0] ?? null; + $name = is_string($provider) ? $provider : get_class($provider); + + return $this->serviceProviders[$name] ?? null; } /** @@ -928,9 +941,11 @@ public function resolveProvider($provider) */ protected function markAsRegistered($provider) { - $this->serviceProviders[] = $provider; + $class = get_class($provider); + + $this->serviceProviders[$class] = $provider; - $this->loadedProviders[get_class($provider)] = true; + $this->loadedProviders[$class] = true; } /** @@ -1003,6 +1018,8 @@ public function registerDeferredProvider($provider, $service = null) * @param string $abstract * @param array $parameters * @return mixed + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException */ public function make($abstract, array $parameters = []) { @@ -1018,6 +1035,9 @@ public function make($abstract, array $parameters = []) * @param array $parameters * @param bool $raiseEvents * @return mixed + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws \Illuminate\Contracts\Container\CircularDependencyException */ protected function resolve($abstract, $parameters = [], $raiseEvents = true) { @@ -1190,6 +1210,28 @@ public function handleCommand(InputInterface $input) return $status; } + /** + * Determine if the framework's base configuration should be merged. + * + * @return bool + */ + public function shouldMergeFrameworkConfiguration() + { + return $this->mergeFrameworkConfiguration; + } + + /** + * Indicate that the framework's base configuration should not be merged. + * + * @return $this + */ + public function dontMergeFrameworkConfiguration() + { + $this->mergeFrameworkConfiguration = false; + + return $this; + } + /** * Determine if middleware has been disabled for the application. * @@ -1384,7 +1426,7 @@ public function terminate() /** * Get the service providers that have been loaded. * - * @return array + * @return array */ public function getLoadedProviders() { diff --git a/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php b/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php index 1d26b960..a1bc7460 100644 --- a/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php +++ b/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php @@ -2,7 +2,6 @@ namespace Illuminate\Foundation\Bootstrap; -use Exception; use Illuminate\Config\Repository; use Illuminate\Contracts\Config\Repository as RepositoryContract; use Illuminate\Contracts\Foundation\Application; @@ -62,11 +61,17 @@ protected function loadConfigurationFiles(Application $app, RepositoryContract $ { $files = $this->getConfigurationFiles($app); - // if (! isset($files['app'])) { - // throw new Exception('Unable to load the "app" configuration file.'); - // } + $shouldMerge = method_exists($app, 'shouldMergeFrameworkConfiguration') + ? $app->shouldMergeFrameworkConfiguration() + : true; - $base = $this->getBaseConfiguration(); + $base = $shouldMerge + ? $this->getBaseConfiguration() + : []; + + foreach (array_diff(array_keys($base), array_keys($files)) as $name => $config) { + $repository->set($name, $config); + } foreach ($files as $name => $path) { $base = $this->loadConfigurationFile($repository, $name, $path, $base); diff --git a/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php b/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php index 70065191..2c608430 100644 --- a/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php +++ b/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php @@ -3,6 +3,7 @@ namespace Illuminate\Foundation\Bootstrap; use Illuminate\Contracts\Foundation\Application; +use Illuminate\Support\ServiceProvider; class RegisterProviders { @@ -57,7 +58,7 @@ protected function mergeAdditionalProviders(Application $app) $app->make('config')->set( 'app.providers', array_merge( - $app->make('config')->get('app.providers'), + $app->make('config')->get('app.providers') ?? ServiceProvider::defaultProviders()->toArray(), static::$merge, array_values($packageProviders ?? []), ), diff --git a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php index 4f4469fe..218e5210 100644 --- a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php +++ b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php @@ -83,15 +83,19 @@ public function withProviders(array $providers = [], bool $withBootstrapProvider /** * Register the core event service provider for the application. * - * @param array $discover + * @param array|bool $discover * @return $this */ - public function withEvents(array $discover = []) + public function withEvents(array|bool $discover = []) { - if (count($discover) > 0) { + if (is_array($discover) && count($discover) > 0) { AppEventServiceProvider::setEventDiscoveryPaths($discover); } + if ($discover === false) { + AppEventServiceProvider::disableEventDiscovery(); + } + if (! isset($this->pendingProviders[AppEventServiceProvider::class])) { $this->app->booting(function () { $this->app->register(AppEventServiceProvider::class); @@ -104,7 +108,7 @@ public function withEvents(array $discover = []) } /** - * Register the braodcasting services for the application. + * Register the broadcasting services for the application. * * @param string $channels * @param array $attributes @@ -127,8 +131,8 @@ public function withBroadcasting(string $channels, array $attributes = []) * Register the routing services for the application. * * @param \Closure|null $using - * @param string|null $web - * @param string|null $api + * @param array|string|null $web + * @param array|string|null $api * @param string|null $commands * @param string|null $channels * @param string|null $pages @@ -137,8 +141,8 @@ public function withBroadcasting(string $channels, array $attributes = []) * @return $this */ public function withRouting(?Closure $using = null, - ?string $web = null, - ?string $api = null, + array|string|null $web = null, + array|string|null $api = null, ?string $commands = null, ?string $channels = null, ?string $pages = null, @@ -146,7 +150,7 @@ public function withRouting(?Closure $using = null, string $apiPrefix = 'api', ?callable $then = null) { - if (is_null($using) && (is_string($web) || is_string($api) || is_string($pages) || is_string($health)) || is_callable($then)) { + if (is_null($using) && (is_string($web) || is_array($web) || is_string($api) || is_array($api) || is_string($pages) || is_string($health)) || is_callable($then)) { $using = $this->buildRoutingCallback($web, $api, $pages, $health, $apiPrefix, $then); } @@ -170,24 +174,32 @@ public function withRouting(?Closure $using = null, /** * Create the routing callback for the application. * - * @param string|null $web - * @param string|null $api + * @param array|string|null $web + * @param array|string|null $api * @param string|null $pages * @param string|null $health * @param string $apiPrefix * @param callable|null $then * @return \Closure */ - protected function buildRoutingCallback(?string $web, - ?string $api, + protected function buildRoutingCallback(array|string|null $web, + array|string|null $api, ?string $pages, ?string $health, string $apiPrefix, ?callable $then) { return function () use ($web, $api, $pages, $health, $apiPrefix, $then) { - if (is_string($api) && realpath($api) !== false) { - Route::middleware('api')->prefix($apiPrefix)->group($api); + if (is_string($api) || is_array($api)) { + if (is_array($api)) { + foreach ($api as $apiRoute) { + if (realpath($apiRoute) !== false) { + Route::middleware('api')->prefix($apiPrefix)->group($apiRoute); + } + } + } else { + Route::middleware('api')->prefix($apiPrefix)->group($api); + } } if (is_string($health)) { @@ -198,8 +210,16 @@ protected function buildRoutingCallback(?string $web, }); } - if (is_string($web) && realpath($web) !== false) { - Route::middleware('web')->group($web); + if (is_string($web) || is_array($web)) { + if (is_array($web)) { + foreach ($web as $webRoute) { + if (realpath($webRoute) !== false) { + Route::middleware('web')->group($webRoute); + } + } + } else { + Route::middleware('web')->group($web); + } } if (is_string($pages) && @@ -285,7 +305,7 @@ protected function withCommandRouting(array $paths) /** * Register the scheduled tasks for the application. * - * @param callable(Schedule $schedule): void $callback + * @param callable(\Illuminate\Console\Scheduling\Schedule $schedule): void $callback * @return $this */ public function withSchedule(callable $callback) diff --git a/src/Illuminate/Foundation/Configuration/Middleware.php b/src/Illuminate/Foundation/Configuration/Middleware.php index e8916d55..30e60ed9 100644 --- a/src/Illuminate/Foundation/Configuration/Middleware.php +++ b/src/Illuminate/Foundation/Configuration/Middleware.php @@ -303,7 +303,6 @@ public function replaceInGroup(string $group, string $search, string $replace) /** * Modify the middleware in the "web" group. * - * @param string $group * @param array|string $append * @param array|string $prepend * @param array|string $remove @@ -318,7 +317,6 @@ public function web(array|string $append = [], array|string $prepend = [], array /** * Modify the middleware in the "api" group. * - * @param string $group * @param array|string $append * @param array|string $prepend * @param array|string $remove @@ -420,9 +418,7 @@ public function getGlobalMiddleware() ])); $middleware = array_map(function ($middleware) { - return isset($this->replacements[$middleware]) - ? $this->replacements[$middleware] - : $middleware; + return $this->replacements[$middleware] ?? $middleware; }, $middleware); return array_values(array_filter( @@ -518,7 +514,7 @@ public function redirectUsersTo(callable|string $redirect) * @param callable|string $users * @return $this */ - public function redirectTo(callable|string $guests = null, callable|string $users = null) + public function redirectTo(callable|string|null $guests = null, callable|string|null $users = null) { $guests = is_string($guests) ? fn () => $guests : $guests; $users = is_string($users) ? fn () => $users : $users; @@ -608,15 +604,15 @@ public function trimStrings(array $except = []) /** * Indicate that the trusted host middleware should be enabled. * - * @param array|null $at + * @param array|(callable(): array)|null $at * @param bool $subdomains * @return $this */ - public function trustHosts(array $at = null, bool $subdomains = true) + public function trustHosts(array|callable|null $at = null, bool $subdomains = true) { $this->trustHosts = true; - if (is_array($at)) { + if (! is_null($at)) { TrustHosts::at($at, $subdomains); } @@ -630,7 +626,7 @@ public function trustHosts(array $at = null, bool $subdomains = true) * @param int|null $headers * @return $this */ - public function trustProxies(array|string $at = null, int $headers = null) + public function trustProxies(array|string|null $at = null, ?int $headers = null) { if (! is_null($at)) { TrustProxies::at($at); diff --git a/src/Illuminate/Foundation/Console/AboutCommand.php b/src/Illuminate/Foundation/Console/AboutCommand.php index 20c24e8e..29ea48a7 100644 --- a/src/Illuminate/Foundation/Console/AboutCommand.php +++ b/src/Illuminate/Foundation/Console/AboutCommand.php @@ -173,6 +173,8 @@ protected function gatherApplicationInformation() 'Debug Mode' => static::format(config('app.debug'), console: $formatEnabledStatus), 'URL' => Str::of(config('app.url'))->replace(['http://', 'https://'], ''), 'Maintenance Mode' => static::format($this->laravel->isDownForMaintenance(), console: $formatEnabledStatus), + 'Timezone' => config('app.timezone'), + 'Locale' => config('app.locale'), ]); static::addToSection('Cache', fn () => [ @@ -232,7 +234,7 @@ protected function hasPhpFiles(string $path): bool * @param string|null $value * @return void */ - public static function add(string $section, $data, string $value = null) + public static function add(string $section, $data, ?string $value = null) { static::$customDataResolvers[] = fn () => static::addToSection($section, $data, $value); } @@ -245,7 +247,7 @@ public static function add(string $section, $data, string $value = null) * @param string|null $value * @return void */ - protected static function addToSection(string $section, $data, string $value = null) + protected static function addToSection(string $section, $data, ?string $value = null) { if (is_array($data)) { foreach ($data as $key => $value) { @@ -279,7 +281,7 @@ protected function sections() * @param (\Closure(mixed):(mixed))|null $json * @return \Closure(bool):mixed */ - public static function format($value, Closure $console = null, Closure $json = null) + public static function format($value, ?Closure $console = null, ?Closure $json = null) { return function ($isJson) use ($value, $console, $json) { if ($isJson === true && $json instanceof Closure) { diff --git a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php index 9eb09793..9802ce10 100644 --- a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php +++ b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php @@ -115,7 +115,14 @@ protected function uncommentChannelsRoutesFile() */ protected function enableBroadcastServiceProvider() { - $config = ($filesystem = new Filesystem)->get(app()->configPath('app.php')); + $filesystem = new Filesystem; + + if (! $filesystem->exists(app()->configPath('app.php')) || + ! $filesystem->exists('app/Providers/BroadcastServiceProvider.php')) { + return; + } + + $config = $filesystem->get(app()->configPath('app.php')); if (str_contains($config, '// App\Providers\BroadcastServiceProvider::class')) { $filesystem->replaceInFile( diff --git a/src/Illuminate/Foundation/Console/ClassMakeCommand.php b/src/Illuminate/Foundation/Console/ClassMakeCommand.php index 5c091c60..17144d35 100644 --- a/src/Illuminate/Foundation/Console/ClassMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ClassMakeCommand.php @@ -55,17 +55,6 @@ protected function resolveStubPath($stub) : __DIR__.$stub; } - /** - * Get the default namespace for the class. - * - * @param string $rootNamespace - * @return string - */ - protected function getDefaultNamespace($rootNamespace) - { - return $rootNamespace; - } - /** * Get the console command arguments. * diff --git a/src/Illuminate/Foundation/Console/ClosureCommand.php b/src/Illuminate/Foundation/Console/ClosureCommand.php index ae51801f..2c2eaf4d 100644 --- a/src/Illuminate/Foundation/Console/ClosureCommand.php +++ b/src/Illuminate/Foundation/Console/ClosureCommand.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Console\Command; +use Illuminate\Console\ManuallyFailedException; use Illuminate\Support\Facades\Schedule; use Illuminate\Support\Traits\ForwardsCalls; use ReflectionFunction; @@ -58,9 +59,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - return (int) $this->laravel->call( - $this->callback->bindTo($this, $this), $parameters - ); + try { + return (int) $this->laravel->call( + $this->callback->bindTo($this, $this), $parameters + ); + } catch (ManuallyFailedException $e) { + $this->components->error($e->getMessage()); + + return static::FAILURE; + } } /** diff --git a/src/Illuminate/Foundation/Console/ComponentMakeCommand.php b/src/Illuminate/Foundation/Console/ComponentMakeCommand.php index 183206ee..709c5360 100644 --- a/src/Illuminate/Foundation/Console/ComponentMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ComponentMakeCommand.php @@ -43,11 +43,7 @@ class ComponentMakeCommand extends GeneratorCommand public function handle() { if ($this->option('view')) { - $this->writeView(function () { - $this->components->info($this->type.' created successfully.'); - }); - - return; + return $this->writeView(); } if (parent::handle() === false && ! $this->option('force')) { @@ -62,10 +58,9 @@ public function handle() /** * Write the view for the component. * - * @param callable|null $onSuccess * @return void */ - protected function writeView($onSuccess = null) + protected function writeView() { $path = $this->viewPath( str_replace('.', '/', 'components.'.$this->getView()).'.blade.php' @@ -88,9 +83,7 @@ protected function writeView($onSuccess = null) ' ); - if ($onSuccess) { - $onSuccess(); - } + $this->components->info(sprintf('%s [%s] created successfully.', 'View', $path)); } /** diff --git a/src/Illuminate/Foundation/Console/ConfigPublishCommand.php b/src/Illuminate/Foundation/Console/ConfigPublishCommand.php index 54ebce46..8e87f064 100644 --- a/src/Illuminate/Foundation/Console/ConfigPublishCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigPublishCommand.php @@ -91,10 +91,14 @@ protected function getBaseConfigurationFiles() { $config = []; + $shouldMergeConfiguration = $this->laravel->shouldMergeFrameworkConfiguration(); + foreach (Finder::create()->files()->name('*.php')->in(__DIR__.'/../../../../config') as $file) { $name = basename($file->getRealPath(), '.php'); - $config[$name] = file_exists($stubPath = (__DIR__.'/../../../../config-stubs/'.$name.'.php')) ? $stubPath : $file->getRealPath(); + $config[$name] = ($shouldMergeConfiguration === true && file_exists($stubPath = (__DIR__.'/../../../../config-stubs/'.$name.'.php'))) + ? $stubPath + : $file->getRealPath(); } return collect($config)->sortKeys()->all(); diff --git a/src/Illuminate/Foundation/Console/ConfigShowCommand.php b/src/Illuminate/Foundation/Console/ConfigShowCommand.php index 60b6b4e2..2c571214 100644 --- a/src/Illuminate/Foundation/Console/ConfigShowCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigShowCommand.php @@ -14,14 +14,14 @@ class ConfigShowCommand extends Command * * @var string */ - protected $signature = 'config:show {config : The configuration file to show}'; + protected $signature = 'config:show {config : The configuration file or key to show}'; /** * The console command description. * * @var string */ - protected $description = 'Display all of the values for a given configuration file'; + protected $description = 'Display all of the values for a given configuration file or key'; /** * Execute the console command. @@ -33,7 +33,7 @@ public function handle() $config = $this->argument('config'); if (! config()->has($config)) { - $this->components->error("Configuration file `{$config}` does not exist."); + $this->components->error("Configuration file or key {$config} does not exist."); return Command::FAILURE; } diff --git a/src/Illuminate/Foundation/Console/EnvironmentEncryptCommand.php b/src/Illuminate/Foundation/Console/EnvironmentEncryptCommand.php index f2c55fd3..d4e7f6aa 100644 --- a/src/Illuminate/Foundation/Console/EnvironmentEncryptCommand.php +++ b/src/Illuminate/Foundation/Console/EnvironmentEncryptCommand.php @@ -21,6 +21,7 @@ class EnvironmentEncryptCommand extends Command {--key= : The encryption key} {--cipher= : The encryption cipher} {--env= : The environment to be encrypted} + {--prune : Delete the original environment file} {--force : Overwrite the existing encrypted environment file}'; /** @@ -98,6 +99,10 @@ public function handle() return Command::FAILURE; } + if ($this->option('prune')) { + $this->files->delete($environmentFile); + } + $this->components->info('Environment successfully encrypted.'); $this->components->twoColumnDetail('Key', $keyPassed ? $key : 'base64:'.base64_encode($key)); diff --git a/src/Illuminate/Foundation/Console/InterfaceMakeCommand.php b/src/Illuminate/Foundation/Console/InterfaceMakeCommand.php index f2dcf7d8..6601be63 100644 --- a/src/Illuminate/Foundation/Console/InterfaceMakeCommand.php +++ b/src/Illuminate/Foundation/Console/InterfaceMakeCommand.php @@ -48,7 +48,11 @@ protected function getStub() */ protected function getDefaultNamespace($rootNamespace) { - return $rootNamespace; + return match (true) { + is_dir(app_path('Contracts')) => $rootNamespace.'\\Contracts', + is_dir(app_path('Interfaces')) => $rootNamespace.'\\Interfaces', + default => $rootNamespace, + }; } /** diff --git a/src/Illuminate/Foundation/Console/MailMakeCommand.php b/src/Illuminate/Foundation/Console/MailMakeCommand.php index af02001c..2ec59a15 100644 --- a/src/Illuminate/Foundation/Console/MailMakeCommand.php +++ b/src/Illuminate/Foundation/Console/MailMakeCommand.php @@ -4,9 +4,14 @@ use Illuminate\Console\Concerns\CreatesMatchingTest; use Illuminate\Console\GeneratorCommand; +use Illuminate\Foundation\Inspiring; use Illuminate\Support\Str; use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +use function Laravel\Prompts\select; #[AsCommand(name: 'make:mail')] class MailMakeCommand extends GeneratorCommand @@ -48,6 +53,10 @@ public function handle() if ($this->option('markdown') !== false) { $this->writeMarkdownTemplate(); } + + if ($this->option('view') !== false) { + $this->writeView(); + } } /** @@ -61,11 +70,35 @@ protected function writeMarkdownTemplate() str_replace('.', '/', $this->getView()).'.blade.php' ); - if (! $this->files->isDirectory(dirname($path))) { - $this->files->makeDirectory(dirname($path), 0755, true); - } + $this->files->ensureDirectoryExists(dirname($path)); $this->files->put($path, file_get_contents(__DIR__.'/stubs/markdown.stub')); + + $this->components->info(sprintf('%s [%s] created successfully.', 'Markdown view', $path)); + } + + /** + * Write the Blade template for the mailable. + * + * @return void + */ + protected function writeView() + { + $path = $this->viewPath( + str_replace('.', '/', $this->getView()).'.blade.php' + ); + + $this->files->ensureDirectoryExists(dirname($path)); + + $stub = str_replace( + '{{ quote }}', + Inspiring::quotes()->random(), + file_get_contents(__DIR__.'/stubs/view.stub') + ); + + $this->files->put($path, $stub); + + $this->components->info(sprintf('%s [%s] created successfully.', 'View', $path)); } /** @@ -82,7 +115,7 @@ protected function buildClass($name) parent::buildClass($name) ); - if ($this->option('markdown') !== false) { + if ($this->option('markdown') !== false || $this->option('view') !== false) { $class = str_replace(['DummyView', '{{ view }}'], $this->getView(), $class); } @@ -96,7 +129,7 @@ protected function buildClass($name) */ protected function getView() { - $view = $this->option('markdown'); + $view = $this->option('markdown') ?: $this->option('view'); if (! $view) { $name = str_replace('\\', '/', $this->argument('name')); @@ -116,10 +149,15 @@ protected function getView() */ protected function getStub() { - return $this->resolveStubPath( - $this->option('markdown') !== false - ? '/stubs/markdown-mail.stub' - : '/stubs/mail.stub'); + if ($this->option('markdown') !== false) { + return $this->resolveStubPath('/stubs/markdown-mail.stub'); + } + + if ($this->option('view') !== false) { + return $this->resolveStubPath('/stubs/view-mail.stub'); + } + + return $this->resolveStubPath('/stubs/mail.stub'); } /** @@ -156,6 +194,31 @@ protected function getOptions() return [ ['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the mailable already exists'], ['markdown', 'm', InputOption::VALUE_OPTIONAL, 'Create a new Markdown template for the mailable', false], + ['view', null, InputOption::VALUE_OPTIONAL, 'Create a new Blade template for the mailable', false], ]; } + + /** + * Interact further with the user if they were prompted for missing arguments. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return void + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + { + if ($this->didReceiveOptions($input)) { + return; + } + + $type = select('Would you like to create a view?', [ + 'markdown' => 'Markdown View', + 'view' => 'Empty View', + 'none' => 'No View', + ]); + + if ($type !== 'none') { + $input->setOption($type, null); + } + } } diff --git a/src/Illuminate/Foundation/Console/NotificationMakeCommand.php b/src/Illuminate/Foundation/Console/NotificationMakeCommand.php index 83b9663d..d9352655 100644 --- a/src/Illuminate/Foundation/Console/NotificationMakeCommand.php +++ b/src/Illuminate/Foundation/Console/NotificationMakeCommand.php @@ -65,6 +65,8 @@ protected function writeMarkdownTemplate() } $this->files->put($path, file_get_contents(__DIR__.'/stubs/markdown.stub')); + + $this->components->info(sprintf('%s [%s] created successfully.', 'Markdown', $path)); } /** diff --git a/src/Illuminate/Foundation/Console/OptimizeClearCommand.php b/src/Illuminate/Foundation/Console/OptimizeClearCommand.php index c75ab3a9..02c9d5ab 100644 --- a/src/Illuminate/Foundation/Console/OptimizeClearCommand.php +++ b/src/Illuminate/Foundation/Console/OptimizeClearCommand.php @@ -36,7 +36,7 @@ public function handle() 'compiled' => fn () => $this->callSilent('clear-compiled') == 0, 'config' => fn () => $this->callSilent('config:clear') == 0, 'events' => fn () => $this->callSilent('event:clear') == 0, - 'route' => fn () => $this->callSilent('route:clear') == 0, + 'routes' => fn () => $this->callSilent('route:clear') == 0, 'views' => fn () => $this->callSilent('view:clear') == 0, ])->each(fn ($task, $description) => $this->components->task($description, $task)); diff --git a/src/Illuminate/Foundation/Console/RouteListCommand.php b/src/Illuminate/Foundation/Console/RouteListCommand.php index 11a8c724..df3126fc 100644 --- a/src/Illuminate/Foundation/Console/RouteListCommand.php +++ b/src/Illuminate/Foundation/Console/RouteListCommand.php @@ -157,9 +157,13 @@ protected function getRouteInformation(Route $route) */ protected function sortRoutes($sort, array $routes) { - return Arr::sort($routes, function ($route) use ($sort) { - return $route[$sort]; - }); + if (Str::contains($sort, ',')) { + $sort = explode(',', $sort); + } + + return collect($routes) + ->sortBy($sort) + ->toArray(); } /** diff --git a/src/Illuminate/Foundation/Console/ServeCommand.php b/src/Illuminate/Foundation/Console/ServeCommand.php index a73bf52e..ae204bf2 100644 --- a/src/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Illuminate/Foundation/Console/ServeCommand.php @@ -5,6 +5,7 @@ use Illuminate\Console\Command; use Illuminate\Support\Carbon; use Illuminate\Support\Env; +use Illuminate\Support\InteractsWithTime; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Process\PhpExecutableFinder; @@ -15,6 +16,8 @@ #[AsCommand(name: 'serve')] class ServeCommand extends Command { + use InteractsWithTime; + /** * The console command name. * @@ -36,6 +39,13 @@ class ServeCommand extends Command */ protected $portOffset = 0; + /** + * The list of lines that are pending to be output. + * + * @var string + */ + protected $outputBuffer = ''; + /** * The list of requests being handled and their start time. * @@ -81,14 +91,14 @@ class ServeCommand extends Command public function handle() { $environmentFile = $this->option('env') - ? base_path('.env').'.'.$this->option('env') - : base_path('.env'); + ? base_path('.env').'.'.$this->option('env') + : base_path('.env'); $hasEnvironment = file_exists($environmentFile); $environmentLastModified = $hasEnvironment - ? filemtime($environmentFile) - : now()->addDays(30)->getTimestamp(); + ? filemtime($environmentFile) + : now()->addDays(30)->getTimestamp(); $process = $this->startProcess($hasEnvironment); @@ -235,7 +245,7 @@ protected function getHostAndPort() protected function canTryAnotherPort() { return is_null($this->input->getOption('port')) && - ($this->input->getOption('tries') > $this->portOffset); + ($this->input->getOption('tries') > $this->portOffset); } /** @@ -245,68 +255,98 @@ protected function canTryAnotherPort() */ protected function handleProcessOutput() { - return fn ($type, $buffer) => str($buffer)->explode("\n")->each(function ($line) { - if (str($line)->contains('Development Server (http')) { - if ($this->serverRunningHasBeenDisplayed) { - return; - } + return function ($type, $buffer) { + $this->outputBuffer .= $buffer; - $this->components->info("Server running on [http://{$this->host()}:{$this->port()}]."); - $this->comment(' Press Ctrl+C to stop the server'); + $this->flushOutputBuffer(); + }; + } - $this->newLine(); + /** + * Flush the output buffer. + * + * @return void + */ + protected function flushOutputBuffer() + { + $lines = str($this->outputBuffer)->explode("\n"); + + $this->outputBuffer = (string) $lines->pop(); - $this->serverRunningHasBeenDisplayed = true; - } elseif (str($line)->contains(' Accepted')) { - $requestPort = $this->getRequestPortFromLine($line); + $lines + ->map(fn ($line) => trim($line)) + ->filter() + ->each(function ($line) { + if (str($line)->contains('Development Server (http')) { + if ($this->serverRunningHasBeenDisplayed === false) { + $this->serverRunningHasBeenDisplayed = true; - $this->requestsPool[$requestPort] = [ - $this->getDateFromLine($line), - false, - ]; - } elseif (str($line)->contains([' [200]: GET '])) { - $requestPort = $this->getRequestPortFromLine($line); + $this->components->info("Server running on [http://{$this->host()}:{$this->port()}]."); + $this->comment(' Press Ctrl+C to stop the server'); - $this->requestsPool[$requestPort][1] = trim(explode('[200]: GET', $line)[1]); - } elseif (str($line)->contains(' Closing')) { - $requestPort = $this->getRequestPortFromLine($line); + $this->newLine(); + } - if (empty($this->requestsPool[$requestPort])) { return; } - [$startDate, $file] = $this->requestsPool[$requestPort]; + if (str($line)->contains(' Accepted')) { + $requestPort = $this->getRequestPortFromLine($line); - $formattedStartedAt = $startDate->format('Y-m-d H:i:s'); + $this->requestsPool[$requestPort] = [ + $this->getDateFromLine($line), + $this->requestsPool[$requestPort][1] ?? false, + microtime(true), + ]; + } elseif (str($line)->contains([' [200]: GET '])) { + $requestPort = $this->getRequestPortFromLine($line); - unset($this->requestsPool[$requestPort]); + $this->requestsPool[$requestPort][1] = trim(explode('[200]: GET', $line)[1]); + } elseif (str($line)->contains('URI:')) { + $requestPort = $this->getRequestPortFromLine($line); - [$date, $time] = explode(' ', $formattedStartedAt); + $this->requestsPool[$requestPort][1] = trim(explode('URI: ', $line)[1]); + } elseif (str($line)->contains(' Closing')) { + $requestPort = $this->getRequestPortFromLine($line); - $this->output->write(" $date $time"); + if (empty($this->requestsPool[$requestPort])) { + $this->requestsPool[$requestPort] = [ + $this->getDateFromLine($line), + false, + microtime(true), + ]; + } - $runTime = $this->getDateFromLine($line)->diffInSeconds($startDate); + [$startDate, $file, $startMicrotime] = $this->requestsPool[$requestPort]; - if ($file) { - $this->output->write($file = " $file"); - } + $formattedStartedAt = $startDate->format('Y-m-d H:i:s'); - $dots = max(terminal()->width() - mb_strlen($formattedStartedAt) - mb_strlen($file) - mb_strlen($runTime) - 9, 0); + unset($this->requestsPool[$requestPort]); - $this->output->write(' '.str_repeat('.', $dots)); - $this->output->writeln(" ~ {$runTime}s"); - } elseif (str($line)->contains(['Closed without sending a request'])) { - // ... - } elseif (! empty($line)) { - $position = strpos($line, '] '); + [$date, $time] = explode(' ', $formattedStartedAt); - if ($position !== false) { - $line = substr($line, $position + 1); - } + $this->output->write(" $date $time"); - $this->components->warn($line); - } - }); + $runTime = $this->runTimeForHumans($startMicrotime); + + if ($file) { + $this->output->write($file = " $file"); + } + + $dots = max(terminal()->width() - mb_strlen($formattedStartedAt) - mb_strlen($file) - mb_strlen($runTime) - 9, 0); + + $this->output->write(' '.str_repeat('.', $dots)); + $this->output->writeln(" ~ {$runTime}"); + } elseif (str($line)->contains(['Closed without sending a request', 'Failed to poll event'])) { + // ... + } elseif (! empty($line)) { + if (str($line)->startsWith('[')) { + $line = str($line)->after('] '); + } + + $this->output->writeln(" $line"); + } + }); } /** diff --git a/src/Illuminate/Foundation/Console/StubPublishCommand.php b/src/Illuminate/Foundation/Console/StubPublishCommand.php index 3ee37b48..1ffcf715 100644 --- a/src/Illuminate/Foundation/Console/StubPublishCommand.php +++ b/src/Illuminate/Foundation/Console/StubPublishCommand.php @@ -60,6 +60,8 @@ public function handle() __DIR__.'/stubs/notification.stub' => 'notification.stub', __DIR__.'/stubs/observer.plain.stub' => 'observer.plain.stub', __DIR__.'/stubs/observer.stub' => 'observer.stub', + __DIR__.'/stubs/pest.stub' => 'pest.stub', + __DIR__.'/stubs/pest.unit.stub' => 'pest.unit.stub', __DIR__.'/stubs/policy.plain.stub' => 'policy.plain.stub', __DIR__.'/stubs/policy.stub' => 'policy.stub', __DIR__.'/stubs/provider.stub' => 'provider.stub', diff --git a/src/Illuminate/Foundation/Console/TraitMakeCommand.php b/src/Illuminate/Foundation/Console/TraitMakeCommand.php index a4542018..a66b1b1d 100644 --- a/src/Illuminate/Foundation/Console/TraitMakeCommand.php +++ b/src/Illuminate/Foundation/Console/TraitMakeCommand.php @@ -61,7 +61,11 @@ protected function resolveStubPath($stub) */ protected function getDefaultNamespace($rootNamespace) { - return $rootNamespace; + return match (true) { + is_dir(app_path('Concerns')) => $rootNamespace.'\\Concerns', + is_dir(app_path('Traits')) => $rootNamespace.'\\Traits', + default => $rootNamespace, + }; } /** diff --git a/src/Illuminate/Foundation/Console/VendorPublishCommand.php b/src/Illuminate/Foundation/Console/VendorPublishCommand.php index 78dd9b68..ca7dc0d0 100644 --- a/src/Illuminate/Foundation/Console/VendorPublishCommand.php +++ b/src/Illuminate/Foundation/Console/VendorPublishCommand.php @@ -394,7 +394,7 @@ protected function status($from, $to, $type) } /** - * Intruct the command to not update the dates on migrations when publishing. + * Instruct the command to not update the dates on migrations when publishing. * * @return void */ diff --git a/src/Illuminate/Foundation/Console/ViewMakeCommand.php b/src/Illuminate/Foundation/Console/ViewMakeCommand.php index 4708f7d9..3f8e4da6 100644 --- a/src/Illuminate/Foundation/Console/ViewMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ViewMakeCommand.php @@ -142,7 +142,11 @@ protected function handleTestCreation($path): bool File::ensureDirectoryExists(dirname($this->getTestPath()), 0755, true); - return File::put($this->getTestPath(), $contents); + $result = File::put($path = $this->getTestPath(), $contents); + + $this->components->info(sprintf('%s [%s] created successfully.', 'Test', $path)); + + return $result !== false; } /** diff --git a/src/Illuminate/Foundation/Console/stubs/view-mail.stub b/src/Illuminate/Foundation/Console/stubs/view-mail.stub new file mode 100644 index 00000000..8889396d --- /dev/null +++ b/src/Illuminate/Foundation/Console/stubs/view-mail.stub @@ -0,0 +1,53 @@ + + */ + public function attachments(): array + { + return []; + } +} diff --git a/src/Illuminate/Foundation/Exceptions/Handler.php b/src/Illuminate/Foundation/Exceptions/Handler.php index 791f2cc6..057d7af4 100644 --- a/src/Illuminate/Foundation/Exceptions/Handler.php +++ b/src/Illuminate/Foundation/Exceptions/Handler.php @@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\MultipleRecordsFoundException; use Illuminate\Database\RecordsNotFoundException; +use Illuminate\Foundation\Exceptions\Renderer\Renderer; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; @@ -277,7 +278,7 @@ public function dontReport(array|string $exceptions) /** * Indicate that the given exception type should not be reported. * - * @param array|string $class + * @param array|string $exceptions * @return $this */ public function ignore(array|string $exceptions) @@ -831,9 +832,15 @@ protected function convertExceptionToResponse(Throwable $e) protected function renderExceptionContent(Throwable $e) { try { - return config('app.debug') && app()->has(ExceptionRenderer::class) - ? $this->renderExceptionWithCustomRenderer($e) - : $this->renderExceptionWithSymfony($e, config('app.debug')); + if (config('app.debug')) { + if (app()->has(ExceptionRenderer::class)) { + return $this->renderExceptionWithCustomRenderer($e); + } elseif ($this->container->bound(Renderer::class)) { + return $this->container->make(Renderer::class)->render(request(), $e); + } + } + + return $this->renderExceptionWithSymfony($e, config('app.debug')); } catch (Throwable $e) { return $this->renderExceptionWithSymfony($e, config('app.debug')); } diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php b/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php new file mode 100644 index 00000000..4aef4257 --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php @@ -0,0 +1,224 @@ +exception = $exception; + $this->request = $request; + $this->listener = $listener; + $this->basePath = $basePath; + } + + /** + * Get the exception title. + * + * @return string + */ + public function title() + { + return $this->exception->getStatusText(); + } + + /** + * Get the exception message. + * + * @return string + */ + public function message() + { + return $this->exception->getMessage(); + } + + /** + * Get the exception class name. + * + * @return string + */ + public function class() + { + return $this->exception->getClass(); + } + + /** + * Get the first "non-vendor" frame index. + * + * @return int + */ + public function defaultFrame() + { + $key = array_search(false, array_map(function (Frame $frame) { + return $frame->isFromVendor(); + }, $this->frames()->all())); + + return $key === false ? 0 : $key; + } + + /** + * Get the exception's frames. + * + * @return \Illuminate\Support\Collection + */ + public function frames() + { + $classMap = once(fn () => array_map(function ($path) { + return (string) realpath($path); + }, array_values(ClassLoader::getRegisteredLoaders())[0]->getClassMap())); + + $trace = array_values(array_filter( + $this->exception->getTrace(), fn ($trace) => isset($trace['file']), + )); + + if (($trace[1]['class'] ?? '') === HandleExceptions::class) { + array_shift($trace); + array_shift($trace); + } + + return collect(array_map( + fn (array $trace) => new Frame($this->exception, $classMap, $trace, $this->basePath), $trace, + )); + } + + /** + * Get the exception's request instance. + * + * @return \Illuminate\Http\Request + */ + public function request() + { + return $this->request; + } + + /** + * Get the request's headers. + * + * @return array + */ + public function requestHeaders() + { + return array_map(function (array $header) { + return implode(', ', $header); + }, $this->request()->headers->all()); + } + + /** + * Get the request's body parameters. + * + * @return string|null + */ + public function requestBody() + { + if (empty($payload = $this->request()->all())) { + return null; + } + + $json = (string) json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + + return str_replace('\\', '', $json); + } + + /** + * Get the application's route context. + * + * @return array + */ + public function applicationRouteContext() + { + $route = $this->request()->route(); + + return $route ? array_filter([ + 'controller' => $route->getActionName(), + 'route name' => $route->getName() ?: null, + 'middleware' => implode(', ', array_map(function ($middleware) { + return $middleware instanceof Closure ? 'Closure' : $middleware; + }, $route->gatherMiddleware())), + ]) : []; + } + + /** + * Get the application's route parameters context. + * + * @return array|null + */ + public function applicationRouteParametersContext() + { + $parameters = $this->request()->route()?->parameters(); + + return $parameters ? json_encode(array_map( + fn ($value) => $value instanceof Model ? $value->withoutRelations() : $value, + $parameters + ), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : null; + } + + /** + * Get the application's SQL queries. + * + * @return array + */ + public function applicationQueries() + { + return array_map(function (array $query) { + $sql = $query['sql']; + + foreach ($query['bindings'] as $binding) { + $sql = match (gettype($binding)) { + 'integer', 'double' => preg_replace('/\?/', $binding, $sql, 1), + 'NULL' => preg_replace('/\?/', 'NULL', $sql, 1), + default => preg_replace('/\?/', "'$binding'", $sql, 1), + }; + } + + return [ + 'connectionName' => $query['connectionName'], + 'time' => $query['time'], + 'sql' => $sql, + ]; + }, $this->listener->queries()); + } +} diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Frame.php b/src/Illuminate/Foundation/Exceptions/Renderer/Frame.php new file mode 100644 index 00000000..8553e0f8 --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Frame.php @@ -0,0 +1,153 @@ + + */ + protected $classMap; + + /** + * The frame's raw data from the "flattened" exception. + * + * @var array{file: string, line: int, class?: string, type?: string, function?: string} + */ + protected $frame; + + /** + * The application's base path. + * + * @var string + */ + protected $basePath; + + /** + * Create a new frame instance. + * + * @param \Symfony\Component\ErrorHandler\Exception\FlattenException $exception + * @param array $classMap + * @param array{file: string, line: int, class?: string, type?: string, function?: string} $frame + * @param string $basePath + * @return void + */ + public function __construct(FlattenException $exception, array $classMap, array $frame, string $basePath) + { + $this->exception = $exception; + $this->classMap = $classMap; + $this->frame = $frame; + $this->basePath = $basePath; + } + + /** + * Get the frame's source / origin. + * + * @return string + */ + public function source() + { + return match (true) { + is_string($this->class()) => $this->class(), + default => $this->file(), + }; + } + + /** + * Get the frame's editor link. + * + * @return string + */ + public function editorHref() + { + return $this->resolveSourceHref($this->frame['file'], $this->line()); + } + + /** + * Get the frame's class, if any. + * + * @return string|null + */ + public function class() + { + $class = array_search((string) realpath($this->frame['file']), $this->classMap, true); + + return $class === false ? null : $class; + } + + /** + * Get the frame's file. + * + * @return string + */ + public function file() + { + return str_replace($this->basePath.'/', '', $this->frame['file']); + } + + /** + * Get the frame's line number. + * + * @return int + */ + public function line() + { + $maxLines = count(file($this->frame['file']) ?: []); + + return $this->frame['line'] > $maxLines ? 1 : $this->frame['line']; + } + + /** + * Get the frame's function or method. + * + * @return string + */ + public function callable() + { + return match (true) { + ! empty($this->frame['function']) => $this->frame['function'], + default => 'throw', + }; + } + + /** + * Get the frame's code snippet. + * + * @return string + */ + public function snippet() + { + $contents = file($this->frame['file']) ?: []; + + $start = max($this->line() - 6, 0); + + $length = 8 * 2 + 1; + + return implode('', array_slice($contents, $start, $length)); + } + + /** + * Determine if the frame is from the vendor directory. + * + * @return bool + */ + public function isFromVendor() + { + return ! str_starts_with($this->frame['file'], $this->basePath) + || str_starts_with($this->frame['file'], $this->basePath.'/vendor'); + } +} diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Listener.php b/src/Illuminate/Foundation/Exceptions/Renderer/Listener.php new file mode 100644 index 00000000..325fad4c --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Listener.php @@ -0,0 +1,73 @@ + + */ + protected $queries = []; + + /** + * Register the appropriate listeners on the given event dispatcher. + * + * @param \Illuminate\Contracts\Events\Dispatcher $events + * @return void + */ + public function registerListeners(Dispatcher $events) + { + $events->listen(QueryExecuted::class, [$this, 'onQueryExecuted']); + + $events->listen([JobProcessing::class, JobProcessed::class], function () { + $this->queries = []; + }); + + if (isset($_SERVER['LARAVEL_OCTANE'])) { + $events->listen([RequestReceived::class, TaskReceived::class, TickReceived::class, RequestTerminated::class], function () { + $this->queries = []; + }); + } + } + + /** + * Returns the queries that have been executed. + * + * @return array + */ + public function queries() + { + return $this->queries; + } + + /** + * Listens for the query executed event. + * + * @param \Illuminate\Database\Events\QueryExecuted $event + * @return void + */ + public function onQueryExecuted(QueryExecuted $event) + { + if (count($this->queries) === 100) { + return; + } + + $this->queries[] = [ + 'connectionName' => $event->connectionName, + 'time' => $event->time, + 'sql' => $event->sql, + 'bindings' => $event->bindings, + ]; + } +} diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Mappers/BladeMapper.php b/src/Illuminate/Foundation/Exceptions/Renderer/Mappers/BladeMapper.php new file mode 100644 index 00000000..f0a540e9 --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Mappers/BladeMapper.php @@ -0,0 +1,338 @@ + + * + * For the full copyright and license information, please review its LICENSE: + * + * The MIT License (MIT) + * + * Copyright (c) Spatie + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class BladeMapper +{ + /** + * The view factory instance. + * + * @var \Illuminate\Contracts\View\Factory + */ + protected $factory; + + /** + * The Blade compiler instance. + * + * @var \Illuminate\View\Compilers\BladeCompiler + */ + protected $bladeCompiler; + + /** + * Create a new Blade mapper instance. + * + * @param \Illuminate\Contracts\View\Factory $factory + * @param \Illuminate\View\Compilers\BladeCompiler $bladeCompiler + * @return void + */ + public function __construct(Factory $factory, BladeCompiler $bladeCompiler) + { + $this->factory = $factory; + $this->bladeCompiler = $bladeCompiler; + } + + /** + * Map cached view paths to their original paths. + * + * @param \Symfony\Component\ErrorHandler\Exception\FlattenException $exception + * @return \Symfony\Component\ErrorHandler\Exception\FlattenException + */ + public function map(FlattenException $exception) + { + while ($exception->getClass() === ViewException::class) { + if (($previous = $exception->getPrevious()) === null) { + break; + } + + $exception = $previous; + } + + $trace = Collection::make($exception->getTrace()) + ->map(function ($frame) { + if ($originalPath = $this->findCompiledView((string) Arr::get($frame, 'file', ''))) { + $frame['file'] = $originalPath; + $frame['line'] = $this->detectLineNumber($frame['file'], $frame['line']); + } + + return $frame; + })->toArray(); + + return tap($exception, fn () => (fn () => $this->trace = $trace)->call($exception)); + } + + /** + * Find the compiled view file for the given compiled path. + * + * @param string $compiledPath + * @return string|null + */ + protected function findCompiledView(string $compiledPath) + { + return once(fn () => $this->getKnownPaths())[$compiledPath] ?? null; + } + + /** + * Get the list of known paths from the compiler engine. + * + * @return array + */ + protected function getKnownPaths() + { + $compilerEngineReflection = new ReflectionClass( + $bladeCompilerEngine = $this->factory->getEngineResolver()->resolve('blade'), + ); + + if (! $compilerEngineReflection->hasProperty('lastCompiled') && $compilerEngineReflection->hasProperty('engine')) { + $compilerEngine = $compilerEngineReflection->getProperty('engine'); + $compilerEngine->setAccessible(true); + $compilerEngine = $compilerEngine->getValue($bladeCompilerEngine); + $lastCompiled = new ReflectionProperty($compilerEngine, 'lastCompiled'); + $lastCompiled->setAccessible(true); + $lastCompiled = $lastCompiled->getValue($compilerEngine); + } else { + $lastCompiled = $compilerEngineReflection->getProperty('lastCompiled'); + $lastCompiled->setAccessible(true); + $lastCompiled = $lastCompiled->getValue($bladeCompilerEngine); + } + + $knownPaths = []; + foreach ($lastCompiled as $lastCompiledPath) { + $compiledPath = $bladeCompilerEngine->getCompiler()->getCompiledPath($lastCompiledPath); + + $knownPaths[realpath($compiledPath ?? $lastCompiledPath)] = realpath($lastCompiledPath); + } + + return $knownPaths; + } + + /** + * Filter out the view data that should not be shown in the exception report. + * + * @param array $data + * @return array + */ + protected function filterViewData(array $data) + { + return array_filter($data, function ($value, $key) { + if ($key === 'app') { + return ! $value instanceof Application; + } + + return $key !== '__env'; + }, ARRAY_FILTER_USE_BOTH); + } + + /** + * Detect the line number in the original blade file. + * + * @param string $filename + * @param int $compiledLineNumber + * @return int + */ + protected function detectLineNumber(string $filename, int $compiledLineNumber) + { + $map = $this->compileSourcemap((string) file_get_contents($filename)); + + return $this->findClosestLineNumberMapping($map, $compiledLineNumber); + } + + /** + * Compile the source map for the given blade file. + * + * @param string $value + * @return string + */ + protected function compileSourcemap(string $value) + { + try { + $value = $this->addEchoLineNumbers($value); + $value = $this->addStatementLineNumbers($value); + $value = $this->addBladeComponentLineNumbers($value); + + $value = $this->bladeCompiler->compileString($value); + + return $this->trimEmptyLines($value); + } catch (Throwable $e) { + report($e); + + return $value; + } + } + + /** + * Add line numbers to echo statements. + * + * @param string $value + * @return string + */ + protected function addEchoLineNumbers(string $value) + { + $echoPairs = [['{{', '}}'], ['{{{', '}}}'], ['{!!', '!!}']]; + + foreach ($echoPairs as $pair) { + // Matches {{ $value }}, {!! $value !!} and {{{ $value }}} depending on $pair + $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $pair[0], $pair[1]); + + if (preg_match_all($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { + foreach (array_reverse($matches[0]) as $match) { + $position = mb_strlen(substr($value, 0, $match[1])); + + $value = $this->insertLineNumberAtPosition($position, $value); + } + } + } + + return $value; + } + + /** + * Add line numbers to blade statements. + * + * @param string $value + * @return string + */ + protected function addStatementLineNumbers(string $value) + { + $shouldInsertLineNumbers = preg_match_all( + '/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', + $value, + $matches, + PREG_OFFSET_CAPTURE + ); + + if ($shouldInsertLineNumbers) { + foreach (array_reverse($matches[0]) as $match) { + $position = mb_strlen(substr($value, 0, $match[1])); + + $value = $this->insertLineNumberAtPosition($position, $value); + } + } + + return $value; + } + + /** + * Add line numbers to blade components. + * + * @param string $value + * @return string + */ + protected function addBladeComponentLineNumbers(string $value) + { + $shouldInsertLineNumbers = preg_match_all( + '/<\s*x[-:]([\w\-:.]*)/mx', + $value, + $matches, + PREG_OFFSET_CAPTURE + ); + + if ($shouldInsertLineNumbers) { + foreach (array_reverse($matches[0]) as $match) { + $position = mb_strlen(substr($value, 0, $match[1])); + + $value = $this->insertLineNumberAtPosition($position, $value); + } + } + + return $value; + } + + /** + * Insert a line number at the given position. + * + * @param int $position + * @param string $value + * @return string + */ + protected function insertLineNumberAtPosition(int $position, string $value) + { + $before = mb_substr($value, 0, $position); + + $lineNumber = count(explode("\n", $before)); + + return mb_substr($value, 0, $position)."|---LINE:{$lineNumber}---|".mb_substr($value, $position); + } + + /** + * Trim empty lines from the given value. + * + * @param string $value + * @return string + */ + protected function trimEmptyLines(string $value) + { + $value = preg_replace('/^\|---LINE:([0-9]+)---\|$/m', '', $value); + + return ltrim((string) $value, PHP_EOL); + } + + /** + * Find the closest line number mapping in the given source map. + * + * @param string $map + * @param int $compiledLineNumber + * @return int + */ + protected function findClosestLineNumberMapping(string $map, int $compiledLineNumber) + { + $map = explode("\n", $map); + + $maxDistance = 20; + + $pattern = '/\|---LINE:(?P[0-9]+)---\|/m'; + + $lineNumberToCheck = $compiledLineNumber - 1; + + while (true) { + if ($lineNumberToCheck < $compiledLineNumber - $maxDistance) { + return min($compiledLineNumber, count($map)); + } + + if (preg_match($pattern, $map[$lineNumberToCheck] ?? '', $matches)) { + return (int) $matches['line']; + } + + $lineNumberToCheck--; + } + } +} diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Renderer.php b/src/Illuminate/Foundation/Exceptions/Renderer/Renderer.php new file mode 100644 index 00000000..823e893d --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Renderer.php @@ -0,0 +1,138 @@ +viewFactory = $viewFactory; + $this->listener = $listener; + $this->htmlErrorRenderer = $htmlErrorRenderer; + $this->bladeMapper = $bladeMapper; + $this->basePath = $basePath; + } + + /** + * Render the given exception as an HTML string. + * + * @param \Illuminate\Http\Request $request + * @param \Throwable $throwable + * @return string + */ + public function render(Request $request, Throwable $throwable) + { + $flattenException = $this->bladeMapper->map( + $this->htmlErrorRenderer->render($throwable), + ); + + return $this->viewFactory->make('laravel-exceptions-renderer::show', [ + 'exception' => new Exception($flattenException, $request, $this->listener, $this->basePath), + ])->render(); + } + + /** + * Get the renderer's CSS content. + * + * @return string + */ + public static function css() + { + return collect([ + ['styles.css', []], + ['light-mode.css', ['data-theme' => 'light']], + ['dark-mode.css', ['data-theme' => 'dark']], + ])->map(function ($fileAndAttributes) { + [$filename, $attributes] = $fileAndAttributes; + + return ''; + })->implode(''); + } + + /** + * Get the renderer's JavaScript content. + * + * @return string + */ + public static function js() + { + $viteJsAutoRefresh = ''; + + $vite = app(\Illuminate\Foundation\Vite::class); + + if (is_file($vite->hotFile())) { + $viteJsAutoRefresh = $vite->__invoke([]); + } + + return ''.$viteJsAutoRefresh; + } +} diff --git a/src/Illuminate/Foundation/Http/FormRequest.php b/src/Illuminate/Foundation/Http/FormRequest.php index 4824c27c..87a1e204 100644 --- a/src/Illuminate/Foundation/Http/FormRequest.php +++ b/src/Illuminate/Foundation/Http/FormRequest.php @@ -226,7 +226,7 @@ protected function failedAuthorization() * @param array|null $keys * @return \Illuminate\Support\ValidatedInput|array */ - public function safe(array $keys = null) + public function safe(?array $keys = null) { return is_array($keys) ? $this->validator->safe()->only($keys) diff --git a/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php b/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php index f5ad195b..80be7a47 100644 --- a/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php +++ b/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Support\Arr; +use Illuminate\Support\Str; class TrimStrings extends TransformsRequest { @@ -61,11 +62,23 @@ protected function transform($key, $value) { $except = array_merge($this->except, static::$neverTrim); - if (in_array($key, $except, true) || ! is_string($value)) { + if ($this->shouldSkip($key, $except) || ! is_string($value)) { return $value; } - return preg_replace('~^[\s\x{FEFF}\x{200B}]+|[\s\x{FEFF}\x{200B}]+$~u', '', $value) ?? trim($value); + return Str::trim($value); + } + + /** + * Determine if the given key should be skipped. + * + * @param string $key + * @param array $except + * @return bool + */ + protected function shouldSkip($key, $except) + { + return in_array($key, $except, true); } /** diff --git a/src/Illuminate/Foundation/Mix.php b/src/Illuminate/Foundation/Mix.php index 6c861c72..f06deb95 100644 --- a/src/Illuminate/Foundation/Mix.php +++ b/src/Illuminate/Foundation/Mix.php @@ -15,7 +15,7 @@ class Mix * @param string $manifestDirectory * @return \Illuminate\Support\HtmlString|string * - * @throws \Exception + * @throws \Illuminate\Foundation\MixManifestNotFoundException */ public function __invoke($path, $manifestDirectory = '') { @@ -49,7 +49,7 @@ public function __invoke($path, $manifestDirectory = '') if (! isset($manifests[$manifestPath])) { if (! is_file($manifestPath)) { - throw new Exception("Mix manifest not found at: {$manifestPath}"); + throw new MixManifestNotFoundException("Mix manifest not found at: {$manifestPath}"); } $manifests[$manifestPath] = json_decode(file_get_contents($manifestPath), true); diff --git a/src/Illuminate/Foundation/MixManifestNotFoundException.php b/src/Illuminate/Foundation/MixManifestNotFoundException.php new file mode 100644 index 00000000..9931be12 --- /dev/null +++ b/src/Illuminate/Foundation/MixManifestNotFoundException.php @@ -0,0 +1,10 @@ + $this->app->resourcePath('views/errors/'), ], 'laravel-errors'); } + + if ($this->app->hasDebugModeEnabled()) { + $this->app->make(Listener::class)->registerListeners( + $this->app->make(Dispatcher::class) + ); + } } /** @@ -76,6 +87,7 @@ public function register() $this->registerRequestValidation(); $this->registerRequestSignatureValidation(); $this->registerExceptionTracking(); + $this->registerExceptionRenderer(); $this->registerMaintenanceModeManager(); } @@ -198,6 +210,36 @@ protected function registerExceptionTracking() }); } + /** + * Register the exceptions renderer. + * + * @return void + */ + protected function registerExceptionRenderer() + { + if (! $this->app->hasDebugModeEnabled()) { + return; + } + + $this->loadViewsFrom(__DIR__.'/../resources/exceptions/renderer', 'laravel-exceptions-renderer'); + + $this->app->singleton(Renderer::class, function (Application $app) { + $errorRenderer = new HtmlErrorRenderer( + $app['config']->get('app.debug'), + ); + + return new Renderer( + $app->make(Factory::class), + $app->make(Listener::class), + $errorRenderer, + $app->make(BladeMapper::class), + $app->basePath(), + ); + }); + + $this->app->singleton(Listener::class); + } + /** * Register the maintenance mode manager service. * diff --git a/src/Illuminate/Foundation/Queue/Queueable.php b/src/Illuminate/Foundation/Queue/Queueable.php new file mode 100644 index 00000000..06e649d5 --- /dev/null +++ b/src/Illuminate/Foundation/Queue/Queueable.php @@ -0,0 +1,13 @@ +app->call(self::$alwaysLoadCachedRoutesUsing); + + return; + } + $this->app->booted(function () { require $this->app->getCachedRoutesPath(); }); diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php index 6c517d33..b4aad547 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php @@ -58,7 +58,7 @@ protected function instance($abstract, $instance) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function mock($abstract, Closure $mock = null) + protected function mock($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::mock(...array_filter(func_get_args()))); } @@ -70,7 +70,7 @@ protected function mock($abstract, Closure $mock = null) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function partialMock($abstract, Closure $mock = null) + protected function partialMock($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::mock(...array_filter(func_get_args()))->makePartial()); } @@ -82,7 +82,7 @@ protected function partialMock($abstract, Closure $mock = null) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function spy($abstract, Closure $mock = null) + protected function spy($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::spy(...array_filter(func_get_args()))); } diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithExceptionHandling.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithExceptionHandling.php index 09556824..67aac9d9 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithExceptionHandling.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithExceptionHandling.php @@ -4,6 +4,7 @@ use Closure; use Illuminate\Contracts\Debug\ExceptionHandler; +use Illuminate\Support\Testing\Fakes\ExceptionHandlerFake; use Illuminate\Testing\Assert; use Illuminate\Validation\ValidationException; use Symfony\Component\Console\Application as ConsoleApplication; @@ -27,7 +28,11 @@ trait InteractsWithExceptionHandling protected function withExceptionHandling() { if ($this->originalExceptionHandler) { - $this->app->instance(ExceptionHandler::class, $this->originalExceptionHandler); + $currentExceptionHandler = app(ExceptionHandler::class); + + $currentExceptionHandler instanceof ExceptionHandlerFake + ? $currentExceptionHandler->setHandler($this->originalExceptionHandler) + : $this->app->instance(ExceptionHandler::class, $this->originalExceptionHandler); } return $this; @@ -63,10 +68,14 @@ protected function handleValidationExceptions() protected function withoutExceptionHandling(array $except = []) { if ($this->originalExceptionHandler == null) { - $this->originalExceptionHandler = app(ExceptionHandler::class); + $currentExceptionHandler = app(ExceptionHandler::class); + + $this->originalExceptionHandler = $currentExceptionHandler instanceof ExceptionHandlerFake + ? $currentExceptionHandler->handler() + : $currentExceptionHandler; } - $this->app->instance(ExceptionHandler::class, new class($this->originalExceptionHandler, $except) implements ExceptionHandler + $exceptionHandler = new class($this->originalExceptionHandler, $except) implements ExceptionHandler, WithoutExceptionHandlingHandler { protected $except; protected $originalHandler; @@ -145,7 +154,13 @@ public function renderForConsole($output, Throwable $e) { (new ConsoleApplication)->renderThrowable($e, $output); } - }); + }; + + $currentExceptionHandler = app(ExceptionHandler::class); + + $currentExceptionHandler instanceof ExceptionHandlerFake + ? $currentExceptionHandler->setHandler($exceptionHandler) + : $this->app->instance(ExceptionHandler::class, $exceptionHandler); return $this; } diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index a5468e37..78344769 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -63,13 +63,6 @@ trait MakesHttpRequests */ protected $withCredentials = false; - /** - * The latest test response (if any). - * - * @var \Illuminate\Testing\TestResponse|null - */ - public static $latestResponse; - /** * Define additional headers to be sent with the request. * @@ -592,7 +585,7 @@ public function call($method, $uri, $parameters = [], $cookies = [], $files = [] $response = $this->followRedirects($response); } - return static::$latestResponse = $this->createTestResponse($response, $request); + return $this->createTestResponse($response, $request); } /** diff --git a/src/Illuminate/Foundation/Testing/Concerns/WithoutExceptionHandlingHandler.php b/src/Illuminate/Foundation/Testing/Concerns/WithoutExceptionHandlingHandler.php new file mode 100644 index 00000000..07cda4f5 --- /dev/null +++ b/src/Illuminate/Foundation/Testing/Concerns/WithoutExceptionHandlingHandler.php @@ -0,0 +1,8 @@ +mockConsoleOutput; + if (property_exists($this, 'mockConsoleOutput')) { + $shouldMockOutput = $this->mockConsoleOutput; - $this->mockConsoleOutput = false; + $this->mockConsoleOutput = false; + } $this->baseRefreshDatabase(); - $this->mockConsoleOutput = $shouldMockOutput; + if (property_exists($this, 'mockConsoleOutput')) { + $this->mockConsoleOutput = $shouldMockOutput; + } }; $database->beforeStartingTransaction($callback); diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 94ec4c71..1d7a17df 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -5,7 +5,6 @@ use Illuminate\Contracts\Console\Kernel; use Illuminate\Foundation\Application; use PHPUnit\Framework\TestCase as BaseTestCase; -use Throwable; abstract class TestCase extends BaseTestCase { @@ -42,8 +41,6 @@ public function createApplication() */ protected function setUp(): void { - static::$latestResponse = null; - $this->setUpTheTestEnvironment(); } @@ -57,20 +54,6 @@ protected function refreshApplication() $this->app = $this->createApplication(); } - /** - * {@inheritdoc} - */ - protected function transformException(Throwable $error): Throwable - { - $response = static::$latestResponse ?? null; - - if (! is_null($response)) { - $response->transformNotSuccessfulException($error); - } - - return $error; - } - /** * Clean up the testing environment before the next test. * @@ -90,8 +73,6 @@ protected function tearDown(): void */ public static function tearDownAfterClass(): void { - static::$latestResponse = null; - static::tearDownAfterClassUsingTestCase(); } } diff --git a/src/Illuminate/Foundation/Validation/ValidatesRequests.php b/src/Illuminate/Foundation/Validation/ValidatesRequests.php index bf2aecdd..4d7aad2a 100644 --- a/src/Illuminate/Foundation/Validation/ValidatesRequests.php +++ b/src/Illuminate/Foundation/Validation/ValidatesRequests.php @@ -18,7 +18,7 @@ trait ValidatesRequests * * @throws \Illuminate\Validation\ValidationException */ - public function validateWith($validator, Request $request = null) + public function validateWith($validator, ?Request $request = null) { $request = $request ?: request(); diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index a81ca161..97e79b6d 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -170,7 +170,7 @@ public function useManifestFilename($filename) /** * Resolve asset paths using the provided resolver. * - * @param callable|null $urlResolver + * @param callable|null $resolver * @return $this */ public function createAssetPathsUsing($resolver) @@ -712,7 +712,7 @@ protected function assetPath($path, $secure = null) } /** - * Get the the manifest file for the given build directory. + * Get the manifest file for the given build directory. * * @param string $buildDirectory * @return array diff --git a/src/Illuminate/Foundation/helpers.php b/src/Illuminate/Foundation/helpers.php index c698bbdf..e03e506d 100644 --- a/src/Illuminate/Foundation/helpers.php +++ b/src/Illuminate/Foundation/helpers.php @@ -16,6 +16,7 @@ use Illuminate\Foundation\Bus\PendingDispatch; use Illuminate\Foundation\Mix; use Illuminate\Http\Exceptions\HttpResponseException; +use Illuminate\Log\Context\Repository as ContextRepository; use Illuminate\Queue\CallQueuedClosure; use Illuminate\Routing\Router; use Illuminate\Support\Facades\Date; @@ -33,6 +34,7 @@ * * @throws \Symfony\Component\HttpKernel\Exception\HttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * @throws \Illuminate\Http\Exceptions\HttpResponseException */ function abort($code, $message = '', array $headers = []) { @@ -107,9 +109,11 @@ function action($name, $parameters = [], $absolute = true) /** * Get the available container instance. * - * @param string|null $abstract + * @template TClass + * + * @param string|class-string|null $abstract * @param array $parameters - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Foundation\Application|mixed + * @return ($abstract is class-string ? TClass : ($abstract is null ? \Illuminate\Foundation\Application : mixed)) */ function app($abstract = null, array $parameters = []) { @@ -153,7 +157,7 @@ function asset($path, $secure = null) * Get the available auth instance. * * @param string|null $guard - * @return \Illuminate\Contracts\Auth\Factory|\Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard + * @return ($guard is null ? \Illuminate\Contracts\Auth\Factory : \Illuminate\Contracts\Auth\StatefulGuard) */ function auth($guard = null) { @@ -226,28 +230,29 @@ function broadcast($event = null) * * If an array is passed, we'll assume you want to put to the cache. * - * @param mixed ...$arguments key|key,default|data,expiration|null - * @return mixed|\Illuminate\Cache\CacheManager + * @param string|array|null $key key|data + * @param mixed $default default|expiration|null + * @return ($key is null ? \Illuminate\Cache\CacheManager : ($key is string ? mixed : bool)) * * @throws \InvalidArgumentException */ - function cache(...$arguments) + function cache($key = null, $default = null) { - if (empty($arguments)) { + if (is_null($key)) { return app('cache'); } - if (is_string($arguments[0])) { - return app('cache')->get(...$arguments); + if (is_string($key)) { + return app('cache')->get($key, $default); } - if (! is_array($arguments[0])) { + if (! is_array($key)) { throw new InvalidArgumentException( 'When setting a value in the cache, you must pass an array of key / value pairs.' ); } - return app('cache')->put(key($arguments[0]), reset($arguments[0]), $arguments[1] ?? null); + return app('cache')->put(key($key), reset($key), ttl: $default); } } @@ -257,9 +262,9 @@ function cache(...$arguments) * * If an array is passed as the key, we will assume you want to set an array of values. * - * @param array|string|null $key + * @param array|string|null $key * @param mixed $default - * @return mixed|\Illuminate\Config\Repository + * @return ($key is null ? \Illuminate\Config\Repository : ($key is string ? mixed : null)) */ function config($key = null, $default = null) { @@ -288,6 +293,26 @@ function config_path($path = '') } } +if (! function_exists('context')) { + /** + * Get / set the specified context value. + * + * @param array|string|null $key + * @param mixed $default + * @return ($key is string ? mixed : \Illuminate\Log\Context\Repository) + */ + function context($key = null, $default = null) + { + $context = app(ContextRepository::class); + + return match (true) { + is_null($key) => $context, + is_array($key) => $context->add($key), + default => $context->get($key, $default), + }; + } +} + if (! function_exists('cookie')) { /** * Create a new cookie instance. @@ -301,7 +326,7 @@ function config_path($path = '') * @param bool $httpOnly * @param bool $raw * @param string|null $sameSite - * @return \Illuminate\Cookie\CookieJar|\Symfony\Component\HttpFoundation\Cookie + * @return ($name is null ? \Illuminate\Cookie\CookieJar : \Symfony\Component\HttpFoundation\Cookie) */ function cookie($name = null, $value = null, $minutes = 0, $path = null, $domain = null, $secure = null, $httpOnly = true, $raw = false, $sameSite = null) { @@ -379,7 +404,7 @@ function decrypt($value, $unserialize = true) * Dispatch a job to its appropriate handler. * * @param mixed $job - * @return \Illuminate\Foundation\Bus\PendingDispatch + * @return ($job is \Closure ? \Illuminate\Foundation\Bus\PendingClosureDispatch : \Illuminate\Foundation\Bus\PendingDispatch) */ function dispatch($job) { @@ -479,7 +504,7 @@ function info($message, $context = []) * * @param string|null $message * @param array $context - * @return \Illuminate\Log\LogManager|null + * @return ($message is null ? \Illuminate\Log\LogManager : null) */ function logger($message = null, array $context = []) { @@ -509,7 +534,7 @@ function lang_path($path = '') * Get a log driver instance. * * @param string|null $driver - * @return \Illuminate\Log\LogManager|\Psr\Log\LoggerInterface + * @return ($driver is null ? \Illuminate\Log\LogManager : \Psr\Log\LoggerInterface) */ function logs($driver = null) { @@ -638,7 +663,7 @@ function public_path($path = '') * @param int $status * @param array $headers * @param bool|null $secure - * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse + * @return ($to is null ? \Illuminate\Routing\Redirector : \Illuminate\Http\RedirectResponse) */ function redirect($to = null, $status = 302, $headers = [], $secure = null) { @@ -703,9 +728,9 @@ function report_unless($boolean, $exception) /** * Get an instance of the current request or an input item from the request. * - * @param array|string|null $key + * @param list|string|null $key * @param mixed $default - * @return mixed|\Illuminate\Http\Request|string|array|null + * @return ($key is null ? \Illuminate\Http\Request : ($key is string ? mixed : array)) */ function request($key = null, $default = null) { @@ -727,13 +752,13 @@ function request($key = null, $default = null) /** * Catch a potential exception and return a default value. * - * @template TRescueValue - * @template TRescueFallback + * @template TValue + * @template TFallback * - * @param callable(): TRescueValue $callback - * @param (callable(\Throwable): TRescueFallback)|TRescueFallback $rescue - * @param bool|callable $report - * @return TRescueValue|TRescueFallback + * @param callable(): TValue $callback + * @param (callable(\Throwable): TFallback)|TFallback $rescue + * @param bool|callable(\Throwable): bool $report + * @return TValue|TFallback */ function rescue(callable $callback, $rescue = null, $report = true) { @@ -753,9 +778,11 @@ function rescue(callable $callback, $rescue = null, $report = true) /** * Resolve a service from the container. * - * @param string $name + * @template TClass + * + * @param string|class-string $name * @param array $parameters - * @return mixed + * @return ($name is class-string ? TClass : mixed) */ function resolve($name, array $parameters = []) { @@ -783,9 +810,9 @@ function resource_path($path = '') * @param \Illuminate\Contracts\View\View|string|array|null $content * @param int $status * @param array $headers - * @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory + * @return ($content is null ? \Illuminate\Contracts\Routing\ResponseFactory : \Illuminate\Http\Response) */ - function response($content = '', $status = 200, array $headers = []) + function response($content = null, $status = 200, array $headers = []) { $factory = app(ResponseFactory::class); @@ -793,7 +820,7 @@ function response($content = '', $status = 200, array $headers = []) return $factory; } - return $factory->make($content, $status, $headers); + return $factory->make($content ?? '', $status, $headers); } } @@ -845,9 +872,9 @@ function secure_url($path, $parameters = []) * * If an array is passed as the key, we will assume you want to set an array of values. * - * @param array|string|null $key + * @param array|string|null $key * @param mixed $default - * @return mixed|\Illuminate\Session\Store|\Illuminate\Session\SessionManager + * @return ($key is null ? \Illuminate\Session\SessionManager : ($key is string ? mixed : null)) */ function session($key = null, $default = null) { @@ -912,7 +939,7 @@ function today($tz = null) * @param string|null $key * @param array $replace * @param string|null $locale - * @return \Illuminate\Contracts\Translation\Translator|string|array|null + * @return ($key is null ? \Illuminate\Contracts\Translation\Translator : array|string) */ function trans($key = null, $replace = [], $locale = null) { @@ -966,7 +993,7 @@ function __($key = null, $replace = [], $locale = null) * @param string|null $path * @param mixed $parameters * @param bool|null $secure - * @return \Illuminate\Contracts\Routing\UrlGenerator|string + * @return ($path is null ? \Illuminate\Contracts\Routing\UrlGenerator : string) */ function url($path = null, $parameters = [], $secure = null) { @@ -982,13 +1009,13 @@ function url($path = null, $parameters = [], $secure = null) /** * Create a new Validator instance. * - * @param array $data + * @param array|null $data * @param array $rules * @param array $messages * @param array $attributes - * @return \Illuminate\Contracts\Validation\Validator|\Illuminate\Contracts\Validation\Factory + * @return ($data is null ? \Illuminate\Contracts\Validation\Factory : \Illuminate\Contracts\Validation\Validator) */ - function validator(array $data = [], array $rules = [], array $messages = [], array $attributes = []) + function validator(?array $data = null, array $rules = [], array $messages = [], array $attributes = []) { $factory = app(ValidationFactory::class); @@ -996,7 +1023,7 @@ function validator(array $data = [], array $rules = [], array $messages = [], ar return $factory; } - return $factory->make($data, $rules, $messages, $attributes); + return $factory->make($data ?? [], $rules, $messages, $attributes); } } @@ -1007,7 +1034,7 @@ function validator(array $data = [], array $rules = [], array $messages = [], ar * @param string|null $view * @param \Illuminate\Contracts\Support\Arrayable|array $data * @param array $mergeData - * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + * @return ($view is null ? \Illuminate\Contracts\View\Factory : \Illuminate\Contracts\View\View) */ function view($view = null, $data = [], $mergeData = []) { diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php new file mode 100644 index 00000000..14dcd4f5 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php @@ -0,0 +1,5 @@ +
merge(['class' => "@container flex flex-col p-6 sm:p-12 bg-white dark:bg-gray-900/80 text-gray-900 dark:text-gray-100 rounded-lg default:col-span-full default:lg:col-span-6 default:row-span-1 dark:ring-1 dark:ring-gray-800 shadow-xl"]) }} +> + {{ $slot }} +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php new file mode 100644 index 00000000..26fae1af --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php @@ -0,0 +1,148 @@ +@use('Illuminate\Support\Str') + +
+ Request +
+ +
+ {{ $exception->request()->method() }} + {{ Str::start($exception->request()->path(), '/') }} +
+ +
+ Headers +
+ +
+ @forelse ($exception->requestHeaders() as $key => $value) +
+ + {{ $key }} + + +
{{ $value }}
+
+
+ @empty + +
No headers data
+
+ @endforelse +
+ +
+ Body +
+ +
+
+ +
{!! $exception->requestBody() ?: 'No body data' !!}
+
+
+
+ +
+ + +
+ Application +
+ +
+ Routing +
+ +
+ @forelse ($exception->applicationRouteContext() as $name => $value) +
+ {{ $name }} + +
{{ $value }}
+
+
+ @empty + +
No routing data
+
+ @endforelse +
+ + @if ($routeParametersContext = $exception->applicationRouteParametersContext()) +
+ Routing Parameters +
+ +
+
+ +
{!! $routeParametersContext !!}
+
+
+
+ @endif + +
+ Database Queries + + @if (count($exception->applicationQueries()) === 100) + only the first 100 queries are displayed + @endif + +
+ +
+ @forelse ($exception->applicationQueries() as ['connectionName' => $connectionName, 'sql' => $sql, 'time' => $time]) +
+
+ {{ $connectionName }} + +
+ +
{{ $sql }}
+
+
+ @empty + +
No query data
+
+ @endforelse +
+
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php new file mode 100644 index 00000000..4171fb8e --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php @@ -0,0 +1,32 @@ +@foreach ($exception->frames() as $frame) +
+
+
+
+ + @if (config('app.editor')) + + {{ $frame->file() }} + + @else + {{ $frame->file() }} + @endif + + :{{ $frame->line() }} +
+
+
+
+
+
+
+@endforeach diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php new file mode 100644 index 00000000..4da473e7 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php @@ -0,0 +1,28 @@ + +
+
+
+ + + {{ implode(' ', array_slice(explode('\\', $exception->class()), -1)) }} + +
+
+ {{ $exception->message() }} +
+
+ + +
+
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-down.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-down.blade.php new file mode 100644 index 00000000..e31eedef --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-down.blade.php @@ -0,0 +1,3 @@ + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-up.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-up.blade.php new file mode 100644 index 00000000..897030ab --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/chevron-up.blade.php @@ -0,0 +1,3 @@ + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/computer-desktop.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/computer-desktop.blade.php new file mode 100644 index 00000000..55c776ac --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/computer-desktop.blade.php @@ -0,0 +1,10 @@ + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/moon.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/moon.blade.php new file mode 100644 index 00000000..f8591695 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/moon.blade.php @@ -0,0 +1,10 @@ + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/sun.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/sun.blade.php new file mode 100644 index 00000000..d0ca2f70 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/icons/sun.blade.php @@ -0,0 +1,10 @@ + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/layout.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/layout.blade.php new file mode 100644 index 00000000..8d460fa4 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/layout.blade.php @@ -0,0 +1,56 @@ +@use('Illuminate\Foundation\Exceptions\Renderer\Renderer') + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + {!! Renderer::css() !!} + + + + + {{ $slot }} + + {!! Renderer::js() !!} + + + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/navigation.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/navigation.blade.php new file mode 100644 index 00000000..47ad038c --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/navigation.blade.php @@ -0,0 +1,28 @@ +
+
+
+
+
+ + + +
+ + + {{ $exception->title() }} + +
+ +
+ +
+
+
+
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/theme-switcher.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/theme-switcher.blade.php new file mode 100644 index 00000000..110b7211 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/theme-switcher.blade.php @@ -0,0 +1,98 @@ + + +
+ + + +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace-and-editor.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace-and-editor.blade.php new file mode 100644 index 00000000..684a8f21 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace-and-editor.blade.php @@ -0,0 +1,13 @@ + +
+
+ + +
+
+
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php new file mode 100644 index 00000000..1930737b --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php @@ -0,0 +1,85 @@ + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/dark-mode.css b/src/Illuminate/Foundation/resources/exceptions/renderer/dark-mode.css new file mode 100644 index 00000000..becdcb48 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/dark-mode.css @@ -0,0 +1 @@ +@import 'highlight.js/styles/atom-one-dark.min.css'; diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/dist/dark-mode.css b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/dark-mode.css new file mode 100644 index 00000000..23609b4d --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/dark-mode.css @@ -0,0 +1 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/dist/light-mode.css b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/light-mode.css new file mode 100644 index 00000000..96af2848 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/light-mode.css @@ -0,0 +1,10 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! + Theme: GitHub + Description: Light theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-light + Current colors taken from GitHub's CSS +*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0} diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js new file mode 100644 index 00000000..3388b691 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js @@ -0,0 +1,7 @@ +var ce="top",be="bottom",ye="right",ue="left",nr="auto",Bt=[ce,be,ye,ue],dt="start",Dt="end",ua="clippingParents",mi="viewport",Et="popper",la="reference",Pr=Bt.reduce(function(e,t){return e.concat([t+"-"+dt,t+"-"+Dt])},[]),xi=[].concat(Bt,[nr]).reduce(function(e,t){return e.concat([t,t+"-"+dt,t+"-"+Dt])},[]),fa="beforeRead",da="read",pa="afterRead",ha="beforeMain",ga="main",va="afterMain",_a="beforeWrite",ba="write",ya="afterWrite",ma=[fa,da,pa,ha,ga,va,_a,ba,ya];function Me(e){return e?(e.nodeName||"").toLowerCase():null}function fe(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function it(e){var t=fe(e).Element;return e instanceof t||e instanceof Element}function _e(e){var t=fe(e).HTMLElement;return e instanceof t||e instanceof HTMLElement}function rr(e){if(typeof ShadowRoot>"u")return!1;var t=fe(e).ShadowRoot;return e instanceof t||e instanceof ShadowRoot}function xa(e){var t=e.state;Object.keys(t.elements).forEach(function(n){var r=t.styles[n]||{},i=t.attributes[n]||{},o=t.elements[n];!_e(o)||!Me(o)||(Object.assign(o.style,r),Object.keys(i).forEach(function(a){var s=i[a];s===!1?o.removeAttribute(a):o.setAttribute(a,s===!0?"":s)}))})}function Ea(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach(function(r){var i=t.elements[r],o=t.attributes[r]||{},a=Object.keys(t.styles.hasOwnProperty(r)?t.styles[r]:n[r]),s=a.reduce(function(c,f){return c[f]="",c},{});!_e(i)||!Me(i)||(Object.assign(i.style,s),Object.keys(o).forEach(function(c){i.removeAttribute(c)}))})}}const Ei={name:"applyStyles",enabled:!0,phase:"write",fn:xa,effect:Ea,requires:["computeStyles"]};function Te(e){return e.split("-")[0]}var Ze=Math.max,rn=Math.min,pt=Math.round;function Pn(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(t){return t.brand+"/"+t.version}).join(" "):navigator.userAgent}function wi(){return!/^((?!chrome|android).)*safari/i.test(Pn())}function ht(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!1);var r=e.getBoundingClientRect(),i=1,o=1;t&&_e(e)&&(i=e.offsetWidth>0&&pt(r.width)/e.offsetWidth||1,o=e.offsetHeight>0&&pt(r.height)/e.offsetHeight||1);var a=it(e)?fe(e):window,s=a.visualViewport,c=!wi()&&n,f=(r.left+(c&&s?s.offsetLeft:0))/i,l=(r.top+(c&&s?s.offsetTop:0))/o,v=r.width/i,b=r.height/o;return{width:v,height:b,top:l,right:f+v,bottom:l+b,left:f,x:f,y:l}}function ir(e){var t=ht(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function Oi(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&rr(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function De(e){return fe(e).getComputedStyle(e)}function wa(e){return["table","td","th"].indexOf(Me(e))>=0}function We(e){return((it(e)?e.ownerDocument:e.document)||window.document).documentElement}function fn(e){return Me(e)==="html"?e:e.assignedSlot||e.parentNode||(rr(e)?e.host:null)||We(e)}function kr(e){return!_e(e)||De(e).position==="fixed"?null:e.offsetParent}function Oa(e){var t=/firefox/i.test(Pn()),n=/Trident/i.test(Pn());if(n&&_e(e)){var r=De(e);if(r.position==="fixed")return null}var i=fn(e);for(rr(i)&&(i=i.host);_e(i)&&["html","body"].indexOf(Me(i))<0;){var o=De(i);if(o.transform!=="none"||o.perspective!=="none"||o.contain==="paint"||["transform","perspective"].indexOf(o.willChange)!==-1||t&&o.willChange==="filter"||t&&o.filter&&o.filter!=="none")return i;i=i.parentNode}return null}function $t(e){for(var t=fe(e),n=kr(e);n&&wa(n)&&De(n).position==="static";)n=kr(n);return n&&(Me(n)==="html"||Me(n)==="body"&&De(n).position==="static")?t:n||Oa(e)||t}function or(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function Tt(e,t,n){return Ze(e,rn(t,n))}function Aa(e,t,n){var r=Tt(e,t,n);return r>n?n:r}function Ai(){return{top:0,right:0,bottom:0,left:0}}function Si(e){return Object.assign({},Ai(),e)}function Ti(e,t){return t.reduce(function(n,r){return n[r]=e,n},{})}var Sa=function(t,n){return t=typeof t=="function"?t(Object.assign({},n.rects,{placement:n.placement})):t,Si(typeof t!="number"?t:Ti(t,Bt))};function Ta(e){var t,n=e.state,r=e.name,i=e.options,o=n.elements.arrow,a=n.modifiersData.popperOffsets,s=Te(n.placement),c=or(s),f=[ue,ye].indexOf(s)>=0,l=f?"height":"width";if(!(!o||!a)){var v=Sa(i.padding,n),b=ir(o),_=c==="y"?ce:ue,A=c==="y"?be:ye,S=n.rects.reference[l]+n.rects.reference[c]-a[c]-n.rects.popper[l],h=a[c]-n.rects.reference[c],E=$t(o),O=E?c==="y"?E.clientHeight||0:E.clientWidth||0:0,M=S/2-h/2,u=v[_],I=O-b[l]-v[A],m=O/2-b[l]/2+M,L=Tt(u,m,I),K=c;n.modifiersData[r]=(t={},t[K]=L,t.centerOffset=L-m,t)}}function Ma(e){var t=e.state,n=e.options,r=n.element,i=r===void 0?"[data-popper-arrow]":r;i!=null&&(typeof i=="string"&&(i=t.elements.popper.querySelector(i),!i)||Oi(t.elements.popper,i)&&(t.elements.arrow=i))}const Ca={name:"arrow",enabled:!0,phase:"main",fn:Ta,effect:Ma,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function gt(e){return e.split("-")[1]}var Ra={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Ia(e,t){var n=e.x,r=e.y,i=t.devicePixelRatio||1;return{x:pt(n*i)/i||0,y:pt(r*i)/i||0}}function Br(e){var t,n=e.popper,r=e.popperRect,i=e.placement,o=e.variation,a=e.offsets,s=e.position,c=e.gpuAcceleration,f=e.adaptive,l=e.roundOffsets,v=e.isFixed,b=a.x,_=b===void 0?0:b,A=a.y,S=A===void 0?0:A,h=typeof l=="function"?l({x:_,y:S}):{x:_,y:S};_=h.x,S=h.y;var E=a.hasOwnProperty("x"),O=a.hasOwnProperty("y"),M=ue,u=ce,I=window;if(f){var m=$t(n),L="clientHeight",K="clientWidth";if(m===fe(n)&&(m=We(n),De(m).position!=="static"&&s==="absolute"&&(L="scrollHeight",K="scrollWidth")),m=m,i===ce||(i===ue||i===ye)&&o===Dt){u=be;var P=v&&m===I&&I.visualViewport?I.visualViewport.height:m[L];S-=P-r.height,S*=c?1:-1}if(i===ue||(i===ce||i===be)&&o===Dt){M=ye;var H=v&&m===I&&I.visualViewport?I.visualViewport.width:m[K];_-=H-r.width,_*=c?1:-1}}var q=Object.assign({position:s},f&&Ra),z=l===!0?Ia({x:_,y:S},fe(n)):{x:_,y:S};if(_=z.x,S=z.y,c){var k;return Object.assign({},q,(k={},k[u]=O?"0":"",k[M]=E?"0":"",k.transform=(I.devicePixelRatio||1)<=1?"translate("+_+"px, "+S+"px)":"translate3d("+_+"px, "+S+"px, 0)",k))}return Object.assign({},q,(t={},t[u]=O?S+"px":"",t[M]=E?_+"px":"",t.transform="",t))}function Da(e){var t=e.state,n=e.options,r=n.gpuAcceleration,i=r===void 0?!0:r,o=n.adaptive,a=o===void 0?!0:o,s=n.roundOffsets,c=s===void 0?!0:s,f={placement:Te(t.placement),variation:gt(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:i,isFixed:t.options.strategy==="fixed"};t.modifiersData.popperOffsets!=null&&(t.styles.popper=Object.assign({},t.styles.popper,Br(Object.assign({},f,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:a,roundOffsets:c})))),t.modifiersData.arrow!=null&&(t.styles.arrow=Object.assign({},t.styles.arrow,Br(Object.assign({},f,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:c})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})}const Na={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:Da,data:{}};var Vt={passive:!0};function La(e){var t=e.state,n=e.instance,r=e.options,i=r.scroll,o=i===void 0?!0:i,a=r.resize,s=a===void 0?!0:a,c=fe(t.elements.popper),f=[].concat(t.scrollParents.reference,t.scrollParents.popper);return o&&f.forEach(function(l){l.addEventListener("scroll",n.update,Vt)}),s&&c.addEventListener("resize",n.update,Vt),function(){o&&f.forEach(function(l){l.removeEventListener("scroll",n.update,Vt)}),s&&c.removeEventListener("resize",n.update,Vt)}}const Pa={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:La,data:{}};var ka={left:"right",right:"left",bottom:"top",top:"bottom"};function en(e){return e.replace(/left|right|bottom|top/g,function(t){return ka[t]})}var Ba={start:"end",end:"start"};function $r(e){return e.replace(/start|end/g,function(t){return Ba[t]})}function ar(e){var t=fe(e),n=t.pageXOffset,r=t.pageYOffset;return{scrollLeft:n,scrollTop:r}}function sr(e){return ht(We(e)).left+ar(e).scrollLeft}function $a(e,t){var n=fe(e),r=We(e),i=n.visualViewport,o=r.clientWidth,a=r.clientHeight,s=0,c=0;if(i){o=i.width,a=i.height;var f=wi();(f||!f&&t==="fixed")&&(s=i.offsetLeft,c=i.offsetTop)}return{width:o,height:a,x:s+sr(e),y:c}}function ja(e){var t,n=We(e),r=ar(e),i=(t=e.ownerDocument)==null?void 0:t.body,o=Ze(n.scrollWidth,n.clientWidth,i?i.scrollWidth:0,i?i.clientWidth:0),a=Ze(n.scrollHeight,n.clientHeight,i?i.scrollHeight:0,i?i.clientHeight:0),s=-r.scrollLeft+sr(e),c=-r.scrollTop;return De(i||n).direction==="rtl"&&(s+=Ze(n.clientWidth,i?i.clientWidth:0)-o),{width:o,height:a,x:s,y:c}}function cr(e){var t=De(e),n=t.overflow,r=t.overflowX,i=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+i+r)}function Mi(e){return["html","body","#document"].indexOf(Me(e))>=0?e.ownerDocument.body:_e(e)&&cr(e)?e:Mi(fn(e))}function Mt(e,t){var n;t===void 0&&(t=[]);var r=Mi(e),i=r===((n=e.ownerDocument)==null?void 0:n.body),o=fe(r),a=i?[o].concat(o.visualViewport||[],cr(r)?r:[]):r,s=t.concat(a);return i?s:s.concat(Mt(fn(a)))}function kn(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function Ha(e,t){var n=ht(e,!1,t==="fixed");return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}function jr(e,t,n){return t===mi?kn($a(e,n)):it(t)?Ha(t,n):kn(ja(We(e)))}function Fa(e){var t=Mt(fn(e)),n=["absolute","fixed"].indexOf(De(e).position)>=0,r=n&&_e(e)?$t(e):e;return it(r)?t.filter(function(i){return it(i)&&Oi(i,r)&&Me(i)!=="body"}):[]}function Ua(e,t,n,r){var i=t==="clippingParents"?Fa(e):[].concat(t),o=[].concat(i,[n]),a=o[0],s=o.reduce(function(c,f){var l=jr(e,f,r);return c.top=Ze(l.top,c.top),c.right=rn(l.right,c.right),c.bottom=rn(l.bottom,c.bottom),c.left=Ze(l.left,c.left),c},jr(e,a,r));return s.width=s.right-s.left,s.height=s.bottom-s.top,s.x=s.left,s.y=s.top,s}function Ci(e){var t=e.reference,n=e.element,r=e.placement,i=r?Te(r):null,o=r?gt(r):null,a=t.x+t.width/2-n.width/2,s=t.y+t.height/2-n.height/2,c;switch(i){case ce:c={x:a,y:t.y-n.height};break;case be:c={x:a,y:t.y+t.height};break;case ye:c={x:t.x+t.width,y:s};break;case ue:c={x:t.x-n.width,y:s};break;default:c={x:t.x,y:t.y}}var f=i?or(i):null;if(f!=null){var l=f==="y"?"height":"width";switch(o){case dt:c[f]=c[f]-(t[l]/2-n[l]/2);break;case Dt:c[f]=c[f]+(t[l]/2-n[l]/2);break}}return c}function Nt(e,t){t===void 0&&(t={});var n=t,r=n.placement,i=r===void 0?e.placement:r,o=n.strategy,a=o===void 0?e.strategy:o,s=n.boundary,c=s===void 0?ua:s,f=n.rootBoundary,l=f===void 0?mi:f,v=n.elementContext,b=v===void 0?Et:v,_=n.altBoundary,A=_===void 0?!1:_,S=n.padding,h=S===void 0?0:S,E=Si(typeof h!="number"?h:Ti(h,Bt)),O=b===Et?la:Et,M=e.rects.popper,u=e.elements[A?O:b],I=Ua(it(u)?u:u.contextElement||We(e.elements.popper),c,l,a),m=ht(e.elements.reference),L=Ci({reference:m,element:M,strategy:"absolute",placement:i}),K=kn(Object.assign({},M,L)),P=b===Et?K:m,H={top:I.top-P.top+E.top,bottom:P.bottom-I.bottom+E.bottom,left:I.left-P.left+E.left,right:P.right-I.right+E.right},q=e.modifiersData.offset;if(b===Et&&q){var z=q[i];Object.keys(H).forEach(function(k){var U=[ye,be].indexOf(k)>=0?1:-1,Y=[ce,be].indexOf(k)>=0?"y":"x";H[k]+=z[Y]*U})}return H}function Wa(e,t){t===void 0&&(t={});var n=t,r=n.placement,i=n.boundary,o=n.rootBoundary,a=n.padding,s=n.flipVariations,c=n.allowedAutoPlacements,f=c===void 0?xi:c,l=gt(r),v=l?s?Pr:Pr.filter(function(A){return gt(A)===l}):Bt,b=v.filter(function(A){return f.indexOf(A)>=0});b.length===0&&(b=v);var _=b.reduce(function(A,S){return A[S]=Nt(e,{placement:S,boundary:i,rootBoundary:o,padding:a})[Te(S)],A},{});return Object.keys(_).sort(function(A,S){return _[A]-_[S]})}function Ka(e){if(Te(e)===nr)return[];var t=en(e);return[$r(e),t,$r(t)]}function za(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var i=n.mainAxis,o=i===void 0?!0:i,a=n.altAxis,s=a===void 0?!0:a,c=n.fallbackPlacements,f=n.padding,l=n.boundary,v=n.rootBoundary,b=n.altBoundary,_=n.flipVariations,A=_===void 0?!0:_,S=n.allowedAutoPlacements,h=t.options.placement,E=Te(h),O=E===h,M=c||(O||!A?[en(h)]:Ka(h)),u=[h].concat(M).reduce(function(se,G){return se.concat(Te(G)===nr?Wa(t,{placement:G,boundary:l,rootBoundary:v,padding:f,flipVariations:A,allowedAutoPlacements:S}):G)},[]),I=t.rects.reference,m=t.rects.popper,L=new Map,K=!0,P=u[0],H=0;H=0,Y=U?"width":"height",te=Nt(t,{placement:q,boundary:l,rootBoundary:v,altBoundary:b,padding:f}),p=U?k?ye:ue:k?be:ce;I[Y]>m[Y]&&(p=en(p));var y=en(p),C=[];if(o&&C.push(te[z]<=0),s&&C.push(te[p]<=0,te[y]<=0),C.every(function(se){return se})){P=q,K=!1;break}L.set(q,C)}if(K)for(var N=A?3:1,W=function(G){var Z=u.find(function(Ce){var de=L.get(Ce);if(de)return de.slice(0,G).every(function(Re){return Re})});if(Z)return P=Z,"break"},J=N;J>0;J--){var re=W(J);if(re==="break")break}t.placement!==P&&(t.modifiersData[r]._skip=!0,t.placement=P,t.reset=!0)}}const Va={name:"flip",enabled:!0,phase:"main",fn:za,requiresIfExists:["offset"],data:{_skip:!1}};function Hr(e,t,n){return n===void 0&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Fr(e){return[ce,ye,be,ue].some(function(t){return e[t]>=0})}function qa(e){var t=e.state,n=e.name,r=t.rects.reference,i=t.rects.popper,o=t.modifiersData.preventOverflow,a=Nt(t,{elementContext:"reference"}),s=Nt(t,{altBoundary:!0}),c=Hr(a,r),f=Hr(s,i,o),l=Fr(c),v=Fr(f);t.modifiersData[n]={referenceClippingOffsets:c,popperEscapeOffsets:f,isReferenceHidden:l,hasPopperEscaped:v},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":l,"data-popper-escaped":v})}const Ga={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:qa};function Xa(e,t,n){var r=Te(e),i=[ue,ce].indexOf(r)>=0?-1:1,o=typeof n=="function"?n(Object.assign({},t,{placement:e})):n,a=o[0],s=o[1];return a=a||0,s=(s||0)*i,[ue,ye].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}function Ya(e){var t=e.state,n=e.options,r=e.name,i=n.offset,o=i===void 0?[0,0]:i,a=xi.reduce(function(l,v){return l[v]=Xa(v,t.rects,o),l},{}),s=a[t.placement],c=s.x,f=s.y;t.modifiersData.popperOffsets!=null&&(t.modifiersData.popperOffsets.x+=c,t.modifiersData.popperOffsets.y+=f),t.modifiersData[r]=a}const Ja={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Ya};function Za(e){var t=e.state,n=e.name;t.modifiersData[n]=Ci({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})}const Qa={name:"popperOffsets",enabled:!0,phase:"read",fn:Za,data:{}};function es(e){return e==="x"?"y":"x"}function ts(e){var t=e.state,n=e.options,r=e.name,i=n.mainAxis,o=i===void 0?!0:i,a=n.altAxis,s=a===void 0?!1:a,c=n.boundary,f=n.rootBoundary,l=n.altBoundary,v=n.padding,b=n.tether,_=b===void 0?!0:b,A=n.tetherOffset,S=A===void 0?0:A,h=Nt(t,{boundary:c,rootBoundary:f,padding:v,altBoundary:l}),E=Te(t.placement),O=gt(t.placement),M=!O,u=or(E),I=es(u),m=t.modifiersData.popperOffsets,L=t.rects.reference,K=t.rects.popper,P=typeof S=="function"?S(Object.assign({},t.rects,{placement:t.placement})):S,H=typeof P=="number"?{mainAxis:P,altAxis:P}:Object.assign({mainAxis:0,altAxis:0},P),q=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,z={x:0,y:0};if(m){if(o){var k,U=u==="y"?ce:ue,Y=u==="y"?be:ye,te=u==="y"?"height":"width",p=m[u],y=p+h[U],C=p-h[Y],N=_?-K[te]/2:0,W=O===dt?L[te]:K[te],J=O===dt?-K[te]:-L[te],re=t.elements.arrow,se=_&&re?ir(re):{width:0,height:0},G=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:Ai(),Z=G[U],Ce=G[Y],de=Tt(0,L[te],se[te]),Re=M?L[te]/2-N-de-Z-H.mainAxis:W-de-Z-H.mainAxis,Oe=M?-L[te]/2+N+de+Ce+H.mainAxis:J+de+Ce+H.mainAxis,Le=t.elements.arrow&&$t(t.elements.arrow),st=Le?u==="y"?Le.clientTop||0:Le.clientLeft||0:0,ze=(k=q==null?void 0:q[u])!=null?k:0,Ie=p+Re-ze-st,Ve=p+Oe-ze,ie=Tt(_?rn(y,Ie):y,p,_?Ze(C,Ve):C);m[u]=ie,z[u]=ie-p}if(s){var qe,Pe=u==="x"?ce:ue,T=u==="x"?be:ye,pe=m[I],V=I==="y"?"height":"width",$=pe+h[Pe],he=pe-h[T],le=[ce,ue].indexOf(E)!==-1,ke=(qe=q==null?void 0:q[I])!=null?qe:0,Be=le?$:pe-L[V]-K[V]-ke+H.altAxis,g=le?pe+L[V]+K[V]-ke-H.altAxis:he,x=_&&le?Aa(Be,pe,g):Tt(_?Be:$,pe,_?g:he);m[I]=x,z[I]=x-pe}t.modifiersData[r]=z}}const ns={name:"preventOverflow",enabled:!0,phase:"main",fn:ts,requiresIfExists:["offset"]};function rs(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}function is(e){return e===fe(e)||!_e(e)?ar(e):rs(e)}function os(e){var t=e.getBoundingClientRect(),n=pt(t.width)/e.offsetWidth||1,r=pt(t.height)/e.offsetHeight||1;return n!==1||r!==1}function as(e,t,n){n===void 0&&(n=!1);var r=_e(t),i=_e(t)&&os(t),o=We(t),a=ht(e,i,n),s={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(r||!r&&!n)&&((Me(t)!=="body"||cr(o))&&(s=is(t)),_e(t)?(c=ht(t,!0),c.x+=t.clientLeft,c.y+=t.clientTop):o&&(c.x=sr(o))),{x:a.left+s.scrollLeft-c.x,y:a.top+s.scrollTop-c.y,width:a.width,height:a.height}}function ss(e){var t=new Map,n=new Set,r=[];e.forEach(function(o){t.set(o.name,o)});function i(o){n.add(o.name);var a=[].concat(o.requires||[],o.requiresIfExists||[]);a.forEach(function(s){if(!n.has(s)){var c=t.get(s);c&&i(c)}}),r.push(o)}return e.forEach(function(o){n.has(o.name)||i(o)}),r}function cs(e){var t=ss(e);return ma.reduce(function(n,r){return n.concat(t.filter(function(i){return i.phase===r}))},[])}function us(e){var t;return function(){return t||(t=new Promise(function(n){Promise.resolve().then(function(){t=void 0,n(e())})})),t}}function ls(e){var t=e.reduce(function(n,r){var i=n[r.name];return n[r.name]=i?Object.assign({},i,r,{options:Object.assign({},i.options,r.options),data:Object.assign({},i.data,r.data)}):r,n},{});return Object.keys(t).map(function(n){return t[n]})}var Ur={placement:"bottom",modifiers:[],strategy:"absolute"};function Wr(){for(var e=arguments.length,t=new Array(e),n=0;n-1}function Li(e,t){return typeof e=="function"?e.apply(void 0,t):e}function Kr(e,t){if(t===0)return e;var n;return function(r){clearTimeout(n),n=setTimeout(function(){e(r)},t)}}function vs(e){return e.split(/\s+/).filter(Boolean)}function ft(e){return[].concat(e)}function zr(e,t){e.indexOf(t)===-1&&e.push(t)}function _s(e){return e.filter(function(t,n){return e.indexOf(t)===n})}function bs(e){return e.split("-")[0]}function on(e){return[].slice.call(e)}function Vr(e){return Object.keys(e).reduce(function(t,n){return e[n]!==void 0&&(t[n]=e[n]),t},{})}function Ct(){return document.createElement("div")}function dn(e){return["Element","Fragment"].some(function(t){return ur(e,t)})}function ys(e){return ur(e,"NodeList")}function ms(e){return ur(e,"MouseEvent")}function xs(e){return!!(e&&e._tippy&&e._tippy.reference===e)}function Es(e){return dn(e)?[e]:ys(e)?on(e):Array.isArray(e)?e:on(document.querySelectorAll(e))}function Sn(e,t){e.forEach(function(n){n&&(n.style.transitionDuration=t+"ms")})}function qr(e,t){e.forEach(function(n){n&&n.setAttribute("data-state",t)})}function ws(e){var t,n=ft(e),r=n[0];return r!=null&&(t=r.ownerDocument)!=null&&t.body?r.ownerDocument:document}function Os(e,t){var n=t.clientX,r=t.clientY;return e.every(function(i){var o=i.popperRect,a=i.popperState,s=i.props,c=s.interactiveBorder,f=bs(a.placement),l=a.modifiersData.offset;if(!l)return!0;var v=f==="bottom"?l.top.y:0,b=f==="top"?l.bottom.y:0,_=f==="right"?l.left.x:0,A=f==="left"?l.right.x:0,S=o.top-r+v>c,h=r-o.bottom-b>c,E=o.left-n+_>c,O=n-o.right-A>c;return S||h||E||O})}function Tn(e,t,n){var r=t+"EventListener";["transitionend","webkitTransitionEnd"].forEach(function(i){e[r](i,n)})}function Gr(e,t){for(var n=t;n;){var r;if(e.contains(n))return!0;n=n.getRootNode==null||(r=n.getRootNode())==null?void 0:r.host}return!1}var Se={isTouch:!1},Xr=0;function As(){Se.isTouch||(Se.isTouch=!0,window.performance&&document.addEventListener("mousemove",Pi))}function Pi(){var e=performance.now();e-Xr<20&&(Se.isTouch=!1,document.removeEventListener("mousemove",Pi)),Xr=e}function Ss(){var e=document.activeElement;if(xs(e)){var t=e._tippy;e.blur&&!t.state.isVisible&&e.blur()}}function Ts(){document.addEventListener("touchstart",As,Xe),window.addEventListener("blur",Ss)}var Ms=typeof window<"u"&&typeof document<"u",Cs=Ms?!!window.msCrypto:!1,Rs={animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},Is={allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999},xe=Object.assign({appendTo:Ni,aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},Rs,Is),Ds=Object.keys(xe),Ns=function(t){var n=Object.keys(t);n.forEach(function(r){xe[r]=t[r]})};function ki(e){var t=e.plugins||[],n=t.reduce(function(r,i){var o=i.name,a=i.defaultValue;if(o){var s;r[o]=e[o]!==void 0?e[o]:(s=xe[o])!=null?s:a}return r},{});return Object.assign({},e,n)}function Ls(e,t){var n=t?Object.keys(ki(Object.assign({},xe,{plugins:t}))):Ds,r=n.reduce(function(i,o){var a=(e.getAttribute("data-tippy-"+o)||"").trim();if(!a)return i;if(o==="content")i[o]=a;else try{i[o]=JSON.parse(a)}catch{i[o]=a}return i},{});return r}function Yr(e,t){var n=Object.assign({},t,{content:Li(t.content,[e])},t.ignoreAttributes?{}:Ls(e,t.plugins));return n.aria=Object.assign({},xe.aria,n.aria),n.aria={expanded:n.aria.expanded==="auto"?t.interactive:n.aria.expanded,content:n.aria.content==="auto"?t.interactive?null:"describedby":n.aria.content},n}var Ps=function(){return"innerHTML"};function Bn(e,t){e[Ps()]=t}function Jr(e){var t=Ct();return e===!0?t.className=Ii:(t.className=Di,dn(e)?t.appendChild(e):Bn(t,e)),t}function Zr(e,t){dn(t.content)?(Bn(e,""),e.appendChild(t.content)):typeof t.content!="function"&&(t.allowHTML?Bn(e,t.content):e.textContent=t.content)}function $n(e){var t=e.firstElementChild,n=on(t.children);return{box:t,content:n.find(function(r){return r.classList.contains(Ri)}),arrow:n.find(function(r){return r.classList.contains(Ii)||r.classList.contains(Di)}),backdrop:n.find(function(r){return r.classList.contains(gs)})}}function Bi(e){var t=Ct(),n=Ct();n.className=hs,n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=Ct();r.className=Ri,r.setAttribute("data-state","hidden"),Zr(r,e.props),t.appendChild(n),n.appendChild(r),i(e.props,e.props);function i(o,a){var s=$n(t),c=s.box,f=s.content,l=s.arrow;a.theme?c.setAttribute("data-theme",a.theme):c.removeAttribute("data-theme"),typeof a.animation=="string"?c.setAttribute("data-animation",a.animation):c.removeAttribute("data-animation"),a.inertia?c.setAttribute("data-inertia",""):c.removeAttribute("data-inertia"),c.style.maxWidth=typeof a.maxWidth=="number"?a.maxWidth+"px":a.maxWidth,a.role?c.setAttribute("role",a.role):c.removeAttribute("role"),(o.content!==a.content||o.allowHTML!==a.allowHTML)&&Zr(f,e.props),a.arrow?l?o.arrow!==a.arrow&&(c.removeChild(l),c.appendChild(Jr(a.arrow))):c.appendChild(Jr(a.arrow)):l&&c.removeChild(l)}return{popper:t,onUpdate:i}}Bi.$$tippy=!0;var ks=1,qt=[],Mn=[];function Bs(e,t){var n=Yr(e,Object.assign({},xe,ki(Vr(t)))),r,i,o,a=!1,s=!1,c=!1,f=!1,l,v,b,_=[],A=Kr(Ie,n.interactiveDebounce),S,h=ks++,E=null,O=_s(n.plugins),M={isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},u={id:h,reference:e,popper:Ct(),popperInstance:E,props:n,state:M,plugins:O,clearDelayTimeouts:Be,setProps:g,setContent:x,show:D,hide:j,hideWithInteractivity:ne,enable:le,disable:ke,unmount:me,destroy:En};if(!n.render)return u;var I=n.render(u),m=I.popper,L=I.onUpdate;m.setAttribute("data-tippy-root",""),m.id="tippy-"+u.id,u.popper=m,e._tippy=u,m._tippy=u;var K=O.map(function(d){return d.fn(u)}),P=e.hasAttribute("aria-expanded");return Le(),N(),p(),y("onCreate",[u]),n.showOnCreate&&$(),m.addEventListener("mouseenter",function(){u.props.interactive&&u.state.isVisible&&u.clearDelayTimeouts()}),m.addEventListener("mouseleave",function(){u.props.interactive&&u.props.trigger.indexOf("mouseenter")>=0&&U().addEventListener("mousemove",A)}),u;function H(){var d=u.props.touch;return Array.isArray(d)?d:[d,0]}function q(){return H()[0]==="hold"}function z(){var d;return!!((d=u.props.render)!=null&&d.$$tippy)}function k(){return S||e}function U(){var d=k().parentNode;return d?ws(d):document}function Y(){return $n(m)}function te(d){return u.state.isMounted&&!u.state.isVisible||Se.isTouch||l&&l.type==="focus"?0:An(u.props.delay,d?0:1,xe.delay)}function p(d){d===void 0&&(d=!1),m.style.pointerEvents=u.props.interactive&&!d?"":"none",m.style.zIndex=""+u.props.zIndex}function y(d,w,R){if(R===void 0&&(R=!0),K.forEach(function(B){B[d]&&B[d].apply(B,w)}),R){var F;(F=u.props)[d].apply(F,w)}}function C(){var d=u.props.aria;if(d.content){var w="aria-"+d.content,R=m.id,F=ft(u.props.triggerTarget||e);F.forEach(function(B){var oe=B.getAttribute(w);if(u.state.isVisible)B.setAttribute(w,oe?oe+" "+R:R);else{var ge=oe&&oe.replace(R,"").trim();ge?B.setAttribute(w,ge):B.removeAttribute(w)}})}}function N(){if(!(P||!u.props.aria.expanded)){var d=ft(u.props.triggerTarget||e);d.forEach(function(w){u.props.interactive?w.setAttribute("aria-expanded",u.state.isVisible&&w===k()?"true":"false"):w.removeAttribute("aria-expanded")})}}function W(){U().removeEventListener("mousemove",A),qt=qt.filter(function(d){return d!==A})}function J(d){if(!(Se.isTouch&&(c||d.type==="mousedown"))){var w=d.composedPath&&d.composedPath()[0]||d.target;if(!(u.props.interactive&&Gr(m,w))){if(ft(u.props.triggerTarget||e).some(function(R){return Gr(R,w)})){if(Se.isTouch||u.state.isVisible&&u.props.trigger.indexOf("click")>=0)return}else y("onClickOutside",[u,d]);u.props.hideOnClick===!0&&(u.clearDelayTimeouts(),u.hide(),s=!0,setTimeout(function(){s=!1}),u.state.isMounted||Z())}}}function re(){c=!0}function se(){c=!1}function G(){var d=U();d.addEventListener("mousedown",J,!0),d.addEventListener("touchend",J,Xe),d.addEventListener("touchstart",se,Xe),d.addEventListener("touchmove",re,Xe)}function Z(){var d=U();d.removeEventListener("mousedown",J,!0),d.removeEventListener("touchend",J,Xe),d.removeEventListener("touchstart",se,Xe),d.removeEventListener("touchmove",re,Xe)}function Ce(d,w){Re(d,function(){!u.state.isVisible&&m.parentNode&&m.parentNode.contains(m)&&w()})}function de(d,w){Re(d,w)}function Re(d,w){var R=Y().box;function F(B){B.target===R&&(Tn(R,"remove",F),w())}if(d===0)return w();Tn(R,"remove",v),Tn(R,"add",F),v=F}function Oe(d,w,R){R===void 0&&(R=!1);var F=ft(u.props.triggerTarget||e);F.forEach(function(B){B.addEventListener(d,w,R),_.push({node:B,eventType:d,handler:w,options:R})})}function Le(){q()&&(Oe("touchstart",ze,{passive:!0}),Oe("touchend",Ve,{passive:!0})),vs(u.props.trigger).forEach(function(d){if(d!=="manual")switch(Oe(d,ze),d){case"mouseenter":Oe("mouseleave",Ve);break;case"focus":Oe(Cs?"focusout":"blur",ie);break;case"focusin":Oe("focusout",ie);break}})}function st(){_.forEach(function(d){var w=d.node,R=d.eventType,F=d.handler,B=d.options;w.removeEventListener(R,F,B)}),_=[]}function ze(d){var w,R=!1;if(!(!u.state.isEnabled||qe(d)||s)){var F=((w=l)==null?void 0:w.type)==="focus";l=d,S=d.currentTarget,N(),!u.state.isVisible&&ms(d)&&qt.forEach(function(B){return B(d)}),d.type==="click"&&(u.props.trigger.indexOf("mouseenter")<0||a)&&u.props.hideOnClick!==!1&&u.state.isVisible?R=!0:$(d),d.type==="click"&&(a=!R),R&&!F&&he(d)}}function Ie(d){var w=d.target,R=k().contains(w)||m.contains(w);if(!(d.type==="mousemove"&&R)){var F=V().concat(m).map(function(B){var oe,ge=B._tippy,ct=(oe=ge.popperInstance)==null?void 0:oe.state;return ct?{popperRect:B.getBoundingClientRect(),popperState:ct,props:n}:null}).filter(Boolean);Os(F,d)&&(W(),he(d))}}function Ve(d){var w=qe(d)||u.props.trigger.indexOf("click")>=0&&a;if(!w){if(u.props.interactive){u.hideWithInteractivity(d);return}he(d)}}function ie(d){u.props.trigger.indexOf("focusin")<0&&d.target!==k()||u.props.interactive&&d.relatedTarget&&m.contains(d.relatedTarget)||he(d)}function qe(d){return Se.isTouch?q()!==d.type.indexOf("touch")>=0:!1}function Pe(){T();var d=u.props,w=d.popperOptions,R=d.placement,F=d.offset,B=d.getReferenceClientRect,oe=d.moveTransition,ge=z()?$n(m).arrow:null,ct=B?{getBoundingClientRect:B,contextElement:B.contextElement||k()}:e,Lr={name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(Kt){var ut=Kt.state;if(z()){var ca=Y(),On=ca.box;["placement","reference-hidden","escaped"].forEach(function(zt){zt==="placement"?On.setAttribute("data-placement",ut.placement):ut.attributes.popper["data-popper-"+zt]?On.setAttribute("data-"+zt,""):On.removeAttribute("data-"+zt)}),ut.attributes.popper={}}}},Ge=[{name:"offset",options:{offset:F}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!oe}},Lr];z()&&ge&&Ge.push({name:"arrow",options:{element:ge,padding:3}}),Ge.push.apply(Ge,(w==null?void 0:w.modifiers)||[]),u.popperInstance=ps(ct,m,Object.assign({},w,{placement:R,onFirstUpdate:b,modifiers:Ge}))}function T(){u.popperInstance&&(u.popperInstance.destroy(),u.popperInstance=null)}function pe(){var d=u.props.appendTo,w,R=k();u.props.interactive&&d===Ni||d==="parent"?w=R.parentNode:w=Li(d,[R]),w.contains(m)||w.appendChild(m),u.state.isMounted=!0,Pe()}function V(){return on(m.querySelectorAll("[data-tippy-root]"))}function $(d){u.clearDelayTimeouts(),d&&y("onTrigger",[u,d]),G();var w=te(!0),R=H(),F=R[0],B=R[1];Se.isTouch&&F==="hold"&&B&&(w=B),w?r=setTimeout(function(){u.show()},w):u.show()}function he(d){if(u.clearDelayTimeouts(),y("onUntrigger",[u,d]),!u.state.isVisible){Z();return}if(!(u.props.trigger.indexOf("mouseenter")>=0&&u.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(d.type)>=0&&a)){var w=te(!1);w?i=setTimeout(function(){u.state.isVisible&&u.hide()},w):o=requestAnimationFrame(function(){u.hide()})}}function le(){u.state.isEnabled=!0}function ke(){u.hide(),u.state.isEnabled=!1}function Be(){clearTimeout(r),clearTimeout(i),cancelAnimationFrame(o)}function g(d){if(!u.state.isDestroyed){y("onBeforeUpdate",[u,d]),st();var w=u.props,R=Yr(e,Object.assign({},w,Vr(d),{ignoreAttributes:!0}));u.props=R,Le(),w.interactiveDebounce!==R.interactiveDebounce&&(W(),A=Kr(Ie,R.interactiveDebounce)),w.triggerTarget&&!R.triggerTarget?ft(w.triggerTarget).forEach(function(F){F.removeAttribute("aria-expanded")}):R.triggerTarget&&e.removeAttribute("aria-expanded"),N(),p(),L&&L(w,R),u.popperInstance&&(Pe(),V().forEach(function(F){requestAnimationFrame(F._tippy.popperInstance.forceUpdate)})),y("onAfterUpdate",[u,d])}}function x(d){u.setProps({content:d})}function D(){var d=u.state.isVisible,w=u.state.isDestroyed,R=!u.state.isEnabled,F=Se.isTouch&&!u.props.touch,B=An(u.props.duration,0,xe.duration);if(!(d||w||R||F)&&!k().hasAttribute("disabled")&&(y("onShow",[u],!1),u.props.onShow(u)!==!1)){if(u.state.isVisible=!0,z()&&(m.style.visibility="visible"),p(),G(),u.state.isMounted||(m.style.transition="none"),z()){var oe=Y(),ge=oe.box,ct=oe.content;Sn([ge,ct],0)}b=function(){var Ge;if(!(!u.state.isVisible||f)){if(f=!0,m.offsetHeight,m.style.transition=u.props.moveTransition,z()&&u.props.animation){var wn=Y(),Kt=wn.box,ut=wn.content;Sn([Kt,ut],B),qr([Kt,ut],"visible")}C(),N(),zr(Mn,u),(Ge=u.popperInstance)==null||Ge.forceUpdate(),y("onMount",[u]),u.props.animation&&z()&&de(B,function(){u.state.isShown=!0,y("onShown",[u])})}},pe()}}function j(){var d=!u.state.isVisible,w=u.state.isDestroyed,R=!u.state.isEnabled,F=An(u.props.duration,1,xe.duration);if(!(d||w||R)&&(y("onHide",[u],!1),u.props.onHide(u)!==!1)){if(u.state.isVisible=!1,u.state.isShown=!1,f=!1,a=!1,z()&&(m.style.visibility="hidden"),W(),Z(),p(!0),z()){var B=Y(),oe=B.box,ge=B.content;u.props.animation&&(Sn([oe,ge],F),qr([oe,ge],"hidden"))}C(),N(),u.props.animation?z()&&Ce(F,u.unmount):u.unmount()}}function ne(d){U().addEventListener("mousemove",A),zr(qt,A),A(d)}function me(){u.state.isVisible&&u.hide(),u.state.isMounted&&(T(),V().forEach(function(d){d._tippy.unmount()}),m.parentNode&&m.parentNode.removeChild(m),Mn=Mn.filter(function(d){return d!==u}),u.state.isMounted=!1,y("onHidden",[u]))}function En(){u.state.isDestroyed||(u.clearDelayTimeouts(),u.unmount(),st(),delete e._tippy,u.state.isDestroyed=!0,y("onDestroy",[u]))}}function jt(e,t){t===void 0&&(t={});var n=xe.plugins.concat(t.plugins||[]);Ts();var r=Object.assign({},t,{plugins:n}),i=Es(e),o=i.reduce(function(a,s){var c=s&&Bs(s,r);return c&&a.push(c),a},[]);return dn(e)?o[0]:o}jt.defaultProps=xe;jt.setDefaultProps=Ns;jt.currentInput=Se;Object.assign({},Ei,{effect:function(t){var n=t.state,r={popper:{position:n.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};Object.assign(n.elements.popper.style,r.popper),n.styles=r,n.elements.arrow&&Object.assign(n.elements.arrow.style,r.arrow)}});jt.setDefaultProps({render:Bi});var jn=!1,Hn=!1,Qe=[],Fn=-1;function $s(e){js(e)}function js(e){Qe.includes(e)||Qe.push(e),Hs()}function $i(e){let t=Qe.indexOf(e);t!==-1&&t>Fn&&Qe.splice(t,1)}function Hs(){!Hn&&!jn&&(jn=!0,queueMicrotask(Fs))}function Fs(){jn=!1,Hn=!0;for(let e=0;ee.effect(t,{scheduler:n=>{Un?$s(n):n()}}),ji=e.raw}function Qr(e){ot=e}function Ks(e){let t=()=>{};return[r=>{let i=ot(r);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),mt(i))},i},()=>{t()}]}function Hi(e,t){let n=!0,r,i=ot(()=>{let o=e();JSON.stringify(o),n?r=o:queueMicrotask(()=>{t(o,r),r=o}),n=!1});return()=>mt(i)}var Fi=[],Ui=[],Wi=[];function zs(e){Wi.push(e)}function lr(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Ui.push(t))}function Ki(e){Fi.push(e)}function zi(e,t,n){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(n)}function Vi(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([n,r])=>{(t===void 0||t.includes(n))&&(r.forEach(i=>i()),delete e._x_attributeCleanups[n])})}function Vs(e){if(e._x_cleanups)for(;e._x_cleanups.length;)e._x_cleanups.pop()()}var fr=new MutationObserver(gr),dr=!1;function pr(){fr.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),dr=!0}function qi(){qs(),fr.disconnect(),dr=!1}var wt=[];function qs(){let e=fr.takeRecords();wt.push(()=>e.length>0&&gr(e));let t=wt.length;queueMicrotask(()=>{if(wt.length===t)for(;wt.length>0;)wt.shift()()})}function ee(e){if(!dr)return e();qi();let t=e();return pr(),t}var hr=!1,an=[];function Gs(){hr=!0}function Xs(){hr=!1,gr(an),an=[]}function gr(e){if(hr){an=an.concat(e);return}let t=new Set,n=new Set,r=new Map,i=new Map;for(let o=0;oa.nodeType===1&&t.add(a)),e[o].removedNodes.forEach(a=>a.nodeType===1&&n.add(a))),e[o].type==="attributes")){let a=e[o].target,s=e[o].attributeName,c=e[o].oldValue,f=()=>{r.has(a)||r.set(a,[]),r.get(a).push({name:s,value:a.getAttribute(s)})},l=()=>{i.has(a)||i.set(a,[]),i.get(a).push(s)};a.hasAttribute(s)&&c===null?f():a.hasAttribute(s)?(l(),f()):l()}i.forEach((o,a)=>{Vi(a,o)}),r.forEach((o,a)=>{Fi.forEach(s=>s(a,o))});for(let o of n)t.has(o)||Ui.forEach(a=>a(o));t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)n.has(o)||o.isConnected&&(delete o._x_ignoreSelf,delete o._x_ignore,Wi.forEach(a=>a(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,n=null,r=null,i=null}function Gi(e){return Ft(vt(e))}function Ht(e,t,n){return e._x_dataStack=[t,...vt(n||e)],()=>{e._x_dataStack=e._x_dataStack.filter(r=>r!==t)}}function vt(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?vt(e.host):e.parentNode?vt(e.parentNode):[]}function Ft(e){return new Proxy({objects:e},Ys)}var Ys={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(n=>Object.prototype.hasOwnProperty.call(n,t)||Reflect.has(n,t))},get({objects:e},t,n){return t=="toJSON"?Js:Reflect.get(e.find(r=>Reflect.has(r,t))||{},t,n)},set({objects:e},t,n,r){const i=e.find(a=>Object.prototype.hasOwnProperty.call(a,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o!=null&&o.set&&(o!=null&&o.get)?Reflect.set(i,t,n,r):Reflect.set(i,t,n)}};function Js(){return Reflect.ownKeys(this).reduce((t,n)=>(t[n]=Reflect.get(this,n),t),{})}function Xi(e){let t=r=>typeof r=="object"&&!Array.isArray(r)&&r!==null,n=(r,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(r)).forEach(([o,{value:a,enumerable:s}])=>{if(s===!1||a===void 0||typeof a=="object"&&a!==null&&a.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof a=="object"&&a!==null&&a._x_interceptor?r[o]=a.initialize(e,c,o):t(a)&&a!==r&&!(a instanceof Element)&&n(a,c)})};return n(e)}function Yi(e,t=()=>{}){let n={initialValue:void 0,_x_interceptor:!0,initialize(r,i,o){return e(this.initialValue,()=>Zs(r,i),a=>Wn(r,i,a),i,o)}};return t(n),r=>{if(typeof r=="object"&&r!==null&&r._x_interceptor){let i=n.initialize.bind(n);n.initialize=(o,a,s)=>{let c=r.initialize(o,a,s);return n.initialValue=c,i(o,a,s)}}else n.initialValue=r;return n}}function Zs(e,t){return t.split(".").reduce((n,r)=>n[r],e)}function Wn(e,t,n){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=n;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),Wn(e[t[0]],t.slice(1),n)}}var Ji={};function we(e,t){Ji[e]=t}function Kn(e,t){return Object.entries(Ji).forEach(([n,r])=>{let i=null;function o(){if(i)return i;{let[a,s]=ro(t);return i={interceptor:Yi,...a},lr(t,s),i}}Object.defineProperty(e,`$${n}`,{get(){return r(t,o())},enumerable:!1})}),e}function Qs(e,t,n,...r){try{return n(...r)}catch(i){Lt(i,e,t)}}function Lt(e,t,n=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:n}),console.warn(`Alpine Expression Error: ${e.message} + +${n?'Expression: "'+n+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var tn=!0;function Zi(e){let t=tn;tn=!1;let n=e();return tn=t,n}function et(e,t,n={}){let r;return ae(e,t)(i=>r=i,n),r}function ae(...e){return Qi(...e)}var Qi=eo;function ec(e){Qi=e}function eo(e,t){let n={};Kn(n,e);let r=[n,...vt(e)],i=typeof t=="function"?tc(r,t):rc(r,t,e);return Qs.bind(null,e,t,i)}function tc(e,t){return(n=()=>{},{scope:r={},params:i=[]}={})=>{let o=t.apply(Ft([r,...e]),i);sn(n,o)}}var Cn={};function nc(e,t){if(Cn[e])return Cn[e];let n=Object.getPrototypeOf(async function(){}).constructor,r=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let a=new n(["__self","scope"],`with (scope) { __self.result = ${r} }; __self.finished = true; return __self.result;`);return Object.defineProperty(a,"name",{value:`[Alpine] ${e}`}),a}catch(a){return Lt(a,t,e),Promise.resolve()}})();return Cn[e]=o,o}function rc(e,t,n){let r=nc(t,n);return(i=()=>{},{scope:o={},params:a=[]}={})=>{r.result=void 0,r.finished=!1;let s=Ft([o,...e]);if(typeof r=="function"){let c=r(r,s).catch(f=>Lt(f,n,t));r.finished?(sn(i,r.result,s,a,n),r.result=void 0):c.then(f=>{sn(i,f,s,a,n)}).catch(f=>Lt(f,n,t)).finally(()=>r.result=void 0)}}}function sn(e,t,n,r,i){if(tn&&typeof t=="function"){let o=t.apply(n,r);o instanceof Promise?o.then(a=>sn(e,a,n,r)).catch(a=>Lt(a,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var vr="x-";function xt(e=""){return vr+e}function ic(e){vr=e}var cn={};function Q(e,t){return cn[e]=t,{before(n){if(!cn[n]){console.warn(String.raw`Cannot find directive \`${n}\`. \`${e}\` will use the default order of execution`);return}const r=Je.indexOf(n);Je.splice(r>=0?r:Je.indexOf("DEFAULT"),0,e)}}}function oc(e){return Object.keys(cn).includes(e)}function _r(e,t,n){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([s,c])=>({name:s,value:c})),a=to(o);o=o.map(s=>a.find(c=>c.name===s.name)?{name:`x-bind:${s.name}`,value:`"${s.value}"`}:s),t=t.concat(o)}let r={};return t.map(ao((o,a)=>r[o]=a)).filter(co).map(cc(r,n)).sort(uc).map(o=>sc(e,o))}function to(e){return Array.from(e).map(ao()).filter(t=>!co(t))}var zn=!1,St=new Map,no=Symbol();function ac(e){zn=!0;let t=Symbol();no=t,St.set(t,[]);let n=()=>{for(;St.get(t).length;)St.get(t).shift()();St.delete(t)},r=()=>{zn=!1,n()};e(n),r()}function ro(e){let t=[],n=s=>t.push(s),[r,i]=Ks(e);return t.push(i),[{Alpine:Wt,effect:r,cleanup:n,evaluateLater:ae.bind(ae,e),evaluate:et.bind(et,e)},()=>t.forEach(s=>s())]}function sc(e,t){let n=()=>{},r=cn[t.type]||n,[i,o]=ro(e);zi(e,t.original,o);let a=()=>{e._x_ignore||e._x_ignoreSelf||(r.inline&&r.inline(e,t,i),r=r.bind(r,e,t,i),zn?St.get(no).push(r):r())};return a.runCleanups=o,a}var io=(e,t)=>({name:n,value:r})=>(n.startsWith(e)&&(n=n.replace(e,t)),{name:n,value:r}),oo=e=>e;function ao(e=()=>{}){return({name:t,value:n})=>{let{name:r,value:i}=so.reduce((o,a)=>a(o),{name:t,value:n});return r!==t&&e(r,t),{name:r,value:i}}}var so=[];function br(e){so.push(e)}function co({name:e}){return uo().test(e)}var uo=()=>new RegExp(`^${vr}([^:^.]+)\\b`);function cc(e,t){return({name:n,value:r})=>{let i=n.match(uo()),o=n.match(/:([a-zA-Z0-9\-_:]+)/),a=n.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],s=t||e[n]||n;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:a.map(c=>c.replace(".","")),expression:r,original:s}}}var Vn="DEFAULT",Je=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",Vn,"teleport"];function uc(e,t){let n=Je.indexOf(e.type)===-1?Vn:e.type,r=Je.indexOf(t.type)===-1?Vn:t.type;return Je.indexOf(n)-Je.indexOf(r)}function Rt(e,t,n={}){e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0,cancelable:!0}))}function He(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>He(i,t));return}let n=!1;if(t(e,()=>n=!0),n)return;let r=e.firstElementChild;for(;r;)He(r,t),r=r.nextElementSibling}function ve(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var ei=!1;function lc(){ei&&ve("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),ei=!0,document.body||ve("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `