diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2715bbff..5471d626 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: if: "!contains(github.event.head_commit.message, '[ci skip]')" strategy: matrix: - php: ['8.2', '8.3', '8.4', '8.5'] + php: ['8.3', '8.4', '8.5'] steps: - name: Checkout the project diff --git a/.gitignore b/.gitignore index 0d43e670..1e6e0ae9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ phpunit.xml /composer.lock /coverage +/scripts/.laravel-version /vendor diff --git a/composer.json b/composer.json index 1445cd2d..16184796 100644 --- a/composer.json +++ b/composer.json @@ -42,34 +42,34 @@ } }, "require": { - "php": ">=8.2", + "php": ">=8.3", "ext-json": "*", "ext-mbstring": "*", "dragonmantank/cron-expression": "^3.4", "guzzlehttp/guzzle": "^7.8", - "illuminate/auth": "^12.0", - "illuminate/cache": "^12.0", - "illuminate/config": "^12.0", - "illuminate/console": "^12.0", - "illuminate/container": "^12.0", - "illuminate/contracts": "^12.0", - "illuminate/cookie": "^12.0", - "illuminate/database": "^12.0", - "illuminate/encryption": "^12.0", - "illuminate/events": "^12.0", - "illuminate/filesystem": "^12.0", - "illuminate/hashing": "^12.0", - "illuminate/http": "^12.0", - "illuminate/log": "^12.0", - "illuminate/pagination": "^12.0", - "illuminate/queue": "^12.0", - "illuminate/routing": "^12.0", - "illuminate/support": "^12.0", - "illuminate/testing": "^12.0", - "illuminate/validation": "^12.0", - "illuminate/view": "^12.0", + "illuminate/auth": "^13.0", + "illuminate/cache": "^13.0", + "illuminate/config": "^13.0", + "illuminate/console": "^13.0", + "illuminate/container": "^13.0", + "illuminate/contracts": "^13.0", + "illuminate/cookie": "^13.0", + "illuminate/database": "^13.0", + "illuminate/encryption": "^13.0", + "illuminate/events": "^13.0", + "illuminate/filesystem": "^13.0", + "illuminate/hashing": "^13.0", + "illuminate/http": "^13.0", + "illuminate/log": "^13.0", + "illuminate/pagination": "^13.0", + "illuminate/queue": "^13.0", + "illuminate/routing": "^13.0", + "illuminate/support": "^13.0", + "illuminate/testing": "^13.0", + "illuminate/validation": "^13.0", + "illuminate/view": "^13.0", "laravel/prompts": "^0.3.0", - "laravel/serializable-closure": "^1.3|^2.0", + "laravel/serializable-closure": "^2.0", "league/flysystem": "^3.26", "ramsey/uuid": "^4.7", "roots/support": "^1.0", @@ -80,10 +80,8 @@ "require-dev": { "laravel/pint": "^1.15", "mockery/mockery": "^1.6", - "pestphp/pest": "^3.0", + "pestphp/pest": "^4.0", "phpcompatibility/php-compatibility": "^9.3", - "roave/security-advisories": "dev-master", - "spatie/laravel-ignition": "^2.5", "spatie/pest-plugin-snapshots": "^2.3", "spatie/temporary-directory": "^2.2", "tmarsteel/mockery-callable-mock": "^2.1", diff --git a/config/app.php b/config/app.php index cf4e1f0a..b89ec6bd 100755 --- a/config/app.php +++ b/config/app.php @@ -130,7 +130,7 @@ 'previous_keys' => [ ...array_filter( - explode(',', env('APP_PREVIOUS_KEYS', '')) + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) ), ], diff --git a/config/cache.php b/config/cache.php index d13a6312..c048c857 100755 --- a/config/cache.php +++ b/config/cache.php @@ -27,7 +27,8 @@ | same cache driver to group types of items stored in your caches. | | Supported drivers: "array", "database", "file", "memcached", - | "redis", "dynamodb", "octane", "null" + | "redis", "dynamodb", "octane", "null", + | "failover" | */ @@ -103,6 +104,19 @@ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + + /* + |-------------------------------------------------------------------------- + | Serializable Classes + |-------------------------------------------------------------------------- + | + | This value determines the classes that can be unserialized from cache + | storage. By default, no PHP classes will be unserialized from your + | cache to prevent gadget chain attacks if your APP_KEY is leaked. + | + */ + + 'serializable_classes' => false, ]; diff --git a/config/database.php b/config/database.php index 316bacfd..550cf23b 100755 --- a/config/database.php +++ b/config/database.php @@ -37,11 +37,11 @@ 'url' => env('DB_URL'), 'database' => env('DB_DATABASE', database_path('database.sqlite')), 'prefix' => '', - 'prefix_indexes' => null, 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 'busy_timeout' => null, 'journal_mode' => null, 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', ], 'wordpress' => [ @@ -59,7 +59,7 @@ 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ - (PHP_VERSION_ID >= 80400 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), ]) : [], ], @@ -79,7 +79,7 @@ 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ - (PHP_VERSION_ID >= 80400 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), ]) : [], ], @@ -99,7 +99,7 @@ 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ - (PHP_VERSION_ID >= 80400 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), ]) : [], ], @@ -115,7 +115,7 @@ 'prefix' => '', 'prefix_indexes' => true, 'search_path' => 'public', - 'sslmode' => 'prefer', + 'sslmode' => env('DB_SSLMODE', 'prefer'), ], 'sqlsrv' => [ @@ -168,7 +168,7 @@ 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), 'persistent' => env('REDIS_PERSISTENT', false), ], @@ -179,6 +179,10 @@ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), ], 'cache' => [ @@ -188,6 +192,10 @@ 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), ], ], diff --git a/config/filesystems.php b/config/filesystems.php index 6cdc0631..61929975 100755 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -40,7 +40,7 @@ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => rtrim((string) env('APP_URL', 'http://localhost'), '/').'/storage', 'visibility' => 'public', 'throw' => false, 'report' => false, diff --git a/config/logging.php b/config/logging.php index 8d94292b..49348976 100755 --- a/config/logging.php +++ b/config/logging.php @@ -54,7 +54,7 @@ 'stack' => [ 'driver' => 'stack', - 'channels' => explode(',', env('LOG_STACK', 'single')), + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), 'ignore_exceptions' => false, ], @@ -99,7 +99,7 @@ 'level' => env('LOG_LEVEL', 'debug'), 'handler' => StreamHandler::class, 'formatter' => env('LOG_STDERR_FORMATTER'), - 'with' => [ + 'handler_with' => [ 'stream' => 'php://stderr', ], 'processors' => [PsrLogMessageProcessor::class], diff --git a/config/mail.php b/config/mail.php index 47e3eed4..544e3db7 100755 --- a/config/mail.php +++ b/config/mail.php @@ -41,11 +41,11 @@ 'url' => env('MAIL_URL'), 'host' => env('MAIL_HOST', '127.0.0.1'), 'port' => env('MAIL_PORT', 2525), - 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'scheme' => env('MAIL_SCHEME'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN'), + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), ], 'ses' => [ @@ -54,7 +54,7 @@ 'postmark' => [ 'transport' => 'postmark', - // 'message_stream_id' => null, + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), // 'client' => [ // 'timeout' => 5, // ], @@ -74,12 +74,26 @@ 'transport' => 'array', ], + 'resend' => [ + 'transport' => 'resend', + ], + 'failover' => [ 'transport' => 'failover', 'mailers' => [ 'smtp', 'log', ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, ], ], @@ -97,26 +111,7 @@ 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), - ], - - /* - |-------------------------------------------------------------------------- - | Markdown Mail Settings - |-------------------------------------------------------------------------- - | - | If you are using Markdown based email rendering, you may configure your - | theme and component paths here, allowing you to customize the design - | of the emails. Or, you may simply stick with the Laravel defaults! - | - */ - - 'markdown' => [ - 'theme' => env('MAIL_MARKDOWN_THEME', 'default'), - - 'paths' => [ - resource_path('views/vendor/mail'), - ], + 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')), ], ]; diff --git a/config/services.php b/config/services.php index 27a36175..0234ced4 100755 --- a/config/services.php +++ b/config/services.php @@ -15,7 +15,7 @@ */ 'postmark' => [ - 'token' => env('POSTMARK_TOKEN'), + 'key' => env('POSTMARK_API_KEY'), ], 'ses' => [ @@ -25,7 +25,7 @@ ], 'resend' => [ - 'key' => env('RESEND_KEY'), + 'key' => env('RESEND_API_KEY'), ], 'slack' => [ diff --git a/config/session.php b/config/session.php index 062192fa..0d426650 100755 --- a/config/session.php +++ b/config/session.php @@ -13,7 +13,7 @@ | incoming requests. Laravel supports a variety of storage options to | persist session data. Database storage is a great default choice. | - | Supported: "file", "cookie", "database", "apc", + | Supported: "file", "cookie", "database", | "memcached", "redis", "dynamodb", "array" | */ @@ -97,7 +97,7 @@ | define the cache store which should be used to store the session data | between requests. This must match one of your defined cache stores. | - | Affects: "apc", "dynamodb", "memcached", "redis" + | Affects: "dynamodb", "memcached", "redis" | */ @@ -129,7 +129,7 @@ 'cookie' => env( 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + Str::slug((string) env('APP_NAME', 'laravel')).'-session' ), /* @@ -214,4 +214,20 @@ 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + /* + |-------------------------------------------------------------------------- + | Session Serialization + |-------------------------------------------------------------------------- + | + | This value controls the serialization strategy for session data, which + | is JSON by default. Setting this to "php" allows the storage of PHP + | objects in the session but can make an application vulnerable to + | "gadget chain" serialization attacks if the APP_KEY is leaked. + | + | Supported: "json", "php" + | + */ + + 'serialization' => 'json', + ]; diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 57248d37..c8ac7c9a 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 = '12.54.1'; + const VERSION = '13.1.1'; /** * The base path for the Laravel installation. diff --git a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php index 8d7bfa1c..c4af2e6b 100644 --- a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php +++ b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php @@ -194,6 +194,8 @@ public function withRouting(?Closure $using = null, * @param string $apiPrefix * @param callable|null $then * @return \Closure + * + * @throws \Throwable */ protected function buildRoutingCallback(array|string|null $web, array|string|null $api, @@ -361,7 +363,13 @@ protected function withCommandRouting(array $paths) */ public function withSchedule(callable $callback) { - Artisan::starting(fn () => $callback($this->app->make(Schedule::class))); + Artisan::starting(function () use ($callback) { + $this->app->afterResolving(Schedule::class, fn ($schedule) => $callback($schedule)); + + if ($this->app->resolved(Schedule::class)) { + $callback($this->app->make(Schedule::class)); + } + }); return $this; } diff --git a/src/Illuminate/Foundation/Configuration/Middleware.php b/src/Illuminate/Foundation/Configuration/Middleware.php index a458bf88..9629eb4a 100644 --- a/src/Illuminate/Foundation/Configuration/Middleware.php +++ b/src/Illuminate/Foundation/Configuration/Middleware.php @@ -8,9 +8,9 @@ use Illuminate\Auth\Middleware\RedirectIfAuthenticated; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; +use Illuminate\Foundation\Http\Middleware\PreventRequestForgery; use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance; use Illuminate\Foundation\Http\Middleware\TrimStrings; -use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken; use Illuminate\Http\Middleware\TrustHosts; use Illuminate\Http\Middleware\TrustProxies; use Illuminate\Routing\Middleware\ValidateSignature; @@ -487,7 +487,7 @@ public function getMiddlewareGroups() \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + \Illuminate\Foundation\Http\Middleware\PreventRequestForgery::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, $this->authenticatedSessions ? 'auth.session' : null, ])), @@ -591,18 +591,38 @@ public function encryptCookies(array $except = []) } /** - * Configure the CSRF token validation middleware. + * Configure the request forgery prevention middleware. * * @param array $except + * @param bool $originOnly + * @param bool $allowSameSite * @return $this */ - public function validateCsrfTokens(array $except = []) + public function preventRequestForgery(array $except = [], bool $originOnly = false, bool $allowSameSite = false) { - ValidateCsrfToken::except($except); + if (! empty($except)) { + PreventRequestForgery::except($except); + } + + PreventRequestForgery::useOriginOnly($originOnly); + PreventRequestForgery::allowSameSite($allowSameSite); return $this; } + /** + * Configure the CSRF token validation middleware. + * + * @deprecated Use preventRequestForgery() instead. + * + * @param array $except + * @return $this + */ + public function validateCsrfTokens(array $except = []) + { + return $this->preventRequestForgery($except); + } + /** * Configure the URL signature validation middleware. * diff --git a/src/Illuminate/Foundation/Console/DocsCommand.php b/src/Illuminate/Foundation/Console/DocsCommand.php index 9edca71d..70f59f15 100644 --- a/src/Illuminate/Foundation/Console/DocsCommand.php +++ b/src/Illuminate/Foundation/Console/DocsCommand.php @@ -82,7 +82,7 @@ class DocsCommand extends Command * * @return void */ - protected function configure() + protected function configure(): void { parent::configure(); @@ -97,6 +97,8 @@ protected function configure() * @param \Illuminate\Http\Client\Factory $http * @param \Illuminate\Contracts\Cache\Repository $cache * @return int + * + * @throws \Symfony\Component\Process\Exception\ProcessFailedException */ public function handle(Http $http, Cache $cache) { @@ -368,6 +370,8 @@ protected function openViaCustomStrategy($url) * * @param string $url * @return void + * + * @throws \Symfony\Component\Process\Exception\ProcessFailedException */ protected function openViaBuiltInStrategy($url) { diff --git a/src/Illuminate/Foundation/Console/RouteListCommand.php b/src/Illuminate/Foundation/Console/RouteListCommand.php index 71a36cc6..a48575e6 100644 --- a/src/Illuminate/Foundation/Console/RouteListCommand.php +++ b/src/Illuminate/Foundation/Console/RouteListCommand.php @@ -47,7 +47,7 @@ class RouteListCommand extends Command * * @var string[] */ - protected $headers = ['Domain', 'Method', 'URI', 'Name', 'Action', 'Middleware']; + protected $headers = ['Domain', 'Method', 'URI', 'Name', 'Action', 'Middleware', 'Path']; /** * The terminal width resolver callback. @@ -142,10 +142,11 @@ protected function getRouteInformation(Route $route) return $this->filterRoute([ 'domain' => $route->domain(), 'method' => implode('|', $route->methods()), - 'uri' => $route->uri(), + 'uri' => $this->resolveUri($route), 'name' => $route->getName(), 'action' => ltrim($route->getActionName(), '\\'), 'middleware' => $this->getMiddleware($route), + 'path' => $this->getClosurePath($route), 'vendor' => $this->isVendorRoute($route), ]); } @@ -200,6 +201,23 @@ protected function displayRoutes(array $routes) ); } + /** + * Get the URI for the given route, including any binding fields. + * + * @param \Illuminate\Routing\Route $route + * @return string + */ + protected function resolveUri(Route $route) + { + $uri = $route->uri(); + + foreach ($route->bindingFields() as $parameter => $field) { + $uri = str_replace("{{$parameter}}", "{{$parameter}:{$field}}", $uri); + } + + return $uri; + } + /** * Get the middleware for the route. * @@ -213,6 +231,25 @@ protected function getMiddleware($route) ->implode("\n"); } + /** + * Get the file path and line number for a closure-based route. + * + * @param \Illuminate\Routing\Route $route + * @return string|null + */ + protected function getClosurePath(Route $route) + { + if (! $route->action['uses'] instanceof Closure) { + return null; + } + + $reflection = new ReflectionFunction($route->action['uses']); + + return str_replace( + '\\', '/', ltrim(Str::after($reflection->getFileName(), base_path()), DIRECTORY_SEPARATOR) + ).':'.$reflection->getStartLine(); + } + /** * Determine if the route has been defined outside of the application. * @@ -424,7 +461,13 @@ protected function formatActionForCli($route) ['action' => $action, 'name' => $name] = $route; if ($action === 'Closure' || $action === ViewController::class) { - return $name; + $path = $route['path'] ?? null; + + if ($name && $path) { + return $name.' '.$path; + } + + return $name ?? $path; } $name = $name ? "$name " : null; diff --git a/src/Illuminate/Foundation/Console/ServeCommand.php b/src/Illuminate/Foundation/Console/ServeCommand.php index 125a52cf..670cc896 100644 --- a/src/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Illuminate/Foundation/Console/ServeCommand.php @@ -95,7 +95,7 @@ class ServeCommand extends Command /** {@inheritdoc} */ #[\Override] - protected function initialize(InputInterface $input, OutputInterface $output) + protected function initialize(InputInterface $input, OutputInterface $output): void { $this->phpServerWorkers = transform((int) env('PHP_CLI_SERVER_WORKERS', 1), function (int $workers) { if ($workers < 2) { @@ -133,7 +133,7 @@ public function handle() $environmentLastModified = $hasEnvironment ? filemtime($environmentFile) - : now()->addDays(30)->getTimestamp(); + : Carbon::now()->addDays(30)->getTimestamp(); $process = $this->startProcess($hasEnvironment); @@ -408,6 +408,8 @@ protected function getDateFromLine($line) * * @param string $line * @return int + * + * @throws \InvalidArgumentException */ public static function getRequestPortFromLine($line) { diff --git a/src/Illuminate/Foundation/Console/VendorPublishCommand.php b/src/Illuminate/Foundation/Console/VendorPublishCommand.php index ac47419e..3134deee 100644 --- a/src/Illuminate/Foundation/Console/VendorPublishCommand.php +++ b/src/Illuminate/Foundation/Console/VendorPublishCommand.php @@ -6,6 +6,7 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Foundation\Events\VendorTagPublished; use Illuminate\Support\Arr; +use Illuminate\Support\Carbon; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; use League\Flysystem\Filesystem as Flysystem; @@ -94,7 +95,7 @@ public function __construct(Filesystem $files) */ public function handle() { - $this->publishedAt = now(); + $this->publishedAt = Carbon::now(); $this->determineWhatShouldBePublished(); diff --git a/src/Illuminate/Foundation/Console/stubs/console.stub b/src/Illuminate/Foundation/Console/stubs/console.stub index 055b7d83..d5dee5e3 100644 --- a/src/Illuminate/Foundation/Console/stubs/console.stub +++ b/src/Illuminate/Foundation/Console/stubs/console.stub @@ -2,24 +2,14 @@ namespace {{ namespace }}; +use Illuminate\Console\Attributes\Description; +use Illuminate\Console\Attributes\Signature; use Illuminate\Console\Command; +#[Signature('{{ command }}')] +#[Description('Command description')] class {{ class }} extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = '{{ command }}'; - - /** - * The console command description. - * - * @var string - */ - protected $description = 'Command description'; - /** * Execute the console command. */ diff --git a/src/Illuminate/Foundation/Exceptions/Handler.php b/src/Illuminate/Foundation/Exceptions/Handler.php index 57dfc30f..a98f7818 100644 --- a/src/Illuminate/Foundation/Exceptions/Handler.php +++ b/src/Illuminate/Foundation/Exceptions/Handler.php @@ -22,6 +22,7 @@ use Illuminate\Database\RecordsNotFoundException; use Illuminate\Foundation\Exceptions\Renderer\Renderer; use Illuminate\Http\Exceptions\HttpResponseException; +use Illuminate\Http\Exceptions\OriginMismatchException; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Response; use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException; @@ -154,6 +155,7 @@ class Handler implements ExceptionHandlerContract HttpResponseException::class, ModelNotFoundException::class, MultipleRecordsFoundException::class, + OriginMismatchException::class, RecordNotFoundException::class, RecordsNotFoundException::class, RequestExceptionInterface::class, @@ -670,6 +672,7 @@ protected function prepareException(Throwable $e) $e->status(), $e->response()?->message() ?: (Response::$statusTexts[$e->status()] ?? 'Whoops, looks like something went wrong.'), $e ), $e instanceof AuthorizationException && ! $e->hasStatus() => new AccessDeniedHttpException($e->getMessage(), $e), + $e instanceof OriginMismatchException => new HttpException(403, $e->getMessage(), $e), $e instanceof TokenMismatchException => new HttpException(419, $e->getMessage(), $e), $e instanceof RequestExceptionInterface => new BadRequestHttpException('Bad request.', $e), $e instanceof RecordNotFoundException => new NotFoundHttpException('Not found.', $e), @@ -916,6 +919,8 @@ protected function renderExceptionWithSymfony(Throwable $e, $debug) * * @param \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \Throwable */ protected function renderHttpException(HttpExceptionInterface $e) { diff --git a/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php b/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php index 2680fda7..51faa502 100644 --- a/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php +++ b/src/Illuminate/Foundation/Exceptions/Renderer/Exception.php @@ -106,6 +106,18 @@ public function httpStatusCode() return $this->exception->getStatusCode(); } + /** + * Get the previous exceptions in the chain. + * + * @return \Illuminate\Support\Collection + */ + public function previousExceptions() + { + return once(fn () => (new Collection($this->exception->getAllPrevious()))->map( + fn ($previous) => new static($previous, $this->request, $this->listener, $this->basePath), + )); + } + /** * Get the exception's frames. * diff --git a/src/Illuminate/Foundation/Exceptions/views/minimal.blade.php b/src/Illuminate/Foundation/Exceptions/views/minimal.blade.php index 99cc78b0..44acf038 100644 --- a/src/Illuminate/Foundation/Exceptions/views/minimal.blade.php +++ b/src/Illuminate/Foundation/Exceptions/views/minimal.blade.php @@ -16,15 +16,15 @@ } - -
+ +
-

+

@yield('code')

-
+
@yield('message')
diff --git a/src/Illuminate/Foundation/Http/Attributes/ErrorBag.php b/src/Illuminate/Foundation/Http/Attributes/ErrorBag.php new file mode 100644 index 00000000..a800e68b --- /dev/null +++ b/src/Illuminate/Foundation/Http/Attributes/ErrorBag.php @@ -0,0 +1,19 @@ +validator; } + $this->configureFromAttributes(); + $factory = $this->container->make(ValidationFactory::class); if (method_exists($this, 'validator')) { @@ -107,6 +114,38 @@ protected function getValidatorInstance() return $this->validator; } + /** + * Configure the form request from class attributes. + * + * @return void + */ + protected function configureFromAttributes() + { + $reflection = new ReflectionClass($this); + + if (count($reflection->getAttributes(StopOnFirstFailure::class)) > 0) { + $this->stopOnFirstFailure = true; + } + + $redirectTo = $reflection->getAttributes(RedirectTo::class); + + if (count($redirectTo) > 0) { + $this->redirect = $redirectTo[0]->newInstance()->url; + } + + $redirectToRoute = $reflection->getAttributes(RedirectToRoute::class); + + if (count($redirectToRoute) > 0) { + $this->redirectRoute = $redirectToRoute[0]->newInstance()->route; + } + + $errorBag = $reflection->getAttributes(ErrorBag::class); + + if (count($errorBag) > 0) { + $this->errorBag = $errorBag[0]->newInstance()->name; + } + } + /** * Create the default validator instance. * diff --git a/src/Illuminate/Foundation/Http/Middleware/PreventRequestForgery.php b/src/Illuminate/Foundation/Http/Middleware/PreventRequestForgery.php new file mode 100644 index 00000000..dc247426 --- /dev/null +++ b/src/Illuminate/Foundation/Http/Middleware/PreventRequestForgery.php @@ -0,0 +1,321 @@ + + */ + protected $except = []; + + /** + * The globally ignored URIs that should be excluded from CSRF verification. + * + * @var array + */ + protected static $neverVerify = []; + + /** + * Indicates whether the XSRF-TOKEN cookie should be set on the response. + * + * @var bool + */ + protected $addHttpCookie = true; + + /** + * Indicates whether requests from the same site should be allowed. + * + * @var bool + */ + protected static $allowSameSite = false; + + /** + * Indicates whether only origin verification should be used. + * + * @var bool + */ + protected static $originOnly = false; + + /** + * Create a new middleware instance. + * + * @param \Illuminate\Contracts\Foundation\Application $app + * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter + */ + public function __construct(Application $app, Encrypter $encrypter) + { + $this->app = $app; + $this->encrypter = $encrypter; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + * + * @throws \Illuminate\Session\TokenMismatchException + * @throws \Illuminate\Http\Exceptions\OriginMismatchException + */ + public function handle($request, Closure $next) + { + if ( + $this->isReading($request) || + $this->runningUnitTests() || + $this->inExceptArray($request) || + $this->hasValidOrigin($request) || + $this->tokensMatch($request) + ) { + return tap($next($request), function ($response) use ($request) { + if ($this->shouldAddXsrfTokenCookie()) { + $this->addCookieToResponse($request, $response); + } + }); + } + + throw new TokenMismatchException('CSRF token mismatch.'); + } + + /** + * Determine if the HTTP request uses a ‘read’ verb. + * + * @param \Illuminate\Http\Request $request + * @return bool + */ + protected function isReading($request) + { + return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']); + } + + /** + * Determine if the application is running unit tests. + * + * @return bool + */ + protected function runningUnitTests() + { + return $this->app->runningInConsole() && $this->app->runningUnitTests(); + } + + /** + * Determine if the request has a valid origin based on the Sec-Fetch-Site header. + * + * @param \Illuminate\Http\Request $request + * @return bool + * + * @throws \Illuminate\Http\Exceptions\OriginMismatchException + */ + protected function hasValidOrigin($request) + { + $secFetchSite = $request->header('Sec-Fetch-Site'); + + if ($secFetchSite === 'same-origin') { + return true; + } + + if ($secFetchSite === 'same-site' && static::$allowSameSite) { + return true; + } + + if (static::$originOnly) { + throw new OriginMismatchException('Origin mismatch.'); + } + + return false; + } + + /** + * Determine if the session and input CSRF tokens match. + * + * @param \Illuminate\Http\Request $request + * @return bool + */ + protected function tokensMatch($request) + { + $token = $this->getTokenFromRequest($request); + + return is_string($request->session()->token()) && + is_string($token) && + hash_equals($request->session()->token(), $token); + } + + /** + * Get the CSRF token from the request. + * + * @param \Illuminate\Http\Request $request + * @return string|null + */ + protected function getTokenFromRequest($request) + { + $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN'); + + if (! $token && $header = $request->header('X-XSRF-TOKEN')) { + try { + $token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized())); + } catch (DecryptException) { + $token = ''; + } + } + + return $token; + } + + /** + * Determine if the cookie should be added to the response. + * + * @return bool + */ + public function shouldAddXsrfTokenCookie() + { + if (static::$originOnly) { + return false; + } + + return $this->addHttpCookie; + } + + /** + * Add the CSRF token to the response cookies. + * + * @param \Illuminate\Http\Request $request + * @param \Symfony\Component\HttpFoundation\Response $response + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function addCookieToResponse($request, $response) + { + $config = config('session'); + + if ($response instanceof Responsable) { + $response = $response->toResponse($request); + } + + $response->headers->setCookie($this->newCookie($request, $config)); + + return $response; + } + + /** + * Create a new "XSRF-TOKEN" cookie that contains the CSRF token. + * + * @param \Illuminate\Http\Request $request + * @param array $config + * @return \Symfony\Component\HttpFoundation\Cookie + */ + protected function newCookie($request, $config) + { + return new Cookie( + 'XSRF-TOKEN', + $request->session()->token(), + $this->availableAt(60 * $config['lifetime']), + $config['path'], + $config['domain'], + $config['secure'], + false, + false, + $config['same_site'] ?? null, + $config['partitioned'] ?? false + ); + } + + /** + * Indicate that the given URIs should be excluded from CSRF verification. + * + * @param array|string $uris + * @return void + */ + public static function except($uris) + { + static::$neverVerify = array_values(array_unique( + array_merge(static::$neverVerify, Arr::wrap($uris)) + )); + } + + /** + * Indicate that requests from the same site should be allowed. + * + * @param bool $allow + * @return void + */ + public static function allowSameSite($allow = true) + { + static::$allowSameSite = $allow; + } + + /** + * Indicate that only origin verification should be used. + * + * @param bool $originOnly + * @return void + */ + public static function useOriginOnly($originOnly = true) + { + static::$originOnly = $originOnly; + } + + /** + * Get the URIs that should be excluded. + * + * @return array + */ + public function getExcludedPaths() + { + return array_merge($this->except, static::$neverVerify); + } + + /** + * Determine if the cookie contents should be serialized. + * + * @return bool + */ + public static function serialized() + { + return EncryptCookies::serialized('XSRF-TOKEN'); + } + + /** + * Flush the state of the middleware. + * + * @return void + */ + public static function flushState() + { + static::$neverVerify = []; + static::$allowSameSite = false; + static::$originOnly = false; + } +} diff --git a/src/Illuminate/Foundation/Http/Middleware/ValidateCsrfToken.php b/src/Illuminate/Foundation/Http/Middleware/ValidateCsrfToken.php index f49d6141..544fa77d 100644 --- a/src/Illuminate/Foundation/Http/Middleware/ValidateCsrfToken.php +++ b/src/Illuminate/Foundation/Http/Middleware/ValidateCsrfToken.php @@ -3,9 +3,9 @@ namespace Illuminate\Foundation\Http\Middleware; /** - * Alias of VerifyCsrfToken for consistency. + * @deprecated Use PreventRequestForgery instead. */ -class ValidateCsrfToken extends VerifyCsrfToken +class ValidateCsrfToken extends PreventRequestForgery { // } diff --git a/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php b/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php index 7a47b4fb..8c267c6b 100644 --- a/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php +++ b/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php @@ -2,248 +2,10 @@ namespace Illuminate\Foundation\Http\Middleware; -use Closure; -use Illuminate\Contracts\Encryption\DecryptException; -use Illuminate\Contracts\Encryption\Encrypter; -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Contracts\Support\Responsable; -use Illuminate\Cookie\CookieValuePrefix; -use Illuminate\Cookie\Middleware\EncryptCookies; -use Illuminate\Foundation\Http\Middleware\Concerns\ExcludesPaths; -use Illuminate\Session\TokenMismatchException; -use Illuminate\Support\Arr; -use Illuminate\Support\InteractsWithTime; -use Symfony\Component\HttpFoundation\Cookie; - -class VerifyCsrfToken +/** + * @deprecated Use PreventRequestForgery instead. + */ +class VerifyCsrfToken extends PreventRequestForgery { - use InteractsWithTime, - ExcludesPaths; - - /** - * The application instance. - * - * @var \Illuminate\Contracts\Foundation\Application - */ - protected $app; - - /** - * The encrypter implementation. - * - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - protected $encrypter; - - /** - * The URIs that should be excluded. - * - * @var array - */ - protected $except = []; - - /** - * The globally ignored URIs that should be excluded from CSRF verification. - * - * @var array - */ - protected static $neverVerify = []; - - /** - * Indicates whether the XSRF-TOKEN cookie should be set on the response. - * - * @var bool - */ - protected $addHttpCookie = true; - - /** - * Create a new middleware instance. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - */ - public function __construct(Application $app, Encrypter $encrypter) - { - $this->app = $app; - $this->encrypter = $encrypter; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @return mixed - * - * @throws \Illuminate\Session\TokenMismatchException - */ - public function handle($request, Closure $next) - { - if ( - $this->isReading($request) || - $this->runningUnitTests() || - $this->inExceptArray($request) || - $this->tokensMatch($request) - ) { - return tap($next($request), function ($response) use ($request) { - if ($this->shouldAddXsrfTokenCookie()) { - $this->addCookieToResponse($request, $response); - } - }); - } - - throw new TokenMismatchException('CSRF token mismatch.'); - } - - /** - * Determine if the HTTP request uses a ‘read’ verb. - * - * @param \Illuminate\Http\Request $request - * @return bool - */ - protected function isReading($request) - { - return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']); - } - - /** - * Determine if the application is running unit tests. - * - * @return bool - */ - protected function runningUnitTests() - { - return $this->app->runningInConsole() && $this->app->runningUnitTests(); - } - - /** - * Get the URIs that should be excluded. - * - * @return array - */ - public function getExcludedPaths() - { - return array_merge($this->except, static::$neverVerify); - } - - /** - * Determine if the session and input CSRF tokens match. - * - * @param \Illuminate\Http\Request $request - * @return bool - */ - protected function tokensMatch($request) - { - $token = $this->getTokenFromRequest($request); - - return is_string($request->session()->token()) && - is_string($token) && - hash_equals($request->session()->token(), $token); - } - - /** - * Get the CSRF token from the request. - * - * @param \Illuminate\Http\Request $request - * @return string|null - */ - protected function getTokenFromRequest($request) - { - $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN'); - - if (! $token && $header = $request->header('X-XSRF-TOKEN')) { - try { - $token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized())); - } catch (DecryptException) { - $token = ''; - } - } - - return $token; - } - - /** - * Determine if the cookie should be added to the response. - * - * @return bool - */ - public function shouldAddXsrfTokenCookie() - { - return $this->addHttpCookie; - } - - /** - * Add the CSRF token to the response cookies. - * - * @param \Illuminate\Http\Request $request - * @param \Symfony\Component\HttpFoundation\Response $response - * @return \Symfony\Component\HttpFoundation\Response - */ - protected function addCookieToResponse($request, $response) - { - $config = config('session'); - - if ($response instanceof Responsable) { - $response = $response->toResponse($request); - } - - $response->headers->setCookie($this->newCookie($request, $config)); - - return $response; - } - - /** - * Create a new "XSRF-TOKEN" cookie that contains the CSRF token. - * - * @param \Illuminate\Http\Request $request - * @param array $config - * @return \Symfony\Component\HttpFoundation\Cookie - */ - protected function newCookie($request, $config) - { - return new Cookie( - 'XSRF-TOKEN', - $request->session()->token(), - $this->availableAt(60 * $config['lifetime']), - $config['path'], - $config['domain'], - $config['secure'], - false, - false, - $config['same_site'] ?? null, - $config['partitioned'] ?? false - ); - } - - /** - * Indicate that the given URIs should be excluded from CSRF verification. - * - * @param array|string $uris - * @return void - */ - public static function except($uris) - { - static::$neverVerify = array_values(array_unique( - array_merge(static::$neverVerify, Arr::wrap($uris)) - )); - } - - /** - * Determine if the cookie contents should be serialized. - * - * @return bool - */ - public static function serialized() - { - return EncryptCookies::serialized('XSRF-TOKEN'); - } - - /** - * Flush the state of the middleware. - * - * @return void - */ - public static function flushState() - { - static::$neverVerify = []; - } + // } diff --git a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php index 0ac742bb..09397ac7 100755 --- a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php @@ -12,6 +12,8 @@ use Illuminate\Console\Scheduling\ScheduleFinishCommand; use Illuminate\Console\Scheduling\ScheduleInterruptCommand; use Illuminate\Console\Scheduling\ScheduleListCommand; +use Illuminate\Console\Scheduling\SchedulePauseCommand; +use Illuminate\Console\Scheduling\ScheduleResumeCommand; use Illuminate\Console\Scheduling\ScheduleRunCommand; use Illuminate\Console\Scheduling\ScheduleTestCommand; use Illuminate\Console\Scheduling\ScheduleWorkCommand; @@ -174,6 +176,8 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid 'ScheduleTest' => ScheduleTestCommand::class, 'ScheduleWork' => ScheduleWorkCommand::class, 'ScheduleInterrupt' => ScheduleInterruptCommand::class, + 'SchedulePause' => SchedulePauseCommand::class, + 'ScheduleResume' => ScheduleResumeCommand::class, 'ShowModel' => ShowModelCommand::class, 'StorageLink' => StorageLinkCommand::class, 'StorageUnlink' => StorageUnlinkCommand::class, diff --git a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php index cc87318f..691915ab 100644 --- a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php @@ -142,6 +142,8 @@ public function registerDumper() * Register the "validate" macro on the request. * * @return void + * + * @throws \Illuminate\Validation\ValidationException */ public function registerRequestValidation() { diff --git a/src/Illuminate/Foundation/Routing/PrecognitionControllerDispatcher.php b/src/Illuminate/Foundation/Routing/PrecognitionControllerDispatcher.php index 7ed86056..b055d44f 100644 --- a/src/Illuminate/Foundation/Routing/PrecognitionControllerDispatcher.php +++ b/src/Illuminate/Foundation/Routing/PrecognitionControllerDispatcher.php @@ -31,6 +31,8 @@ public function dispatch(Route $route, $controller, $method) * @param object $controller * @param string $method * @return $this + * + * @throws \RuntimeException */ protected function ensureMethodExists($controller, $method) { diff --git a/src/Illuminate/Foundation/Testing/Attributes/Seed.php b/src/Illuminate/Foundation/Testing/Attributes/Seed.php new file mode 100644 index 00000000..2f1cd24d --- /dev/null +++ b/src/Illuminate/Foundation/Testing/Attributes/Seed.php @@ -0,0 +1,17 @@ +beforeApplicationDestroyed(fn () => $this->{$method}()); } + + foreach ((new ReflectionClass($trait))->getMethods() as $method) { + if ($method->getAttributes(SetUp::class) !== []) { + $this->{$method->getName()}(); + } + + if ($method->getAttributes(TearDown::class) !== []) { + $this->beforeApplicationDestroyed(fn () => $this->{$method->getName()}()); + } + } } return $uses; diff --git a/src/Illuminate/Foundation/Testing/Traits/CanConfigureMigrationCommands.php b/src/Illuminate/Foundation/Testing/Traits/CanConfigureMigrationCommands.php index aafca6f1..2e5029d5 100644 --- a/src/Illuminate/Foundation/Testing/Traits/CanConfigureMigrationCommands.php +++ b/src/Illuminate/Foundation/Testing/Traits/CanConfigureMigrationCommands.php @@ -2,6 +2,10 @@ namespace Illuminate\Foundation\Testing\Traits; +use Illuminate\Foundation\Testing\Attributes\Seed; +use Illuminate\Foundation\Testing\Attributes\Seeder; +use ReflectionClass; + trait CanConfigureMigrationCommands { /** @@ -49,6 +53,14 @@ protected function shouldDropTypes() */ protected function shouldSeed() { + $class = new ReflectionClass($this); + + do { + if (count($class->getAttributes(Seed::class)) > 0) { + return true; + } + } while ($class = $class->getParentClass()); + return property_exists($this, 'seed') ? $this->seed : false; } @@ -59,6 +71,16 @@ protected function shouldSeed() */ protected function seeder() { + $class = new ReflectionClass($this); + + do { + $seeder = $class->getAttributes(Seeder::class); + + if (count($seeder) > 0) { + return $seeder[0]->newInstance()->class; + } + } while ($class = $class->getParentClass()); + return property_exists($this, 'seeder') ? $this->seeder : false; } } diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/previous-exceptions.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/previous-exceptions.blade.php new file mode 100644 index 00000000..fc9cc1ed --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/previous-exceptions.blade.php @@ -0,0 +1,91 @@ +@props(['exception']) + +
+
+
+ +
+

Previous {{ Str::plural('exception', $exception->previousExceptions()->count()) }}

+
+ +
+ @foreach ($exception->previousExceptions() as $index => $previous) +
+ {{-- Timeline column --}} + @if ($exception->previousExceptions()->count() > 1) +
+ @if ($index > 0) +
+ @else +
+ @endif + +
+ + @if ($index < $exception->previousExceptions()->count() - 1) +
+ @else +
+ @endif +
+ @endif + + {{-- Exception content --}} +
+ {{-- Header + Message --}} +
+
+

{{ $previous->class() }}

+

{{ $previous->message() }}

+
+ +
+ + {{-- Collapsible trace --}} +
+ @foreach ($previous->frameGroups() as $group) + @if ($group['is_vendor']) + + @else + @foreach ($group['frames'] as $frame) + + @endforeach + @endif + @endforeach +
+
+
+ @endforeach +
+
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php index 0162868d..dd79c41b 100644 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/trace.blade.php @@ -6,6 +6,11 @@

Exception trace

+ @if ($exception->previousExceptions()->isNotEmpty()) + + {{ $exception->previousExceptions()->count() }} previous {{ Str::plural('exception', $exception->previousExceptions()->count()) }} + + @endif
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js index 6b1cf58f..7569eff7 100644 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/dist/scripts.js @@ -1,10 +1,10 @@ -var cr=!1,lr=!1,ot=[],ur=-1;function Vl(e){Zl(e)}function Zl(e){ot.includes(e)||ot.push(e),Xl()}function Yl(e){let t=ot.indexOf(e);t!==-1&&t>ur&&ot.splice(t,1)}function Xl(){!lr&&!cr&&(cr=!0,queueMicrotask(Kl))}function Kl(){cr=!1,lr=!0;for(let e=0;ee.effect(t,{scheduler:n=>{pr?Vl(n):n()}}),ao=e.raw}function Pi(e){gt=e}function eu(e){let t=()=>{};return[a=>{let r=gt(a);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(i=>i())}),e._x_effects.add(r),t=()=>{r!==void 0&&(e._x_effects.delete(r),Dt(r))},r},()=>{t()}]}function ro(e,t){let n=!0,a,r=gt(()=>{let i=e();JSON.stringify(i),n?a=i:queueMicrotask(()=>{t(i,a),a=i}),n=!1});return()=>Dt(r)}var io=[],so=[],oo=[];function tu(e){oo.push(e)}function Vr(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,so.push(t))}function co(e){io.push(e)}function lo(e,t,n){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(n)}function uo(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([n,a])=>{(t===void 0||t.includes(n))&&(a.forEach(r=>r()),delete e._x_attributeCleanups[n])})}function nu(e){for(e._x_effects?.forEach(Yl);e._x_cleanups?.length;)e._x_cleanups.pop()()}var Zr=new MutationObserver(Qr),Yr=!1;function Xr(){Zr.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),Yr=!0}function po(){au(),Zr.disconnect(),Yr=!1}var Kt=[];function au(){let e=Zr.takeRecords();Kt.push(()=>e.length>0&&Qr(e));let t=Kt.length;queueMicrotask(()=>{if(Kt.length===t)for(;Kt.length>0;)Kt.shift()()})}function z(e){if(!Yr)return e();po();let t=e();return Xr(),t}var Kr=!1,ia=[];function ru(){Kr=!0}function iu(){Kr=!1,Qr(ia),ia=[]}function Qr(e){if(Kr){ia=ia.concat(e);return}let t=[],n=new Set,a=new Map,r=new Map;for(let i=0;i{s.nodeType===1&&s._x_marker&&n.add(s)}),e[i].addedNodes.forEach(s=>{if(s.nodeType===1){if(n.has(s)){n.delete(s);return}s._x_marker||t.push(s)}})),e[i].type==="attributes")){let s=e[i].target,o=e[i].attributeName,c=e[i].oldValue,l=()=>{a.has(s)||a.set(s,[]),a.get(s).push({name:o,value:s.getAttribute(o)})},u=()=>{r.has(s)||r.set(s,[]),r.get(s).push(o)};s.hasAttribute(o)&&c===null?l():s.hasAttribute(o)?(u(),l()):u()}r.forEach((i,s)=>{uo(s,i)}),a.forEach((i,s)=>{io.forEach(o=>o(s,i))});for(let i of n)t.some(s=>s.contains(i))||so.forEach(s=>s(i));for(let i of t)i.isConnected&&oo.forEach(s=>s(i));t=null,n=null,a=null,r=null}function mo(e){return wn(At(e))}function vn(e,t,n){return e._x_dataStack=[t,...At(n||e)],()=>{e._x_dataStack=e._x_dataStack.filter(a=>a!==t)}}function At(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?At(e.host):e.parentNode?At(e.parentNode):[]}function wn(e){return new Proxy({objects:e},su)}var su={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"?ou:Reflect.get(e.find(a=>Reflect.has(a,t))||{},t,n)},set({objects:e},t,n,a){const r=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],i=Object.getOwnPropertyDescriptor(r,t);return i?.set&&i?.get?i.set.call(a,n)||!0:Reflect.set(r,t,n)}};function ou(){return Reflect.ownKeys(this).reduce((t,n)=>(t[n]=Reflect.get(this,n),t),{})}function ho(e){let t=a=>typeof a=="object"&&!Array.isArray(a)&&a!==null,n=(a,r="")=>{Object.entries(Object.getOwnPropertyDescriptors(a)).forEach(([i,{value:s,enumerable:o}])=>{if(o===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=r===""?i:`${r}.${i}`;typeof s=="object"&&s!==null&&s._x_interceptor?a[i]=s.initialize(e,c,i):t(s)&&s!==a&&!(s instanceof Element)&&n(s,c)})};return n(e)}function go(e,t=()=>{}){let n={initialValue:void 0,_x_interceptor:!0,initialize(a,r,i){return e(this.initialValue,()=>cu(a,r),s=>dr(a,r,s),r,i)}};return t(n),a=>{if(typeof a=="object"&&a!==null&&a._x_interceptor){let r=n.initialize.bind(n);n.initialize=(i,s,o)=>{let c=a.initialize(i,s,o);return n.initialValue=c,r(i,s,o)}}else n.initialValue=a;return n}}function cu(e,t){return t.split(".").reduce((n,a)=>n[a],e)}function dr(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]]={}),dr(e[t[0]],t.slice(1),n)}}var fo={};function ve(e,t){fo[e]=t}function mr(e,t){let n=lu(t);return Object.entries(fo).forEach(([a,r])=>{Object.defineProperty(e,`$${a}`,{get(){return r(t,n)},enumerable:!1})}),e}function lu(e){let[t,n]=xo(e),a={interceptor:go,...t};return Vr(e,n),a}function uu(e,t,n,...a){try{return n(...a)}catch(r){dn(r,e,t)}}function dn(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} +var lr=!1,ur=!1,ot=[],pr=-1,Wr=!1;function Ql(e){tu(e)}function Jl(){Wr=!0}function eu(){Wr=!1,io()}function tu(e){ot.includes(e)||ot.push(e),io()}function nu(e){let t=ot.indexOf(e);t!==-1&&t>pr&&ot.splice(t,1)}function io(){if(!ur&&!lr){if(Wr)return;lr=!0,queueMicrotask(au)}}function au(){lr=!1,ur=!0;for(let e=0;ee.effect(t,{scheduler:n=>{dr?Ql(n):n()}}),so=e.raw}function Mi(e){_t=e}function su(e){let t=()=>{};return[a=>{let r=_t(a);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(i=>i())}),e._x_effects.add(r),t=()=>{r!==void 0&&(e._x_effects.delete(r),Bt(r))},r},()=>{t()}]}function oo(e,t){let n=!0,a,r=_t(()=>{let i=e();if(JSON.stringify(i),!n&&(typeof i=="object"||i!==a)){let s=a;queueMicrotask(()=>{t(i,s)})}a=i,n=!1});return()=>Bt(r)}async function ou(e){Jl();try{await e(),await Promise.resolve()}finally{eu()}}var co=[],lo=[],uo=[];function cu(e){uo.push(e)}function Vr(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,lo.push(t))}function po(e){co.push(e)}function mo(e,t,n){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(n)}function ho(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([n,a])=>{(t===void 0||t.includes(n))&&(a.forEach(r=>r()),delete e._x_attributeCleanups[n])})}function lu(e){for(e._x_effects?.forEach(nu);e._x_cleanups?.length;)e._x_cleanups.pop()()}var Zr=new MutationObserver(Qr),Yr=!1;function Xr(){Zr.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),Yr=!0}function go(){uu(),Zr.disconnect(),Yr=!1}var tn=[];function uu(){let e=Zr.takeRecords();tn.push(()=>e.length>0&&Qr(e));let t=tn.length;queueMicrotask(()=>{if(tn.length===t)for(;tn.length>0;)tn.shift()()})}function z(e){if(!Yr)return e();go();let t=e();return Xr(),t}var Kr=!1,la=[];function pu(){Kr=!0}function du(){Kr=!1,Qr(la),la=[]}function Qr(e){if(Kr){la=la.concat(e);return}let t=[],n=new Set,a=new Map,r=new Map;for(let i=0;i{s.nodeType===1&&s._x_marker&&n.add(s)}),e[i].addedNodes.forEach(s=>{if(s.nodeType===1){if(n.has(s)){n.delete(s);return}s._x_marker||t.push(s)}})),e[i].type==="attributes")){let s=e[i].target,o=e[i].attributeName,c=e[i].oldValue,l=()=>{a.has(s)||a.set(s,[]),a.get(s).push({name:o,value:s.getAttribute(o)})},u=()=>{r.has(s)||r.set(s,[]),r.get(s).push(o)};s.hasAttribute(o)&&c===null?l():s.hasAttribute(o)?(u(),l()):u()}r.forEach((i,s)=>{ho(s,i)}),a.forEach((i,s)=>{co.forEach(o=>o(s,i))});for(let i of n)t.some(s=>s.contains(i))||lo.forEach(s=>s(i));for(let i of t)i.isConnected&&uo.forEach(s=>s(i));t=null,n=null,a=null,r=null}function fo(e){return mt(dt(e))}function Fn(e,t,n){return e._x_dataStack=[t,...dt(n||e)],()=>{e._x_dataStack=e._x_dataStack.filter(a=>a!==t)}}function dt(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?dt(e.host):e.parentNode?dt(e.parentNode):[]}function mt(e){return new Proxy({objects:e},mu)}var mu={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"?hu:Reflect.get(e.find(a=>Reflect.has(a,t))||{},t,n)},set({objects:e},t,n,a){const r=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],i=Object.getOwnPropertyDescriptor(r,t);return i?.set&&i?.get?i.set.call(a,n)||!0:Reflect.set(r,t,n)}};function hu(){return Reflect.ownKeys(this).reduce((t,n)=>(t[n]=Reflect.get(this,n),t),{})}function Jr(e){let t=a=>typeof a=="object"&&!Array.isArray(a)&&a!==null,n=(a,r="")=>{Object.entries(Object.getOwnPropertyDescriptors(a)).forEach(([i,{value:s,enumerable:o}])=>{if(o===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=r===""?i:`${r}.${i}`;typeof s=="object"&&s!==null&&s._x_interceptor?a[i]=s.initialize(e,c,i):t(s)&&s!==a&&!(s instanceof Element)&&n(s,c)})};return n(e)}function bo(e,t=()=>{}){let n={initialValue:void 0,_x_interceptor:!0,initialize(a,r,i){return e(this.initialValue,()=>gu(a,r),s=>mr(a,r,s),r,i)}};return t(n),a=>{if(typeof a=="object"&&a!==null&&a._x_interceptor){let r=n.initialize.bind(n);n.initialize=(i,s,o)=>{let c=a.initialize(i,s,o);return n.initialValue=c,r(i,s,o)}}else n.initialValue=a;return n}}function gu(e,t){return t.split(".").reduce((n,a)=>n[a],e)}function mr(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]]={}),mr(e[t[0]],t.slice(1),n)}}var _o={};function ve(e,t){_o[e]=t}function fn(e,t){let n=fu(t);return Object.entries(_o).forEach(([a,r])=>{Object.defineProperty(e,`$${a}`,{get(){return r(t,n)},enumerable:!1})}),e}function fu(e){let[t,n]=Fo(e),a={interceptor:bo,...t};return Vr(e,n),a}function bu(e,t,n,...a){try{return n(...a)}catch(r){bn(r,e,t)}}function bn(...e){return yo(...e)}var yo=yu;function _u(e){yo=e}function yu(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 Jn=!0;function bo(e){let t=Jn;Jn=!1;let n=e();return Jn=t,n}function ct(e,t,n={}){let a;return ne(e,t)(r=>a=r,n),a}function ne(...e){return _o(...e)}var _o=yo;function pu(e){_o=e}function yo(e,t){let n={};mr(n,e);let a=[n,...At(e)],r=typeof t=="function"?du(a,t):hu(a,t,e);return uu.bind(null,e,t,r)}function du(e,t){return(n=()=>{},{scope:a={},params:r=[]}={})=>{let i=t.apply(wn([a,...e]),r);sa(n,i)}}var Ma={};function mu(e,t){if(Ma[e])return Ma[e];let n=Object.getPrototypeOf(async function(){}).constructor,a=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,i=(()=>{try{let s=new n(["__self","scope"],`with (scope) { __self.result = ${a} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return dn(s,t,e),Promise.resolve()}})();return Ma[e]=i,i}function hu(e,t,n){let a=mu(t,n);return(r=()=>{},{scope:i={},params:s=[]}={})=>{a.result=void 0,a.finished=!1;let o=wn([i,...e]);if(typeof a=="function"){let c=a(a,o).catch(l=>dn(l,n,t));a.finished?(sa(r,a.result,o,s,n),a.result=void 0):c.then(l=>{sa(r,l,o,s,n)}).catch(l=>dn(l,n,t)).finally(()=>a.result=void 0)}}}function sa(e,t,n,a,r){if(Jn&&typeof t=="function"){let i=t.apply(n,a);i instanceof Promise?i.then(s=>sa(e,s,n,a)).catch(s=>dn(s,r,t)):e(i)}else typeof t=="object"&&t instanceof Promise?t.then(i=>e(i)):e(t)}var Jr="x-";function Mt(e=""){return Jr+e}function gu(e){Jr=e}var oa={};function W(e,t){return oa[e]=t,{before(n){if(!oa[n]){console.warn(String.raw`Cannot find directive \`${n}\`. \`${e}\` will use the default order of execution`);return}const a=rt.indexOf(n);rt.splice(a>=0?a:rt.indexOf("DEFAULT"),0,e)}}}function fu(e){return Object.keys(oa).includes(e)}function ei(e,t,n){if(t=Array.from(t),e._x_virtualDirectives){let i=Object.entries(e._x_virtualDirectives).map(([o,c])=>({name:o,value:c})),s=vo(i);i=i.map(o=>s.find(c=>c.name===o.name)?{name:`x-bind:${o.name}`,value:`"${o.value}"`}:o),t=t.concat(i)}let a={};return t.map(Eo((i,s)=>a[i]=s)).filter($o).map(yu(a,n)).sort(vu).map(i=>_u(e,i))}function vo(e){return Array.from(e).map(Eo()).filter(t=>!$o(t))}var hr=!1,nn=new Map,wo=Symbol();function bu(e){hr=!0;let t=Symbol();wo=t,nn.set(t,[]);let n=()=>{for(;nn.get(t).length;)nn.get(t).shift()();nn.delete(t)},a=()=>{hr=!1,n()};e(n),a()}function xo(e){let t=[],n=o=>t.push(o),[a,r]=eu(e);return t.push(r),[{Alpine:xn,effect:a,cleanup:n,evaluateLater:ne.bind(ne,e),evaluate:ct.bind(ct,e)},()=>t.forEach(o=>o())]}function _u(e,t){let n=()=>{},a=oa[t.type]||n,[r,i]=xo(e);lo(e,t.original,i);let s=()=>{e._x_ignore||e._x_ignoreSelf||(a.inline&&a.inline(e,t,r),a=a.bind(a,e,t,r),hr?nn.get(wo).push(a):a())};return s.runCleanups=i,s}var ko=(e,t)=>({name:n,value:a})=>(n.startsWith(e)&&(n=n.replace(e,t)),{name:n,value:a}),Co=e=>e;function Eo(e=()=>{}){return({name:t,value:n})=>{let{name:a,value:r}=Fo.reduce((i,s)=>s(i),{name:t,value:n});return a!==t&&e(a,t),{name:a,value:r}}}var Fo=[];function ti(e){Fo.push(e)}function $o({name:e}){return jo().test(e)}var jo=()=>new RegExp(`^${Jr}([^:^.]+)\\b`);function yu(e,t){return({name:n,value:a})=>{let r=n.match(jo()),i=n.match(/:([a-zA-Z0-9\-_:]+)/),s=n.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],o=t||e[n]||n;return{type:r?r[1]:null,value:i?i[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:a,original:o}}}var gr="DEFAULT",rt=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",gr,"teleport"];function vu(e,t){let n=rt.indexOf(e.type)===-1?gr:e.type,a=rt.indexOf(t.type)===-1?gr:t.type;return rt.indexOf(n)-rt.indexOf(a)}function rn(e,t,n={}){e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0,cancelable:!0}))}function dt(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(r=>dt(r,t));return}let n=!1;if(t(e,()=>n=!0),n)return;let a=e.firstElementChild;for(;a;)dt(a,t),a=a.nextElementSibling}function pe(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var Di=!1;function wu(){Di&&pe("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),Di=!0,document.body||pe("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's `