diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 0000000..a52b235 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"95f2d618-b0a9-4b52-b725-3a049ae47e93","pid":146659,"procStart":"1263491","acquiredAt":1777828038888} \ No newline at end of file diff --git a/.junie/guidelines.md b/.junie/guidelines.md deleted file mode 100644 index ad7c157..0000000 --- a/.junie/guidelines.md +++ /dev/null @@ -1,473 +0,0 @@ -# Livewire Flux Component Guidelines -## Usage Pattern -Components use the format: `` -## Component Reference -### Layout & Structure -- **flux:card** - Basic container with default slot -- **flux:field** - Form field wrapper with label/description support -- **flux:brand** - Logo/company name display with href navigation - -### Navigation -- **flux:breadcrumbs** - Navigation breadcrumbs - - **flux:breadcrumbs.item** - Individual breadcrumb with href/icon - -- **flux:accordion** - Collapsible content sections - - **flux:accordion.item** - Individual accordion item with heading/content - - **flux:accordion.heading** - Accordion header - - **flux:accordion.content** - Accordion body - -### Form Controls -- **flux:input** - Text input with wire:model, validation, icons, masks -- **flux:select** - Select input -- **flux:select.option** - Select options -- **flux:autocomplete** - Searchable input with dropdown items -- **flux:checkbox** - Single checkbox or grouped checkboxes -- **flux:date-picker** - Date selection with calendar, ranges, presets -- **flux:editor** - Rich text editor with toolbar - -### Interactive Elements -- **flux:button** - Button with variants (primary, outline, danger), icons, loading states -- **flux:dropdown** - Dropdown menu with positioning options -- **flux:menu** - Complex menu with items, submenus, separators, checkboxes, radio buttons -- **flux:command** - Command palette with searchable items -- **flux:context** - Right-click context menu wrapper - -### Display Components -- **flux:avatar** - User avatar with initials, images, badges, grouping -- **flux:badge** - Status/label badges with colors and variants -- **flux:callout** - Highlighted information blocks with icons and actions -- **flux:calendar** - Calendar display with date selection modes -- **flux:chart** - Data visualization with lines, areas, axes, tooltips - -### Key Props -- **wire:model** - Livewire property binding -- **variant** - Visual style options (outline, primary, filled, etc.) -- **size** - Component sizing (xs, sm, base, lg, xl, 2xl) -- **disabled/invalid** - State management -- **icon/icon:trailing** - Icon placement with variants -- **label/description** - Form field labeling -- **color** - Color theming options - -### Common Patterns -- Most form components support wire:model binding -- Many components have label/description props for field wrapping -- Icon components accept variant options (outline, solid, mini, micro) -- Size props typically offer xs, sm, base, lg, xl, 2xl options -- Variant props provide visual style alternatives - -=== - - -=== foundation rules === - -# Laravel Boost Guidelines - -The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. - -## Foundational Context -This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. - -- php - 8.5.5 -- laravel/framework (LARAVEL) - v12 -- laravel/horizon (HORIZON) - v5 -- laravel/nightwatch (NIGHTWATCH) - v1 -- laravel/prompts (PROMPTS) - v0 -- laravel/sanctum (SANCTUM) - v4 -- livewire/flux (FLUXUI_FREE) - v2 -- livewire/flux-pro (FLUXUI_PRO) - v2 -- livewire/livewire (LIVEWIRE) - v4 -- laravel/mcp (MCP) - v0 -- laravel/pint (PINT) - v1 -- pestphp/pest (PEST) - v4 -- phpunit/phpunit (PHPUNIT) - v12 -- tailwindcss (TAILWINDCSS) - v4 - -## Conventions -- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming. -- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. -- Check for existing components to reuse before writing a new one. - -## Verification Scripts -- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. - -## Application Structure & Architecture -- Stick to existing directory structure; don't create new base folders without approval. -- Do not change the application's dependencies without approval. - -## Frontend Bundling -- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `yarn run build`, `yarn run dev`, or `composer run dev`. Ask them. - -## Replies -- Be concise in your explanations - focus on what's important rather than explaining obvious details. - -## Documentation Files -- You must only create documentation files if explicitly requested by the user. - -=== boost rules === - -## Laravel Boost -- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. - -## Artisan -- Use the `list-artisan-commands` tool when you need to call an Artisan command to double-check the available parameters. - -## URLs -- Whenever you share a project URL with the user, you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain/IP, and port. - -## Tinker / Debugging -- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. -- Use the `database-query` tool when you only need to read from the database. - -## Reading Browser Logs With the `browser-logs` Tool -- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. -- Only recent browser logs will be useful - ignore old logs. - -## Searching Documentation (Critically Important) -- Boost comes with a powerful `search-docs` tool you should use before any other approaches when dealing with Laravel or Laravel ecosystem packages. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. -- The `search-docs` tool is perfect for all Laravel-related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. -- You must use this tool to search for Laravel ecosystem documentation before falling back to other approaches. -- Search the documentation before making code changes to ensure we are taking the correct approach. -- Use multiple, broad, simple, topic-based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. -- Do not add package names to queries; package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`. - -### Available Search Syntax -- You can and should pass multiple queries at once. The most relevant results will be returned first. - -1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth'. -2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit". -3. Quoted Phrases (Exact Position) - query="infinite scroll" - words must be adjacent and in that order. -4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit". -5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms. - -=== php rules === - -## PHP - -- Always use curly braces for control structures, even if it has one line. - -### Constructors -- Use PHP 8 constructor property promotion in `__construct()`. - - public function __construct(public GitHub $github) { } -- Do not allow empty `__construct()` methods with zero parameters unless the constructor is private. - -### Type Declarations -- Always use explicit return type declarations for methods and functions. -- Use appropriate PHP type hints for method parameters. - - -protected function isAccessible(User $user, ?string $path = null): bool -{ - ... -} - - -## Comments -- Prefer PHPDoc blocks over inline comments. Never use comments within the code itself unless there is something very complex going on. - -## PHPDoc Blocks -- Add useful array shape type definitions for arrays when appropriate. - -## Enums -- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. - -=== laravel/core rules === - -## Do Things the Laravel Way - -- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. -- If you're creating a generic PHP class, use `php artisan make:class`. -- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior. - -### Database -- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins. -- Use Eloquent models and relationships before suggesting raw database queries. -- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them. -- Generate code that prevents N+1 query problems by using eager loading. -- Use Laravel's query builder for very complex database operations. - -### Model Creation -- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. - -### APIs & Eloquent Resources -- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention. - -### Controllers & Validation -- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages. -- Check sibling Form Requests to see if the application uses array or string based validation rules. - -### Queues -- Use queued jobs for time-consuming operations with the `ShouldQueue` interface. - -### Authentication & Authorization -- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.). - -### URL Generation -- When generating links to other pages, prefer named routes and the `route()` function. - -### Configuration -- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`. - -### Testing -- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model. -- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`. -- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. - -### Vite Error -- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `yarn run build` or ask the user to run `yarn run dev` or `composer run dev`. - -=== laravel/v12 rules === - -## Laravel 12 - -- Use the `search-docs` tool to get version-specific documentation. -- Since Laravel 11, Laravel has a new streamlined file structure which this project uses. - -### Laravel 12 Structure -- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`. -- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`. -- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files. -- `bootstrap/providers.php` contains application specific service providers. -- The `app\Console\Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration. -- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration. - -### Database -- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost. -- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`. - -### Models -- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models. - -=== fluxui-pro/core rules === - -## Flux UI Pro - -- This project is using the Pro version of Flux UI. It has full access to the free components and variants, as well as full access to the Pro components and variants. -- Flux UI is a component library for Livewire. Flux is a robust, hand-crafted UI component library for your Livewire applications. It's built using Tailwind CSS and provides a set of components that are easy to use and customize. -- You should use Flux UI components when available. -- Fallback to standard Blade components if Flux is unavailable. -- If available, use the `search-docs` tool to get the exact documentation and code snippets available for this project. -- Flux UI components look like this: - - - - - -### Available Components -This is correct as of Boost installation, but there may be additional components within the codebase. - - -accordion, autocomplete, avatar, badge, brand, breadcrumbs, button, calendar, callout, card, chart, checkbox, command, composer, context, date-picker, dropdown, editor, field, file-upload, heading, icon, input, kanban, modal, navbar, otp-input, pagination, pillbox, popover, profile, radio, select, separator, skeleton, slider, switch, table, tabs, text, textarea, time-picker, toast, tooltip - - -=== livewire/core rules === - -## Livewire - -- Use the `search-docs` tool to find exact version-specific documentation for how to write Livewire and Livewire tests. -- Use the `php artisan make:livewire [Posts\CreatePost]` Artisan command to create new components. -- State should live on the server, with the UI reflecting it. -- All Livewire requests hit the Laravel backend; they're like regular HTTP requests. Always validate form data and run authorization checks in Livewire actions. - -## Livewire Best Practices -- Livewire components require a single root element. -- Use `wire:loading` and `wire:dirty` for delightful loading states. -- Add `wire:key` in loops: - - ```blade - @foreach ($items as $item) -
- {{ $item->name }} -
- @endforeach - ``` - -- Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects: - - - public function mount(User $user) { $this->user = $user; } - public function updatedSearch() { $this->resetPage(); } - - -## Testing Livewire - - - Livewire::test(Counter::class) - ->assertSet('count', 0) - ->call('increment') - ->assertSet('count', 1) - ->assertSee(1) - ->assertStatus(200); - - - - $this->get('/posts/create') - ->assertSeeLivewire(CreatePost::class); - - -=== pint/core rules === - -## Laravel Pint Code Formatter - -- You must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style. -- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues. - -=== pest/core rules === - -## Pest -### Testing -- If you need to verify a feature is working, write or update a Unit / Feature test. - -### Pest Tests -- All tests must be written using Pest. Use `php artisan make:test --pest {name}`. -- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application. -- Tests should test all of the happy paths, failure paths, and weird paths. -- Tests live in the `tests/Feature` and `tests/Unit` directories. -- Pest tests look and behave like this: - -it('is true', function () { - expect(true)->toBeTrue(); -}); - - -### Running Tests -- Run the minimal number of tests using an appropriate filter before finalizing code edits. -- To run all tests: `php artisan test --compact`. -- To run all tests in a file: `php artisan test --compact tests/Feature/ExampleTest.php`. -- To filter on a particular test name: `php artisan test --compact --filter=testName` (recommended after making a change to a related file). -- When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing. - -### Pest Assertions -- When asserting status codes on a response, use the specific method like `assertForbidden` and `assertNotFound` instead of using `assertStatus(403)` or similar, e.g.: - -it('returns all', function () { - $response = $this->postJson('/api/docs', []); - - $response->assertSuccessful(); -}); - - -### Mocking -- Mocking can be very helpful when appropriate. -- When mocking, you can use the `Pest\Laravel\mock` Pest function, but always import it via `use function Pest\Laravel\mock;` before using it. Alternatively, you can use `$this->mock()` if existing tests do. -- You can also create partial mocks using the same import or self method. - -### Datasets -- Use datasets in Pest to simplify tests that have a lot of duplicated data. This is often the case when testing validation rules, so consider this solution when writing tests for validation rules. - - -it('has emails', function (string $email) { - expect($email)->not->toBeEmpty(); -})->with([ - 'james' => 'james@laravel.com', - 'taylor' => 'taylor@laravel.com', -]); - - -=== pest/v4 rules === - -## Pest 4 - -- Pest 4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage. -- Browser testing is incredibly powerful and useful for this project. -- Browser tests should live in `tests/Browser/`. -- Use the `search-docs` tool for detailed guidance on utilizing these features. - -### Browser Testing -- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest 4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test. -- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test. -- If requested, test on multiple browsers (Chrome, Firefox, Safari). -- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints). -- Switch color schemes (light/dark mode) when appropriate. -- Take screenshots or pause tests for debugging when appropriate. - -### Example Tests - - -it('may reset the password', function () { - Notification::fake(); - - $this->actingAs(User::factory()->create()); - - $page = visit('/sign-in'); // Visit on a real browser... - - $page->assertSee('Sign In') - ->assertNoJavascriptErrors() // or ->assertNoConsoleLogs() - ->click('Forgot Password?') - ->fill('email', 'nuno@laravel.com') - ->click('Send Reset Link') - ->assertSee('We have emailed your password reset link!') - - Notification::assertSent(ResetPassword::class); -}); - - - -$pages = visit(['/', '/about', '/contact']); - -$pages->assertNoJavascriptErrors()->assertNoConsoleLogs(); - - -=== tailwindcss/core rules === - -## Tailwind CSS - -- Use Tailwind CSS classes to style HTML; check and use existing Tailwind conventions within the project before writing your own. -- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc.). -- Think through class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child carefully to limit repetition, and group elements logically. -- You can use the `search-docs` tool to get exact examples from the official documentation when needed. - -### Spacing -- When listing items, use gap utilities for spacing; don't use margins. - - -
-
Superior
-
Michigan
-
Erie
-
-
- -### Dark Mode -- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`. - -=== tailwindcss/v4 rules === - -## Tailwind CSS 4 - -- Always use Tailwind CSS v4; do not use the deprecated utilities. -- `corePlugins` is not supported in Tailwind v4. -- In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed. - - -@theme { - --color-brand: oklch(0.72 0.11 178); -} - - -- In Tailwind v4, you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3: - - - - @tailwind base; - - @tailwind components; - - @tailwind utilities; - + @import "tailwindcss"; - - -### Replaced Utilities -- Tailwind v4 removed deprecated utilities. Do not use the deprecated option; use the replacement. -- Opacity values are still numeric. - -| Deprecated | Replacement | -|------------+--------------| -| bg-opacity-* | bg-black/* | -| text-opacity-* | text-black/* | -| border-opacity-* | border-black/* | -| divide-opacity-* | divide-black/* | -| ring-opacity-* | ring-black/* | -| placeholder-opacity-* | placeholder-black/* | -| flex-shrink-* | shrink-* | -| flex-grow-* | grow-* | -| overflow-ellipsis | text-ellipsis | -| decoration-slice | box-decoration-slice | -| decoration-clone | box-decoration-clone | -
diff --git a/.junie/mcp/mcp.json b/.junie/mcp/mcp.json deleted file mode 100644 index 406fbdc..0000000 --- a/.junie/mcp/mcp.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "mcpServers": { - "laravel-boost": { - "command": "/usr/bin/php", - "args": [ - "/var/home/user/Code/einundzwanzig-app/artisan", - "boost:mcp" - ] - } - } -} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 40419ec..952f6d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -111,6 +111,13 @@ protected function isAccessible(User $user, ?string $path = null): bool ## Enums - Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. +=== tests rules === + +## Test Enforcement + +- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. +- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter. + === laravel/core rules === ## Do Things the Laravel Way diff --git a/CLAUDE.md b/CLAUDE.md index 40419ec..952f6d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -111,6 +111,13 @@ protected function isAccessible(User $user, ?string $path = null): bool ## Enums - Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. +=== tests rules === + +## Test Enforcement + +- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. +- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter. + === laravel/core rules === ## Do Things the Laravel Way diff --git a/app/Http/Controllers/Api/MeetupController.php b/app/Http/Controllers/Api/MeetupController.php index d29dc31..be21a90 100644 --- a/app/Http/Controllers/Api/MeetupController.php +++ b/app/Http/Controllers/Api/MeetupController.php @@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\Meetup; -use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; @@ -17,14 +16,10 @@ class MeetupController extends Controller public function index(Request $request) { - if (!is_numeric($request->input('user_id'))) { - abort(404); - } + $user = $request->user(); + abort_unless($user, 401); - $myMeetupIds = User::query() - ->findOrFail($request->input('user_id')) - ?->meetups - ->pluck('id'); + $myMeetupIds = $user->meetups->pluck('id'); return Meetup::query() ->select('id', 'name', 'city_id', 'slug') diff --git a/app/Http/Controllers/DownloadMeetupCalendar.php b/app/Http/Controllers/DownloadMeetupCalendar.php index fb12aaa..62d3460 100644 --- a/app/Http/Controllers/DownloadMeetupCalendar.php +++ b/app/Http/Controllers/DownloadMeetupCalendar.php @@ -29,7 +29,17 @@ class DownloadMeetupCalendar extends Controller $events = $meetup->meetupEvents()->where('start', '>=', now())->get(); $image = $meetup->getFirstMediaUrl('logo'); } elseif ($request->has('my')) { - $ids = $request->input('my'); + $validated = $request->validate([ + 'my' => ['required', 'array'], + 'my.*' => ['integer'], + ]); + + $ids = $validated['my']; + if (auth()->check()) { + $ownedIds = auth()->user()->meetups->pluck('id')->all(); + $ids = array_values(array_intersect($ids, $ownedIds)); + } + $events = MeetupEvent::query() ->with([ 'meetup', diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php index 9d7f400..fb25944 100644 --- a/app/Http/Controllers/ImageController.php +++ b/app/Http/Controllers/ImageController.php @@ -12,6 +12,8 @@ class ImageController extends Controller { public function __invoke(Request $request, $path) { + abort_if(str_contains($path, '..'), 404); + $source = new \League\Flysystem\Filesystem( new \League\Flysystem\Local\LocalFilesystemAdapter(storage_path('app')) ); diff --git a/app/Http/Controllers/LnurlAuthController.php b/app/Http/Controllers/LnurlAuthController.php index b381bad..f0e324d 100644 --- a/app/Http/Controllers/LnurlAuthController.php +++ b/app/Http/Controllers/LnurlAuthController.php @@ -154,14 +154,10 @@ final class LnurlAuthController extends Controller */ private function ensureLoginKeyExists(string $k1, int $userId): void { - $loginKey = LoginKey::where('k1', $k1)->first(); - - if (! $loginKey) { - LoginKey::create([ - 'k1' => $k1, - 'user_id' => $userId, - ]); - } + LoginKey::query()->updateOrCreate( + ['k1' => $k1], + ['user_id' => $userId], + ); } /** diff --git a/app/Jobs/FetchNostrProfileJob.php b/app/Jobs/FetchNostrProfileJob.php index 050f5a9..a2f9fda 100644 --- a/app/Jobs/FetchNostrProfileJob.php +++ b/app/Jobs/FetchNostrProfileJob.php @@ -133,6 +133,15 @@ class FetchNostrProfileJob implements ShouldQueue private function downloadAndSaveProfilePhoto(User $user, string $photoUrl): void { + if (!$this->isPublicHttpUrl($photoUrl)) { + \Log::warning('Refused to download Nostr profile photo from disallowed URL', [ + 'user_id' => $user->id, + 'url' => $photoUrl, + ]); + + return; + } + try { // Download the image from the URL $response = Http::timeout(10)->get($photoUrl); @@ -178,6 +187,41 @@ class FetchNostrProfileJob implements ShouldQueue } } + /** + * Reject URLs that are not http(s) or that resolve to a private/loopback + * address, to prevent SSRF when fetching arbitrary profile photo URLs. + */ + private function isPublicHttpUrl(string $url): bool + { + $parts = parse_url($url); + if ($parts === false || empty($parts['scheme']) || empty($parts['host'])) { + return false; + } + + if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) { + return false; + } + + $host = $parts['host']; + $ips = filter_var($host, FILTER_VALIDATE_IP) ? [$host] : (gethostbynamel($host) ?: []); + + if (empty($ips)) { + return false; + } + + foreach ($ips as $ip) { + if (!filter_var( + $ip, + FILTER_VALIDATE_IP, + FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE, + )) { + return false; + } + } + + return true; + } + private function getImageExtension(?string $contentType, string $url): string { // Try to get extension from content type diff --git a/app/Models/Course.php b/app/Models/Course.php index 6e0c04d..d36b191 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -20,11 +20,13 @@ class Course extends Model implements HasMedia use InteractsWithMedia; /** - * The attributes that aren't mass assignable. - * - * @var array + * @var array */ - protected $guarded = []; + protected $fillable = [ + 'name', + 'lecturer_id', + 'description', + ]; /** * The attributes that should be cast to native types. diff --git a/app/Models/Lecturer.php b/app/Models/Lecturer.php index 94218ce..cd21e9d 100644 --- a/app/Models/Lecturer.php +++ b/app/Models/Lecturer.php @@ -22,11 +22,24 @@ class Lecturer extends Model implements HasMedia use InteractsWithMedia; /** - * The attributes that aren't mass assignable. - * - * @var array + * @var array */ - protected $guarded = []; + protected $fillable = [ + 'name', + 'slug', + 'subtitle', + 'intro', + 'description', + 'active', + 'website', + 'twitter_username', + 'nostr', + 'lightning_address', + 'lnurl', + 'node_id', + 'paynym', + 'team_id', + ]; /** * The attributes that should be cast to native types. diff --git a/app/Models/Meetup.php b/app/Models/Meetup.php index d0e1059..9fec58c 100644 --- a/app/Models/Meetup.php +++ b/app/Models/Meetup.php @@ -23,11 +23,25 @@ class Meetup extends Model implements HasMedia use InteractsWithMedia; /** - * The attributes that aren't mass assignable. - * - * @var array + * @var array */ - protected $guarded = []; + protected $fillable = [ + 'name', + 'slug', + 'city_id', + 'intro', + 'telegram_link', + 'webpage', + 'twitter_username', + 'matrix_group', + 'nostr', + 'nostr_status', + 'simplex', + 'signal', + 'community', + 'github_data', + 'visible_on_map', + ]; /** * The attributes that should be cast to native types. diff --git a/app/Models/SelfHostedService.php b/app/Models/SelfHostedService.php index 193f438..d3ed4a0 100644 --- a/app/Models/SelfHostedService.php +++ b/app/Models/SelfHostedService.php @@ -22,7 +22,22 @@ class SelfHostedService extends Model implements HasMedia use HasTags; use InteractsWithMedia; - protected $guarded = []; + /** + * @var array + */ + protected $fillable = [ + 'name', + 'slug', + 'type', + 'intro', + 'url_clearnet', + 'url_onion', + 'url_i2p', + 'url_pkdns', + 'ip', + 'contact', + 'anon', + ]; protected $casts = [ 'id' => 'integer', diff --git a/app/Models/User.php b/app/Models/User.php index 1b03c14..cb79c79 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,7 +24,28 @@ class User extends Authenticatable implements CipherSweetEncrypted use Notifiable; use UsesCipherSweet; - protected $guarded = []; + protected $fillable = [ + 'name', + 'email', + 'password', + 'email_verified_at', + 'remember_token', + 'profile_photo_path', + 'public_key', + 'is_lecturer', + 'is_leader', + 'current_team_id', + 'current_language', + 'timezone', + 'lightning_address', + 'lnurl', + 'node_id', + 'paynym', + 'nostr', + 'lnbits', + 'change', + 'change_time', + ]; /** * The attributes that should be hidden for serialization. diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 7fbef57..0289854 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; use Laravel\Nightwatch\Facades\Nightwatch; use Laravel\Nightwatch\Http\Middleware\Sample; @@ -36,6 +37,10 @@ class AppServiceProvider extends ServiceProvider { $this->configureRateLimiting(); + if ($this->app->environment('production')) { + URL::forceScheme('https'); + } + Livewire::setUpdateRoute(function ($handle) { return Route::post('/livewire/update', $handle) ->middleware(['web', Sample::rate(0)]); diff --git a/bootstrap/app.php b/bootstrap/app.php index dc34ac6..c7d97bf 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -16,6 +16,8 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware) { + $middleware->trustProxies(at: '*'); + $middleware->web(append: [ DomainMiddleware::class, LangCountrySession::class, diff --git a/resources/js/nostrLogin.js b/resources/js/nostrLogin.js index ec0f21e..98cda94 100644 --- a/resources/js/nostrLogin.js +++ b/resources/js/nostrLogin.js @@ -1,5 +1,3 @@ -import {npubEncode} from "nostr-tools/nip19"; - export default () => ({ pollingInterval: null, errorCheckInterval: null, @@ -13,11 +11,21 @@ export default () => ({ }, async openNostrLogin() { - const pubkey = await window.nostr.getPublicKey(); - const npub = npubEncode(pubkey); - console.log(pubkey); - console.log(npub); - this.$dispatch('nostrLoggedIn', {pubkey: npub}); + const livewireComponent = this.$el.closest('[wire\\:id]')?.__livewire; + const challenge = livewireComponent?.$wire?.nostrChallenge; + if (!challenge) { + this.showAuthError('Login challenge missing. Please reload and try again.'); + return; + } + + const signedEvent = await window.nostr.signEvent({ + kind: 22242, + created_at: Math.floor(Date.now() / 1000), + tags: [['challenge', challenge]], + content: '', + }); + + this.$dispatch('nostrLoggedIn', {signedEvent}); }, initErrorPolling() { diff --git a/resources/views/components/layouts/app/sidebar.blade.php b/resources/views/components/layouts/app/sidebar.blade.php index 56cb292..7d9c4df 100644 --- a/resources/views/components/layouts/app/sidebar.blade.php +++ b/resources/views/components/layouts/app/sidebar.blade.php @@ -29,7 +29,7 @@
{{ __('Meetups') }} {{ request()->route('country') }}
@@ -48,7 +48,7 @@
{{ __('Karte') }} {{ request()->route('country') }}
@@ -81,7 +81,7 @@
{{ __('Kurse') }} {{ request()->route('country') }}
@@ -92,7 +92,7 @@
{{ __('Dozenten') }} {{ request()->route('country') }}
diff --git a/resources/views/livewire/auth/login.blade.php b/resources/views/livewire/auth/login.blade.php index 33ae6c5..eeb5e24 100644 --- a/resources/views/livewire/auth/login.blade.php +++ b/resources/views/livewire/auth/login.blade.php @@ -13,10 +13,13 @@ use Illuminate\Support\Facades\Session; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; use Livewire\Attributes\Layout; +use Livewire\Attributes\Locked; use Livewire\Attributes\On; use Livewire\Attributes\Validate; use Livewire\Component; use SimpleSoftwareIO\QrCode\Facades\QrCode; +use swentel\nostr\Event\Event as NostrEvent; +use swentel\nostr\Key\Key as NostrKey; new #[Layout('components.layouts.auth')] class extends Component { @@ -42,6 +45,11 @@ class extends Component { public ?string $authError = null; + #[Locked] + public ?string $nostrChallenge = null; + + private const NOSTR_CHALLENGE_TTL_SECONDS = 300; + /** * Handle authError property type conversion. * Ensure array values from frontend are converted to string or null. @@ -57,6 +65,10 @@ class extends Component { { $this->currentLangCountry = session('lang_country') ?? 'de-DE'; + $this->nostrChallenge = bin2hex(random_bytes(32)); + Session::put('nostr_login_challenge', $this->nostrChallenge); + Session::put('nostr_login_challenge_expires_at', now()->addSeconds(self::NOSTR_CHALLENGE_TTL_SECONDS)->timestamp); + // Nur beim ersten Mount initialisieren if ($this->k1 === null) { $this->k1 = bin2hex(str()->random(32)); @@ -80,18 +92,19 @@ class extends Component { } #[On('nostrLoggedIn')] - public function loginListener($pubkey): void + public function loginListener($signedEvent = null): void { - $user = \App\Models\User::query()->where('nostr', $pubkey)->first(); + $npub = $this->verifyNostrLoginEvent($signedEvent); + + $user = User::query()->where('nostr', $npub)->first(); if (!$user) { $fakeName = str()->random(10); - // create User $user = User::create([ 'public_key' => null, 'is_lecturer' => true, 'name' => $fakeName, - 'email' => str($pubkey)->substr(-12).'@portal.einundzwanzig.space', - 'nostr' => $pubkey, + 'email' => str($npub)->substr(-12).'@portal.einundzwanzig.space', + 'nostr' => $npub, 'lnbits' => [ 'read_key' => null, 'url' => null, @@ -137,6 +150,79 @@ class extends Component { ); } + /** + * Verify a NIP-42-style signed login event and return the user's npub. + * + * Throws ValidationException on any invalid input — never trust client data. + */ + protected function verifyNostrLoginEvent(mixed $signedEvent): string + { + if (!is_array($signedEvent)) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + $required = ['id', 'pubkey', 'created_at', 'kind', 'tags', 'content', 'sig']; + foreach ($required as $key) { + if (!array_key_exists($key, $signedEvent)) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + } + + if ((int) $signedEvent['kind'] !== 22242) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + $expectedChallenge = Session::get('nostr_login_challenge'); + $expiresAt = (int) Session::get('nostr_login_challenge_expires_at', 0); + + if (!is_string($expectedChallenge) || $expectedChallenge === '' || $expiresAt < now()->timestamp) { + Session::forget(['nostr_login_challenge', 'nostr_login_challenge_expires_at']); + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + $challengeFromEvent = null; + foreach ($signedEvent['tags'] as $tag) { + if (is_array($tag) && ($tag[0] ?? null) === 'challenge') { + $challengeFromEvent = (string) ($tag[1] ?? ''); + break; + } + } + + if ($challengeFromEvent === null || !hash_equals($expectedChallenge, $challengeFromEvent)) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + $createdAt = (int) $signedEvent['created_at']; + if (abs(now()->timestamp - $createdAt) > self::NOSTR_CHALLENGE_TTL_SECONDS) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + $eventJson = json_encode([ + 'id' => (string) $signedEvent['id'], + 'pubkey' => (string) $signedEvent['pubkey'], + 'created_at' => $createdAt, + 'kind' => 22242, + 'tags' => $signedEvent['tags'], + 'content' => (string) $signedEvent['content'], + 'sig' => (string) $signedEvent['sig'], + ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + $isValid = false; + try { + $isValid = (new NostrEvent())->verify($eventJson); + } catch (\Throwable $e) { + $isValid = false; + } + + if (!$isValid) { + throw ValidationException::withMessages(['email' => __('auth.failed')]); + } + + Session::forget(['nostr_login_challenge', 'nostr_login_challenge_expires_at']); + + return (new NostrKey())->convertPublicKeyToBech32((string) $signedEvent['pubkey']); + } + /** * Ensure the authentication request is not rate limited. */ diff --git a/resources/views/livewire/auth/register.blade.php b/resources/views/livewire/auth/register.blade.php deleted file mode 100644 index 33601df..0000000 --- a/resources/views/livewire/auth/register.blade.php +++ /dev/null @@ -1,104 +0,0 @@ -validate([ - 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], - 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], - ]); - - $validated['password'] = Hash::make($validated['password']); - - event(new Registered(($user = User::create($validated)))); - - Auth::login($user); - - $this->redirectIntended(route('dashboard', ['country' => str(session('lang_country', config('app.domain_country')))->after('-')->lower()],absolute: false), navigate: true); - } -}; ?> - -
- - - - - -
- - - - - - - - - - - - -
- - {{ __('Create account') }} - -
- - -
- {{ __('Already have an account?') }} - {{ __('Log in') }} -
-
diff --git a/resources/views/livewire/courses/create.blade.php b/resources/views/livewire/courses/create.blade.php index 1f8a439..1fbe07d 100644 --- a/resources/views/livewire/courses/create.blade.php +++ b/resources/views/livewire/courses/create.blade.php @@ -14,7 +14,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $logo; public string $name = ''; diff --git a/resources/views/livewire/courses/edit.blade.php b/resources/views/livewire/courses/edit.blade.php index 6e88904..c3817c8 100644 --- a/resources/views/livewire/courses/edit.blade.php +++ b/resources/views/livewire/courses/edit.blade.php @@ -16,7 +16,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $logo; public Course $course; diff --git a/resources/views/livewire/courses/landingpage.blade.php b/resources/views/livewire/courses/landingpage.blade.php index 8702fbe..cb59e9d 100644 --- a/resources/views/livewire/courses/landingpage.blade.php +++ b/resources/views/livewire/courses/landingpage.blade.php @@ -87,7 +87,7 @@ class extends Component {
@if($course->lecturer->website) - Website @@ -96,7 +96,7 @@ class extends Component { @if($course->lecturer->twitter_username) + target="_blank" rel="noopener noreferrer" variant="ghost" size="xs"> @@ -106,7 +106,7 @@ class extends Component { @endif @if($course->lecturer->nostr) - Nostr @@ -173,6 +173,7 @@ class extends Component {
{{ $country->code }}
{{ $country->name }}
diff --git a/resources/views/livewire/lecturers/create.blade.php b/resources/views/livewire/lecturers/create.blade.php index 085a2df..baf2dd4 100644 --- a/resources/views/livewire/lecturers/create.blade.php +++ b/resources/views/livewire/lecturers/create.blade.php @@ -13,7 +13,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $avatar; public string $name = ''; diff --git a/resources/views/livewire/lecturers/edit.blade.php b/resources/views/livewire/lecturers/edit.blade.php index f9fffbf..ce31d46 100644 --- a/resources/views/livewire/lecturers/edit.blade.php +++ b/resources/views/livewire/lecturers/edit.blade.php @@ -15,7 +15,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $avatar; public Lecturer $lecturer; @@ -45,8 +45,17 @@ class extends Component { #[Locked] public ?string $updated_at = null; + protected function authorizeAccess(): void + { + if (!is_null($this->lecturer->created_by) && auth()->id() !== $this->lecturer->created_by) { + abort(403); + } + } + public function mount(): void { + $this->authorizeAccess(); + $this->lecturer->load('media'); $this->name = $this->lecturer->name ?? ''; @@ -70,6 +79,8 @@ class extends Component { public function updateLecturer(): void { + $this->authorizeAccess(); + $validated = $this->validate([ 'name' => ['required', 'string', 'max:255', Rule::unique('lecturers')->ignore($this->lecturer->id)], 'subtitle' => ['nullable', 'string'], diff --git a/resources/views/livewire/meetups/create.blade.php b/resources/views/livewire/meetups/create.blade.php index b8bb054..3822016 100644 --- a/resources/views/livewire/meetups/create.blade.php +++ b/resources/views/livewire/meetups/create.blade.php @@ -15,7 +15,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $logo; // Basic Information diff --git a/resources/views/livewire/meetups/edit.blade.php b/resources/views/livewire/meetups/edit.blade.php index 7ea8e30..e093c36 100644 --- a/resources/views/livewire/meetups/edit.blade.php +++ b/resources/views/livewire/meetups/edit.blade.php @@ -17,7 +17,7 @@ class extends Component { use WithFileUploads; use SeoTrait; - #[Validate('image|max:10240')] // 10MB Max + #[Validate('image|mimes:jpeg,png,webp|max:5120|dimensions:max_width=4000,max_height=4000')] public $logo; public Meetup $meetup; @@ -83,8 +83,46 @@ class extends Component { \Flux\Flux::modal('add-city')->close(); } + protected function authorizeAccess(): void + { + if (!is_null($this->meetup->created_by) && auth()->id() !== $this->meetup->created_by) { + abort(403); + } + } + + /** + * Whitelist the keys allowed inside github_data and coerce types so a + * tampered payload cannot smuggle arbitrary keys into the stored JSON. + */ + protected function sanitizeGithubData(?string $raw): ?array + { + if (empty($raw)) { + return null; + } + + $decoded = json_decode($raw, true); + if (!is_array($decoded)) { + return null; + } + + $clean = []; + if (array_key_exists('top', $decoded) && (is_string($decoded['top']) || is_numeric($decoded['top']))) { + $clean['top'] = (string) $decoded['top']; + } + if (array_key_exists('left', $decoded) && (is_string($decoded['left']) || is_numeric($decoded['left']))) { + $clean['left'] = (string) $decoded['left']; + } + if (array_key_exists('state', $decoded) && is_string($decoded['state'])) { + $clean['state'] = mb_substr($decoded['state'], 0, 64); + } + + return $clean === [] ? null : $clean; + } + public function mount(): void { + $this->authorizeAccess(); + $this->meetup->load('media'); // Basic Information @@ -117,6 +155,8 @@ class extends Component { public function updateMeetup(): void { + $this->authorizeAccess(); + $validated = $this->validate([ 'name' => ['required', 'string', 'max:255', Rule::unique('meetups')->ignore($this->meetup->id)], 'city_id' => ['nullable', 'exists:cities,id'], @@ -129,19 +169,10 @@ class extends Component { 'simplex' => ['nullable', 'string', 'max:255'], 'signal' => ['nullable', 'string', 'max:255'], 'community' => ['required', 'string', 'max:255'], + 'github_data' => ['nullable', 'json'], ]); - // Convert github_data string back to array if provided - if (!empty($validated['github_data'])) { - $decoded = json_decode($validated['github_data'], true); - if (json_last_error() === JSON_ERROR_NONE) { - $validated['github_data'] = $decoded; - } else { - $validated['github_data'] = null; - } - } else { - $validated['github_data'] = null; - } + $validated['github_data'] = $this->sanitizeGithubData($validated['github_data'] ?? null); $this->meetup->update($validated); diff --git a/resources/views/livewire/meetups/landingpage.blade.php b/resources/views/livewire/meetups/landingpage.blade.php index c8d4a28..d710832 100644 --- a/resources/views/livewire/meetups/landingpage.blade.php +++ b/resources/views/livewire/meetups/landingpage.blade.php @@ -84,7 +84,7 @@ class extends Component {
@if($meetup->webpage) - Webseite @@ -92,7 +92,7 @@ class extends Component { @endif @if($meetup->telegram_link) - Telegram @@ -100,7 +100,7 @@ class extends Component { @endif @if($meetup->twitter_username) - matrix_group) - Matrix @@ -119,14 +119,14 @@ class extends Component { @endif @if($meetup->signal) - + Signal @endif @if($meetup->simplex) - SimpleX diff --git a/routes/api.php b/routes/api.php index 84aa625..315ce4b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -11,7 +11,7 @@ use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; -Route::middleware([]) +Route::middleware(['throttle:60,1']) ->as('api.') ->group(function () { Route::resource('countries', CountryController::class); @@ -22,7 +22,9 @@ Route::middleware([]) Route::resource('cities', CityController::class); Route::resource('venues', VenueController::class); Route::get('highscores', [HighscoreController::class, 'index'])->name('highscores.index'); - Route::post('highscores', [HighscoreController::class, 'store'])->name('highscores.store'); + Route::post('highscores', [HighscoreController::class, 'store']) + ->middleware('throttle:10,1') + ->name('highscores.store'); Route::get('nostrplebs', function () { return User::query() ->select([ diff --git a/routes/auth.php b/routes/auth.php index a12dee9..cdddf29 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -1,6 +1,7 @@ name('login'); - Route::livewire('/register', 'auth.register') - ->name('register'); - Route::livewire('/forgot-password', 'auth.forgot-password') ->name('password.request'); @@ -31,5 +29,5 @@ Route::middleware('auth') ->name('password.confirm'); }); -Route::post('logout', App\Livewire\Actions\Logout::class) +Route::post('logout', Logout::class) ->name('logout'); diff --git a/routes/web.php b/routes/web.php index 830959b..e1909bd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,20 +2,13 @@ use App\Http\Controllers\DownloadMeetupCalendar; use App\Http\Controllers\ImageController; -use App\Jobs\FetchNostrProfileJob; use App\Livewire\Helper\FollowTheRabbit; -use App\Models\User; use Illuminate\Support\Facades\Route; use Laravel\Nightwatch\Http\Middleware\Sample; // Redirect root URL to 'welcome' page Route::redirect('/', 'welcome'); -// Test route that dispatches a job to fetch Nostr profile for user with ID 1426 -Route::get('test', function () { - FetchNostrProfileJob::dispatchSync(User::find(1426)); -}); - // Error page route that aborts with given HTTP status code (digits only, // constrained to valid 4xx/5xx range to avoid TypeErrors from bot scans). Route::get('error/{code}', function (string $code) { @@ -50,12 +43,12 @@ Route::livewire('/kaninchenbau', FollowTheRabbit::class) // Generic image handler route that serves images from storage Route::get('/img/{path}', ImageController::class) - ->where('path', '.*') + ->where('path', '[A-Za-z0-9._\-/]+') ->name('img'); // Public image handler route for serving public images Route::get('/img-public/{path}', ImageController::class) - ->where('path', '.*') + ->where('path', '[A-Za-z0-9._\-/]+') ->name('imgPublic'); // Welcome page route using Volt component diff --git a/tests/Browser/AuthFlowTest.php b/tests/Browser/AuthFlowTest.php index c203132..3fceba1 100644 --- a/tests/Browser/AuthFlowTest.php +++ b/tests/Browser/AuthFlowTest.php @@ -7,9 +7,3 @@ it('renders the login page with QR code and language selector', function () { ->assertSee('Bitcoin, not blockchain') ->assertNoJavaScriptErrors(); }); - -it('renders the registration page', function () { - $page = visit('/register'); - - $page->assertNoJavaScriptErrors(); -}); diff --git a/tests/Browser/Cities/CityVenueCrudFlowTest.php b/tests/Browser/Cities/CityVenueCrudFlowTest.php new file mode 100644 index 0000000..9f6fdcf --- /dev/null +++ b/tests/Browser/Cities/CityVenueCrudFlowTest.php @@ -0,0 +1,67 @@ +country = Country::factory()->create(['code' => 'de', 'name' => 'Deutschland']); +}); + +it('creates a new city end-to-end with valid coordinates', function () { + actingAsUser(); + + $page = visit('/de/city-create'); + + $page->fill('[wire\\:model="name"]', 'BrowserTestCity') + ->fill('[wire\\:model="latitude"]', '52.520008') + ->fill('[wire\\:model="longitude"]', '13.404954') + ->click('[data-flux-button][type="submit"]') + ->wait(2) + ->assertNoJavaScriptErrors(); + + expect(City::query()->where('name', 'BrowserTestCity')->exists())->toBeTrue(); +}); + +it('shows validation errors when city form is submitted empty', function () { + actingAsUser(); + + $page = visit('/de/city-create'); + + $page->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(City::query()->count())->toBe(0); +}); + +it('creates a new venue connected to an existing city', function () { + actingAsUser(); + $city = City::factory()->create(['country_id' => $this->country->id, 'name' => 'VenueTestCity']); + + $page = visit('/de/venue-create'); + + $page->fill('[wire\\:model="name"]', 'BrowserTestVenue') + ->fill('[wire\\:model="street"]', 'Teststraße 1'); + $page->script("Livewire.getByName('venues.create')[0].set('city_id', {$city->id})"); + $page->wait(0.5) + ->click('[data-flux-button][type="submit"]') + ->wait(2) + ->assertNoJavaScriptErrors(); + + expect(Venue::query()->where('name', 'BrowserTestVenue')->exists())->toBeTrue(); +}); + +it('shows a validation error when creating a venue without a name', function () { + actingAsUser(); + City::factory()->create(['country_id' => $this->country->id]); + + $page = visit('/de/venue-create'); + + $page->fill('[wire\\:model="street"]', 'No-Name Street') + ->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Venue::query()->count())->toBe(0); +}); diff --git a/tests/Browser/Lecturers/LecturerCrudFlowTest.php b/tests/Browser/Lecturers/LecturerCrudFlowTest.php new file mode 100644 index 0000000..225a1b8 --- /dev/null +++ b/tests/Browser/Lecturers/LecturerCrudFlowTest.php @@ -0,0 +1,58 @@ +fill('[wire\\:model="name"]', 'BrowserTester Saylor') + ->fill('[wire\\:model="subtitle"]', 'Browser Test Subject') + ->fill('[wire\\:model="intro"]', 'A short intro line.') + ->click('[data-flux-button][type="submit"]') + ->wait(2) + ->assertNoJavaScriptErrors(); + + expect(Lecturer::query()->where('name', 'BrowserTester Saylor')->exists())->toBeTrue(); +}); + +it('shows a required error when submitting without a lecturer name', function () { + actingAsUser(); + + $page = visit('/de/lecturer-create'); + + $page->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Lecturer::query()->count())->toBe(0); +}); + +it('rejects creation when the lecturer name already exists', function () { + actingAsUser(); + Lecturer::factory()->create(['name' => 'Existing Saylor']); + + $page = visit('/de/lecturer-create'); + + $page->fill('[wire\\:model="name"]', 'Existing Saylor') + ->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Lecturer::query()->where('name', 'Existing Saylor')->count())->toBe(1); +}); + +it('rejects creation with an invalid website URL', function () { + actingAsUser(); + + $page = visit('/de/lecturer-create'); + + $page->fill('[wire\\:model="name"]', 'Bad URL Lecturer') + ->fill('[wire\\:model="website"]', 'not-a-url') + ->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Lecturer::query()->where('name', 'Bad URL Lecturer')->exists())->toBeFalse(); +}); diff --git a/tests/Browser/Meetups/MeetupCrudFlowTest.php b/tests/Browser/Meetups/MeetupCrudFlowTest.php new file mode 100644 index 0000000..50a8782 --- /dev/null +++ b/tests/Browser/Meetups/MeetupCrudFlowTest.php @@ -0,0 +1,60 @@ +country = Country::factory()->create(['code' => 'de', 'name' => 'Deutschland']); + $this->city = City::factory()->create([ + 'country_id' => $this->country->id, + 'name' => 'BrowserMeetupTestCity', + ]); +}); + +it('creates a new meetup end-to-end via the create form', function () { + actingAsUser(); + $cityId = $this->city->id; + + $page = visit('/de/meetup-create'); + + $page->fill('[wire\\:model="name"]', 'BrowserSeeded Meetup'); + $page->script("Livewire.getByName('meetups.create')[0].set('city_id', {$cityId})"); + $page->wait(0.5) + ->select('[wire\\:model="community"]', 'einundzwanzig'); + $page->script("Livewire.getByName('meetups.create')[0].call('createMeetup')"); + $page->wait(2) + ->assertNoJavaScriptErrors(); + + expect(Meetup::query()->where('name', 'BrowserSeeded Meetup')->exists())->toBeTrue(); +}); + +it('blocks meetup creation when the name is missing', function () { + actingAsUser(); + $cityId = $this->city->id; + + $page = visit('/de/meetup-create'); + + $page->script("Livewire.getByName('meetups.create')[0].set('city_id', {$cityId})"); + $page->wait(0.5) + ->select('[wire\\:model="community"]', 'einundzwanzig'); + $page->script("Livewire.getByName('meetups.create')[0].call('createMeetup')"); + $page->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Meetup::query()->count())->toBe(0); +}); + +it('blocks meetup creation when no city is selected', function () { + actingAsUser(); + + $page = visit('/de/meetup-create'); + + $page->fill('[wire\\:model="name"]', 'NoCityMeetup') + ->select('[wire\\:model="community"]', 'einundzwanzig'); + $page->script("Livewire.getByName('meetups.create')[0].call('createMeetup')"); + $page->wait(1) + ->assertNoJavaScriptErrors(); + + expect(Meetup::query()->where('name', 'NoCityMeetup')->exists())->toBeFalse(); +}); diff --git a/tests/Browser/Public/ListFilteringTest.php b/tests/Browser/Public/ListFilteringTest.php new file mode 100644 index 0000000..98a7a2d --- /dev/null +++ b/tests/Browser/Public/ListFilteringTest.php @@ -0,0 +1,57 @@ +country = Country::factory()->create(['code' => 'de']); + $this->city = City::factory()->create(['country_id' => $this->country->id]); +}); + +it('renders all seeded services on the public services index', function () { + SelfHostedService::factory()->create(['name' => 'NodeAlpha', 'type' => SelfHostedServiceType::Mempool]); + SelfHostedService::factory()->create(['name' => 'BetaService', 'type' => SelfHostedServiceType::Other]); + + $page = visit('/de/services'); + + $page->assertSee('NodeAlpha') + ->assertSee('BetaService') + ->assertNoJavaScriptErrors(); +}); + +it('filters services by clicking a type-badge in the type-cloud', function () { + SelfHostedService::factory()->create(['name' => 'OnlyMempoolNode', 'type' => SelfHostedServiceType::Mempool]); + SelfHostedService::factory()->create(['name' => 'OnlyOtherThing', 'type' => SelfHostedServiceType::Other]); + + $page = visit('/de/services'); + + $page->assertSee('OnlyMempoolNode') + ->assertSee('OnlyOtherThing') + ->click('[wire\\:click="filterByType(\'mempool\')"]') + ->wait(1) + ->assertSee('OnlyMempoolNode') + ->assertDontSee('OnlyOtherThing') + ->assertNoJavaScriptErrors(); +}); + +it('shows seeded meetups on the public meetups index', function () { + Meetup::factory()->create([ + 'city_id' => $this->city->id, + 'name' => 'BrowserSeeded Meetup XYZ', + 'visible_on_map' => true, + ]); + + $page = visit('/de/meetups'); + + $page->assertSee('BrowserSeeded Meetup XYZ') + ->assertNoJavaScriptErrors(); +}); diff --git a/tests/Browser/Services/ServiceCrudFlowTest.php b/tests/Browser/Services/ServiceCrudFlowTest.php new file mode 100644 index 0000000..a9d92b6 --- /dev/null +++ b/tests/Browser/Services/ServiceCrudFlowTest.php @@ -0,0 +1,49 @@ +fill('[wire\\:model="form.name"]', 'BrowserTestNode') + ->select('[wire\\:model="form.type"]', SelfHostedServiceType::Mempool->value) + ->fill('[wire\\:model="form.url_clearnet"]', 'https://browsertest.example.com') + ->fill('[wire\\:model="form.intro"]', 'A node spun up by a browser test.') + ->click('[data-flux-button][type="submit"]') + ->wait(2) + ->assertNoJavaScriptErrors() + ->assertSee('BrowserTestNode'); + + expect(SelfHostedService::query()->where('name', 'BrowserTestNode')->exists())->toBeTrue(); +}); + +it('blocks submission without a name and shows a required error', function () { + actingAsUser(); + + $page = visit('/de/service-create'); + + $page->select('[wire\\:model="form.type"]', SelfHostedServiceType::Other->value) + ->fill('[wire\\:model="form.url_clearnet"]', 'https://no-name.example.com') + ->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(SelfHostedService::query()->count())->toBe(0); +}); + +it('rejects submission when no URL or IP is provided', function () { + actingAsUser(); + + $page = visit('/de/service-create'); + + $page->fill('[wire\\:model="form.name"]', 'NoUrlService') + ->select('[wire\\:model="form.type"]', SelfHostedServiceType::Other->value) + ->click('[data-flux-button][type="submit"]') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(SelfHostedService::query()->where('name', 'NoUrlService')->exists())->toBeFalse(); +}); diff --git a/tests/Browser/Settings/ProfileUpdateTest.php b/tests/Browser/Settings/ProfileUpdateTest.php new file mode 100644 index 0000000..ab0dab7 --- /dev/null +++ b/tests/Browser/Settings/ProfileUpdateTest.php @@ -0,0 +1,46 @@ + 'Old Name']); + + $page = visit('/de/settings/profile'); + + $page->assertSee('Old Name') + ->fill('name', 'New Browser Name') + ->click('Save') + ->wait(1) + ->assertSee('Saved.') + ->assertNoJavaScriptErrors(); + + expect($user->refresh()->name)->toBe('New Browser Name'); +}); + +it('shows a validation error when the profile name is cleared', function () { + actingAsUser(['name' => 'Original']); + + $page = visit('/de/settings/profile'); + + $page->fill('name', '') + ->click('Save') + ->wait(1) + ->assertNoJavaScriptErrors(); + + expect(User::query()->where('name', '')->exists())->toBeFalse(); +}); + +it('still shows the updated name after a full page reload', function () { + $user = actingAsUser(['name' => 'Before Reload']); + + $page = visit('/de/settings/profile'); + $page->fill('name', 'After Reload') + ->click('Save') + ->wait(1); + + $reloaded = visit('/de/settings/profile'); + $reloaded->assertSee('After Reload') + ->assertNoJavaScriptErrors(); + + expect($user->refresh()->name)->toBe('After Reload'); +}); diff --git a/tests/Browser/SmokeTest.php b/tests/Browser/SmokeTest.php index ce79a29..aff6b77 100644 --- a/tests/Browser/SmokeTest.php +++ b/tests/Browser/SmokeTest.php @@ -20,7 +20,6 @@ it('loads all listed public pages without console errors or JS errors', function $pages = visit([ '/welcome', '/login', - '/register', '/forgot-password', '/de/meetups', '/de/courses', diff --git a/tests/Feature/Auth/NostrLoginTest.php b/tests/Feature/Auth/NostrLoginTest.php index 64ab409..9269d74 100644 --- a/tests/Feature/Auth/NostrLoginTest.php +++ b/tests/Feature/Auth/NostrLoginTest.php @@ -3,17 +3,61 @@ use App\Jobs\FetchNostrProfileJob; use App\Models\User; use Illuminate\Support\Facades\Queue; +use Illuminate\Support\Facades\Session; use Livewire\Livewire; +use swentel\nostr\Event\Event as NostrEvent; +use swentel\nostr\Key\Key as NostrKey; +use swentel\nostr\Sign\Sign as NostrSign; + +/** + * Build a NIP-42-style signed login event using the challenge that the + * login component placed in the session during mount(), and return + * [signedEventArray, npubBech32]. + * + * @return array{0: array, 1: string} + */ +function makeSignedNostrLoginEvent(): array +{ + $keyGen = new NostrKey; + $privateKey = $keyGen->generatePrivateKey(); + $publicKey = $keyGen->getPublicKey($privateKey); + + $challenge = Session::get('nostr_login_challenge'); + + $event = new NostrEvent; + $event->setKind(22242) + ->setCreatedAt(time()) + ->setContent('') + ->setTags([['challenge', (string) $challenge]]); + + (new NostrSign)->signEvent($event, $privateKey); + + $signed = [ + 'id' => $event->getId(), + 'pubkey' => $event->getPublicKey(), + 'created_at' => $event->getCreatedAt(), + 'kind' => $event->getKind(), + 'tags' => $event->getTags(), + 'content' => $event->getContent(), + 'sig' => $event->getSignature(), + ]; + + $npub = $keyGen->convertPublicKeyToBech32($publicKey); + + return [$signed, $npub]; +} it('creates a new user and dispatches FetchNostrProfileJob when an unknown pubkey logs in', function () { Queue::fake(); - $pubkey = 'npub1'.str_repeat('z', 58); - Livewire::test('auth.login') - ->dispatch('nostrLoggedIn', pubkey: $pubkey) + $component = Livewire::test('auth.login'); + [$signedEvent, $npub] = makeSignedNostrLoginEvent(); + + $component + ->dispatch('nostrLoggedIn', signedEvent: $signedEvent) ->assertRedirect(); - $user = User::query()->where('nostr', $pubkey)->first(); + $user = User::query()->where('nostr', $npub)->first(); expect($user)->not->toBeNull() ->and((bool) $user->is_lecturer)->toBeTrue() ->and($user->email)->toEndWith('@portal.einundzwanzig.space'); @@ -24,14 +68,17 @@ it('creates a new user and dispatches FetchNostrProfileJob when an unknown pubke it('logs in an existing user without creating a duplicate when their pubkey is already known', function () { Queue::fake(); - $pubkey = 'npub1'.str_repeat('a', 58); - $existing = User::factory()->create(['nostr' => $pubkey]); - Livewire::test('auth.login') - ->dispatch('nostrLoggedIn', pubkey: $pubkey) + $component = Livewire::test('auth.login'); + [$signedEvent, $npub] = makeSignedNostrLoginEvent(); + + $existing = User::factory()->create(['nostr' => $npub]); + + $component + ->dispatch('nostrLoggedIn', signedEvent: $signedEvent) ->assertRedirect(); - expect(User::query()->where('nostr', $pubkey)->count())->toBe(1); + expect(User::query()->where('nostr', $npub)->count())->toBe(1); expect(auth()->id())->toBe($existing->id); Queue::assertPushed(FetchNostrProfileJob::class); }); diff --git a/tests/Feature/Lecturers/LecturerCrudTest.php b/tests/Feature/Lecturers/LecturerCrudTest.php index c3547c1..c382515 100644 --- a/tests/Feature/Lecturers/LecturerCrudTest.php +++ b/tests/Feature/Lecturers/LecturerCrudTest.php @@ -43,8 +43,8 @@ it('rejects lecturer creation with invalid website URL', function () { }); it('updates an existing lecturer', function () { - $lecturer = Lecturer::factory()->create(['name' => 'Old Name']); - actingAsUser(); + $owner = actingAsUser(); + $lecturer = Lecturer::factory()->create(['name' => 'Old Name', 'created_by' => $owner->id]); Livewire::test('lecturers.edit', ['lecturer' => $lecturer]) ->set('name', 'New Name') diff --git a/tests/Feature/Livewire/AuthMountTest.php b/tests/Feature/Livewire/AuthMountTest.php index cf15f1a..b8a13ba 100644 --- a/tests/Feature/Livewire/AuthMountTest.php +++ b/tests/Feature/Livewire/AuthMountTest.php @@ -6,10 +6,6 @@ it('mounts the auth.login component', function () { Livewire::test('auth.login')->assertStatus(200); }); -it('mounts the auth.register component', function () { - Livewire::test('auth.register')->assertStatus(200); -}); - it('mounts the auth.forgot-password component', function () { Livewire::test('auth.forgot-password')->assertStatus(200); }); diff --git a/tests/Feature/Livewire/CrudMountTest.php b/tests/Feature/Livewire/CrudMountTest.php index 233c584..a831b0a 100644 --- a/tests/Feature/Livewire/CrudMountTest.php +++ b/tests/Feature/Livewire/CrudMountTest.php @@ -20,9 +20,17 @@ it('mounts lecturers.create when authenticated', function () { Livewire::test('lecturers.create')->assertStatus(200); }); -it('mounts lecturers.edit when authenticated', function () { +it('mounts lecturers.edit when authenticated as the lecturer creator', function () { + $owner = actingAsUser(); + $lecturer = Lecturer::factory()->create(['created_by' => $owner->id]); + + Livewire::test('lecturers.edit', ['lecturer' => $lecturer])->assertStatus(200); +}); + +it('aborts lecturers.edit with 403 when authenticated user is not the creator', function () { actingAsUser(); - Livewire::test('lecturers.edit', ['lecturer' => $this->lecturer])->assertStatus(200); + + Livewire::test('lecturers.edit', ['lecturer' => $this->lecturer])->assertStatus(403); }); it('mounts cities.create when authenticated', function () { diff --git a/tests/Feature/Livewire/MeetupMountTest.php b/tests/Feature/Livewire/MeetupMountTest.php index e67b690..e1b9883 100644 --- a/tests/Feature/Livewire/MeetupMountTest.php +++ b/tests/Feature/Livewire/MeetupMountTest.php @@ -29,9 +29,20 @@ it('mounts meetups.create when authenticated', function () { Livewire::test('meetups.create')->assertStatus(200); }); -it('mounts meetups.edit when authenticated', function () { +it('mounts meetups.edit when authenticated as the meetup creator', function () { + $owner = actingAsUser(); + $meetup = Meetup::factory()->create([ + 'city_id' => $this->city->id, + 'created_by' => $owner->id, + ]); + + Livewire::test('meetups.edit', ['meetup' => $meetup])->assertStatus(200); +}); + +it('aborts meetups.edit with 403 when authenticated user is not the creator', function () { actingAsUser(); - Livewire::test('meetups.edit', ['meetup' => $this->meetup])->assertStatus(200); + + Livewire::test('meetups.edit', ['meetup' => $this->meetup])->assertStatus(403); }); it('mounts meetups.create-edit-events for new event', function () { diff --git a/tests/Feature/Meetups/EditMeetupTest.php b/tests/Feature/Meetups/EditMeetupTest.php index d130daa..f40ecc7 100644 --- a/tests/Feature/Meetups/EditMeetupTest.php +++ b/tests/Feature/Meetups/EditMeetupTest.php @@ -8,36 +8,50 @@ use Livewire\Livewire; beforeEach(function () { $country = Country::factory()->create(['code' => 'de']); $this->city = City::factory()->create(['country_id' => $country->id]); - $this->meetup = Meetup::factory()->create(['city_id' => $this->city->id, 'name' => 'Original Name']); }); it('updates an existing Meetup name when authenticated', function () { - actingAsUser(); + $owner = actingAsUser(); + $meetup = Meetup::factory()->create([ + 'city_id' => $this->city->id, + 'name' => 'Original Name', + 'created_by' => $owner->id, + ]); - Livewire::test('meetups.edit', ['meetup' => $this->meetup]) + Livewire::test('meetups.edit', ['meetup' => $meetup]) ->set('name', 'Updated Name') ->set('city_id', $this->city->id) ->set('community', 'einundzwanzig') ->call('updateMeetup') ->assertHasNoErrors(); - expect($this->meetup->refresh()->name)->toBe('Updated Name'); + expect($meetup->refresh()->name)->toBe('Updated Name'); }); it('rejects update when name collides with another existing Meetup', function () { + $owner = actingAsUser(); + $meetup = Meetup::factory()->create([ + 'city_id' => $this->city->id, + 'name' => 'Original Name', + 'created_by' => $owner->id, + ]); Meetup::factory()->create(['name' => 'Other Name', 'city_id' => $this->city->id]); - actingAsUser(); - Livewire::test('meetups.edit', ['meetup' => $this->meetup]) + Livewire::test('meetups.edit', ['meetup' => $meetup]) ->set('name', 'Other Name') ->call('updateMeetup') ->assertHasErrors(['name' => 'unique']); }); it('allows update when name is unchanged (Rule::unique ignores own id)', function () { - actingAsUser(); + $owner = actingAsUser(); + $meetup = Meetup::factory()->create([ + 'city_id' => $this->city->id, + 'name' => 'Original Name', + 'created_by' => $owner->id, + ]); - Livewire::test('meetups.edit', ['meetup' => $this->meetup]) + Livewire::test('meetups.edit', ['meetup' => $meetup]) ->set('name', 'Original Name') ->set('community', 'einundzwanzig') ->call('updateMeetup') @@ -45,5 +59,7 @@ it('allows update when name is unchanged (Rule::unique ignores own id)', functio }); it('redirects guests when accessing meetup-edit', function () { - $this->get('/de/meetup-edit/'.$this->meetup->id)->assertRedirect(route('login')); + $meetup = Meetup::factory()->create(['city_id' => $this->city->id]); + + $this->get('/de/meetup-edit/'.$meetup->id)->assertRedirect(route('login')); }); diff --git a/tests/Feature/Smoke/ApiRoutesTest.php b/tests/Feature/Smoke/ApiRoutesTest.php index 60d28f8..53a5b3c 100644 --- a/tests/Feature/Smoke/ApiRoutesTest.php +++ b/tests/Feature/Smoke/ApiRoutesTest.php @@ -46,8 +46,8 @@ it('returns 404 for /api/meetup/ical (currently a stub that aborts)', function ( $this->get('/api/meetup/ical')->assertNotFound(); }); -it('returns 404 for /api/meetup index without user_id (currently aborts on missing param)', function () { - $this->getJson('/api/meetup')->assertNotFound(); +it('returns 401 for /api/meetup index when unauthenticated (auth-only after IDOR fix)', function () { + $this->getJson('/api/meetup')->assertUnauthorized(); }); it('returns a successful response for /stream-calendar', function () { diff --git a/tests/Feature/Smoke/PublicRoutesTest.php b/tests/Feature/Smoke/PublicRoutesTest.php index 4bee343..07c21e5 100644 --- a/tests/Feature/Smoke/PublicRoutesTest.php +++ b/tests/Feature/Smoke/PublicRoutesTest.php @@ -25,7 +25,6 @@ it('returns a successful response for the listed public route', function (string })->with([ 'welcome' => '/welcome', 'login' => '/login', - 'register' => '/register', 'forgot password' => '/forgot-password', 'meetups index' => '/de/meetups', 'meetups all' => '/de/all-meetups', @@ -42,6 +41,10 @@ it('redirects / to /welcome', function () { $this->get('/')->assertRedirect('/welcome'); }); +it('returns 404 for /register because public registration is disabled', function () { + $this->get('/register')->assertNotFound(); +}); + it('redirects /de/dashboard to login when guest', function () { $this->get('/de/dashboard')->assertRedirect(route('login')); }); diff --git a/vite.config.js b/vite.config.js index 12d48de..39d5f76 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,4 +1,6 @@ -import {defineConfig} from 'vite'; +import { + defineConfig +} from 'vite'; import laravel from 'laravel-vite-plugin'; import tailwindcss from "@tailwindcss/vite"; @@ -12,5 +14,10 @@ export default defineConfig({ ], server: { cors: true, + watch: { + ignored: [ + '**/storage/framework/views/**' + ], + }, }, }); diff --git a/yarn.lock b/yarn.lock index 44098a0..3b50fb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,25 +2,25 @@ # yarn lockfile v1 -"@emnapi/core@^1.7.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.8.1.tgz#fd9efe721a616288345ffee17a1f26ac5dd01349" - integrity sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg== +"@emnapi/core@^1.8.1": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467" + integrity sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw== dependencies: - "@emnapi/wasi-threads" "1.1.0" + "@emnapi/wasi-threads" "1.2.1" tslib "^2.4.0" -"@emnapi/runtime@^1.7.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.8.1.tgz#550fa7e3c0d49c5fb175a116e8cd70614f9a22a5" - integrity sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg== +"@emnapi/runtime@^1.8.1": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c" + integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.1.0", "@emnapi/wasi-threads@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" - integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== +"@emnapi/wasi-threads@1.2.1", "@emnapi/wasi-threads@^1.1.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz#28fed21a1ba1ce797c44a070abc94d42f3ae8548" + integrity sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w== dependencies: tslib "^2.4.0" @@ -162,7 +162,7 @@ "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/remapping@^2.3.4": +"@jridgewell/remapping@^2.3.5": version "2.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== @@ -188,251 +188,227 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@napi-rs/wasm-runtime@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz#c3705ab549d176b8dc5172723d6156c3dc426af2" - integrity sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A== +"@napi-rs/wasm-runtime@^1.1.1": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz#a46bbfedc29751b7170c5d23bc1d8ee8c7e3c1e1" + integrity sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow== dependencies: - "@emnapi/core" "^1.7.1" - "@emnapi/runtime" "^1.7.1" "@tybys/wasm-util" "^0.10.1" -"@noble/ciphers@^0.5.1": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.5.3.tgz#48b536311587125e0d0c1535f73ec8375cd76b23" - integrity sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w== +"@noble/ciphers@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.1.1.tgz#c8c74fcda8c3d1f88797d0ecda24f9fc8b92b052" + integrity sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw== -"@noble/curves@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" - integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== +"@noble/curves@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-2.0.1.tgz#64ba8bd5e8564a02942655602515646df1cdb3ad" + integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== dependencies: - "@noble/hashes" "1.3.2" + "@noble/hashes" "2.0.1" -"@noble/curves@~1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" - integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== - dependencies: - "@noble/hashes" "1.3.1" +"@noble/hashes@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" + integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== -"@noble/hashes@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" - integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== +"@rollup/rollup-android-arm-eabi@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz#a19c645c375158cd5c50a344106f0fa18eb821c4" + integrity sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw== -"@noble/hashes@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@rollup/rollup-android-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz#1af19aa9d3ad6d00df2681f59cfcb8bf7499576b" + integrity sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg== -"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@rollup/rollup-darwin-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz#3b8463e03ba2a393453fea70e7d907379c27b649" + integrity sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA== -"@rollup/rollup-android-arm-eabi@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz#76e0fef6533b3ce313f969879e61e8f21f0eeb28" - integrity sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg== +"@rollup/rollup-darwin-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz#28da23d69fe117f5f0ff330a8549e51bd09f1b6a" + integrity sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g== -"@rollup/rollup-android-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz#d3cfc675a40bbdec97bda6d7fe3b3b05f0e1cd93" - integrity sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg== +"@rollup/rollup-freebsd-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz#94bacac3190f621de1355922b599f3817786044c" + integrity sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw== -"@rollup/rollup-darwin-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz#eb912b8f59dd47c77b3c50a78489013b1d6772b4" - integrity sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg== +"@rollup/rollup-freebsd-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz#8a0094f533b9fda160b5c90ad9e0c78fca341788" + integrity sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ== -"@rollup/rollup-darwin-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz#e7d0839fdfd1276a1d34bc5ebbbd0dfd7d0b81a0" - integrity sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ== +"@rollup/rollup-linux-arm-gnueabihf@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz#3b7e901a555c7245c87f7440979bee0a1ec882bb" + integrity sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg== -"@rollup/rollup-freebsd-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz#7ff8118760f7351e48fd0cd3717ff80543d6aac8" - integrity sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg== +"@rollup/rollup-linux-arm-musleabihf@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz#ee9a09b72e8ad764cfd6188b32ff1de528ff7ebe" + integrity sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw== -"@rollup/rollup-freebsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz#49d330dadbda1d4e9b86b4a3951b59928a9489a9" - integrity sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw== +"@rollup/rollup-linux-arm64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz#ba483f4aca9be141171d086dbd01ada6ab03b58d" + integrity sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg== -"@rollup/rollup-linux-arm-gnueabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz#98c5f1f8b9776b4a36e466e2a1c9ed1ba52ef1b6" - integrity sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ== +"@rollup/rollup-linux-arm64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz#17b595b790e6df68e91c5d02526fc832a985ce4f" + integrity sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA== -"@rollup/rollup-linux-arm-musleabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz#b9acecd3672e742f70b0c8a94075c816a91ff040" - integrity sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg== +"@rollup/rollup-linux-loong64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz#551718714075a2bfb36a2813c466e3a0e9d56abf" + integrity sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A== -"@rollup/rollup-linux-arm64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz#7a6ab06651bc29e18b09a50ed1a02bc972977c9b" - integrity sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ== +"@rollup/rollup-linux-loong64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz#ba156ed1243447a3d710972001d5dcfe3827ff3d" + integrity sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q== -"@rollup/rollup-linux-arm64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz#3c8c9072ba4a4d4ef1156b85ab9a2cbb57c1fad0" - integrity sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA== +"@rollup/rollup-linux-ppc64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz#6a957a709b86ac62ef68e597ac03dbd4336782b1" + integrity sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw== -"@rollup/rollup-linux-loong64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz#17a7af13530f4e4a7b12cd26276c54307a84a8b0" - integrity sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g== +"@rollup/rollup-linux-ppc64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz#ca4176b4ad53f3edee3b4bfa6f9ef48ff38f167b" + integrity sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ== -"@rollup/rollup-linux-loong64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz#5cd7a900fd7b077ecd753e34a9b7ff1157fe70c1" - integrity sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw== +"@rollup/rollup-linux-riscv64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz#4e6b08f72ebeafdb41f3ec433bd228ba8573473b" + integrity sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A== -"@rollup/rollup-linux-ppc64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz#03a097e70243ddf1c07b59d3c20f38e6f6800539" - integrity sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw== +"@rollup/rollup-linux-riscv64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz#a0b8b8580c7680c8086cb3226527e5472253b895" + integrity sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ== -"@rollup/rollup-linux-ppc64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz#a5389873039d4650f35b4fa060d286392eb21a94" - integrity sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw== +"@rollup/rollup-linux-s390x-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz#79fe15b92ce0bae2b609cf26dd158cd3e2b73634" + integrity sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA== -"@rollup/rollup-linux-riscv64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz#789e60e7d6e2b76132d001ffb24ba80007fb17d0" - integrity sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw== - -"@rollup/rollup-linux-riscv64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz#3556fa88d139282e9a73c337c9a170f3c5fe7aa4" - integrity sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg== - -"@rollup/rollup-linux-s390x-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz#c085995b10143c16747a67f1a5487512b2ff04b2" - integrity sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg== - -"@rollup/rollup-linux-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz#9563a5419dd2604841bad31a39ccfdd2891690fb" - integrity sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg== +"@rollup/rollup-linux-x64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz#6aa8302fa45fd3cbbc510ccd223c9c37bf67e53f" + integrity sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ== "@rollup/rollup-linux-x64-gnu@4.9.5": version "4.9.5" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz#85946ee4d068bd12197aeeec2c6f679c94978a49" integrity sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA== -"@rollup/rollup-linux-x64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz#691bb06e6269a8959c13476b0cd2aa7458facb31" - integrity sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w== +"@rollup/rollup-linux-x64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz#0c1a5e9799f80c47a66f2c3a5f1a280f38356047" + integrity sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw== -"@rollup/rollup-openbsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz#223e71224746a59ce6d955bbc403577bb5a8be9d" - integrity sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg== +"@rollup/rollup-openbsd-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz#5f07c863e74fd428794f1dc5749f321b661d1f17" + integrity sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg== -"@rollup/rollup-openharmony-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz#0817e5d8ecbfeb8b7939bf58f8ce3c9dd67fce77" - integrity sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw== +"@rollup/rollup-openharmony-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz#8e0d71324be0f423428b12b25a2eb8ea8e0a7833" + integrity sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q== -"@rollup/rollup-win32-arm64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz#de56d8f2013c84570ef5fb917aae034abda93e4a" - integrity sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g== +"@rollup/rollup-win32-arm64-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz#a553fdf90a785ace6d7501eed6241c468b088999" + integrity sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ== -"@rollup/rollup-win32-ia32-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz#659aff5244312475aeea2c9479a6c7d397b517bf" - integrity sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA== +"@rollup/rollup-win32-ia32-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz#0fb04f0a88027fbfd323e25a446debce4773868c" + integrity sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg== -"@rollup/rollup-win32-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz#2cb09549cbb66c1b979f9238db6dd454cac14a88" - integrity sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg== +"@rollup/rollup-win32-x64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz#aaa9e36dbdc0f0e397e5966dcce1b4285354ede2" + integrity sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA== -"@rollup/rollup-win32-x64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz#f79437939020b83057faf07e98365b1fa51c458b" - integrity sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw== +"@rollup/rollup-win32-x64-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz#3418dcf1388f2abd6b0ccc08fe1ef205ae76d696" + integrity sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA== -"@scure/base@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98" + integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== -"@scure/base@~1.1.0": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" - integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== - -"@scure/bip32@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" - integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== +"@scure/bip32@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-2.0.1.tgz#4ceea207cee8626d3fe8f0b6ab68b6af8f81c482" + integrity sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA== dependencies: - "@noble/curves" "~1.1.0" - "@noble/hashes" "~1.3.1" - "@scure/base" "~1.1.0" + "@noble/curves" "2.0.1" + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" -"@scure/bip39@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" - integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== +"@scure/bip39@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-2.0.1.tgz#47a6dc15e04faf200041239d46ae3bb7c3c96add" + integrity sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg== dependencies: - "@noble/hashes" "~1.3.0" - "@scure/base" "~1.1.0" + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" -"@shikijs/core@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-3.21.0.tgz#9641d09865c43612b28e7931f9af68c8a62edd90" - integrity sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA== +"@shikijs/core@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-3.23.0.tgz#79248ec4ad3de4fd5c12993f5c30cb071ec04812" + integrity sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA== dependencies: - "@shikijs/types" "3.21.0" + "@shikijs/types" "3.23.0" "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" hast-util-to-html "^9.0.5" -"@shikijs/engine-javascript@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-3.21.0.tgz#f04554fe87bed39d00ba4b140894b41cd207f5cb" - integrity sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ== +"@shikijs/engine-javascript@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz#eae89a47913f486e5a05130d13b965c424c33b21" + integrity sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA== dependencies: - "@shikijs/types" "3.21.0" + "@shikijs/types" "3.23.0" "@shikijs/vscode-textmate" "^10.0.2" oniguruma-to-es "^4.3.4" -"@shikijs/engine-oniguruma@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.21.0.tgz#0e666454a03fd85d6c634d9dbe70a63f007a6323" - integrity sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ== +"@shikijs/engine-oniguruma@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz#789421048d66ac1b33613169d6d18b9cc6e340ed" + integrity sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g== dependencies: - "@shikijs/types" "3.21.0" + "@shikijs/types" "3.23.0" "@shikijs/vscode-textmate" "^10.0.2" -"@shikijs/langs@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.21.0.tgz#da33400a85c7cba75fc9f4a6b9feb69a6c39c800" - integrity sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA== +"@shikijs/langs@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.23.0.tgz#00959d8b16c7f671221ae79b3ad8cde7e6a5c112" + integrity sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg== dependencies: - "@shikijs/types" "3.21.0" + "@shikijs/types" "3.23.0" -"@shikijs/themes@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.21.0.tgz#1955d642ea37d70d1137e6cf47da7dc9c34ff4c0" - integrity sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw== +"@shikijs/themes@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.23.0.tgz#fd96ca5ad52639057995bc2093682884e1846f27" + integrity sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA== dependencies: - "@shikijs/types" "3.21.0" + "@shikijs/types" "3.23.0" -"@shikijs/types@3.21.0": - version "3.21.0" - resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-3.21.0.tgz#510d6ddbea65add27980a6ca36cc7bdabc7afe90" - integrity sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA== +"@shikijs/types@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-3.23.0.tgz#d441571a058641926018ae3de99866f39e5bbdf2" + integrity sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ== dependencies: "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" @@ -442,112 +418,112 @@ resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== -"@tailwindcss/node@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.1.18.tgz#9863be0d26178638794a38d6c7c14666fb992e8a" - integrity sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ== +"@tailwindcss/node@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/node/-/node-4.2.4.tgz#1f7fc0c1741037ded1fa92fbe62a786a197771ce" + integrity sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA== dependencies: - "@jridgewell/remapping" "^2.3.4" - enhanced-resolve "^5.18.3" + "@jridgewell/remapping" "^2.3.5" + enhanced-resolve "^5.19.0" jiti "^2.6.1" - lightningcss "1.30.2" + lightningcss "1.32.0" magic-string "^0.30.21" source-map-js "^1.2.1" - tailwindcss "4.1.18" + tailwindcss "4.2.4" -"@tailwindcss/oxide-android-arm64@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz#79717f87e90135e5d3d23a3d3aecde4ca5595dd5" - integrity sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q== +"@tailwindcss/oxide-android-arm64@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz#d533e52ee98d58f55d1d4753774251513ba8a911" + integrity sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g== -"@tailwindcss/oxide-darwin-arm64@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz#7fa47608d62d60e9eb020682249d20159667fbb0" - integrity sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A== +"@tailwindcss/oxide-darwin-arm64@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz#2a6250aa7d8791fc1b5797e64e09e51da57514a6" + integrity sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg== -"@tailwindcss/oxide-darwin-x64@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz#c05991c85aa2af47bf9d1f8172fe9e4636591e79" - integrity sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw== +"@tailwindcss/oxide-darwin-x64@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz#d647299812946b6ab5140c61a334c8ebc8d877de" + integrity sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg== -"@tailwindcss/oxide-freebsd-x64@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz#3d48e8d79fd08ece0e02af8e72d5059646be34d0" - integrity sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA== +"@tailwindcss/oxide-freebsd-x64@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz#019b7fce37aaf5ddfed0f231c536108292e87ffb" + integrity sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw== -"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz#982ecd1a65180807ccfde67dc17c6897f2e50aa8" - integrity sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA== +"@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz#c88a95d69095e84f811b302daa66f5287ad8ce0f" + integrity sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA== -"@tailwindcss/oxide-linux-arm64-gnu@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz#df49357bc9737b2e9810ea950c1c0647ba6573c3" - integrity sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw== +"@tailwindcss/oxide-linux-arm64-gnu@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz#1292f1c222994bfe4a5e990ac0a701de6487dd02" + integrity sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw== -"@tailwindcss/oxide-linux-arm64-musl@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz#b266c12822bf87883cf152615f8fffb8519d689c" - integrity sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg== +"@tailwindcss/oxide-linux-arm64-musl@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz#afb6492b22616f0d9d3346d39c1a6e285f994a08" + integrity sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g== -"@tailwindcss/oxide-linux-x64-gnu@4.1.18", "@tailwindcss/oxide-linux-x64-gnu@^4.0.1": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz#5c737f13dd9529b25b314e6000ff54e05b3811da" - integrity sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g== +"@tailwindcss/oxide-linux-x64-gnu@4.2.4", "@tailwindcss/oxide-linux-x64-gnu@^4.0.1": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz#400b0ccfc53937c7804ed8e0e9652b42bd86f2eb" + integrity sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA== -"@tailwindcss/oxide-linux-x64-musl@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz#3380e17f7be391f1ef924be9f0afe1f304fe3478" - integrity sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ== +"@tailwindcss/oxide-linux-x64-musl@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz#5c23c476e5de4ed9cd6ab39c2718b9a4be2bbb2b" + integrity sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA== -"@tailwindcss/oxide-wasm32-wasi@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz#9464df0e28a499aab1c55e97682be37b3a656c88" - integrity sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA== +"@tailwindcss/oxide-wasm32-wasi@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz#21b7f53ba7c6c03f26ccb8cef5d09f5c2973ae5e" + integrity sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw== dependencies: - "@emnapi/core" "^1.7.1" - "@emnapi/runtime" "^1.7.1" + "@emnapi/core" "^1.8.1" + "@emnapi/runtime" "^1.8.1" "@emnapi/wasi-threads" "^1.1.0" - "@napi-rs/wasm-runtime" "^1.1.0" + "@napi-rs/wasm-runtime" "^1.1.1" "@tybys/wasm-util" "^0.10.1" - tslib "^2.4.0" + tslib "^2.8.1" -"@tailwindcss/oxide-win32-arm64-msvc@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz#bbcdd59c628811f6a0a4d5b09616967d8fb0c4d4" - integrity sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA== +"@tailwindcss/oxide-win32-arm64-msvc@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz#13bc1cf3818e3345a965d36b40c237817124d070" + integrity sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ== -"@tailwindcss/oxide-win32-x64-msvc@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz#9c628d04623aa4c3536c508289f58d58ba4b3fb1" - integrity sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q== +"@tailwindcss/oxide-win32-x64-msvc@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz#5476dbbbf6b8934d58452340cec737fdaa5ec8c6" + integrity sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw== -"@tailwindcss/oxide@4.1.18": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.1.18.tgz#c8335cd0a83e9880caecd60abf7904f43ebab582" - integrity sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A== +"@tailwindcss/oxide@4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide/-/oxide-4.2.4.tgz#e2ca51d04e8ad94d569222fa727de479b097db39" + integrity sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q== optionalDependencies: - "@tailwindcss/oxide-android-arm64" "4.1.18" - "@tailwindcss/oxide-darwin-arm64" "4.1.18" - "@tailwindcss/oxide-darwin-x64" "4.1.18" - "@tailwindcss/oxide-freebsd-x64" "4.1.18" - "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.18" - "@tailwindcss/oxide-linux-arm64-gnu" "4.1.18" - "@tailwindcss/oxide-linux-arm64-musl" "4.1.18" - "@tailwindcss/oxide-linux-x64-gnu" "4.1.18" - "@tailwindcss/oxide-linux-x64-musl" "4.1.18" - "@tailwindcss/oxide-wasm32-wasi" "4.1.18" - "@tailwindcss/oxide-win32-arm64-msvc" "4.1.18" - "@tailwindcss/oxide-win32-x64-msvc" "4.1.18" + "@tailwindcss/oxide-android-arm64" "4.2.4" + "@tailwindcss/oxide-darwin-arm64" "4.2.4" + "@tailwindcss/oxide-darwin-x64" "4.2.4" + "@tailwindcss/oxide-freebsd-x64" "4.2.4" + "@tailwindcss/oxide-linux-arm-gnueabihf" "4.2.4" + "@tailwindcss/oxide-linux-arm64-gnu" "4.2.4" + "@tailwindcss/oxide-linux-arm64-musl" "4.2.4" + "@tailwindcss/oxide-linux-x64-gnu" "4.2.4" + "@tailwindcss/oxide-linux-x64-musl" "4.2.4" + "@tailwindcss/oxide-wasm32-wasi" "4.2.4" + "@tailwindcss/oxide-win32-arm64-msvc" "4.2.4" + "@tailwindcss/oxide-win32-x64-msvc" "4.2.4" "@tailwindcss/vite@^4.0.7": - version "4.1.18" - resolved "https://registry.yarnpkg.com/@tailwindcss/vite/-/vite-4.1.18.tgz#614b9d5483559518c72d31bca05d686f8df28e9a" - integrity sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA== + version "4.2.4" + resolved "https://registry.yarnpkg.com/@tailwindcss/vite/-/vite-4.2.4.tgz#2c2586ed964de044778b294538481878ffb9ae5c" + integrity sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw== dependencies: - "@tailwindcss/node" "4.1.18" - "@tailwindcss/oxide" "4.1.18" - tailwindcss "4.1.18" + "@tailwindcss/node" "4.2.4" + "@tailwindcss/oxide" "4.2.4" + tailwindcss "4.2.4" "@tybys/wasm-util@^0.10.1": version "0.10.1" @@ -603,40 +579,40 @@ asynckit@^0.4.0: integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== autoprefixer@^10.4.20: - version "10.4.23" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.23.tgz#c6aa6db8e7376fcd900f9fd79d143ceebad8c4e6" - integrity sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA== + version "10.5.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.5.0.tgz#33d87e443430f020a0f85319d6ff1593cb291be9" + integrity sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong== dependencies: - browserslist "^4.28.1" - caniuse-lite "^1.0.30001760" + browserslist "^4.28.2" + caniuse-lite "^1.0.30001787" fraction.js "^5.3.4" picocolors "^1.1.1" postcss-value-parser "^4.2.0" axios@^1.7.4: - version "1.13.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687" - integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA== + version "1.16.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.0.tgz#f8e5dd931cef2a5f8c32216d5784eda2f8750eb7" + integrity sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w== dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.4" - proxy-from-env "^1.1.0" + follow-redirects "^1.16.0" + form-data "^4.0.5" + proxy-from-env "^2.1.0" -baseline-browser-mapping@^2.9.0: - version "2.9.15" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz#6baaa0069883f50a99cdb31b56646491f47c05d7" - integrity sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg== +baseline-browser-mapping@^2.10.12: + version "2.10.25" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.25.tgz#183b45c0d3bdd12addb352426555fb3627eb022a" + integrity sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA== -browserslist@^4.28.1: - version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" - integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== +browserslist@^4.28.2: + version "4.28.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" + integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== dependencies: - baseline-browser-mapping "^2.9.0" - caniuse-lite "^1.0.30001759" - electron-to-chromium "^1.5.263" - node-releases "^2.0.27" - update-browserslist-db "^1.2.0" + baseline-browser-mapping "^2.10.12" + caniuse-lite "^1.0.30001782" + electron-to-chromium "^1.5.328" + node-releases "^2.0.36" + update-browserslist-db "^1.2.3" call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" @@ -646,10 +622,10 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: es-errors "^1.3.0" function-bind "^1.1.2" -caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001760: - version "1.0.30001764" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz#03206c56469f236103b90f9ae10bcb8b9e1f6005" - integrity sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g== +caniuse-lite@^1.0.30001782, caniuse-lite@^1.0.30001787: + version "1.0.30001791" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz#dfb93d85c40ad380c57123e72e10f3c575786b51" + integrity sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ== ccount@^2.0.0: version "2.0.1" @@ -750,23 +726,23 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -electron-to-chromium@^1.5.263: - version "1.5.267" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" - integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== +electron-to-chromium@^1.5.328: + version "1.5.349" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz#9b9c6a6d84d1107557c18a9336099ce0ee890e5b" + integrity sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -enhanced-resolve@^5.18.3: - version "5.18.4" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828" - integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q== +enhanced-resolve@^5.19.0: + version "5.21.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz#bb8e6fabaf74930de70e61397798750429e5b1ae" + integrity sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA== dependencies: graceful-fs "^4.2.4" - tapable "^2.2.0" + tapable "^2.3.3" es-define-property@^1.0.1: version "1.0.1" @@ -837,12 +813,12 @@ fdir@^6.4.4, fdir@^6.5.0: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== -follow-redirects@^1.15.6: - version "1.15.11" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" - integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== +follow-redirects@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== -form-data@^4.0.4: +form-data@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -930,9 +906,9 @@ has-tostringtag@^1.0.2: has-symbols "^1.0.3" hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c" + integrity sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg== dependencies: function-bind "^1.1.2" @@ -988,79 +964,79 @@ leaflet@^1.9.4: resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d" integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA== -lightningcss-android-arm64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307" - integrity sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A== +lightningcss-android-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968" + integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg== -lightningcss-darwin-arm64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz#a5fa946d27c029e48c7ff929e6e724a7de46eb2c" - integrity sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA== +lightningcss-darwin-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5" + integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ== -lightningcss-darwin-x64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz#5ce87e9cd7c4f2dcc1b713f5e8ee185c88d9b7cd" - integrity sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ== +lightningcss-darwin-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e" + integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w== -lightningcss-freebsd-x64@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz#6ae1d5e773c97961df5cff57b851807ef33692a5" - integrity sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA== +lightningcss-freebsd-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575" + integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig== -lightningcss-linux-arm-gnueabihf@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz#62c489610c0424151a6121fa99d77731536cdaeb" - integrity sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA== +lightningcss-linux-arm-gnueabihf@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d" + integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw== -lightningcss-linux-arm64-gnu@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz#2a3661b56fe95a0cafae90be026fe0590d089298" - integrity sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A== +lightningcss-linux-arm64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335" + integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ== -lightningcss-linux-arm64-musl@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz#d7ddd6b26959245e026bc1ad9eb6aa983aa90e6b" - integrity sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA== +lightningcss-linux-arm64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133" + integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg== -lightningcss-linux-x64-gnu@1.30.2, lightningcss-linux-x64-gnu@^1.29.1: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz#5a89814c8e63213a5965c3d166dff83c36152b1a" - integrity sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w== +lightningcss-linux-x64-gnu@1.32.0, lightningcss-linux-x64-gnu@^1.29.1: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6" + integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA== -lightningcss-linux-x64-musl@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz#808c2e91ce0bf5d0af0e867c6152e5378c049728" - integrity sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA== +lightningcss-linux-x64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b" + integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg== -lightningcss-win32-arm64-msvc@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz#ab4a8a8a2e6a82a4531e8bbb6bf0ff161ee6625a" - integrity sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ== +lightningcss-win32-arm64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38" + integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw== -lightningcss-win32-x64-msvc@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz#f01f382c8e0a27e1c018b0bee316d210eac43b6e" - integrity sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw== +lightningcss-win32-x64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a" + integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q== -lightningcss@1.30.2: - version "1.30.2" - resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.30.2.tgz#4ade295f25d140f487d37256f4cd40dc607696d0" - integrity sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ== +lightningcss@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9" + integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== dependencies: detect-libc "^2.0.3" optionalDependencies: - lightningcss-android-arm64 "1.30.2" - lightningcss-darwin-arm64 "1.30.2" - lightningcss-darwin-x64 "1.30.2" - lightningcss-freebsd-x64 "1.30.2" - lightningcss-linux-arm-gnueabihf "1.30.2" - lightningcss-linux-arm64-gnu "1.30.2" - lightningcss-linux-arm64-musl "1.30.2" - lightningcss-linux-x64-gnu "1.30.2" - lightningcss-linux-x64-musl "1.30.2" - lightningcss-win32-arm64-msvc "1.30.2" - lightningcss-win32-x64-msvc "1.30.2" + lightningcss-android-arm64 "1.32.0" + lightningcss-darwin-arm64 "1.32.0" + lightningcss-darwin-x64 "1.32.0" + lightningcss-freebsd-x64 "1.32.0" + lightningcss-linux-arm-gnueabihf "1.32.0" + lightningcss-linux-arm64-gnu "1.32.0" + lightningcss-linux-arm64-musl "1.32.0" + lightningcss-linux-x64-gnu "1.32.0" + lightningcss-linux-x64-musl "1.32.0" + lightningcss-win32-arm64-msvc "1.32.0" + lightningcss-win32-x64-msvc "1.32.0" magic-string@^0.30.21: version "0.30.21" @@ -1134,26 +1110,26 @@ mime-types@^2.1.12: mime-db "1.52.0" nanoid@^3.3.11: - version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + version "3.3.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.12.tgz#ab3d912e217a6d0a514f00a72a16543a28982c05" + integrity sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ== -node-releases@^2.0.27: - version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" - integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== +node-releases@^2.0.36: + version "2.0.38" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.38.tgz#791569b9e4424a044e12c3abfad418ed83ce9947" + integrity sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw== nostr-tools@^2.17.4: - version "2.19.4" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.19.4.tgz#c4e2b56914db7f3b91e5fbef250e7ce65754ed74" - integrity sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg== + version "2.23.3" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.23.3.tgz#1a7501988b72499cf27c8f3951f00d11d9ac6025" + integrity sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA== dependencies: - "@noble/ciphers" "^0.5.1" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.1" - "@scure/base" "1.1.1" - "@scure/bip32" "1.3.1" - "@scure/bip39" "1.2.1" + "@noble/ciphers" "2.1.1" + "@noble/curves" "2.0.1" + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" + "@scure/bip32" "2.0.1" + "@scure/bip39" "2.0.1" nostr-wasm "0.1.0" nostr-wasm@0.1.0: @@ -1161,18 +1137,18 @@ nostr-wasm@0.1.0: resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94" integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA== -oniguruma-parser@^0.12.1: - version "0.12.1" - resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz#82ba2208d7a2b69ee344b7efe0ae930c627dcc4a" - integrity sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w== +oniguruma-parser@^0.12.2: + version "0.12.2" + resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz#e27ca446f7fcf0969662a3ab9b4f43176d62b139" + integrity sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw== oniguruma-to-es@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz#0b909d960faeb84511c979b1f2af64e9bc37ce34" - integrity sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA== + version "4.3.6" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz#43e640280241b0d687a314e7a641d476407a1c4d" + integrity sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA== dependencies: - oniguruma-parser "^0.12.1" - regex "^6.0.1" + oniguruma-parser "^0.12.2" + regex "^6.1.0" regex-recursion "^6.0.2" picocolors@^1.0.0, picocolors@^1.1.1: @@ -1181,14 +1157,14 @@ picocolors@^1.0.0, picocolors@^1.1.1: integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== -picomatch@^4.0.2, picomatch@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" - integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.2, picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== playwright-core@1.59.1: version "1.59.1" @@ -1210,9 +1186,9 @@ postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.5.3: - version "8.5.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" - integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + version "8.5.13" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.13.tgz#6cfaf647f2e7ef69850208eccd849e0d3f65d420" + integrity sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag== dependencies: nanoid "^3.3.11" picocolors "^1.1.1" @@ -1223,10 +1199,10 @@ property-information@^7.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" + integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== regex-recursion@^6.0.2: version "6.0.2" @@ -1240,7 +1216,7 @@ regex-utilities@^2.3.0: resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== -regex@^6.0.1: +regex@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803" integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== @@ -1253,37 +1229,37 @@ require-directory@^2.1.1: integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== rollup@^4.34.9: - version "4.55.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.55.1.tgz#4ec182828be440648e7ee6520dc35e9f20e05144" - integrity sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A== + version "4.60.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.60.2.tgz#ac23fe4bd530304cef9fa61e639d7098b6762cf4" + integrity sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.55.1" - "@rollup/rollup-android-arm64" "4.55.1" - "@rollup/rollup-darwin-arm64" "4.55.1" - "@rollup/rollup-darwin-x64" "4.55.1" - "@rollup/rollup-freebsd-arm64" "4.55.1" - "@rollup/rollup-freebsd-x64" "4.55.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.55.1" - "@rollup/rollup-linux-arm-musleabihf" "4.55.1" - "@rollup/rollup-linux-arm64-gnu" "4.55.1" - "@rollup/rollup-linux-arm64-musl" "4.55.1" - "@rollup/rollup-linux-loong64-gnu" "4.55.1" - "@rollup/rollup-linux-loong64-musl" "4.55.1" - "@rollup/rollup-linux-ppc64-gnu" "4.55.1" - "@rollup/rollup-linux-ppc64-musl" "4.55.1" - "@rollup/rollup-linux-riscv64-gnu" "4.55.1" - "@rollup/rollup-linux-riscv64-musl" "4.55.1" - "@rollup/rollup-linux-s390x-gnu" "4.55.1" - "@rollup/rollup-linux-x64-gnu" "4.55.1" - "@rollup/rollup-linux-x64-musl" "4.55.1" - "@rollup/rollup-openbsd-x64" "4.55.1" - "@rollup/rollup-openharmony-arm64" "4.55.1" - "@rollup/rollup-win32-arm64-msvc" "4.55.1" - "@rollup/rollup-win32-ia32-msvc" "4.55.1" - "@rollup/rollup-win32-x64-gnu" "4.55.1" - "@rollup/rollup-win32-x64-msvc" "4.55.1" + "@rollup/rollup-android-arm-eabi" "4.60.2" + "@rollup/rollup-android-arm64" "4.60.2" + "@rollup/rollup-darwin-arm64" "4.60.2" + "@rollup/rollup-darwin-x64" "4.60.2" + "@rollup/rollup-freebsd-arm64" "4.60.2" + "@rollup/rollup-freebsd-x64" "4.60.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.60.2" + "@rollup/rollup-linux-arm-musleabihf" "4.60.2" + "@rollup/rollup-linux-arm64-gnu" "4.60.2" + "@rollup/rollup-linux-arm64-musl" "4.60.2" + "@rollup/rollup-linux-loong64-gnu" "4.60.2" + "@rollup/rollup-linux-loong64-musl" "4.60.2" + "@rollup/rollup-linux-ppc64-gnu" "4.60.2" + "@rollup/rollup-linux-ppc64-musl" "4.60.2" + "@rollup/rollup-linux-riscv64-gnu" "4.60.2" + "@rollup/rollup-linux-riscv64-musl" "4.60.2" + "@rollup/rollup-linux-s390x-gnu" "4.60.2" + "@rollup/rollup-linux-x64-gnu" "4.60.2" + "@rollup/rollup-linux-x64-musl" "4.60.2" + "@rollup/rollup-openbsd-x64" "4.60.2" + "@rollup/rollup-openharmony-arm64" "4.60.2" + "@rollup/rollup-win32-arm64-msvc" "4.60.2" + "@rollup/rollup-win32-ia32-msvc" "4.60.2" + "@rollup/rollup-win32-x64-gnu" "4.60.2" + "@rollup/rollup-win32-x64-msvc" "4.60.2" fsevents "~2.3.2" rxjs@7.8.2: @@ -1299,16 +1275,16 @@ shell-quote@1.8.3: integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== shiki@^3.15.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-3.21.0.tgz#64686fe6bfc6b2b602d209eb6c8cdbc79d0eff22" - integrity sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w== + version "3.23.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-3.23.0.tgz#fca5332195e3afd6c94b384103ae9671a29c7fb9" + integrity sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA== dependencies: - "@shikijs/core" "3.21.0" - "@shikijs/engine-javascript" "3.21.0" - "@shikijs/engine-oniguruma" "3.21.0" - "@shikijs/langs" "3.21.0" - "@shikijs/themes" "3.21.0" - "@shikijs/types" "3.21.0" + "@shikijs/core" "3.23.0" + "@shikijs/engine-javascript" "3.23.0" + "@shikijs/engine-oniguruma" "3.23.0" + "@shikijs/langs" "3.23.0" + "@shikijs/themes" "3.23.0" + "@shikijs/types" "3.23.0" "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" @@ -1360,23 +1336,23 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -tailwindcss@4.1.18, tailwindcss@^4.0.7: - version "4.1.18" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.18.tgz#f488ba47853abdb5354daf9679d3e7791fc4f4e3" - integrity sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw== +tailwindcss@4.2.4, tailwindcss@^4.0.7: + version "4.2.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.2.4.tgz#f7e3090edb22d56394db4d68e6464d2628dc2aa9" + integrity sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA== -tapable@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" - integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== +tapable@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.3.tgz#5da7c9992c46038221267985ab28421a8879f160" + integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A== tinyglobby@^0.2.13: - version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" - integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== dependencies: fdir "^6.5.0" - picomatch "^4.0.3" + picomatch "^4.0.4" tree-kill@1.2.2: version "1.2.2" @@ -1388,7 +1364,7 @@ trim-lines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== -tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.1.0, tslib@^2.4.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -1423,15 +1399,15 @@ unist-util-visit-parents@^6.0.0: unist-util-is "^6.0.0" unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + version "5.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468" + integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== dependencies: "@types/unist" "^3.0.0" unist-util-is "^6.0.0" unist-util-visit-parents "^6.0.0" -update-browserslist-db@^1.2.0: +update-browserslist-db@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== @@ -1464,9 +1440,9 @@ vite-plugin-full-reload@^1.1.0: picomatch "^2.3.1" vite@^6.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.1.tgz#afbe14518cdd6887e240a4b0221ab6d0ce733f96" - integrity sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g== + version "6.4.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.2.tgz#a4e548ca3a90ca9f3724582cab35e1ba15efc6f2" + integrity sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ== dependencies: esbuild "^0.25.0" fdir "^6.4.4"