From a62887db750786259fcef5073c6415fd803c2855 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 11:33:48 +0100 Subject: [PATCH 1/9] changes --- README.md | 141 +++++++ app/Http/Controllers/DashboardController.php | 19 + .../Controllers/FileManagerController.php | 127 +++++++ app/Http/Controllers/PageController.php | 79 ++++ app/Http/Controllers/PageServeController.php | 138 +++++++ app/Livewire/FileManager.php | 337 +++++++++++++++++ app/Models/Page.php | 140 +++++++ app/Models/User.php | 50 +-- app/Policies/PagePolicy.php | 24 ++ app/Providers/AppServiceProvider.php | 6 +- app/Services/FileManagerService.php | 229 ++++++++++++ config/app.php | 26 ++ config/filesystems.php | 14 + .../2026_01_01_000001_create_pages_table.php | 25 ++ .../views/components/action-message.blade.php | 14 - .../views/components/app-logo-icon.blade.php | 8 - resources/views/components/app-logo.blade.php | 17 - .../views/components/auth-header.blade.php | 9 - .../components/auth-session-status.blade.php | 9 - .../components/desktop-user-menu.blade.php | 39 -- .../components/placeholder-pattern.blade.php | 12 - .../components/settings/layout.blade.php | 20 - resources/views/dashboard.blade.php | 18 - .../views/flux/icon/book-open-text.blade.php | 47 --- .../flux/icon/chevrons-up-down.blade.php | 43 --- .../views/flux/icon/folder-git-2.blade.php | 45 --- .../views/flux/icon/layout-grid.blade.php | 45 --- resources/views/flux/navlist/group.blade.php | 51 --- resources/views/layouts/app.blade.php | 49 ++- resources/views/layouts/app/header.blade.php | 78 ---- resources/views/layouts/app/sidebar.blade.php | 95 ----- resources/views/layouts/auth.blade.php | 3 - resources/views/layouts/auth/card.blade.php | 26 -- resources/views/layouts/auth/simple.blade.php | 22 -- resources/views/layouts/auth/split.blade.php | 43 --- .../livewire/auth/confirm-password.blade.php | 28 -- .../livewire/auth/forgot-password.blade.php | 31 -- resources/views/livewire/auth/login.blade.php | 59 --- .../views/livewire/auth/register.blade.php | 67 ---- .../livewire/auth/reset-password.blade.php | 52 --- .../auth/two-factor-challenge.blade.php | 95 ----- .../livewire/auth/verify-email.blade.php | 29 -- .../views/livewire/file-manager.blade.php | 347 ++++++++++++++++++ .../livewire/settings/appearance.blade.php | 13 - .../settings/delete-user-form.blade.php | 34 -- .../views/livewire/settings/profile.blade.php | 47 --- .../livewire/settings/security.blade.php | 239 ------------ .../two-factor/recovery-codes.blade.php | 89 ----- resources/views/pages/create.blade.php | 52 +++ resources/views/pages/dashboard.blade.php | 59 +++ resources/views/pages/index.blade.php | 59 +++ resources/views/partials/head.blade.php | 16 - .../views/partials/settings-heading.blade.php | 5 - resources/views/welcome.blade.php | 278 -------------- routes/web.php | 61 ++- 55 files changed, 1931 insertions(+), 1777 deletions(-) create mode 100644 README.md create mode 100644 app/Http/Controllers/DashboardController.php create mode 100644 app/Http/Controllers/FileManagerController.php create mode 100644 app/Http/Controllers/PageController.php create mode 100644 app/Http/Controllers/PageServeController.php create mode 100644 app/Livewire/FileManager.php create mode 100644 app/Models/Page.php create mode 100644 app/Policies/PagePolicy.php create mode 100644 app/Services/FileManagerService.php create mode 100644 database/migrations/2026_01_01_000001_create_pages_table.php delete mode 100644 resources/views/components/action-message.blade.php delete mode 100644 resources/views/components/app-logo-icon.blade.php delete mode 100644 resources/views/components/app-logo.blade.php delete mode 100644 resources/views/components/auth-header.blade.php delete mode 100644 resources/views/components/auth-session-status.blade.php delete mode 100644 resources/views/components/desktop-user-menu.blade.php delete mode 100644 resources/views/components/placeholder-pattern.blade.php delete mode 100644 resources/views/components/settings/layout.blade.php delete mode 100644 resources/views/dashboard.blade.php delete mode 100644 resources/views/flux/icon/book-open-text.blade.php delete mode 100644 resources/views/flux/icon/chevrons-up-down.blade.php delete mode 100644 resources/views/flux/icon/folder-git-2.blade.php delete mode 100644 resources/views/flux/icon/layout-grid.blade.php delete mode 100644 resources/views/flux/navlist/group.blade.php delete mode 100644 resources/views/layouts/app/header.blade.php delete mode 100644 resources/views/layouts/app/sidebar.blade.php delete mode 100644 resources/views/layouts/auth.blade.php delete mode 100644 resources/views/layouts/auth/card.blade.php delete mode 100644 resources/views/layouts/auth/simple.blade.php delete mode 100644 resources/views/layouts/auth/split.blade.php delete mode 100644 resources/views/livewire/auth/confirm-password.blade.php delete mode 100644 resources/views/livewire/auth/forgot-password.blade.php delete mode 100644 resources/views/livewire/auth/login.blade.php delete mode 100644 resources/views/livewire/auth/register.blade.php delete mode 100644 resources/views/livewire/auth/reset-password.blade.php delete mode 100644 resources/views/livewire/auth/two-factor-challenge.blade.php delete mode 100644 resources/views/livewire/auth/verify-email.blade.php create mode 100644 resources/views/livewire/file-manager.blade.php delete mode 100644 resources/views/livewire/settings/appearance.blade.php delete mode 100644 resources/views/livewire/settings/delete-user-form.blade.php delete mode 100644 resources/views/livewire/settings/profile.blade.php delete mode 100644 resources/views/livewire/settings/security.blade.php delete mode 100644 resources/views/livewire/settings/two-factor/recovery-codes.blade.php create mode 100644 resources/views/pages/create.blade.php create mode 100644 resources/views/pages/dashboard.blade.php create mode 100644 resources/views/pages/index.blade.php delete mode 100644 resources/views/partials/head.blade.php delete mode 100644 resources/views/partials/settings-heading.blade.php delete mode 100644 resources/views/welcome.blade.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..b273ed1 --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# Static HTML Sites + +A Laravel 12 application that lets authenticated users create and serve static mini-sites. +Each page is accessible via **subdomain** (`{slug}.statichtmlsites.mtex.dev`) and **path** (`statichtmlsites.mtex.dev/{slug}/`). + +--- + +## Stack + +| Layer | Choice | +|------------|--------------------------------| +| Backend | Laravel 12 | +| Frontend | Livewire v3 + Alpine.js | +| Editor | CodeMirror 6 (via ESM CDN) | +| Auth | Laravel Breeze | +| DB | MySQL / SQLite | +| Storage | Local disk (`storage/app/pages/`) | + +--- + +## Installation + +```bash +# 1. Clone & install +composer install + +# 2. Environment +cp .env.example .env +php artisan key:generate + +# 3. Set APP_BASE_DOMAIN in .env +# APP_BASE_DOMAIN=statichtmlsites.mtex.dev + +# 4. Database +php artisan migrate + +# 5. Install Breeze (if not committed) +composer require laravel/breeze --dev +php artisan breeze:install blade +npm install && npm run build + +# 6. Livewire +composer require livewire/livewire + +# 7. Storage +php artisan storage:link +mkdir -p storage/app/pages + +# 8. Serve +php artisan serve +``` + +--- + +## Subdomain Routing (production) + +Add a wildcard DNS record: + +``` +*.statichtmlsites.mtex.dev → your server IP +``` + +Nginx vhost: + +```nginx +server { + listen 443 ssl; + server_name statichtmlsites.mtex.dev *.statichtmlsites.mtex.dev; + + root /var/www/statichtmlsites/public; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.3-fpm.sock; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + } +} +``` + +--- + +## File Structure (generated files) + +``` +app/ + Http/Controllers/ + PageController.php ← CRUD for pages + PageServeController.php ← serves static files + base-tag injection + Livewire/ + FileManager.php ← full IDE Livewire component + Models/ + Page.php + User.php + Policies/ + PagePolicy.php + Providers/ + AppServiceProvider.php + +database/migrations/ + ..._create_pages_table.php + +resources/views/ + layouts/app.blade.php + pages/ + index.blade.php ← dashboard + create.blade.php + livewire/ + file-manager.blade.php ← full IDE UI with CodeMirror 6 + +routes/ + web.php ← includes subdomain + path serving routes + +config/ + app.php ← adds base_domain, max_upload_mb + filesystems.php ← adds 'pages' disk +``` + +--- + +## Key Design Decisions + +### Base-tag injection +`PageServeController` injects `` into every HTML response so relative +asset references (`./style.css`, `src="video.mp4"`) resolve correctly regardless of +whether the page is accessed via subdomain or path. + +### File storage +Files are stored directly on disk (not in the database) under `storage/app/pages/{slug}/`. +The Livewire `FileManager` reads/writes the filesystem via Laravel's `Storage::disk('pages')`. + +### Path traversal prevention +All file paths are validated to reject `..` segments and leading `/` before any disk operation. + +### MIME detection +`PageServeController` uses a manual extension→MIME map (no `finfo` dependency) and streams +binary files directly without loading them into memory via `response()->file()`. diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php new file mode 100644 index 0000000..f296637 --- /dev/null +++ b/app/Http/Controllers/DashboardController.php @@ -0,0 +1,19 @@ +user() + ->pages() + ->latest() + ->get(); + + return view('pages.dashboard', compact('pages')); + } +} diff --git a/app/Http/Controllers/FileManagerController.php b/app/Http/Controllers/FileManagerController.php new file mode 100644 index 0000000..361dd9b --- /dev/null +++ b/app/Http/Controllers/FileManagerController.php @@ -0,0 +1,127 @@ +authorizedPage($request, $slug); + return view('pages.manager', compact('page')); + } + + // ── API endpoints ───────────────────────────────────────────────────────── + + /** List all files/folders as a tree */ + public function list(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + return response()->json($this->fm->tree($page)); + } + + /** Read a single file's content */ + public function read(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $path = $request->query('path', ''); + + return response()->json([ + 'content' => $this->fm->read($page, $path), + 'path' => $path, + ]); + } + + /** Save a text file */ + public function save(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $data = $request->validate([ + 'path' => ['required', 'string'], + 'content' => ['required', 'string'], + ]); + + $this->fm->save($page, $data['path'], $data['content']); + return response()->json(['ok' => true]); + } + + /** Create a new blank text file */ + public function create(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $data = $request->validate([ + 'path' => ['required', 'string'], + ]); + + $this->fm->createFile($page, $data['path']); + return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + } + + /** Upload binary or text file(s) */ + public function upload(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $maxMb = (int) config('filesystems.max_upload_mb', 50); + + $request->validate([ + 'file' => ["required", "file", "max:{$maxMb}000"], + 'folder' => ['nullable', 'string'], + ]); + + $this->fm->upload($page, $request->file('file'), $request->input('folder', '')); + return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + } + + /** Delete a file or directory */ + public function delete(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $data = $request->validate([ + 'path' => ['required', 'string'], + ]); + + $this->fm->delete($page, $data['path']); + return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + } + + /** Create a folder */ + public function createFolder(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $data = $request->validate([ + 'path' => ['required', 'string'], + ]); + + $this->fm->createFolder($page, $data['path']); + return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + } + + /** Rename a file or folder */ + public function rename(Request $request, string $slug): JsonResponse + { + $page = $this->authorizedPage($request, $slug); + $data = $request->validate([ + 'from' => ['required', 'string'], + 'to' => ['required', 'string'], + ]); + + $this->fm->rename($page, $data['from'], $data['to']); + return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + } + + // ── Shared ──────────────────────────────────────────────────────────────── + + private function authorizedPage(Request $request, string $slug): Page + { + return $request->user()->pages()->where('slug', $slug)->firstOrFail(); + } +} diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php new file mode 100644 index 0000000..c7b837c --- /dev/null +++ b/app/Http/Controllers/PageController.php @@ -0,0 +1,79 @@ +validate([ + 'name' => ['required', 'string', 'max:100'], + 'slug' => ['nullable', 'string', 'max:60', 'regex:/^[a-z0-9\-_]+$/'], + ]); + + $slug = $data['slug'] ?? Page::makeUniqueSlug($data['name']); + + // Ensure uniqueness even if user provided a slug + if (Page::where('slug', $slug)->exists()) { + return back() + ->withInput() + ->withErrors(['slug' => 'This slug is already taken.']); + } + + $page = $request->user()->pages()->create([ + 'name' => $data['name'], + 'slug' => $slug, + 'is_public' => true, + ]); + + return redirect() + ->route('pages.manager', $page->slug) + ->with('success', "Page \"{$page->name}\" created!"); + } + + public function updateSettings(Request $request, string $slug): RedirectResponse + { + $page = $request->user()->pages()->where('slug', $slug)->firstOrFail(); + + $data = $request->validate([ + 'name' => ['required', 'string', 'max:100'], + 'slug' => ['required', 'string', 'max:60', 'regex:/^[a-z0-9\-_]+$/'], + 'is_public' => ['boolean'], + ]); + + // Slug uniqueness check (excluding current page) + if ( + $data['slug'] !== $page->slug && + Page::where('slug', $data['slug'])->exists() + ) { + return back()->withErrors(['slug' => 'This slug is already taken.']); + } + + $page->update($data); + + return redirect() + ->route('pages.manager', $page->slug) + ->with('success', 'Settings updated.'); + } + + public function destroy(Request $request, string $slug): RedirectResponse + { + $page = $request->user()->pages()->where('slug', $slug)->firstOrFail(); + $page->delete(); + + return redirect() + ->route('dashboard') + ->with('success', "Page \"{$page->name}\" deleted."); + } +} diff --git a/app/Http/Controllers/PageServeController.php b/app/Http/Controllers/PageServeController.php new file mode 100644 index 0000000..247101b --- /dev/null +++ b/app/Http/Controllers/PageServeController.php @@ -0,0 +1,138 @@ +firstOrFail(); + + // Authorization: private pages require auth + if (! $page->is_public && ! auth()->check()) { + abort(403, 'This page is private.'); + } + + // Normalise path and resolve default document + $filePath = $this->resolvePath($slug, $path); + + if (! Storage::exists($filePath)) { + abort(404, 'File not found.'); + } + + // Security: prevent path traversal + $this->guardTraversal($slug, $filePath); + + $absolutePath = Storage::path($filePath); + $mime = $this->detectMime($filePath); + $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); + + // Inject tag into HTML responses + if ($ext === 'html') { + $html = Storage::get($filePath); + $html = $this->injectBase($html, $page, $request, $slug); + + return response($html, 200, ['Content-Type' => 'text/html; charset=utf-8']); + } + + // Stream binary / text files as-is + return response()->file($absolutePath, ['Content-Type' => $mime]); + } + + // ── Helpers ────────────────────────────────────────────────────────────── + + private function resolvePath(string $slug, string $path): string + { + $path = ltrim($path, '/'); + + if ($path === '' || $path === '/') { + $path = 'index.html'; + } + + // If path is a directory (no extension), try index.html inside it + if (! pathinfo($path, PATHINFO_EXTENSION)) { + $candidate = rtrim($path, '/') . '/index.html'; + $diskPath = "pages/{$slug}/{$candidate}"; + if (Storage::exists($diskPath)) { + return $diskPath; + } + } + + return "pages/{$slug}/{$path}"; + } + + private function guardTraversal(string $slug, string $filePath): void + { + $root = realpath(Storage::path("pages/{$slug}")); + $real = realpath(Storage::path($filePath)); + + if ($root && $real && ! str_starts_with($real, $root)) { + abort(403, 'Path traversal detected.'); + } + } + + private function injectBase(string $html, Page $page, Request $request, string $slug): string + { + // Detect subdomain access + $host = $request->getHost(); + $baseDomain = config('app.base_domain', 'statichtmlsites.mtex.dev'); + $isSubdomain = str_ends_with($host, ".{$baseDomain}"); + + $baseUrl = $isSubdomain + ? $page->subdomainUrl() + : $page->pathUrl(); + + $baseTag = "\n"; + + // Inject as first child of + if (preg_match('/]*)>/i', $html)) { + return preg_replace('/]*)>/i', "\n{$baseTag}", $html, 1); + } + + // No – prepend at top + return $baseTag . $html; + } + + private function detectMime(string $path): string + { + $map = [ + 'html' => 'text/html', + 'css' => 'text/css', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'svg' => 'image/svg+xml', + 'txt' => 'text/plain', + 'png' => 'image/png', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'ico' => 'image/x-icon', + 'mp4' => 'video/mp4', + 'webm' => 'video/webm', + 'mp3' => 'audio/mpeg', + 'wav' => 'audio/wav', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'woff' => 'font/woff', + 'woff2' => 'font/woff2', + ]; + + $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); + return $map[$ext] ?? 'application/octet-stream'; + } +} diff --git a/app/Livewire/FileManager.php b/app/Livewire/FileManager.php new file mode 100644 index 0000000..1fc2bf6 --- /dev/null +++ b/app/Livewire/FileManager.php @@ -0,0 +1,337 @@ +page = Page::where('slug', $slug)->firstOrFail(); + Gate::authorize('update', $this->page); + + $this->page->ensureStorageExists(); + + $this->pageName = $this->page->name; + $this->pageSlug = $this->page->slug; + $this->pageIsPublic = $this->page->is_public; + + // Auto-open index.html if it exists + $indexPath = 'index.html'; + if (Storage::disk('pages')->exists($this->page->storagePath($indexPath))) { + $this->openFile($indexPath); + } + } + + // ─── Computed ───────────────────────────────────────────────────────────── + + #[Computed] + public function fileTree(): array + { + $disk = Storage::disk('pages'); + $root = $this->page->storagePath($this->currentDir); + $prefix = $this->page->storagePath() . '/'; + + $entries = []; + + // Directories + foreach ($disk->directories($root) as $dir) { + $entries[] = [ + 'type' => 'dir', + 'name' => basename($dir), + 'path' => ltrim(str_replace($prefix, '', $dir), '/'), + ]; + } + + // Files + foreach ($disk->files($root) as $file) { + $rel = ltrim(str_replace($prefix, '', $file), '/'); + $entries[] = [ + 'type' => 'file', + 'name' => basename($file), + 'path' => $rel, + 'active' => $rel === $this->activeFile, + 'isIndex' => basename($file) === 'index.html', + ]; + } + + usort($entries, fn ($a, $b) => [$a['type'] === 'file' ? 1 : 0, $a['name']] <=> [$b['type'] === 'file' ? 1 : 0, $b['name']]); + + return $entries; + } + + #[Computed] + public function editorLanguage(): string + { + return match (strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION))) { + 'html', 'htm' => 'html', + 'css' => 'css', + 'js' => 'javascript', + 'json' => 'json', + 'xml' => 'xml', + 'svg' => 'xml', + 'txt' => 'text', + default => 'text', + }; + } + + #[Computed] + public function isEditable(): bool + { + $editable = ['html', 'htm', 'css', 'js', 'json', 'txt', 'xml', 'svg']; + return in_array(strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), $editable); + } + + #[Computed] + public function isPreviewable(): bool + { + $media = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf']; + return in_array(strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), $media); + } + + #[Computed] + public function previewUrl(): string + { + return $this->page->subdomainUrl() . $this->activeFile; + } + + // ─── File operations ────────────────────────────────────────────────────── + + public function openFile(string $path): void + { + $this->ensureSafe($path); + $disk = Storage::disk('pages'); + $full = $this->page->storagePath($path); + + if (! $disk->exists($full)) { + $this->flash = "File not found: {$path}"; + return; + } + + $this->activeFile = $path; + $this->fileContent = $disk->get($full); + $this->isDirty = false; + + $this->dispatch('load-editor', content: $this->fileContent, language: $this->editorLanguage); + } + + public function saveFile(string $content): void + { + if ($this->activeFile === '') return; + + $this->ensureSafe($this->activeFile); + Storage::disk('pages')->put( + $this->page->storagePath($this->activeFile), + $content + ); + + $this->fileContent = $content; + $this->isDirty = false; + $this->flash = 'Saved ✓'; + } + + public function createFile(): void + { + $name = $this->sanitiseName($this->newFileName); + if ($name === '') { + $this->flash = 'File name cannot be empty.'; + return; + } + + $relative = $this->currentDir ? "{$this->currentDir}/{$name}" : $name; + $full = $this->page->storagePath($relative); + + if (Storage::disk('pages')->exists($full)) { + $this->flash = 'A file with that name already exists.'; + return; + } + + Storage::disk('pages')->put($full, ''); + $this->newFileName = ''; + $this->openFile($relative); + unset($this->fileTree); + } + + public function createFolder(): void + { + $name = $this->sanitiseName($this->newFolderName); + if ($name === '') { + $this->flash = 'Folder name cannot be empty.'; + return; + } + + $relative = $this->currentDir ? "{$this->currentDir}/{$name}" : $name; + Storage::disk('pages')->makeDirectory($this->page->storagePath($relative)); + + $this->newFolderName = ''; + $this->flash = "Folder '{$name}' created."; + unset($this->fileTree); + } + + public function deleteFile(string $path): void + { + $this->ensureSafe($path); + Storage::disk('pages')->delete($this->page->storagePath($path)); + + if ($this->activeFile === $path) { + $this->activeFile = ''; + $this->fileContent = ''; + } + + $this->flash = "Deleted {$path}."; + unset($this->fileTree); + } + + public function startRename(string $path): void + { + $this->renameTarget = $path; + $this->renameTo = basename($path); + } + + public function confirmRename(): void + { + if ($this->renameTarget === null) return; + + $this->ensureSafe($this->renameTarget); + $newName = $this->sanitiseName($this->renameTo); + if ($newName === '') return; + + $dir = dirname($this->renameTarget); + $newPath = ($dir === '.' ? '' : $dir . '/') . $newName; + + Storage::disk('pages')->move( + $this->page->storagePath($this->renameTarget), + $this->page->storagePath($newPath) + ); + + if ($this->activeFile === $this->renameTarget) { + $this->openFile($newPath); + } + + $this->renameTarget = null; + $this->renameTo = ''; + unset($this->fileTree); + } + + public function enterDirectory(string $path): void + { + $this->currentDir = $path; + unset($this->fileTree); + } + + public function goUp(): void + { + $this->currentDir = dirname($this->currentDir) === '.' ? '' : dirname($this->currentDir); + unset($this->fileTree); + } + + // ─── Upload ─────────────────────────────────────────────────────────────── + + public function uploadFile(): void + { + $this->validate([ + 'upload' => 'required|file|max:' . (config('app.max_upload_mb', 50) * 1024), + ]); + + $originalName = $this->sanitiseName($this->upload->getClientOriginalName()); + $relative = $this->currentDir ? "{$this->currentDir}/{$originalName}" : $originalName; + + Storage::disk('pages')->putFileAs( + $this->page->storagePath($this->currentDir ?: ''), + $this->upload, + $originalName + ); + + $this->upload = null; + $this->flash = "Uploaded {$originalName}."; + unset($this->fileTree); + } + + // ─── Page settings ──────────────────────────────────────────────────────── + + public function saveSettings(): void + { + $data = $this->validate([ + 'pageName' => 'required|string|max:255', + 'pageSlug' => "required|string|max:100|regex:/^[a-z0-9\-]+$/|unique:pages,slug,{$this->page->id}", + 'pageIsPublic' => 'boolean', + ]); + + $oldSlug = $this->page->slug; + $newSlug = $this->pageSlug; + + if ($oldSlug !== $newSlug) { + Storage::disk('pages')->move($oldSlug, $newSlug); + } + + $this->page->update([ + 'name' => $this->pageName, + 'slug' => $newSlug, + 'is_public' => $this->pageIsPublic, + ]); + + $this->page->refresh(); + $this->showSettings = false; + $this->flash = 'Settings saved.'; + + if ($oldSlug !== $newSlug) { + $this->redirect(route('pages.manager', $newSlug)); + } + } + + // ─── Security ───────────────────────────────────────────────────────────── + + private function ensureSafe(string $path): void + { + if (str_contains($path, '..') || str_starts_with($path, '/')) { + abort(400, 'Invalid path.'); + } + } + + private function sanitiseName(string $name): string + { + // Allow alphanumeric, dash, underscore, dot + return preg_replace('/[^a-zA-Z0-9\-_.]/', '-', trim($name)); + } + + // ─── Render ─────────────────────────────────────────────────────────────── + + public function render() + { + return view('livewire.file-manager') + ->layout('layouts.app', ['title' => "Manager — {$this->page->name}"]); + } +} diff --git a/app/Models/Page.php b/app/Models/Page.php new file mode 100644 index 0000000..fcfedae --- /dev/null +++ b/app/Models/Page.php @@ -0,0 +1,140 @@ + 'boolean', + ]; + + // ── Relationships ──────────────────────────────────────────────────────── + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + // ── Helpers ────────────────────────────────────────────────────────────── + + /** + * Absolute path to this page's storage root. + */ + public function storagePath(string $relative = ''): string + { + $base = storage_path("app/pages/{$this->slug}"); + return $relative ? $base . '/' . ltrim($relative, '/') : $base; + } + + /** + * Laravel disk-relative path (for Storage facade calls). + */ + public function diskPath(string $relative = ''): string + { + $base = "pages/{$this->slug}"; + return $relative ? $base . '/' . ltrim($relative, '/') : $base; + } + + /** + * Public URL for subdomain access. + */ + public function subdomainUrl(string $path = ''): string + { + $base = "https://{$this->slug}." . config('app.base_domain', 'statichtmlsites.mtex.dev'); + return $path ? $base . '/' . ltrim($path, '/') : $base . '/'; + } + + /** + * Public URL for path-based access. + */ + public function pathUrl(string $path = ''): string + { + $base = config('app.url') . '/' . $this->slug; + return $path ? $base . '/' . ltrim($path, '/') : $base . '/'; + } + + /** + * Generate a unique slug from a name. + */ + public static function makeUniqueSlug(string $name): string + { + $slug = Str::slug($name); + $original = $slug; + $counter = 1; + + while (static::where('slug', $slug)->exists()) { + $slug = "{$original}-{$counter}"; + $counter++; + } + + return $slug; + } + + // ── Boot ───────────────────────────────────────────────────────────────── + + protected static function booted(): void + { + // Ensure storage directory exists when a page is created + static::created(function (Page $page) { + Storage::makeDirectory($page->diskPath()); + + // Bootstrap with a blank index.html + if (! Storage::exists($page->diskPath('index.html'))) { + Storage::put($page->diskPath('index.html'), static::defaultHtml($page->name)); + } + }); + + // Remove files when a page is deleted + static::deleted(function (Page $page) { + Storage::deleteDirectory($page->diskPath()); + }); + + // If slug changes, rename the storage directory + static::updating(function (Page $page) { + if ($page->isDirty('slug')) { + $old = $page->getOriginal('slug'); + $new = $page->slug; + Storage::move("pages/{$old}", "pages/{$new}"); + } + }); + } + + private static function defaultHtml(string $name): string + { + $escaped = htmlspecialchars($name, ENT_QUOTES); + return << + + + + + {$escaped} + + + +

Welcome to {$escaped}

+ + + HTML; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 6e0ea6e..c9f95c3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,64 +2,28 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; -use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -use Illuminate\Support\Str; -use Laravel\Fortify\TwoFactorAuthenticatable; class User extends Authenticatable { - /** @use HasFactory */ - use HasFactory, Notifiable, TwoFactorAuthenticatable; + use HasFactory, Notifiable; - /** - * The attributes that are mass assignable. - * - * @var list - */ - protected $fillable = [ - 'name', - 'email', - 'password', - ]; + protected $fillable = ['name', 'email', 'password']; + protected $hidden = ['password', 'remember_token']; - /** - * The attributes that should be hidden for serialization. - * - * @var list - */ - protected $hidden = [ - 'password', - 'two_factor_secret', - 'two_factor_recovery_codes', - 'remember_token', - ]; - - /** - * Get the attributes that should be cast. - * - * @return array - */ protected function casts(): array { return [ 'email_verified_at' => 'datetime', - 'password' => 'hashed', + 'password' => 'hashed', ]; } - /** - * Get the user's initials - */ - public function initials(): string + public function pages(): HasMany { - return Str::of($this->name) - ->explode(' ') - ->take(2) - ->map(fn ($word) => Str::substr($word, 0, 1)) - ->implode(''); + return $this->hasMany(Page::class); } } diff --git a/app/Policies/PagePolicy.php b/app/Policies/PagePolicy.php new file mode 100644 index 0000000..04708f7 --- /dev/null +++ b/app/Policies/PagePolicy.php @@ -0,0 +1,24 @@ +id === $page->user_id; + } + + public function update(User $user, Page $page): bool + { + return $user->id === $page->user_id; + } + + public function delete(User $user, Page $page): bool + { + return $user->id === $page->user_id; + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f1525e9..27769d9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -5,6 +5,9 @@ use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\DB; +use App\Models\Page; +use App\Policies\PagePolicy; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; use Illuminate\Validation\Rules\Password; @@ -24,6 +27,7 @@ public function register(): void public function boot(): void { $this->configureDefaults(); + Gate::policy(Page::class, PagePolicy::class); } /** @@ -47,4 +51,4 @@ protected function configureDefaults(): void : null, ); } -} +} \ No newline at end of file diff --git a/app/Services/FileManagerService.php b/app/Services/FileManagerService.php new file mode 100644 index 0000000..b1edfeb --- /dev/null +++ b/app/Services/FileManagerService.php @@ -0,0 +1,229 @@ +diskPath(); + return $this->buildTree($root, $root); + } + + private function buildTree(string $directory, string $root): array + { + $items = []; + + $dirs = Storage::directories($directory); + $files = Storage::files($directory); + + foreach ($dirs as $dir) { + $name = basename($dir); + $relative = $this->relative($dir, $root); + $items[] = [ + 'type' => 'directory', + 'name' => $name, + 'path' => $relative, + 'children' => $this->buildTree($dir, $root), + ]; + } + + foreach ($files as $file) { + $name = basename($file); + $relative = $this->relative($file, $root); + $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); + $items[] = [ + 'type' => 'file', + 'name' => $name, + 'path' => $relative, + 'editable' => in_array($ext, self::EDITABLE_EXT, true), + 'ext' => $ext, + 'size' => Storage::size($file), + ]; + } + + // Directories first, then files, both alphabetically + usort($items, fn($a, $b) => + [$a['type'] === 'file' ? 1 : 0, $a['name']] + <=> + [$b['type'] === 'file' ? 1 : 0, $b['name']] + ); + + return $items; + } + + // ── Read / Write ───────────────────────────────────────────────────────── + + public function read(Page $page, string $relative): string + { + $path = $this->resolve($page, $relative); + $this->guard($page, $path); + + if (! Storage::exists($path)) { + throw new RuntimeException("File not found: {$relative}"); + } + + return Storage::get($path); + } + + public function save(Page $page, string $relative, string $content): void + { + $path = $this->resolve($page, $relative); + $this->guard($page, $path); + Storage::put($path, $content); + } + + public function createFile(Page $page, string $relative): void + { + $relative = $this->sanitizePath($relative); + $path = $this->resolve($page, $relative); + $this->guard($page, $path); + + if (Storage::exists($path)) { + throw new RuntimeException("File already exists: {$relative}"); + } + + Storage::put($path, ''); + } + + // ── Upload ─────────────────────────────────────────────────────────────── + + public function upload(Page $page, UploadedFile $file, string $folder = ''): void + { + $name = $this->sanitizeName($file->getClientOriginalName()); + $ext = strtolower($file->getClientOriginalExtension()); + + $allowed = array_merge(self::EDITABLE_EXT, self::BINARY_EXT); + if (! in_array($ext, $allowed, true)) { + throw new RuntimeException("File type .{$ext} is not allowed."); + } + + $folder = $folder ? $this->sanitizePath($folder) : ''; + $diskDir = $folder + ? $page->diskPath($folder) + : $page->diskPath(); + + $file->storeAs($diskDir, $name); + } + + // ── Delete ─────────────────────────────────────────────────────────────── + + public function delete(Page $page, string $relative): void + { + $path = $this->resolve($page, $relative); + $this->guard($page, $path); + + if (Storage::directoryExists($path)) { + Storage::deleteDirectory($path); + } elseif (Storage::exists($path)) { + Storage::delete($path); + } else { + throw new RuntimeException("Path not found: {$relative}"); + } + } + + // ── Folder ─────────────────────────────────────────────────────────────── + + public function createFolder(Page $page, string $relative): void + { + $relative = $this->sanitizePath($relative); + $path = $this->resolve($page, $relative); + $this->guard($page, $path); + Storage::makeDirectory($path); + } + + // ── Rename ─────────────────────────────────────────────────────────────── + + public function rename(Page $page, string $from, string $to): void + { + $fromPath = $this->resolve($page, $this->sanitizePath($from)); + $toPath = $this->resolve($page, $this->sanitizePath($to)); + + $this->guard($page, $fromPath); + $this->guard($page, $toPath); + + if (Storage::directoryExists($fromPath)) { + // Laravel doesn't have a move-directory helper; use the filesystem + $this->moveDirectory($fromPath, $toPath); + } elseif (Storage::exists($fromPath)) { + Storage::move($fromPath, $toPath); + } else { + throw new RuntimeException("Path not found: {$from}"); + } + } + + private function moveDirectory(string $from, string $to): void + { + Storage::makeDirectory($to); + + foreach (Storage::allFiles($from) as $file) { + $relative = Str::after($file, $from . '/'); + Storage::move($file, $to . '/' . $relative); + } + + Storage::deleteDirectory($from); + } + + // ── Helpers ────────────────────────────────────────────────────────────── + + private function resolve(Page $page, string $relative): string + { + return $page->diskPath(ltrim($relative, '/')); + } + + private function relative(string $full, string $root): string + { + return ltrim(Str::after($full, $root), '/'); + } + + /** + * Prevent path traversal attacks. + */ + private function guard(Page $page, string $diskPath): void + { + $root = realpath(Storage::path($page->diskPath())); + $real = realpath(Storage::path($diskPath)) + ?: Storage::path($diskPath); // file may not exist yet + + if ($root && ! str_starts_with($real, $root)) { + throw new RuntimeException('Path traversal detected.'); + } + } + + private function sanitizeName(string $name): string + { + // Keep extension, slugify the stem + $ext = pathinfo($name, PATHINFO_EXTENSION); + $stem = pathinfo($name, PATHINFO_FILENAME); + $safe = preg_replace('/[^a-zA-Z0-9\-_]/', '-', $stem); + return $ext ? "{$safe}.{$ext}" : $safe; + } + + private function sanitizePath(string $path): string + { + // Explode on slashes, sanitize each segment, rejoin + $segments = array_filter(explode('/', str_replace('\\', '/', $path))); + $safe = array_map(fn($s) => preg_replace('/[^a-zA-Z0-9\-_.]/', '-', $s), $segments); + return implode('/', $safe); + } +} diff --git a/config/app.php b/config/app.php index 440992f..e7211da 100644 --- a/config/app.php +++ b/config/app.php @@ -1,5 +1,8 @@ env('APP_FAKER_LOCALE', 'en_US'), + /* + |-------------------------------------------------------------------------- + | Base domain + | Used for subdomain routing and URL helpers. + |-------------------------------------------------------------------------- + */ + 'base_domain' => env('APP_BASE_DOMAIN', 'statichtmlsites.mtex.dev'), + + /* + |-------------------------------------------------------------------------- + | Max upload size (MB) + |-------------------------------------------------------------------------- + */ + 'max_upload_mb' => (int) env('MAX_UPLOAD_MB', 50), + /* |-------------------------------------------------------------------------- | Encryption Key @@ -123,4 +141,12 @@ 'store' => env('APP_MAINTENANCE_STORE', 'database'), ], + + 'providers' => ServiceProvider::defaultProviders()->merge([ + App\Providers\AppServiceProvider::class, + ])->toArray(), + + 'aliases' => Facade::defaultAliases()->merge([ + // 'Example' => App\Facades\Example::class, + ])->toArray(), ]; diff --git a/config/filesystems.php b/config/filesystems.php index 37d8fca..023680d 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -47,6 +47,20 @@ 'report' => false, ], + /* + |---------------------------------------------------------------------- + | Pages disk + | All user page files live here, scoped per slug: + | storage/app/pages/{slug}/ + |---------------------------------------------------------------------- + */ + 'pages' => [ + 'driver' => 'local', + 'root' => storage_path('app/pages'), + 'visibility' => 'public', + 'throw' => true, + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/database/migrations/2026_01_01_000001_create_pages_table.php b/database/migrations/2026_01_01_000001_create_pages_table.php new file mode 100644 index 0000000..522dadb --- /dev/null +++ b/database/migrations/2026_01_01_000001_create_pages_table.php @@ -0,0 +1,25 @@ +ulid('id')->primary(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('slug')->unique(); + $table->boolean('is_public')->default(true); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('pages'); + } +}; diff --git a/resources/views/components/action-message.blade.php b/resources/views/components/action-message.blade.php deleted file mode 100644 index d313ee6..0000000 --- a/resources/views/components/action-message.blade.php +++ /dev/null @@ -1,14 +0,0 @@ -@props([ - 'on', -]) - -
merge(['class' => 'text-sm']) }} -> - {{ $slot->isEmpty() ? __('Saved.') : $slot }} -
diff --git a/resources/views/components/app-logo-icon.blade.php b/resources/views/components/app-logo-icon.blade.php deleted file mode 100644 index 0adc3a2..0000000 --- a/resources/views/components/app-logo-icon.blade.php +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/resources/views/components/app-logo.blade.php b/resources/views/components/app-logo.blade.php deleted file mode 100644 index 26e8f68..0000000 --- a/resources/views/components/app-logo.blade.php +++ /dev/null @@ -1,17 +0,0 @@ -@props([ - 'sidebar' => false, -]) - -@if($sidebar) - - - - - -@else - - - - - -@endif diff --git a/resources/views/components/auth-header.blade.php b/resources/views/components/auth-header.blade.php deleted file mode 100644 index e596a3f..0000000 --- a/resources/views/components/auth-header.blade.php +++ /dev/null @@ -1,9 +0,0 @@ -@props([ - 'title', - 'description', -]) - -
- {{ $title }} - {{ $description }} -
diff --git a/resources/views/components/auth-session-status.blade.php b/resources/views/components/auth-session-status.blade.php deleted file mode 100644 index 98e0011..0000000 --- a/resources/views/components/auth-session-status.blade.php +++ /dev/null @@ -1,9 +0,0 @@ -@props([ - 'status', -]) - -@if ($status) -
merge(['class' => 'font-medium text-sm text-green-600']) }}> - {{ $status }} -
-@endif diff --git a/resources/views/components/desktop-user-menu.blade.php b/resources/views/components/desktop-user-menu.blade.php deleted file mode 100644 index 7fb3706..0000000 --- a/resources/views/components/desktop-user-menu.blade.php +++ /dev/null @@ -1,39 +0,0 @@ - - - - -
- -
- {{ auth()->user()->name }} - {{ auth()->user()->email }} -
-
- - - - {{ __('Settings') }} - -
- @csrf - - {{ __('Log out') }} - -
-
-
-
diff --git a/resources/views/components/placeholder-pattern.blade.php b/resources/views/components/placeholder-pattern.blade.php deleted file mode 100644 index 8a434f0..0000000 --- a/resources/views/components/placeholder-pattern.blade.php +++ /dev/null @@ -1,12 +0,0 @@ -@props([ - 'id' => uniqid(), -]) - - - - - - - - - diff --git a/resources/views/components/settings/layout.blade.php b/resources/views/components/settings/layout.blade.php deleted file mode 100644 index 3a65247..0000000 --- a/resources/views/components/settings/layout.blade.php +++ /dev/null @@ -1,20 +0,0 @@ -
-
- - {{ __('Profile') }} - {{ __('Security') }} - {{ __('Appearance') }} - -
- - - -
- {{ $heading ?? '' }} - {{ $subheading ?? '' }} - -
- {{ $slot }} -
-
-
diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php deleted file mode 100644 index 8f08c05..0000000 --- a/resources/views/dashboard.blade.php +++ /dev/null @@ -1,18 +0,0 @@ - -
-
-
- -
-
- -
-
- -
-
-
- -
-
-
diff --git a/resources/views/flux/icon/book-open-text.blade.php b/resources/views/flux/icon/book-open-text.blade.php deleted file mode 100644 index bff20a3..0000000 --- a/resources/views/flux/icon/book-open-text.blade.php +++ /dev/null @@ -1,47 +0,0 @@ -{{-- Credit: Lucide (https://lucide.dev) --}} - -@props([ - 'variant' => 'outline', -]) - -@php - if ($variant === 'solid') { - throw new \Exception('The "solid" variant is not supported in Lucide.'); - } - - $classes = Flux::classes('shrink-0')->add( - match ($variant) { - 'outline' => '[:where(&)]:size-6', - 'solid' => '[:where(&)]:size-6', - 'mini' => '[:where(&)]:size-5', - 'micro' => '[:where(&)]:size-4', - }, - ); - - $strokeWidth = match ($variant) { - 'outline' => 2, - 'mini' => 2.25, - 'micro' => 2.5, - }; -@endphp - -class($classes) }} - data-flux-icon - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - stroke-width="{{ $strokeWidth }}" - stroke-linecap="round" - stroke-linejoin="round" - aria-hidden="true" - data-slot="icon" -> - - - - - - - diff --git a/resources/views/flux/icon/chevrons-up-down.blade.php b/resources/views/flux/icon/chevrons-up-down.blade.php deleted file mode 100644 index bf1ba2b..0000000 --- a/resources/views/flux/icon/chevrons-up-down.blade.php +++ /dev/null @@ -1,43 +0,0 @@ -{{-- Credit: Lucide (https://lucide.dev) --}} - -@props([ - 'variant' => 'outline', -]) - -@php - if ($variant === 'solid') { - throw new \Exception('The "solid" variant is not supported in Lucide.'); - } - - $classes = Flux::classes('shrink-0')->add( - match ($variant) { - 'outline' => '[:where(&)]:size-6', - 'solid' => '[:where(&)]:size-6', - 'mini' => '[:where(&)]:size-5', - 'micro' => '[:where(&)]:size-4', - }, - ); - - $strokeWidth = match ($variant) { - 'outline' => 2, - 'mini' => 2.25, - 'micro' => 2.5, - }; -@endphp - -class($classes) }} - data-flux-icon - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - stroke-width="{{ $strokeWidth }}" - stroke-linecap="round" - stroke-linejoin="round" - aria-hidden="true" - data-slot="icon" -> - - - diff --git a/resources/views/flux/icon/folder-git-2.blade.php b/resources/views/flux/icon/folder-git-2.blade.php deleted file mode 100644 index 292171b..0000000 --- a/resources/views/flux/icon/folder-git-2.blade.php +++ /dev/null @@ -1,45 +0,0 @@ -{{-- Credit: Lucide (https://lucide.dev) --}} - -@props([ - 'variant' => 'outline', -]) - -@php - if ($variant === 'solid') { - throw new \Exception('The "solid" variant is not supported in Lucide.'); - } - - $classes = Flux::classes('shrink-0')->add( - match ($variant) { - 'outline' => '[:where(&)]:size-6', - 'solid' => '[:where(&)]:size-6', - 'mini' => '[:where(&)]:size-5', - 'micro' => '[:where(&)]:size-4', - }, - ); - - $strokeWidth = match ($variant) { - 'outline' => 2, - 'mini' => 2.25, - 'micro' => 2.5, - }; -@endphp - -class($classes) }} - data-flux-icon - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - stroke-width="{{ $strokeWidth }}" - stroke-linecap="round" - stroke-linejoin="round" - aria-hidden="true" - data-slot="icon" -> - - - - - diff --git a/resources/views/flux/icon/layout-grid.blade.php b/resources/views/flux/icon/layout-grid.blade.php deleted file mode 100644 index 88c5698..0000000 --- a/resources/views/flux/icon/layout-grid.blade.php +++ /dev/null @@ -1,45 +0,0 @@ -{{-- Credit: Lucide (https://lucide.dev) --}} - -@props([ - 'variant' => 'outline', -]) - -@php - if ($variant === 'solid') { - throw new \Exception('The "solid" variant is not supported in Lucide.'); - } - - $classes = Flux::classes('shrink-0')->add( - match ($variant) { - 'outline' => '[:where(&)]:size-6', - 'solid' => '[:where(&)]:size-6', - 'mini' => '[:where(&)]:size-5', - 'micro' => '[:where(&)]:size-4', - }, - ); - - $strokeWidth = match ($variant) { - 'outline' => 2, - 'mini' => 2.25, - 'micro' => 2.5, - }; -@endphp - -class($classes) }} - data-flux-icon - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - stroke-width="{{ $strokeWidth }}" - stroke-linecap="round" - stroke-linejoin="round" - aria-hidden="true" - data-slot="icon" -> - - - - - diff --git a/resources/views/flux/navlist/group.blade.php b/resources/views/flux/navlist/group.blade.php deleted file mode 100644 index 5e691a2..0000000 --- a/resources/views/flux/navlist/group.blade.php +++ /dev/null @@ -1,51 +0,0 @@ -@props([ - 'expandable' => false, - 'expanded' => true, - 'heading' => null, -]) - - - -class('group/disclosure') }} - @if ($expanded === true) open @endif - data-flux-navlist-group -> - - - - - - - -
class('block space-y-[2px]') }}> -
-
{{ $heading }}
-
- -
- {{ $slot }} -
-
- - - -
class('block space-y-[2px]') }}> - {{ $slot }} -
- - diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 037dd1b..e9daa9e 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -1,5 +1,44 @@ - - - {{ $slot }} - - + + + + + + + {{ $title ?? 'StaticHTMLSites' }} + @vite(['resources/css/app.css', 'resources/js/app.js']) + @livewireStyles + @stack('styles') + + + + + +@if (session('success')) +
+ {{ session('success') }} +
+@endif + +
+ {{ $slot }} +
+ +@livewireScripts +@stack('scripts') + + diff --git a/resources/views/layouts/app/header.blade.php b/resources/views/layouts/app/header.blade.php deleted file mode 100644 index e1f84d9..0000000 --- a/resources/views/layouts/app/header.blade.php +++ /dev/null @@ -1,78 +0,0 @@ - - - - @include('partials.head') - - - - - - - - - - {{ __('Dashboard') }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ __('Dashboard') }} - - - - - - - - - {{ __('Repository') }} - - - {{ __('Documentation') }} - - - - - {{ $slot }} - - @fluxScripts - - diff --git a/resources/views/layouts/app/sidebar.blade.php b/resources/views/layouts/app/sidebar.blade.php deleted file mode 100644 index 3d04ed1..0000000 --- a/resources/views/layouts/app/sidebar.blade.php +++ /dev/null @@ -1,95 +0,0 @@ - - - - @include('partials.head') - - - - - - - - - - - - {{ __('Dashboard') }} - - - - - - - - - {{ __('Repository') }} - - - - {{ __('Documentation') }} - - - - - - - - - - - - - - - - -
-
- - -
- {{ auth()->user()->name }} - {{ auth()->user()->email }} -
-
-
-
- - - - - - {{ __('Settings') }} - - - - - -
- @csrf - - {{ __('Log out') }} - -
-
-
-
- - {{ $slot }} - - @fluxScripts - - diff --git a/resources/views/layouts/auth.blade.php b/resources/views/layouts/auth.blade.php deleted file mode 100644 index 7150091..0000000 --- a/resources/views/layouts/auth.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - - {{ $slot }} - diff --git a/resources/views/layouts/auth/card.blade.php b/resources/views/layouts/auth/card.blade.php deleted file mode 100644 index db94716..0000000 --- a/resources/views/layouts/auth/card.blade.php +++ /dev/null @@ -1,26 +0,0 @@ - - - - @include('partials.head') - - - - @fluxScripts - - diff --git a/resources/views/layouts/auth/simple.blade.php b/resources/views/layouts/auth/simple.blade.php deleted file mode 100644 index 6e0d909..0000000 --- a/resources/views/layouts/auth/simple.blade.php +++ /dev/null @@ -1,22 +0,0 @@ - - - - @include('partials.head') - - - - @fluxScripts - - diff --git a/resources/views/layouts/auth/split.blade.php b/resources/views/layouts/auth/split.blade.php deleted file mode 100644 index 4e9788b..0000000 --- a/resources/views/layouts/auth/split.blade.php +++ /dev/null @@ -1,43 +0,0 @@ - - - - @include('partials.head') - - -
- - -
- @fluxScripts - - diff --git a/resources/views/livewire/auth/confirm-password.blade.php b/resources/views/livewire/auth/confirm-password.blade.php deleted file mode 100644 index 13dd110..0000000 --- a/resources/views/livewire/auth/confirm-password.blade.php +++ /dev/null @@ -1,28 +0,0 @@ - -
- - - - -
- @csrf - - - - - {{ __('Confirm') }} - - -
-
diff --git a/resources/views/livewire/auth/forgot-password.blade.php b/resources/views/livewire/auth/forgot-password.blade.php deleted file mode 100644 index 5f1cdad..0000000 --- a/resources/views/livewire/auth/forgot-password.blade.php +++ /dev/null @@ -1,31 +0,0 @@ - -
- - - - - -
- @csrf - - - - - - {{ __('Email password reset link') }} - - - -
- {{ __('Or, return to') }} - {{ __('log in') }} -
-
-
diff --git a/resources/views/livewire/auth/login.blade.php b/resources/views/livewire/auth/login.blade.php deleted file mode 100644 index e9167da..0000000 --- a/resources/views/livewire/auth/login.blade.php +++ /dev/null @@ -1,59 +0,0 @@ - -
- - - - - -
- @csrf - - - - - -
- - - @if (Route::has('password.request')) - - {{ __('Forgot your password?') }} - - @endif -
- - - - -
- - {{ __('Log in') }} - -
- - - @if (Route::has('register')) -
- {{ __('Don\'t have an account?') }} - {{ __('Sign up') }} -
- @endif -
-
diff --git a/resources/views/livewire/auth/register.blade.php b/resources/views/livewire/auth/register.blade.php deleted file mode 100644 index 6c12579..0000000 --- a/resources/views/livewire/auth/register.blade.php +++ /dev/null @@ -1,67 +0,0 @@ - -
- - - - - -
- @csrf - - - - - - - - - - - - -
- - {{ __('Create account') }} - -
- - -
- {{ __('Already have an account?') }} - {{ __('Log in') }} -
-
-
diff --git a/resources/views/livewire/auth/reset-password.blade.php b/resources/views/livewire/auth/reset-password.blade.php deleted file mode 100644 index e649471..0000000 --- a/resources/views/livewire/auth/reset-password.blade.php +++ /dev/null @@ -1,52 +0,0 @@ - -
- - - - - -
- @csrf - - - - - - - - - - - - -
- - {{ __('Reset password') }} - -
- -
-
diff --git a/resources/views/livewire/auth/two-factor-challenge.blade.php b/resources/views/livewire/auth/two-factor-challenge.blade.php deleted file mode 100644 index 0726c0b..0000000 --- a/resources/views/livewire/auth/two-factor-challenge.blade.php +++ /dev/null @@ -1,95 +0,0 @@ - -
-
-
- -
- -
- -
- -
- @csrf - -
-
-
- -
-
- -
-
- -
- - @error('recovery_code') - - {{ $message }} - - @enderror -
- - - {{ __('Continue') }} - -
- -
- {{ __('or you can') }} -
- {{ __('login using a recovery code') }} - {{ __('login using an authentication code') }} -
-
-
-
-
-
diff --git a/resources/views/livewire/auth/verify-email.blade.php b/resources/views/livewire/auth/verify-email.blade.php deleted file mode 100644 index 50e7b44..0000000 --- a/resources/views/livewire/auth/verify-email.blade.php +++ /dev/null @@ -1,29 +0,0 @@ - -
- - {{ __('Please verify your email address by clicking on the link we just emailed to you.') }} - - - @if (session('status') == 'verification-link-sent') - - {{ __('A new verification link has been sent to the email address you provided during registration.') }} - - @endif - -
-
- @csrf - - {{ __('Resend verification email') }} - -
- -
- @csrf - - {{ __('Log out') }} - -
-
-
-
diff --git a/resources/views/livewire/file-manager.blade.php b/resources/views/livewire/file-manager.blade.php new file mode 100644 index 0000000..115a0c7 --- /dev/null +++ b/resources/views/livewire/file-manager.blade.php @@ -0,0 +1,347 @@ +{{-- File Manager — Full-screen IDE layout --}} + +@push('head') +{{-- CodeMirror 6 via CDN --}} + +@endpush + +
+ + {{-- ── Flash notification ───────────────────────────────────────────── --}} +
+ + {{-- ── Sidebar / File Tree ──────────────────────────────────────────── --}} + + + {{-- ── Editor / Preview pane ───────────────────────────────────────── --}} +
+ + {{-- Toolbar --}} +
+ + {{ $activeFile ?: 'No file open' }} + + +
+ @if ($isDirty) + ● Unsaved + @endif + + @if ($this->isEditable && $activeFile) + + @endif + + + Preview ↗ + +
+
+ + {{-- Editor --}} + @if ($this->isEditable && $activeFile) +
+ @elseif ($this->isPreviewable && $activeFile) + @php $ext = strtolower(pathinfo($activeFile, PATHINFO_EXTENSION)); @endphp +
+ @if (in_array($ext, ['png','jpg','jpeg','gif','webp','svg'])) + + @elseif (in_array($ext, ['mp4','webm'])) + + @elseif (in_array($ext, ['mp3','wav','ogg'])) + + @elseif ($ext === 'pdf') + + @endif +
+ @elseif ($activeFile) +
+ Binary file — not editable. +
+ @else +
+

👈

+

Select a file from the sidebar to edit.

+
+ @endif + +
+ + {{-- ── Settings slide-over ─────────────────────────────────────────── --}} + @if ($showSettings) +
+
+
+

Page Settings

+ +
+ +
+
+ + + @error('pageName')

{{ $message }}

@enderror +
+ +
+ + + @error('pageSlug')

{{ $message }}

@enderror +

{{ config('app.base_domain') }}/{{ $pageSlug }}

+
+ +
+ + +
+
+ +
+ + +
+
+ @csrf @method('DELETE') + +
+
+
+
+
+ @endif + +
+ +{{-- ── CodeMirror 6 script ──────────────────────────────────────────────── --}} + diff --git a/resources/views/livewire/settings/appearance.blade.php b/resources/views/livewire/settings/appearance.blade.php deleted file mode 100644 index 86c7ec7..0000000 --- a/resources/views/livewire/settings/appearance.blade.php +++ /dev/null @@ -1,13 +0,0 @@ -
- @include('partials.settings-heading') - - {{ __('Appearance settings') }} - - - - {{ __('Light') }} - {{ __('Dark') }} - {{ __('System') }} - - -
diff --git a/resources/views/livewire/settings/delete-user-form.blade.php b/resources/views/livewire/settings/delete-user-form.blade.php deleted file mode 100644 index a641407..0000000 --- a/resources/views/livewire/settings/delete-user-form.blade.php +++ /dev/null @@ -1,34 +0,0 @@ -
-
- {{ __('Delete account') }} - {{ __('Delete your account and all of its resources') }} -
- - - - {{ __('Delete account') }} - - - - -
-
- {{ __('Are you sure you want to delete your account?') }} - - - {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} - -
- - - -
- - {{ __('Cancel') }} - - - {{ __('Delete account') }} -
- -
-
diff --git a/resources/views/livewire/settings/profile.blade.php b/resources/views/livewire/settings/profile.blade.php deleted file mode 100644 index 3b36a74..0000000 --- a/resources/views/livewire/settings/profile.blade.php +++ /dev/null @@ -1,47 +0,0 @@ -
- @include('partials.settings-heading') - - {{ __('Profile settings') }} - - -
- - -
- - - @if ($this->hasUnverifiedEmail) -
- - {{ __('Your email address is unverified.') }} - - - {{ __('Click here to re-send the verification email.') }} - - - - @if (session('status') === 'verification-link-sent') - - {{ __('A new verification link has been sent to your email address.') }} - - @endif -
- @endif -
- -
-
- {{ __('Save') }} -
- - - {{ __('Saved.') }} - -
- - - @if ($this->showDeleteUser) - - @endif -
-
diff --git a/resources/views/livewire/settings/security.blade.php b/resources/views/livewire/settings/security.blade.php deleted file mode 100644 index 9e973d3..0000000 --- a/resources/views/livewire/settings/security.blade.php +++ /dev/null @@ -1,239 +0,0 @@ -
- @include('partials.settings-heading') - - {{ __('Security settings') }} - - -
- - - - -
-
- {{ __('Save') }} -
- - - {{ __('Saved.') }} - -
- - - @if ($canManageTwoFactor) -
- {{ __('Two-factor authentication') }} - {{ __('Manage your two-factor authentication settings') }} - -
- @if ($twoFactorEnabled) -
- - {{ __('You will be prompted for a secure, random pin during login, which you can retrieve from the TOTP-supported application on your phone.') }} - - -
- - {{ __('Disable 2FA') }} - -
- - -
- @else -
- - {{ __('When you enable two-factor authentication, you will be prompted for a secure pin during login. This pin can be retrieved from a TOTP-supported application on your phone.') }} - - - - {{ __('Enable 2FA') }} - -
- @endif -
-
- - -
-
-
-
-
- @for ($i = 1; $i <= 5; $i++) -
- @endfor -
- -
- @for ($i = 1; $i <= 5; $i++) -
- @endfor -
- - -
-
- -
- {{ $this->modalConfig['title'] }} - {{ $this->modalConfig['description'] }} -
-
- - @if ($showVerificationStep) -
-
- -
- -
- - {{ __('Back') }} - - - - {{ __('Confirm') }} - -
-
- @else - @error('setupData') - - @enderror - -
-
- @empty($qrCodeSvg) -
- -
- @else -
-
- {!! $qrCodeSvg !!} -
-
- @endempty -
-
- -
- - {{ $this->modalConfig['buttonText'] }} - -
- -
-
-
- - {{ __('or, enter the code manually') }} - -
- -
-
- @empty($manualSetupKey) -
- -
- @else - - - - @endempty -
-
-
- @endif -
-
- @endif -
-
diff --git a/resources/views/livewire/settings/two-factor/recovery-codes.blade.php b/resources/views/livewire/settings/two-factor/recovery-codes.blade.php deleted file mode 100644 index 9ed2674..0000000 --- a/resources/views/livewire/settings/two-factor/recovery-codes.blade.php +++ /dev/null @@ -1,89 +0,0 @@ -
-
-
- - {{ __('2FA recovery codes') }} -
- - {{ __('Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.') }} - -
- -
-
- - - - {{ __('Hide recovery codes') }} - - - @if (filled($recoveryCodes)) - - {{ __('Regenerate codes') }} - - @endif -
- -
-
- @error('recoveryCodes') - - @enderror - - @if (filled($recoveryCodes)) -
- @foreach($recoveryCodes as $code) -
- {{ $code }} -
- @endforeach -
- - {{ __('Each recovery code can be used once to access your account and will be removed after use. If you need more, click Regenerate codes above.') }} - - @endif -
-
-
-
diff --git a/resources/views/pages/create.blade.php b/resources/views/pages/create.blade.php new file mode 100644 index 0000000..3cf1966 --- /dev/null +++ b/resources/views/pages/create.blade.php @@ -0,0 +1,52 @@ + + New Page — StaticHTMLSites + +
+

Create New Page

+ +
+ @csrf + +
+ + + @error('name') +

{{ $message }}

+ @enderror +
+ +
+ +
+ + statichtmlsites.mtex.dev/ + + +
+ @error('slug') +

{{ $message }}

+ @enderror +
+ +
+ + + Cancel + +
+
+
+
diff --git a/resources/views/pages/dashboard.blade.php b/resources/views/pages/dashboard.blade.php new file mode 100644 index 0000000..42d0ffc --- /dev/null +++ b/resources/views/pages/dashboard.blade.php @@ -0,0 +1,59 @@ + + Dashboard — StaticHTMLSites + +
+

My Pages

+ + + New Page + +
+ + @if ($pages->isEmpty()) +
+

📄

+

No pages yet. Create your first one!

+
+ @else +
+ @foreach ($pages as $page) +
+
+
+

{{ $page->name }}

+ + {{ $page->slug }} + +
+ + {{ $page->is_public ? 'Public' : 'Private' }} + +
+ +
+ Updated {{ $page->updated_at->diffForHumans() }} +
+ +
+ + Edit + + + Preview ↗ + +
+ @csrf @method('DELETE') + +
+
+
+ @endforeach +
+ @endif +
diff --git a/resources/views/pages/index.blade.php b/resources/views/pages/index.blade.php new file mode 100644 index 0000000..54b808f --- /dev/null +++ b/resources/views/pages/index.blade.php @@ -0,0 +1,59 @@ + + +
+ +
+

Your Pages

+ + + + + New Page + +
+ + @if ($pages->isEmpty()) +
+

📄

+

No pages yet.

+ Create your first page → +
+ @else +
+ @foreach ($pages as $page) +
+
+
+

{{ $page->name }}

+

{{ $page->slug }}

+
+ + {{ $page->is_public ? 'Public' : 'Private' }} + +
+ + + +
+ @csrf @method('DELETE') + +
+
+ @endforeach +
+ @endif + +
+ +
diff --git a/resources/views/partials/head.blade.php b/resources/views/partials/head.blade.php deleted file mode 100644 index 52703a4..0000000 --- a/resources/views/partials/head.blade.php +++ /dev/null @@ -1,16 +0,0 @@ - - - - - {{ filled($title ?? null) ? $title.' - '.config('app.name', 'Laravel') : config('app.name', 'Laravel') }} - - - - - - - - - -@vite(['resources/css/app.css', 'resources/js/app.js']) -@fluxAppearance diff --git a/resources/views/partials/settings-heading.blade.php b/resources/views/partials/settings-heading.blade.php deleted file mode 100644 index 925ace9..0000000 --- a/resources/views/partials/settings-heading.blade.php +++ /dev/null @@ -1,5 +0,0 @@ -
- {{ __('Settings') }} - {{ __('Manage your profile and account settings') }} - -
diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php deleted file mode 100644 index c7b6eb4..0000000 --- a/resources/views/welcome.blade.php +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - {{ __('Welcome') }} - {{ config('app.name', 'Laravel') }} - - - - - - - - - - - - - -
- @if (Route::has('login')) - - @endif -
-
-
-
-

Let's get started

-

Laravel has an incredibly rich ecosystem.
We suggest starting with the following.

- - -
-
- {{-- Laravel Logo --}} - - - - - - - - - - - {{-- Light Mode 12 SVG --}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{-- Dark Mode 12 SVG --}} - -
-
-
-
- - @if (Route::has('login')) - - @endif - - diff --git a/routes/web.php b/routes/web.php index 12e932e..f2f057a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,11 +1,68 @@ name('home'); +/* +|-------------------------------------------------------------------------- +| Main Application Routes +|-------------------------------------------------------------------------- +*/ + +Route::get('/', function () { + return auth()->check() + ? redirect()->route('dashboard') + : view('welcome'); +})->name('home'); Route::middleware(['auth', 'verified'])->group(function () { - Route::view('dashboard', 'dashboard')->name('dashboard'); + Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); + + // Page management + Route::get('/pages/create', [PageController::class, 'create'])->name('pages.create'); + Route::post('/pages', [PageController::class, 'store'])->name('pages.store'); + Route::get('/pages/{slug}/manager', [FileManagerController::class, 'index'])->name('pages.manager'); + Route::delete('/pages/{slug}', [PageController::class, 'destroy'])->name('pages.destroy'); + + // File Manager API (JSON, consumed by Livewire / JS) + Route::prefix('/pages/{slug}/files')->name('pages.files.')->group(function () { + Route::get('/', [FileManagerController::class, 'list'])->name('list'); + Route::post('/', [FileManagerController::class, 'upload'])->name('upload'); + Route::post('/create', [FileManagerController::class, 'create'])->name('create'); + Route::put('/', [FileManagerController::class, 'save'])->name('save'); + Route::delete('/', [FileManagerController::class, 'delete'])->name('delete'); + Route::post('/folder', [FileManagerController::class, 'createFolder'])->name('folder'); + Route::put('/rename', [FileManagerController::class, 'rename'])->name('rename'); + Route::get('/read', [FileManagerController::class, 'read'])->name('read'); + }); + + // Page settings + Route::put('/pages/{slug}/settings', [PageController::class, 'updateSettings'])->name('pages.settings'); }); +/* +|-------------------------------------------------------------------------- +| Subdomain Routes (must be defined before path-based catch-all) +|-------------------------------------------------------------------------- +*/ +Route::domain('{slug}.statichtmlsites.mtex.dev') + ->group(function () { + Route::get('/{path?}', [PageServeController::class, 'serve']) + ->where('path', '.*') + ->name('page.subdomain'); + }); + +/* +|-------------------------------------------------------------------------- +| Path-based Page Serving (catch-all – keep last) +|-------------------------------------------------------------------------- +*/ +Route::get('/{slug}/{path?}', [PageServeController::class, 'serve']) + ->where('slug', '[a-z0-9\-_]+') + ->where('path', '.*') + ->name('page.path'); + require __DIR__.'/settings.php'; From 9e5eb9c49496bcb00abd7234e0b63ea22b51de2a Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 11:37:40 +0100 Subject: [PATCH 2/9] =?UTF-8?q?STILL=20BROKEN=20=E2=80=93=20Changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 152 ++++---- .../Controllers/FileManagerController.php | 133 ++++--- app/Http/Controllers/PageServeController.php | 96 +++-- app/Livewire/FileManager.php | 196 +++++----- app/Models/Page.php | 76 ++-- app/Policies/PagePolicy.php | 18 +- app/Services/FileManagerService.php | 161 ++++---- database/factories/PageFactory.php | 31 ++ database/factories/UserFactory.php | 38 +- database/seeders/DatabaseSeeder.php | 8 +- resources/css/app.css | 72 +--- resources/js/app.js | 9 + resources/js/bootstrap.js | 3 + resources/js/file-manager.js | 347 ++++++++++++++++++ resources/views/errors/403.blade.php | 12 + resources/views/errors/404.blade.php | 12 + resources/views/errors/419.blade.php | 12 + resources/views/errors/429.blade.php | 12 + resources/views/errors/500.blade.php | 12 + resources/views/errors/503.blade.php | 12 + resources/views/errors/layout.blade.php | 43 +++ resources/views/layouts/app.blade.php | 32 +- resources/views/pages/dashboard.blade.php | 113 +++--- resources/views/pages/index.blade.php | 59 --- resources/views/pages/manager.blade.php | 35 ++ resources/views/welcome.blade.php | 48 +++ routes/auth.php | 50 +++ routes/web.php | 1 + 28 files changed, 1146 insertions(+), 647 deletions(-) create mode 100644 database/factories/PageFactory.php create mode 100644 resources/js/bootstrap.js create mode 100644 resources/js/file-manager.js create mode 100644 resources/views/errors/403.blade.php create mode 100644 resources/views/errors/404.blade.php create mode 100644 resources/views/errors/419.blade.php create mode 100644 resources/views/errors/429.blade.php create mode 100644 resources/views/errors/500.blade.php create mode 100644 resources/views/errors/503.blade.php create mode 100644 resources/views/errors/layout.blade.php delete mode 100644 resources/views/pages/index.blade.php create mode 100644 resources/views/pages/manager.blade.php create mode 100644 resources/views/welcome.blade.php create mode 100644 routes/auth.php diff --git a/README.md b/README.md index b273ed1..aad01f7 100644 --- a/README.md +++ b/README.md @@ -1,141 +1,133 @@ -# Static HTML Sites +# StaticHTMLSites -A Laravel 12 application that lets authenticated users create and serve static mini-sites. -Each page is accessible via **subdomain** (`{slug}.statichtmlsites.mtex.dev`) and **path** (`statichtmlsites.mtex.dev/{slug}/`). +A Laravel 12 application for hosting static HTML mini-sites. +Each page is accessible via subdomain (`{slug}.statichtmlsites.mtex.dev`) and path URL (`statichtmlsites.mtex.dev/{slug}/...`). --- -## Stack +## Tech Stack -| Layer | Choice | -|------------|--------------------------------| -| Backend | Laravel 12 | -| Frontend | Livewire v3 + Alpine.js | -| Editor | CodeMirror 6 (via ESM CDN) | -| Auth | Laravel Breeze | -| DB | MySQL / SQLite | -| Storage | Local disk (`storage/app/pages/`) | +| Layer | Technology | +|----------|----------------------------------| +| Backend | Laravel 12, PHP 8.2+ | +| Frontend | Tailwind CSS 3, Alpine.js 3 | +| Editor | CodeMirror 6 (npm) | +| Build | Vite 5 + laravel-vite-plugin | +| Auth | Laravel Breeze (blade stack) | +| Database | SQLite (default) or MySQL | +| Storage | Local filesystem | --- ## Installation ```bash -# 1. Clone & install +# 1. Install PHP + JS dependencies composer install +npm install # 2. Environment cp .env.example .env php artisan key:generate -# 3. Set APP_BASE_DOMAIN in .env -# APP_BASE_DOMAIN=statichtmlsites.mtex.dev - -# 4. Database +# 3. Migrate php artisan migrate -# 5. Install Breeze (if not committed) -composer require laravel/breeze --dev +# 4. Install Breeze auth scaffolding php artisan breeze:install blade -npm install && npm run build +php artisan migrate -# 6. Livewire -composer require livewire/livewire +# 5. Build assets +npm run build +``` -# 7. Storage -php artisan storage:link -mkdir -p storage/app/pages +Key `.env` variables: -# 8. Serve -php artisan serve -``` +| Variable | Value | +|-------------------|--------------------------------------| +| APP_URL | https://statichtmlsites.mtex.dev | +| APP_BASE_DOMAIN | statichtmlsites.mtex.dev | +| MAX_UPLOAD_MB | 50 | --- -## Subdomain Routing (production) - -Add a wildcard DNS record: +## Wildcard Subdomain (DNS + Nginx) +**DNS:** ``` -*.statichtmlsites.mtex.dev → your server IP +*.statichtmlsites.mtex.dev CNAME statichtmlsites.mtex.dev ``` -Nginx vhost: - +**Nginx:** ```nginx server { listen 443 ssl; server_name statichtmlsites.mtex.dev *.statichtmlsites.mtex.dev; - + ssl_certificate /path/to/wildcard.crt; + ssl_certificate_key /path/to/wildcard.key; root /var/www/statichtmlsites/public; index index.php; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - + location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { - fastcgi_pass unix:/run/php/php8.3-fpm.sock; - include fastcgi_params; + fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; } } ``` +**config/filesystems.php** — add to the returned array: +```php +'max_upload_mb' => env('MAX_UPLOAD_MB', 50), +``` + --- -## File Structure (generated files) +## Project Structure ``` app/ Http/Controllers/ - PageController.php ← CRUD for pages - PageServeController.php ← serves static files + base-tag injection - Livewire/ - FileManager.php ← full IDE Livewire component + DashboardController.php Dashboard page + FileManagerController.php File CRUD JSON API + PageController.php Create / update / delete pages + PageServeController.php Serve static files + injection Models/ - Page.php - User.php - Policies/ - PagePolicy.php - Providers/ - AppServiceProvider.php + Page.php ULID model, storage helpers, slug uniqueness + User.php Breeze user + pages() HasMany + Policies/PagePolicy.php Ownership checks + Providers/AppServiceProvider.php Register FileManagerService singleton + policy + Services/FileManagerService.php All file ops: tree, read, save, upload, delete, rename + +resources/ + css/app.css Tailwind + CodeMirror overrides + js/ + app.js Global entry (Alpine on non-manager pages) + file-manager.js CodeMirror 6 + Alpine file-tree component + views/ + layouts/app.blade.php App shell with nav + welcome.blade.php Marketing landing + pages/ + dashboard.blade.php Page grid + create.blade.php New page form + manager.blade.php Full-screen file manager database/migrations/ ..._create_pages_table.php -resources/views/ - layouts/app.blade.php - pages/ - index.blade.php ← dashboard - create.blade.php - livewire/ - file-manager.blade.php ← full IDE UI with CodeMirror 6 - -routes/ - web.php ← includes subdomain + path serving routes - -config/ - app.php ← adds base_domain, max_upload_mb - filesystems.php ← adds 'pages' disk +routes/web.php All routes + subdomain group ``` --- ## Key Design Decisions -### Base-tag injection -`PageServeController` injects `` into every HTML response so relative -asset references (`./style.css`, `src="video.mp4"`) resolve correctly regardless of -whether the page is accessed via subdomain or path. +- **`` injection** — `PageServeController` detects subdomain vs. path access and injects `` into every HTML file before serving, keeping relative asset paths correct under both URL shapes. +- **Path traversal guard** — `FileManagerService::guard()` and `PageServeController` both resolve real paths and assert they sit inside the page's storage root. +- **Storage isolation** — `storage/app/pages/{slug}/`. Directories are auto-created on page creation, renamed on slug change, and deleted on page deletion via Eloquent model events. +- **File tree** — Pure PHP recursive directory scan returned as JSON, rendered by an Alpine component with inline HTML strings. -### File storage -Files are stored directly on disk (not in the database) under `storage/app/pages/{slug}/`. -The Livewire `FileManager` reads/writes the filesystem via Laravel's `Storage::disk('pages')`. - -### Path traversal prevention -All file paths are validated to reject `..` segments and leading `/` before any disk operation. +--- -### MIME detection -`PageServeController` uses a manual extension→MIME map (no `finfo` dependency) and streams -binary files directly without loading them into memory via `response()->file()`. +## License +MIT diff --git a/app/Http/Controllers/FileManagerController.php b/app/Http/Controllers/FileManagerController.php index 361dd9b..32376ca 100644 --- a/app/Http/Controllers/FileManagerController.php +++ b/app/Http/Controllers/FileManagerController.php @@ -7,7 +7,13 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\View\View; +use RuntimeException; +/** + * REST/JSON API for the file manager. + * The primary UI is the Livewire\FileManager component; this controller + * exists for programmatic / JS access and is also used by the manager page. + */ class FileManagerController extends Controller { public function __construct(private readonly FileManagerService $fm) {} @@ -16,112 +22,103 @@ public function __construct(private readonly FileManagerService $fm) {} public function index(Request $request, string $slug): View { - $page = $this->authorizedPage($request, $slug); + $page = $this->ownedPage($request, $slug); return view('pages.manager', compact('page')); } - // ── API endpoints ───────────────────────────────────────────────────────── + // ── JSON endpoints ──────────────────────────────────────────────────────── - /** List all files/folders as a tree */ public function list(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - return response()->json($this->fm->tree($page)); + return $this->run(fn () => $this->fm->tree($this->ownedPage($request, $slug))); } - /** Read a single file's content */ public function read(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $path = $request->query('path', ''); - - return response()->json([ - 'content' => $this->fm->read($page, $path), - 'path' => $path, - ]); + return $this->run(function () use ($request, $slug) { + $page = $this->ownedPage($request, $slug); + $path = $request->query('path', ''); + return ['content' => $this->fm->read($page, $path), 'path' => $path]; + }); } - /** Save a text file */ public function save(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $data = $request->validate([ - 'path' => ['required', 'string'], - 'content' => ['required', 'string'], - ]); - - $this->fm->save($page, $data['path'], $data['content']); - return response()->json(['ok' => true]); + return $this->run(function () use ($request, $slug) { + $data = $request->validate(['path' => 'required|string', 'content' => 'required|string']); + $this->fm->save($this->ownedPage($request, $slug), $data['path'], $data['content']); + return ['ok' => true]; + }); } - /** Create a new blank text file */ public function create(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $data = $request->validate([ - 'path' => ['required', 'string'], - ]); - - $this->fm->createFile($page, $data['path']); - return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + return $this->run(function () use ($request, $slug) { + $data = $request->validate(['path' => 'required|string']); + $page = $this->ownedPage($request, $slug); + $this->fm->createFile($page, $data['path']); + return ['ok' => true, 'tree' => $this->fm->tree($page)]; + }); } - /** Upload binary or text file(s) */ public function upload(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $maxMb = (int) config('filesystems.max_upload_mb', 50); - - $request->validate([ - 'file' => ["required", "file", "max:{$maxMb}000"], - 'folder' => ['nullable', 'string'], - ]); - - $this->fm->upload($page, $request->file('file'), $request->input('folder', '')); - return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + return $this->run(function () use ($request, $slug) { + $maxMb = (int) config('filesystems.max_upload_mb', 50); + $request->validate([ + 'file' => "required|file|max:{$maxMb}000", + 'folder' => 'nullable|string', + ]); + $page = $this->ownedPage($request, $slug); + $this->fm->upload($page, $request->file('file'), $request->input('folder', '')); + return ['ok' => true, 'tree' => $this->fm->tree($page)]; + }); } - /** Delete a file or directory */ public function delete(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $data = $request->validate([ - 'path' => ['required', 'string'], - ]); - - $this->fm->delete($page, $data['path']); - return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + return $this->run(function () use ($request, $slug) { + $data = $request->validate(['path' => 'required|string']); + $page = $this->ownedPage($request, $slug); + $this->fm->delete($page, $data['path']); + return ['ok' => true, 'tree' => $this->fm->tree($page)]; + }); } - /** Create a folder */ public function createFolder(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $data = $request->validate([ - 'path' => ['required', 'string'], - ]); - - $this->fm->createFolder($page, $data['path']); - return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + return $this->run(function () use ($request, $slug) { + $data = $request->validate(['path' => 'required|string']); + $page = $this->ownedPage($request, $slug); + $this->fm->createFolder($page, $data['path']); + return ['ok' => true, 'tree' => $this->fm->tree($page)]; + }); } - /** Rename a file or folder */ public function rename(Request $request, string $slug): JsonResponse { - $page = $this->authorizedPage($request, $slug); - $data = $request->validate([ - 'from' => ['required', 'string'], - 'to' => ['required', 'string'], - ]); - - $this->fm->rename($page, $data['from'], $data['to']); - return response()->json(['ok' => true, 'tree' => $this->fm->tree($page)]); + return $this->run(function () use ($request, $slug) { + $data = $request->validate(['from' => 'required|string', 'to' => 'required|string']); + $page = $this->ownedPage($request, $slug); + $this->fm->rename($page, $data['from'], $data['to']); + return ['ok' => true, 'tree' => $this->fm->tree($page)]; + }); } - // ── Shared ──────────────────────────────────────────────────────────────── + // ── Helpers ─────────────────────────────────────────────────────────────── - private function authorizedPage(Request $request, string $slug): Page + private function ownedPage(Request $request, string $slug): Page { return $request->user()->pages()->where('slug', $slug)->firstOrFail(); } + + private function run(callable $fn): JsonResponse + { + try { + $result = $fn(); + return response()->json($result); + } catch (RuntimeException $e) { + return response()->json(['error' => $e->getMessage()], 422); + } + } } diff --git a/app/Http/Controllers/PageServeController.php b/app/Http/Controllers/PageServeController.php index 247101b..d09e16f 100644 --- a/app/Http/Controllers/PageServeController.php +++ b/app/Http/Controllers/PageServeController.php @@ -7,115 +7,116 @@ use Illuminate\Http\Response; use Illuminate\Support\Facades\Storage; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\StreamedResponse; class PageServeController extends Controller { /** - * Serve a static file from a page's storage root. - * - * Works for both subdomain access ({slug}.statichtmlsites.mtex.dev) - * and path-based access (statichtmlsites.mtex.dev/{slug}/...). + * Serve a static file from the pages disk. + * Called by both subdomain and path-based routes. */ - public function serve(Request $request, string $slug, string $path = ''): Response|BinaryFileResponse|StreamedResponse + public function serve(Request $request, string $slug, string $path = ''): Response|BinaryFileResponse { $page = Page::where('slug', $slug)->firstOrFail(); - // Authorization: private pages require auth if (! $page->is_public && ! auth()->check()) { abort(403, 'This page is private.'); } - // Normalise path and resolve default document - $filePath = $this->resolvePath($slug, $path); + // Resolve the disk path and guard against traversal + $diskPath = $this->resolveDiskPath($slug, $path); + $this->guardTraversal($slug, $diskPath); - if (! Storage::exists($filePath)) { + $disk = Storage::disk('pages'); + + if (! $disk->exists($diskPath)) { abort(404, 'File not found.'); } - // Security: prevent path traversal - $this->guardTraversal($slug, $filePath); - - $absolutePath = Storage::path($filePath); - $mime = $this->detectMime($filePath); - $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); + $ext = strtolower(pathinfo($diskPath, PATHINFO_EXTENSION)); + $mime = $this->detectMime($ext); // Inject tag into HTML responses if ($ext === 'html') { - $html = Storage::get($filePath); - $html = $this->injectBase($html, $page, $request, $slug); - + $html = $disk->get($diskPath); + $html = $this->injectBase($html, $page, $request); return response($html, 200, ['Content-Type' => 'text/html; charset=utf-8']); } - // Stream binary / text files as-is - return response()->file($absolutePath, ['Content-Type' => $mime]); + return response()->file($disk->path($diskPath), ['Content-Type' => $mime]); } - // ── Helpers ────────────────────────────────────────────────────────────── + // ── Helpers ─────────────────────────────────────────────────────────────── - private function resolvePath(string $slug, string $path): string + /** + * Resolve a path within the page's slug directory on the pages disk. + * Handles directory index resolution. + */ + private function resolveDiskPath(string $slug, string $path): string { $path = ltrim($path, '/'); if ($path === '' || $path === '/') { - $path = 'index.html'; + return "{$slug}/index.html"; } - // If path is a directory (no extension), try index.html inside it + // No extension → try index.html inside that directory if (! pathinfo($path, PATHINFO_EXTENSION)) { - $candidate = rtrim($path, '/') . '/index.html'; - $diskPath = "pages/{$slug}/{$candidate}"; - if (Storage::exists($diskPath)) { - return $diskPath; + $candidate = "{$slug}/" . rtrim($path, '/') . '/index.html'; + if (Storage::disk('pages')->exists($candidate)) { + return $candidate; } } - return "pages/{$slug}/{$path}"; + return "{$slug}/{$path}"; } - private function guardTraversal(string $slug, string $filePath): void + /** + * Prevent path traversal by checking the real path stays inside the slug dir. + */ + private function guardTraversal(string $slug, string $diskPath): void { - $root = realpath(Storage::path("pages/{$slug}")); - $real = realpath(Storage::path($filePath)); + $disk = Storage::disk('pages'); + $root = realpath($disk->path($slug)); + $realFile = realpath($disk->path($diskPath)); - if ($root && $real && ! str_starts_with($real, $root)) { + // realpath returns false for non-existent files – only check when both resolve + if ($root && $realFile && ! str_starts_with($realFile, $root)) { abort(403, 'Path traversal detected.'); } } - private function injectBase(string $html, Page $page, Request $request, string $slug): string + /** + * Inject as the first child of . + * Detects subdomain vs path-based access automatically. + */ + private function injectBase(string $html, Page $page, Request $request): string { - // Detect subdomain access - $host = $request->getHost(); + $host = $request->getHost(); $baseDomain = config('app.base_domain', 'statichtmlsites.mtex.dev'); $isSubdomain = str_ends_with($host, ".{$baseDomain}"); - $baseUrl = $isSubdomain - ? $page->subdomainUrl() - : $page->pathUrl(); - + $baseUrl = $isSubdomain ? $page->subdomainUrl() : $page->pathUrl(); $baseTag = "\n"; - // Inject as first child of if (preg_match('/]*)>/i', $html)) { return preg_replace('/]*)>/i', "\n{$baseTag}", $html, 1); } - // No – prepend at top return $baseTag . $html; } - private function detectMime(string $path): string + private function detectMime(string $ext): string { - $map = [ + return [ 'html' => 'text/html', 'css' => 'text/css', 'js' => 'application/javascript', + 'mjs' => 'application/javascript', 'json' => 'application/json', 'xml' => 'application/xml', 'svg' => 'image/svg+xml', 'txt' => 'text/plain', + 'md' => 'text/plain', 'png' => 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', @@ -130,9 +131,6 @@ private function detectMime(string $path): string 'pdf' => 'application/pdf', 'woff' => 'font/woff', 'woff2' => 'font/woff2', - ]; - - $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); - return $map[$ext] ?? 'application/octet-stream'; + ][$ext] ?? 'application/octet-stream'; } } diff --git a/app/Livewire/FileManager.php b/app/Livewire/FileManager.php index 1fc2bf6..db3f7f5 100644 --- a/app/Livewire/FileManager.php +++ b/app/Livewire/FileManager.php @@ -5,7 +5,6 @@ use App\Models\Page; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; use Livewire\Attributes\Computed; use Livewire\Component; use Livewire\WithFileUploads; @@ -14,7 +13,7 @@ class FileManager extends Component { use WithFileUploads; - // ─── State ──────────────────────────────────────────────────────────────── + // ─── Public state ───────────────────────────────────────────────────────── public Page $page; public string $activeFile = ''; @@ -22,19 +21,19 @@ class FileManager extends Component public bool $isDirty = false; public string $newFileName = ''; public string $newFolderName = ''; - public string $currentDir = ''; // relative to page root + public string $currentDir = ''; // relative inside the page root public ?string $renameTarget = null; - public string $renameTo = ''; - public $upload = null; // Livewire temp upload + public string $renameTo = ''; + public mixed $upload = null; // Settings panel - public string $pageName = ''; - public string $pageSlug = ''; - public bool $pageIsPublic = false; - public bool $showSettings = false; + public string $pageName = ''; + public string $pageSlug = ''; + public bool $pageIsPublic = false; + public bool $showSettings = false; - // Notification - public string $flash = ''; + // Status bar + public string $flash = ''; // ─── Lifecycle ──────────────────────────────────────────────────────────── @@ -49,46 +48,52 @@ public function mount(string $slug): void $this->pageSlug = $this->page->slug; $this->pageIsPublic = $this->page->is_public; - // Auto-open index.html if it exists - $indexPath = 'index.html'; - if (Storage::disk('pages')->exists($this->page->storagePath($indexPath))) { - $this->openFile($indexPath); + // Auto-open index.html + if ($this->disk()->exists($this->page->storagePath('index.html'))) { + $this->openFile('index.html'); } } - // ─── Computed ───────────────────────────────────────────────────────────── + // ─── Computed properties ────────────────────────────────────────────────── #[Computed] public function fileTree(): array { - $disk = Storage::disk('pages'); - $root = $this->page->storagePath($this->currentDir); + $dir = $this->page->storagePath($this->currentDir ?: ''); $prefix = $this->page->storagePath() . '/'; + return $this->buildTree($dir, $prefix); + } + + private function buildTree(string $dir, string $prefix): array + { $entries = []; - // Directories - foreach ($disk->directories($root) as $dir) { + foreach ($this->disk()->directories($dir) as $d) { $entries[] = [ - 'type' => 'dir', - 'name' => basename($dir), - 'path' => ltrim(str_replace($prefix, '', $dir), '/'), + 'type' => 'dir', + 'name' => basename($d), + 'path' => $this->relative($d, $prefix), + 'children' => $this->buildTree($d, $prefix), ]; } - // Files - foreach ($disk->files($root) as $file) { - $rel = ltrim(str_replace($prefix, '', $file), '/'); + foreach ($this->disk()->files($dir) as $f) { + $rel = $this->relative($f, $prefix); $entries[] = [ 'type' => 'file', - 'name' => basename($file), + 'name' => basename($f), 'path' => $rel, 'active' => $rel === $this->activeFile, - 'isIndex' => basename($file) === 'index.html', + 'isIndex' => basename($f) === 'index.html', ]; } - usort($entries, fn ($a, $b) => [$a['type'] === 'file' ? 1 : 0, $a['name']] <=> [$b['type'] === 'file' ? 1 : 0, $b['name']]); + usort($entries, fn ($a, $b) => + [$a['type'] === 'file' ? 1 : 0, $a['name']] + <=> + [$b['type'] === 'file' ? 1 : 0, $b['name']] + ); return $entries; } @@ -101,9 +106,7 @@ public function editorLanguage(): string 'css' => 'css', 'js' => 'javascript', 'json' => 'json', - 'xml' => 'xml', - 'svg' => 'xml', - 'txt' => 'text', + 'xml', 'svg' => 'xml', default => 'text', }; } @@ -111,52 +114,57 @@ public function editorLanguage(): string #[Computed] public function isEditable(): bool { - $editable = ['html', 'htm', 'css', 'js', 'json', 'txt', 'xml', 'svg']; - return in_array(strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), $editable); + return in_array( + strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), + ['html', 'htm', 'css', 'js', 'json', 'txt', 'xml', 'svg', 'md'], + true + ); } #[Computed] public function isPreviewable(): bool { - $media = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf']; - return in_array(strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), $media); + return in_array( + strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), + ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf'], + true + ); } #[Computed] public function previewUrl(): string { - return $this->page->subdomainUrl() . $this->activeFile; + return $this->page->subdomainUrl($this->activeFile); } // ─── File operations ────────────────────────────────────────────────────── public function openFile(string $path): void { - $this->ensureSafe($path); - $disk = Storage::disk('pages'); + $this->guardPath($path); $full = $this->page->storagePath($path); - if (! $disk->exists($full)) { + if (! $this->disk()->exists($full)) { $this->flash = "File not found: {$path}"; return; } $this->activeFile = $path; - $this->fileContent = $disk->get($full); + $this->fileContent = $this->disk()->get($full) ?? ''; $this->isDirty = false; - $this->dispatch('load-editor', content: $this->fileContent, language: $this->editorLanguage); + $this->dispatch('load-editor', + content: $this->fileContent, + language: $this->editorLanguage + ); } public function saveFile(string $content): void { if ($this->activeFile === '') return; - $this->ensureSafe($this->activeFile); - Storage::disk('pages')->put( - $this->page->storagePath($this->activeFile), - $content - ); + $this->guardPath($this->activeFile); + $this->disk()->put($this->page->storagePath($this->activeFile), $content); $this->fileContent = $content; $this->isDirty = false; @@ -166,20 +174,17 @@ public function saveFile(string $content): void public function createFile(): void { $name = $this->sanitiseName($this->newFileName); - if ($name === '') { - $this->flash = 'File name cannot be empty.'; - return; - } + if ($name === '') { $this->flash = 'File name cannot be empty.'; return; } - $relative = $this->currentDir ? "{$this->currentDir}/{$name}" : $name; + $relative = $this->buildRelative($name); $full = $this->page->storagePath($relative); - if (Storage::disk('pages')->exists($full)) { + if ($this->disk()->exists($full)) { $this->flash = 'A file with that name already exists.'; return; } - Storage::disk('pages')->put($full, ''); + $this->disk()->put($full, ''); $this->newFileName = ''; $this->openFile($relative); unset($this->fileTree); @@ -188,13 +193,10 @@ public function createFile(): void public function createFolder(): void { $name = $this->sanitiseName($this->newFolderName); - if ($name === '') { - $this->flash = 'Folder name cannot be empty.'; - return; - } + if ($name === '') { $this->flash = 'Folder name cannot be empty.'; return; } - $relative = $this->currentDir ? "{$this->currentDir}/{$name}" : $name; - Storage::disk('pages')->makeDirectory($this->page->storagePath($relative)); + $relative = $this->buildRelative($name); + $this->disk()->makeDirectory($this->page->storagePath($relative)); $this->newFolderName = ''; $this->flash = "Folder '{$name}' created."; @@ -203,8 +205,14 @@ public function createFolder(): void public function deleteFile(string $path): void { - $this->ensureSafe($path); - Storage::disk('pages')->delete($this->page->storagePath($path)); + $this->guardPath($path); + $full = $this->page->storagePath($path); + + if ($this->disk()->directoryExists($full)) { + $this->disk()->deleteDirectory($full); + } else { + $this->disk()->delete($full); + } if ($this->activeFile === $path) { $this->activeFile = ''; @@ -225,14 +233,14 @@ public function confirmRename(): void { if ($this->renameTarget === null) return; - $this->ensureSafe($this->renameTarget); + $this->guardPath($this->renameTarget); $newName = $this->sanitiseName($this->renameTo); if ($newName === '') return; $dir = dirname($this->renameTarget); $newPath = ($dir === '.' ? '' : $dir . '/') . $newName; - Storage::disk('pages')->move( + $this->disk()->move( $this->page->storagePath($this->renameTarget), $this->page->storagePath($newPath) ); @@ -254,7 +262,8 @@ public function enterDirectory(string $path): void public function goUp(): void { - $this->currentDir = dirname($this->currentDir) === '.' ? '' : dirname($this->currentDir); + $parent = dirname($this->currentDir); + $this->currentDir = ($parent === '.' || $parent === '') ? '' : $parent; unset($this->fileTree); } @@ -262,21 +271,19 @@ public function goUp(): void public function uploadFile(): void { + $maxKb = (int) config('filesystems.max_upload_mb', 50) * 1024; + $this->validate([ - 'upload' => 'required|file|max:' . (config('app.max_upload_mb', 50) * 1024), + 'upload' => "required|file|max:{$maxKb}", ]); - $originalName = $this->sanitiseName($this->upload->getClientOriginalName()); - $relative = $this->currentDir ? "{$this->currentDir}/{$originalName}" : $originalName; + $name = $this->sanitiseName($this->upload->getClientOriginalName()); + $storeDir = $this->page->storagePath($this->currentDir ?: ''); - Storage::disk('pages')->putFileAs( - $this->page->storagePath($this->currentDir ?: ''), - $this->upload, - $originalName - ); + $this->disk()->putFileAs($storeDir, $this->upload, $name); $this->upload = null; - $this->flash = "Uploaded {$originalName}."; + $this->flash = "Uploaded {$name}."; unset($this->fileTree); } @@ -284,22 +291,16 @@ public function uploadFile(): void public function saveSettings(): void { - $data = $this->validate([ + $this->validate([ 'pageName' => 'required|string|max:255', - 'pageSlug' => "required|string|max:100|regex:/^[a-z0-9\-]+$/|unique:pages,slug,{$this->page->id}", + 'pageSlug' => "required|string|max:100|regex:/^[a-z0-9\\-]+$/|unique:pages,slug,{$this->page->id}", 'pageIsPublic' => 'boolean', ]); - $oldSlug = $this->page->slug; - $newSlug = $this->pageSlug; - - if ($oldSlug !== $newSlug) { - Storage::disk('pages')->move($oldSlug, $newSlug); - } - + // The Page model's `updating` event handles directory rename automatically. $this->page->update([ 'name' => $this->pageName, - 'slug' => $newSlug, + 'slug' => $this->pageSlug, 'is_public' => $this->pageIsPublic, ]); @@ -307,23 +308,37 @@ public function saveSettings(): void $this->showSettings = false; $this->flash = 'Settings saved.'; - if ($oldSlug !== $newSlug) { - $this->redirect(route('pages.manager', $newSlug)); + if ($this->page->wasChanged('slug')) { + $this->redirect(route('pages.manager', $this->page->slug), navigate: true); } } - // ─── Security ───────────────────────────────────────────────────────────── + // ─── Security / helpers ─────────────────────────────────────────────────── - private function ensureSafe(string $path): void + private function disk(): \Illuminate\Contracts\Filesystem\Filesystem + { + return Storage::disk('pages'); + } + + private function guardPath(string $path): void { if (str_contains($path, '..') || str_starts_with($path, '/')) { abort(400, 'Invalid path.'); } } + private function buildRelative(string $name): string + { + return $this->currentDir ? "{$this->currentDir}/{$name}" : $name; + } + + private function relative(string $fullPath, string $prefix): string + { + return ltrim(str_replace($prefix, '', $fullPath), '/'); + } + private function sanitiseName(string $name): string { - // Allow alphanumeric, dash, underscore, dot return preg_replace('/[^a-zA-Z0-9\-_.]/', '-', trim($name)); } @@ -331,7 +346,6 @@ private function sanitiseName(string $name): string public function render() { - return view('livewire.file-manager') - ->layout('layouts.app', ['title' => "Manager — {$this->page->name}"]); + return view('livewire.file-manager'); } } diff --git a/app/Models/Page.php b/app/Models/Page.php index fcfedae..1d659a2 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -13,70 +13,71 @@ class Page extends Model { use HasFactory, HasUlids; - protected $fillable = [ - 'user_id', - 'name', - 'slug', - 'is_public', - ]; + protected $fillable = ['user_id', 'name', 'slug', 'is_public']; - protected $casts = [ - 'is_public' => 'boolean', - ]; + protected $casts = ['is_public' => 'boolean']; - // ── Relationships ──────────────────────────────────────────────────────── + // ── Relationships ───────────────────────────────────────────────────────── public function user(): BelongsTo { return $this->belongsTo(User::class); } - // ── Helpers ────────────────────────────────────────────────────────────── + // ── Storage path helpers ────────────────────────────────────────────────── /** - * Absolute path to this page's storage root. + * Path relative to the "pages" disk root (storage/app/pages/). + * Used with Storage::disk('pages'). + * e.g. my-site/index.html */ public function storagePath(string $relative = ''): string { - $base = storage_path("app/pages/{$this->slug}"); - return $relative ? $base . '/' . ltrim($relative, '/') : $base; + return $relative + ? $this->slug . '/' . ltrim($relative, '/') + : $this->slug; } /** - * Laravel disk-relative path (for Storage facade calls). + * Path relative to the default "local" disk root (storage/app/). + * Used with Storage::disk('local') / the Storage facade without a disk. + * e.g. pages/my-site/index.html */ public function diskPath(string $relative = ''): string { - $base = "pages/{$this->slug}"; + $base = 'pages/' . $this->slug; return $relative ? $base . '/' . ltrim($relative, '/') : $base; } /** - * Public URL for subdomain access. + * Ensure the storage directory exists. */ + public function ensureStorageExists(): void + { + Storage::disk('pages')->makeDirectory($this->slug); + } + + // ── URL helpers ─────────────────────────────────────────────────────────── + public function subdomainUrl(string $path = ''): string { - $base = "https://{$this->slug}." . config('app.base_domain', 'statichtmlsites.mtex.dev'); - return $path ? $base . '/' . ltrim($path, '/') : $base . '/'; + $base = 'https://' . $this->slug . '.' . config('app.base_domain', 'statichtmlsites.mtex.dev'); + return $path ? rtrim($base, '/') . '/' . ltrim($path, '/') : $base . '/'; } - /** - * Public URL for path-based access. - */ public function pathUrl(string $path = ''): string { - $base = config('app.url') . '/' . $this->slug; + $base = rtrim(config('app.url'), '/') . '/' . $this->slug; return $path ? $base . '/' . ltrim($path, '/') : $base . '/'; } - /** - * Generate a unique slug from a name. - */ + // ── Slug factory ────────────────────────────────────────────────────────── + public static function makeUniqueSlug(string $name): string { - $slug = Str::slug($name); + $slug = Str::slug($name); $original = $slug; - $counter = 1; + $counter = 1; while (static::where('slug', $slug)->exists()) { $slug = "{$original}-{$counter}"; @@ -86,31 +87,30 @@ public static function makeUniqueSlug(string $name): string return $slug; } - // ── Boot ───────────────────────────────────────────────────────────────── + // ── Model events ────────────────────────────────────────────────────────── protected static function booted(): void { - // Ensure storage directory exists when a page is created static::created(function (Page $page) { - Storage::makeDirectory($page->diskPath()); + Storage::disk('pages')->makeDirectory($page->slug); - // Bootstrap with a blank index.html - if (! Storage::exists($page->diskPath('index.html'))) { - Storage::put($page->diskPath('index.html'), static::defaultHtml($page->name)); + if (! Storage::disk('pages')->exists($page->storagePath('index.html'))) { + Storage::disk('pages')->put( + $page->storagePath('index.html'), + static::defaultHtml($page->name) + ); } }); - // Remove files when a page is deleted static::deleted(function (Page $page) { - Storage::deleteDirectory($page->diskPath()); + Storage::disk('pages')->deleteDirectory($page->slug); }); - // If slug changes, rename the storage directory static::updating(function (Page $page) { if ($page->isDirty('slug')) { $old = $page->getOriginal('slug'); $new = $page->slug; - Storage::move("pages/{$old}", "pages/{$new}"); + Storage::disk('pages')->move($old, $new); } }); } diff --git a/app/Policies/PagePolicy.php b/app/Policies/PagePolicy.php index 04708f7..b6f411d 100644 --- a/app/Policies/PagePolicy.php +++ b/app/Policies/PagePolicy.php @@ -4,21 +4,13 @@ use App\Models\Page; use App\Models\User; +use Illuminate\Auth\Access\HandlesAuthorization; class PagePolicy { - public function view(User $user, Page $page): bool - { - return $user->id === $page->user_id; - } + use HandlesAuthorization; - public function update(User $user, Page $page): bool - { - return $user->id === $page->user_id; - } - - public function delete(User $user, Page $page): bool - { - return $user->id === $page->user_id; - } + public function view(User $user, Page $page): bool { return $user->id === $page->user_id; } + public function update(User $user, Page $page): bool { return $user->id === $page->user_id; } + public function delete(User $user, Page $page): bool { return $user->id === $page->user_id; } } diff --git a/app/Services/FileManagerService.php b/app/Services/FileManagerService.php index b1edfeb..a858ba5 100644 --- a/app/Services/FileManagerService.php +++ b/app/Services/FileManagerService.php @@ -8,49 +8,51 @@ use Illuminate\Support\Str; use RuntimeException; +/** + * All file operations use Storage::disk('pages'). + * Paths passed in/out are always relative to the page root: + * e.g. "index.html" or "assets/logo.png" + */ class FileManagerService { - // Editable in the code editor private const EDITABLE_EXT = ['html', 'css', 'js', 'json', 'txt', 'xml', 'svg', 'md']; - // Allowed uploads - private const BINARY_EXT = [ + private const ALLOWED_EXT = [ + 'html', 'css', 'js', 'json', 'txt', 'xml', 'svg', 'md', 'mp4', 'mp3', 'wav', 'ogg', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'webm', 'pdf', 'ico', 'woff', 'woff2', ]; + private function disk(): \Illuminate\Contracts\Filesystem\Filesystem + { + return Storage::disk('pages'); + } + // ── Tree ───────────────────────────────────────────────────────────────── - /** - * Return the file tree as a nested array suitable for JSON. - */ public function tree(Page $page): array { - $root = $page->diskPath(); - return $this->buildTree($root, $root); + return $this->buildTree($page->slug, $page->slug); } - private function buildTree(string $directory, string $root): array + private function buildTree(string $dir, string $root): array { $items = []; - $dirs = Storage::directories($directory); - $files = Storage::files($directory); - - foreach ($dirs as $dir) { - $name = basename($dir); - $relative = $this->relative($dir, $root); + foreach ($this->disk()->directories($dir) as $d) { + $name = basename($d); + $relative = $this->relative($d, $root); $items[] = [ 'type' => 'directory', 'name' => $name, 'path' => $relative, - 'children' => $this->buildTree($dir, $root), + 'children' => $this->buildTree($d, $root), ]; } - foreach ($files as $file) { - $name = basename($file); - $relative = $this->relative($file, $root); + foreach ($this->disk()->files($dir) as $f) { + $name = basename($f); + $relative = $this->relative($f, $root); $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); $items[] = [ 'type' => 'file', @@ -58,12 +60,11 @@ private function buildTree(string $directory, string $root): array 'path' => $relative, 'editable' => in_array($ext, self::EDITABLE_EXT, true), 'ext' => $ext, - 'size' => Storage::size($file), + 'size' => $this->disk()->size($f), ]; } - // Directories first, then files, both alphabetically - usort($items, fn($a, $b) => + usort($items, fn ($a, $b) => [$a['type'] === 'file' ? 1 : 0, $a['name']] <=> [$b['type'] === 'file' ? 1 : 0, $b['name']] @@ -72,101 +73,100 @@ private function buildTree(string $directory, string $root): array return $items; } - // ── Read / Write ───────────────────────────────────────────────────────── + // ── Read / Write ────────────────────────────────────────────────────────── public function read(Page $page, string $relative): string { - $path = $this->resolve($page, $relative); - $this->guard($page, $path); + $path = $page->storagePath($relative); + $this->guard($page, $relative); - if (! Storage::exists($path)) { + if (! $this->disk()->exists($path)) { throw new RuntimeException("File not found: {$relative}"); } - return Storage::get($path); + return $this->disk()->get($path); } public function save(Page $page, string $relative, string $content): void { - $path = $this->resolve($page, $relative); - $this->guard($page, $path); - Storage::put($path, $content); + $this->guard($page, $relative); + $this->disk()->put($page->storagePath($relative), $content); } public function createFile(Page $page, string $relative): void { $relative = $this->sanitizePath($relative); - $path = $this->resolve($page, $relative); - $this->guard($page, $path); + $path = $page->storagePath($relative); + $this->guard($page, $relative); - if (Storage::exists($path)) { + if ($this->disk()->exists($path)) { throw new RuntimeException("File already exists: {$relative}"); } - Storage::put($path, ''); + $this->disk()->put($path, ''); } - // ── Upload ─────────────────────────────────────────────────────────────── + // ── Upload ──────────────────────────────────────────────────────────────── public function upload(Page $page, UploadedFile $file, string $folder = ''): void { $name = $this->sanitizeName($file->getClientOriginalName()); $ext = strtolower($file->getClientOriginalExtension()); - $allowed = array_merge(self::EDITABLE_EXT, self::BINARY_EXT); - if (! in_array($ext, $allowed, true)) { + if (! in_array($ext, self::ALLOWED_EXT, true)) { throw new RuntimeException("File type .{$ext} is not allowed."); } $folder = $folder ? $this->sanitizePath($folder) : ''; $diskDir = $folder - ? $page->diskPath($folder) - : $page->diskPath(); + ? $page->storagePath($folder) + : $page->slug; - $file->storeAs($diskDir, $name); + $this->disk()->putFileAs($diskDir, $file, $name); } - // ── Delete ─────────────────────────────────────────────────────────────── + // ── Delete ──────────────────────────────────────────────────────────────── public function delete(Page $page, string $relative): void { - $path = $this->resolve($page, $relative); - $this->guard($page, $path); + $path = $page->storagePath($relative); + $this->guard($page, $relative); - if (Storage::directoryExists($path)) { - Storage::deleteDirectory($path); - } elseif (Storage::exists($path)) { - Storage::delete($path); + if ($this->disk()->directoryExists($path)) { + $this->disk()->deleteDirectory($path); + } elseif ($this->disk()->exists($path)) { + $this->disk()->delete($path); } else { throw new RuntimeException("Path not found: {$relative}"); } } - // ── Folder ─────────────────────────────────────────────────────────────── + // ── Folder ──────────────────────────────────────────────────────────────── public function createFolder(Page $page, string $relative): void { $relative = $this->sanitizePath($relative); - $path = $this->resolve($page, $relative); - $this->guard($page, $path); - Storage::makeDirectory($path); + $this->guard($page, $relative); + $this->disk()->makeDirectory($page->storagePath($relative)); } - // ── Rename ─────────────────────────────────────────────────────────────── + // ── Rename ──────────────────────────────────────────────────────────────── public function rename(Page $page, string $from, string $to): void { - $fromPath = $this->resolve($page, $this->sanitizePath($from)); - $toPath = $this->resolve($page, $this->sanitizePath($to)); + $from = $this->sanitizePath($from); + $to = $this->sanitizePath($to); + + $this->guard($page, $from); + $this->guard($page, $to); - $this->guard($page, $fromPath); - $this->guard($page, $toPath); + $fromPath = $page->storagePath($from); + $toPath = $page->storagePath($to); - if (Storage::directoryExists($fromPath)) { - // Laravel doesn't have a move-directory helper; use the filesystem + if ($this->disk()->directoryExists($fromPath)) { $this->moveDirectory($fromPath, $toPath); - } elseif (Storage::exists($fromPath)) { - Storage::move($fromPath, $toPath); + } elseif ($this->disk()->exists($fromPath)) { + $this->disk()->move($fromPath, $toPath); } else { throw new RuntimeException("Path not found: {$from}"); } @@ -174,45 +174,37 @@ public function rename(Page $page, string $from, string $to): void private function moveDirectory(string $from, string $to): void { - Storage::makeDirectory($to); + $this->disk()->makeDirectory($to); - foreach (Storage::allFiles($from) as $file) { + foreach ($this->disk()->allFiles($from) as $file) { $relative = Str::after($file, $from . '/'); - Storage::move($file, $to . '/' . $relative); + $this->disk()->move($file, $to . '/' . $relative); } - Storage::deleteDirectory($from); - } - - // ── Helpers ────────────────────────────────────────────────────────────── - - private function resolve(Page $page, string $relative): string - { - return $page->diskPath(ltrim($relative, '/')); + $this->disk()->deleteDirectory($from); } - private function relative(string $full, string $root): string - { - return ltrim(Str::after($full, $root), '/'); - } + // ── Security ────────────────────────────────────────────────────────────── /** - * Prevent path traversal attacks. + * Prevent path traversal. 'relative' must not escape the page slug directory. */ - private function guard(Page $page, string $diskPath): void + private function guard(Page $page, string $relative): void { - $root = realpath(Storage::path($page->diskPath())); - $real = realpath(Storage::path($diskPath)) - ?: Storage::path($diskPath); // file may not exist yet - - if ($root && ! str_starts_with($real, $root)) { + if (str_contains($relative, '..') || str_starts_with($relative, '/')) { throw new RuntimeException('Path traversal detected.'); } } + // ── Helpers ─────────────────────────────────────────────────────────────── + + private function relative(string $full, string $root): string + { + return ltrim(Str::after($full, $root . '/'), '/'); + } + private function sanitizeName(string $name): string { - // Keep extension, slugify the stem $ext = pathinfo($name, PATHINFO_EXTENSION); $stem = pathinfo($name, PATHINFO_FILENAME); $safe = preg_replace('/[^a-zA-Z0-9\-_]/', '-', $stem); @@ -221,9 +213,8 @@ private function sanitizeName(string $name): string private function sanitizePath(string $path): string { - // Explode on slashes, sanitize each segment, rejoin $segments = array_filter(explode('/', str_replace('\\', '/', $path))); - $safe = array_map(fn($s) => preg_replace('/[^a-zA-Z0-9\-_.]/', '-', $s), $segments); + $safe = array_map(fn ($s) => preg_replace('/[^a-zA-Z0-9\-_.]/', '-', $s), $segments); return implode('/', $safe); } } diff --git a/database/factories/PageFactory.php b/database/factories/PageFactory.php new file mode 100644 index 0000000..e7721f6 --- /dev/null +++ b/database/factories/PageFactory.php @@ -0,0 +1,31 @@ +unique()->words(3, true); + return [ + 'user_id' => User::factory(), + 'name' => ucwords($name), + 'slug' => Str::slug($name), + 'is_public' => true, + ]; + } + + public function private(): static + { + return $this->state(['is_public' => false]); + } + + public function withSlug(string $slug): static + { + return $this->state(['slug' => $slug]); + } +} diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 98825c8..29c4d3f 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,59 +2,29 @@ namespace Database\Factories; -use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; -/** - * @extends Factory - */ class UserFactory extends Factory { - /** - * The current password being used by the factory. - */ protected static ?string $password; - /** - * Define the model's default state. - * - * @return array - */ public function definition(): array { return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), - 'two_factor_secret' => null, - 'two_factor_recovery_codes' => null, - 'two_factor_confirmed_at' => null, + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), ]; } - /** - * Indicate that the model's email address should be unverified. - */ public function unverified(): static { return $this->state(fn (array $attributes) => [ 'email_verified_at' => null, ]); } - - /** - * Indicate that the model has two-factor authentication configured. - */ - public function withTwoFactor(): static - { - return $this->state(fn (array $attributes) => [ - 'two_factor_secret' => encrypt('secret'), - 'two_factor_recovery_codes' => encrypt(json_encode(['recovery-code-1'])), - 'two_factor_confirmed_at' => now(), - ]); - } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d01a0ef..a47f669 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -3,20 +3,14 @@ namespace Database\Seeders; use App\Models\User; -// use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { - /** - * Seed the application's database. - */ public function run(): void { - // User::factory(10)->create(); - User::factory()->create([ - 'name' => 'Test User', + 'name' => 'Test User', 'email' => 'test@example.com', ]); } diff --git a/resources/css/app.css b/resources/css/app.css index ad6eeed..86477d5 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,66 +1,14 @@ -@import 'tailwindcss'; -@import '../../vendor/livewire/flux/dist/flux.css'; +@tailwind base; +@tailwind components; +@tailwind utilities; -@source '../views'; -@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; -@source '../../vendor/livewire/flux-pro/stubs/**/*.blade.php'; -@source '../../vendor/livewire/flux/stubs/**/*.blade.php'; - -@custom-variant dark (&:where(.dark, .dark *)); - -@theme { - --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - - --color-zinc-50: #fafafa; - --color-zinc-100: #f5f5f5; - --color-zinc-200: #e5e5e5; - --color-zinc-300: #d4d4d4; - --color-zinc-400: #a3a3a3; - --color-zinc-500: #737373; - --color-zinc-600: #525252; - --color-zinc-700: #404040; - --color-zinc-800: #262626; - --color-zinc-900: #171717; - --color-zinc-950: #0a0a0a; - - --color-accent: var(--color-neutral-800); - --color-accent-content: var(--color-neutral-800); - --color-accent-foreground: var(--color-white); -} - -@layer theme { - .dark { - --color-accent: var(--color-white); - --color-accent-content: var(--color-white); - --color-accent-foreground: var(--color-neutral-800); - } -} - -@layer base { - - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentColor); - } -} - -[data-flux-field]:not(ui-radio, ui-checkbox) { - @apply grid gap-2; +/* CodeMirror 6 host element must fill its container */ +.cm-editor { + height: 100%; + font-size: 0.85rem; } -[data-flux-label] { - @apply !mb-0 !leading-tight; +.cm-scroller { + overflow: auto; + font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; } - -input:focus[data-flux-control], -textarea:focus[data-flux-control], -select:focus[data-flux-control] { - @apply outline-hidden ring-2 ring-accent ring-offset-2 ring-offset-accent-foreground; -} - -/* \[:where(&)\]:size-4 { - @apply size-4; -} */ diff --git a/resources/js/app.js b/resources/js/app.js index e69de29..aa35102 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -0,0 +1,9 @@ +import './bootstrap'; +import Alpine from 'alpinejs'; + +// Only boot Alpine on non-manager pages. +// The file manager boots its own Alpine instance with registered components. +if (!window.PAGE_SLUG) { + window.Alpine = Alpine; + Alpine.start(); +} diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..953d266 --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,3 @@ +import axios from 'axios'; +window.axios = axios; +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/resources/js/file-manager.js b/resources/js/file-manager.js new file mode 100644 index 0000000..ac93be9 --- /dev/null +++ b/resources/js/file-manager.js @@ -0,0 +1,347 @@ +/** + * file-manager.js + * Handles: CodeMirror 6 editor, file tree (Alpine), save/load, uploads, binary preview. + */ + +import { EditorView, keymap, lineNumbers, highlightActiveLine } from '@codemirror/view'; +import { EditorState } from '@codemirror/state'; +import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; +import { indentOnInput, bracketMatching } from '@codemirror/language'; +import { html } from '@codemirror/lang-html'; +import { css } from '@codemirror/lang-css'; +import { javascript } from '@codemirror/lang-javascript'; +import { json } from '@codemirror/lang-json'; +import { xml } from '@codemirror/lang-xml'; +import { oneDark } from '@codemirror/theme-one-dark'; +import Alpine from 'alpinejs'; + +// ── Globals ─────────────────────────────────────────────────────────────────── + +const SLUG = window.PAGE_SLUG; +const CSRF = window.CSRF_TOKEN; +const BASE = `/pages/${SLUG}`; + +const EDITABLE = new Set(['html','css','js','json','txt','xml','svg','md']); +const IMG_EXT = new Set(['png','jpg','jpeg','gif','webp','svg','ico']); +const VIDEO_EXT = new Set(['mp4','webm','ogg']); +const AUDIO_EXT = new Set(['mp3','wav','ogg']); + +// ── CodeMirror setup ────────────────────────────────────────────────────────── + +let editorView = null; +let currentFile = null; +let saveTimer = null; +let isDirty = false; + +function langExtension(ext) { + switch (ext) { + case 'html': return html(); + case 'css': return css(); + case 'js': return javascript(); + case 'json': return json(); + case 'xml': + case 'svg': return xml(); + default: return []; + } +} + +function createEditor(content, ext) { + if (editorView) { + editorView.destroy(); + editorView = null; + } + + const updateListener = EditorView.updateListener.of(update => { + if (update.docChanged) { + markDirty(); + } + }); + + const saveCmd = { + key: 'Mod-s', + run() { saveCurrentFile(); return true; } + }; + + editorView = new EditorView({ + state: EditorState.create({ + doc: content, + extensions: [ + oneDark, + lineNumbers(), + highlightActiveLine(), + history(), + indentOnInput(), + bracketMatching(), + keymap.of([saveCmd, ...defaultKeymap, ...historyKeymap]), + langExtension(ext), + updateListener, + EditorView.theme({ '&': { height: '100%' } }), + ], + }), + parent: document.getElementById('cm-editor'), + }); +} + +function markDirty() { + isDirty = true; + setStatus('Unsaved changes'); + + clearTimeout(saveTimer); + saveTimer = setTimeout(() => { + if (isDirty) saveCurrentFile(); + }, 1000); +} + +function setStatus(msg) { + document.getElementById('editor-status').textContent = msg; +} + +// ── File open / save ────────────────────────────────────────────────────────── + +async function openFile(path, name) { + const ext = name.split('.').pop().toLowerCase(); + + // Show/hide panels + document.getElementById('editor-empty').classList.add('hidden'); + document.getElementById('binary-preview').classList.add('hidden'); + document.getElementById('cm-editor').classList.remove('hidden'); + document.getElementById('btn-save').classList.add('hidden'); + document.getElementById('editor-filename').textContent = path; + + if (EDITABLE.has(ext)) { + // Text file – load into editor + try { + const res = await api(`${BASE}/files/read?path=${encodeURIComponent(path)}`); + const data = await res.json(); + createEditor(data.content, ext); + currentFile = path; + isDirty = false; + setStatus('Saved'); + document.getElementById('btn-save').classList.remove('hidden'); + } catch (e) { + setStatus('Error loading file'); + } + return; + } + + // Binary file – show preview + document.getElementById('cm-editor').classList.add('hidden'); + document.getElementById('binary-preview').classList.remove('hidden'); + showBinaryPreview(path, name, ext); +} + +function showBinaryPreview(path, name, ext) { + const url = `/${SLUG}/${path}`; + const img = document.getElementById('preview-img'); + const video = document.getElementById('preview-video'); + const audio = document.getElementById('preview-audio'); + const file = document.getElementById('preview-file'); + + [img, video, audio, file].forEach(el => el.classList.add('hidden')); + + if (IMG_EXT.has(ext)) { + img.src = url; + img.classList.remove('hidden'); + } else if (VIDEO_EXT.has(ext)) { + video.src = url; + video.classList.remove('hidden'); + } else if (AUDIO_EXT.has(ext)) { + audio.src = url; + audio.classList.remove('hidden'); + } else { + document.getElementById('preview-file-name').textContent = name; + file.classList.remove('hidden'); + } +} + +async function saveCurrentFile() { + if (!currentFile || !editorView) return; + + setStatus('Saving…'); + try { + await api(`${BASE}/files`, { + method: 'PUT', + body: JSON.stringify({ path: currentFile, content: editorView.state.doc.toString() }), + }); + isDirty = false; + setStatus('Saved ✓'); + } catch (e) { + setStatus('Save failed!'); + } +} + +document.getElementById('btn-save')?.addEventListener('click', saveCurrentFile); + +// ── Utility fetch wrapper ───────────────────────────────────────────────────── + +function api(url, opts = {}) { + return fetch(url, { + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': CSRF, + 'Accept': 'application/json', + ...(opts.headers || {}), + }, + ...opts, + }); +} + +// ── Settings panel toggle ───────────────────────────────────────────────────── + +window.togglePanel = function(name) { + const el = document.getElementById(`panel-${name}`); + el.classList.toggle('hidden'); +}; + +// ── Alpine file tree component ──────────────────────────────────────────────── + +Alpine.data('fileTree', (slug) => ({ + tree: [], + loading: true, + slug, + + async init() { + await this.refresh(); + }, + + async refresh() { + this.loading = true; + const res = await api(`${BASE}/files`); + const data = await res.json(); + this.tree = data; + this.loading = false; + }, + + openFile(path, name) { + openFile(path, name); + }, + + async promptNewFile() { + const name = prompt('File name (e.g. about.html):'); + if (!name) return; + + await api(`${BASE}/files/create`, { + method: 'POST', + body: JSON.stringify({ path: name }), + }); + await this.refresh(); + openFile(name, name); + }, + + async promptNewFolder() { + const name = prompt('Folder name:'); + if (!name) return; + + await api(`${BASE}/files/folder`, { + method: 'POST', + body: JSON.stringify({ path: name }), + }); + await this.refresh(); + }, + + async uploadFiles(event) { + const files = Array.from(event.target.files); + for (const file of files) { + const fd = new FormData(); + fd.append('file', file); + fd.append('_token', CSRF); + + await fetch(`${BASE}/files`, { method: 'POST', body: fd }); + } + await this.refresh(); + event.target.value = ''; + }, + + async deleteItem(path, type) { + const label = type === 'directory' ? 'folder and all its contents' : 'file'; + if (!confirm(`Delete this ${label}?`)) return; + + await api(`${BASE}/files`, { + method: 'DELETE', + body: JSON.stringify({ path }), + }); + await this.refresh(); + }, + + async renameItem(path, currentName) { + const newName = prompt('New name:', currentName); + if (!newName || newName === currentName) return; + + const dir = path.includes('/') ? path.rsplit('/', 1)[0] + '/' : ''; + const to = dir + newName; + + await api(`${BASE}/files/rename`, { + method: 'PUT', + body: JSON.stringify({ from: path, to }), + }); + await this.refresh(); + }, + + renderItem(item, depth) { + const indent = depth * 14; + const isIndex = item.name === 'index.html'; + const icon = item.type === 'directory' ? '📁' : fileIcon(item.ext); + + const highlight = isIndex + ? 'text-yellow-300 font-semibold' + : 'text-gray-300 hover:text-white'; + + if (item.type === 'directory') { + let children = ''; + if (item.children && item.children.length) { + children = item.children + .map(c => this.renderItem(c, depth + 1)) + .join(''); + } + return ` +
+
+ ${icon} + ${item.name} + +
+
${children}
+
`; + } + + return ` +
+ ${icon} + ${item.name} + +
`; + }, +})); + +// Bridge: called from rendered HTML strings +window._fileTree_openFile = openFile; + +// ── Helper: file icons ──────────────────────────────────────────────────────── + +function fileIcon(ext) { + const map = { + html: '🌐', css: '🎨', js: '⚡', json: '{}', txt: '📄', + xml: '📋', svg: '🖼', md: '📝', + png: '🖼', jpg: '🖼', jpeg: '🖼', gif: '🖼', webp: '🖼', ico: '🖼', + mp4: '🎬', webm: '🎬', mp3: '🎵', wav: '🎵', ogg: '🎵', + pdf: '📕', woff: '🔤', woff2: '🔤', + }; + return map[ext] ?? '📄'; +} + +// ── Boot Alpine ─────────────────────────────────────────────────────────────── +window.Alpine = Alpine; +Alpine.start(); diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php new file mode 100644 index 0000000..02f090b --- /dev/null +++ b/resources/views/errors/403.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[403] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 403, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php new file mode 100644 index 0000000..f7ccdea --- /dev/null +++ b/resources/views/errors/404.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[404] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 404, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/419.blade.php b/resources/views/errors/419.blade.php new file mode 100644 index 0000000..781a3a1 --- /dev/null +++ b/resources/views/errors/419.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[419] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 419, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/429.blade.php b/resources/views/errors/429.blade.php new file mode 100644 index 0000000..ca20a7d --- /dev/null +++ b/resources/views/errors/429.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[429] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 429, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php new file mode 100644 index 0000000..84155d2 --- /dev/null +++ b/resources/views/errors/500.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[500] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 500, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 0000000..3c9989f --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,12 @@ +@php +$map = [ + 403 => ['title' => 'Access Forbidden', 'msg' => 'You don\'t have permission to view this page.'], + 404 => ['title' => 'Page Not Found', 'msg' => 'The page or file you\'re looking for doesn\'t exist.'], + 419 => ['title' => 'Session Expired', 'msg' => 'Your session has expired. Please refresh and try again.'], + 429 => ['title' => 'Too Many Requests', 'msg' => 'You\'re doing that too fast. Please wait a moment.'], + 500 => ['title' => 'Server Error', 'msg' => 'Something went wrong on our end. We\'ve been notified.'], + 503 => ['title' => 'Service Unavailable', 'msg' => 'We\'re doing some maintenance. Back shortly!'], +]; +$info = $map[503] ?? ['title' => 'Error', 'msg' => 'An unexpected error occurred.']; +@endphp +@include('errors.layout', ['code' => 503, 'title' => $info['title'], 'message' => $info['msg']]) diff --git a/resources/views/errors/layout.blade.php b/resources/views/errors/layout.blade.php new file mode 100644 index 0000000..f5a09ae --- /dev/null +++ b/resources/views/errors/layout.blade.php @@ -0,0 +1,43 @@ + + + + + + {{ $code }} — {{ config('app.name') }} + + + +
+
{{ $code }}
+

{{ $title }}

+

{{ $message }}

+ ← Back home +
+ + diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index e9daa9e..0391011 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -4,37 +4,49 @@ - {{ $title ?? 'StaticHTMLSites' }} + {{ $title ?? config('app.name') }} @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles - @stack('styles') + @stack('head') @if (session('success')) -
- {{ session('success') }} +
+ {{ session('success') }} +
@endif -
+@if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
{{ $slot }}
diff --git a/resources/views/pages/dashboard.blade.php b/resources/views/pages/dashboard.blade.php index 42d0ffc..3d42a6b 100644 --- a/resources/views/pages/dashboard.blade.php +++ b/resources/views/pages/dashboard.blade.php @@ -1,59 +1,70 @@ - Dashboard — StaticHTMLSites + Dashboard — {{ config('app.name') }} -
-

My Pages

- - + New Page - -
+
- @if ($pages->isEmpty()) -
-

📄

-

No pages yet. Create your first one!

+
+

Your Pages

+ + + + + New Page +
- @else -
- @foreach ($pages as $page) -
-
-
-

{{ $page->name }}

- - {{ $page->slug }} - + + @if ($pages->isEmpty()) +
+

📄

+

No pages yet.

+ + Create your first page → + +
+ @else +
+ @foreach ($pages as $page) +
+
+
+

{{ $page->name }}

+ + {{ $page->slug }} + +
+ + {{ $page->is_public ? 'Public' : 'Private' }} +
- - {{ $page->is_public ? 'Public' : 'Private' }} - -
-
- Updated {{ $page->updated_at->diffForHumans() }} -
+

+ Updated {{ $page->updated_at->diffForHumans() }} +

-
- - Edit - - - Preview ↗ - -
- @csrf @method('DELETE') - -
-
-
- @endforeach -
- @endif +
+ + Edit + + + Preview ↗ + +
+ @csrf @method('DELETE') + +
+
+ + @endforeach +
+ @endif +
diff --git a/resources/views/pages/index.blade.php b/resources/views/pages/index.blade.php deleted file mode 100644 index 54b808f..0000000 --- a/resources/views/pages/index.blade.php +++ /dev/null @@ -1,59 +0,0 @@ - - -
- -
-

Your Pages

- - - - - New Page - -
- - @if ($pages->isEmpty()) -
-

📄

-

No pages yet.

- Create your first page → -
- @else -
- @foreach ($pages as $page) -
-
-
-

{{ $page->name }}

-

{{ $page->slug }}

-
- - {{ $page->is_public ? 'Public' : 'Private' }} - -
- - - -
- @csrf @method('DELETE') - -
-
- @endforeach -
- @endif - -
- -
diff --git a/resources/views/pages/manager.blade.php b/resources/views/pages/manager.blade.php new file mode 100644 index 0000000..55b3c3b --- /dev/null +++ b/resources/views/pages/manager.blade.php @@ -0,0 +1,35 @@ + + + + + + + {{ $page->name }} — File Manager + @vite(['resources/css/app.css']) + @livewireStyles + + {{-- CodeMirror 6 via ESM import map (no bundler required for editor) --}} + + + + + + + @livewireScripts + + diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000..f56878f --- /dev/null +++ b/resources/views/welcome.blade.php @@ -0,0 +1,48 @@ + + + + + + StaticHTMLSites — Host your static pages instantly + @vite(['resources/css/app.css']) + + +
+

+ StaticHTMLSites +

+

+ Create and host static HTML pages instantly.
+ Each page gets its own subdomain and path URL. +

+ + +
+
+
+ Instant Deploy + Edit files in-browser, changes go live immediately. +
+
+
🌐
+ Dual URLs + Subdomain + path-based access. Choose your style. +
+
+
🗂
+ Full File Manager + Upload images, videos, code. Manage everything visually. +
+
+
+ + diff --git a/routes/auth.php b/routes/auth.php new file mode 100644 index 0000000..252fa71 --- /dev/null +++ b/routes/auth.php @@ -0,0 +1,50 @@ +group(function () { + Route::get('register', [RegisteredUserController::class, 'create'])->name('register'); + Route::post('register', [RegisteredUserController::class, 'store']); + + Route::get('login', [AuthenticatedSessionController::class, 'create'])->name('login'); + Route::post('login', [AuthenticatedSessionController::class, 'store']); + + Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])->name('password.request'); + Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])->name('password.email'); + + Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])->name('password.reset'); + Route::post('reset-password', [NewPasswordController::class, 'store'])->name('password.store'); +}); + +Route::middleware('auth')->group(function () { + Route::get('verify-email', EmailVerificationPromptController::class)->name('verification.notice'); + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) + ->middleware('throttle:6,1') + ->name('verification.send'); + + Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])->name('password.confirm'); + Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); + + Route::put('password', [PasswordController::class, 'update'])->name('password.update'); + + Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])->name('logout'); +}); diff --git a/routes/web.php b/routes/web.php index f2f057a..5a6a4a7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -65,4 +65,5 @@ ->where('path', '.*') ->name('page.path'); +require __DIR__ . '/auth.php'; require __DIR__.'/settings.php'; From acbf8e406869a053edb596d3a841dc4775c9e16b Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 11:49:20 +0100 Subject: [PATCH 3/9] (some stuff fixed) Changes ... --- app/Console/Commands/CleanupPageStorage.php | 52 +++ .../Auth/AuthenticatedSessionController.php | 36 ++ .../Auth/ConfirmablePasswordController.php | 34 ++ ...mailVerificationNotificationController.php | 21 + .../EmailVerificationPromptController.php | 23 + .../Auth/NewPasswordController.php | 47 ++ .../Controllers/Auth/PasswordController.php | 29 ++ .../Auth/PasswordResetLinkController.php | 33 ++ .../Auth/RegisteredUserController.php | 42 ++ .../Auth/VerifyEmailController.php | 27 ++ app/Http/Middleware/HandleAppearance.php | 19 + app/Http/Requests/Auth/LoginRequest.php | 67 +++ bootstrap/app.php | 31 +- composer.json | 14 +- package-lock.json | 432 +++++++++++++++++- package.json | 19 +- resources/js/file-manager.js | 347 -------------- .../views/auth/confirm-password.blade.php | 30 ++ .../views/auth/forgot-password.blade.php | 39 ++ resources/views/auth/login.blade.php | 59 +++ resources/views/auth/register.blade.php | 68 +++ resources/views/auth/reset-password.blade.php | 51 +++ resources/views/auth/verify-email.blade.php | 35 ++ .../views/components/app-layout.blade.php | 62 +++ .../views/components/auth-layout.blade.php | 33 ++ resources/views/layouts/auth.blade.php | 31 ++ routes/auth.php | 52 ++- routes/console.php | 4 + routes/web.php | 1 - tests/Feature/Auth/AuthenticationTest.php | 67 --- tests/Feature/Auth/EmailVerificationTest.php | 72 --- .../Feature/Auth/PasswordConfirmationTest.php | 11 - tests/Feature/Auth/PasswordResetTest.php | 65 --- tests/Feature/Auth/RegistrationTest.php | 27 -- tests/Feature/Auth/TwoFactorChallengeTest.php | 28 -- tests/Feature/DashboardTest.php | 16 - tests/Feature/ExampleTest.php | 7 - tests/Feature/FileManagerApiTest.php | 187 ++++++++ tests/Feature/PageServeTest.php | 132 ++++++ tests/Feature/PageTest.php | 152 ++++++ tests/Feature/Settings/ProfileUpdateTest.php | 76 --- tests/Feature/Settings/SecurityTest.php | 105 ----- tests/TestCase.php | 11 +- tests/Unit/ExampleTest.php | 5 - tests/Unit/FileManagerServiceTest.php | 128 ++++++ 45 files changed, 1955 insertions(+), 872 deletions(-) create mode 100644 app/Console/Commands/CleanupPageStorage.php create mode 100644 app/Http/Controllers/Auth/AuthenticatedSessionController.php create mode 100644 app/Http/Controllers/Auth/ConfirmablePasswordController.php create mode 100644 app/Http/Controllers/Auth/EmailVerificationNotificationController.php create mode 100644 app/Http/Controllers/Auth/EmailVerificationPromptController.php create mode 100644 app/Http/Controllers/Auth/NewPasswordController.php create mode 100644 app/Http/Controllers/Auth/PasswordController.php create mode 100644 app/Http/Controllers/Auth/PasswordResetLinkController.php create mode 100644 app/Http/Controllers/Auth/RegisteredUserController.php create mode 100644 app/Http/Controllers/Auth/VerifyEmailController.php create mode 100644 app/Http/Middleware/HandleAppearance.php create mode 100644 app/Http/Requests/Auth/LoginRequest.php delete mode 100644 resources/js/file-manager.js create mode 100644 resources/views/auth/confirm-password.blade.php create mode 100644 resources/views/auth/forgot-password.blade.php create mode 100644 resources/views/auth/login.blade.php create mode 100644 resources/views/auth/register.blade.php create mode 100644 resources/views/auth/reset-password.blade.php create mode 100644 resources/views/auth/verify-email.blade.php create mode 100644 resources/views/components/app-layout.blade.php create mode 100644 resources/views/components/auth-layout.blade.php create mode 100644 resources/views/layouts/auth.blade.php delete mode 100644 tests/Feature/Auth/AuthenticationTest.php delete mode 100644 tests/Feature/Auth/EmailVerificationTest.php delete mode 100644 tests/Feature/Auth/PasswordConfirmationTest.php delete mode 100644 tests/Feature/Auth/PasswordResetTest.php delete mode 100644 tests/Feature/Auth/RegistrationTest.php delete mode 100644 tests/Feature/Auth/TwoFactorChallengeTest.php delete mode 100644 tests/Feature/DashboardTest.php delete mode 100644 tests/Feature/ExampleTest.php create mode 100644 tests/Feature/FileManagerApiTest.php create mode 100644 tests/Feature/PageServeTest.php create mode 100644 tests/Feature/PageTest.php delete mode 100644 tests/Feature/Settings/ProfileUpdateTest.php delete mode 100644 tests/Feature/Settings/SecurityTest.php delete mode 100644 tests/Unit/ExampleTest.php create mode 100644 tests/Unit/FileManagerServiceTest.php diff --git a/app/Console/Commands/CleanupPageStorage.php b/app/Console/Commands/CleanupPageStorage.php new file mode 100644 index 0000000..62aa56f --- /dev/null +++ b/app/Console/Commands/CleanupPageStorage.php @@ -0,0 +1,52 @@ +directories('/'); + + $slugs = Page::pluck('slug')->flip(); // O(1) lookups + $orphans = collect($dirs)->filter(fn ($d) => ! $slugs->has($d)); + + if ($orphans->isEmpty()) { + $this->info('No orphaned directories found.'); + return self::SUCCESS; + } + + $dryRun = $this->option('dry-run'); + + foreach ($orphans as $dir) { + if ($dryRun) { + $this->line(" [dry-run] Would delete: {$dir}"); + } else { + $disk->deleteDirectory($dir); + $this->line(" Deleted: {$dir}"); + } + } + + $count = $orphans->count(); + $verb = $dryRun ? 'Found' : 'Cleaned up'; + $this->info("{$verb} {$count} orphaned " . str('directory')->plural($count) . '.'); + + return self::SUCCESS; + } +} diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php new file mode 100644 index 0000000..ed07e05 --- /dev/null +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -0,0 +1,36 @@ +authenticate(); + $request->session()->regenerate(); + + return redirect()->intended(route('dashboard', absolute: false)); + } + + public function destroy(Request $request): RedirectResponse + { + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect('/'); + } +} diff --git a/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/app/Http/Controllers/Auth/ConfirmablePasswordController.php new file mode 100644 index 0000000..c3ac5b7 --- /dev/null +++ b/app/Http/Controllers/Auth/ConfirmablePasswordController.php @@ -0,0 +1,34 @@ +validate([ + 'email' => $request->user()->email, + 'password' => $request->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + $request->session()->put('auth.password_confirmed_at', time()); + + return redirect()->intended(route('dashboard', absolute: false)); + } +} diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php new file mode 100644 index 0000000..f8fdebb --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php @@ -0,0 +1,21 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard', absolute: false)); + } + + $request->user()->sendEmailVerificationNotification(); + + return back()->with('status', 'verification-link-sent'); + } +} diff --git a/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/app/Http/Controllers/Auth/EmailVerificationPromptController.php new file mode 100644 index 0000000..0cc9c66 --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationPromptController.php @@ -0,0 +1,23 @@ +user()->hasVerifiedEmail() + ? redirect()->intended(route('dashboard', absolute: false)) + : view('auth.verify-email'); + } +} diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000..5c53f0e --- /dev/null +++ b/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,47 @@ + $request]); + } + + public function store(Request $request): RedirectResponse + { + $request->validate([ + 'token' => ['required'], + 'email' => ['required', 'email'], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user) use ($request) { + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + return $status === Password::PASSWORD_RESET + ? redirect()->route('login')->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 0000000..7985ba5 --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,29 @@ +validateWithBag('updatePassword', [ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('status', 'password-updated'); + } +} diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000..4af48ca --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,33 @@ +validate([ + 'email' => ['required', 'email'], + ]); + + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status === Password::RESET_LINK_SENT + ? back()->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php new file mode 100644 index 0000000..99927f5 --- /dev/null +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -0,0 +1,42 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + ]); + + event(new Registered($user)); + + Auth::login($user); + + return redirect(route('dashboard', absolute: false)); + } +} diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 0000000..bfdd971 --- /dev/null +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,27 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard', absolute: false) . '?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(route('dashboard', absolute: false) . '?verified=1'); + } +} diff --git a/app/Http/Middleware/HandleAppearance.php b/app/Http/Middleware/HandleAppearance.php new file mode 100644 index 0000000..45491c6 --- /dev/null +++ b/app/Http/Middleware/HandleAppearance.php @@ -0,0 +1,19 @@ + ['required', 'string', 'email'], + 'password' => ['required', 'string'], + ]; + } + + /** + * Attempt authentication; throw on failure or rate-limit breach. + */ + public function authenticate(): void + { + $this->ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + public function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout($this)); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + public function throttleKey(): string + { + return Str::transliterate(Str::lower($this->string('email')) . '|' . $this->ip()); + } +} diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..8aef765 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -3,16 +3,33 @@ use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; +use Illuminate\Http\Request; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( - web: __DIR__.'/../routes/web.php', - commands: __DIR__.'/../routes/console.php', + web: __DIR__ . '/../routes/web.php', + commands: __DIR__ . '/../routes/console.php', health: '/up', ) - ->withMiddleware(function (Middleware $middleware): void { - // + ->withMiddleware(function (Middleware $middleware) { + $middleware->web(append: [ + \App\Http\Middleware\HandleAppearance::class, + ]); + + $middleware->alias([ + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + ]); + + // Exclude page-serving routes from CSRF so subdomain requests work + $middleware->validateCsrfTokens(except: [ + // CSRF is only needed for state-mutating requests from the browser, + // but we serve pages publicly so no exclusions needed here. + ]); + }) + ->withExceptions(function (Exceptions $exceptions) { + // Return JSON for API-like file manager requests + $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { + return $request->is('*/files*') || $request->expectsJson(); + }); }) - ->withExceptions(function (Exceptions $exceptions): void { - // - })->create(); + ->create(); diff --git a/composer.json b/composer.json index f2255e1..175ac39 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,12 @@ { - "$schema": "https://getcomposer.org/schema.json", - "name": "laravel/livewire-starter-kit", + "name": "mtex/statichtmlsites", "type": "project", - "description": "The official Laravel starter kit for Livewire.", - "keywords": [ - "laravel", - "framework" - ], + "description": "Host and manage static HTML mini-sites.", + "keywords": ["laravel", "framework"], "license": "MIT", "require": { "php": "^8.2", + "laravel/breeze": "^2.1", "laravel/fortify": "^1.30", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", @@ -25,7 +22,8 @@ "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", "pestphp/pest": "^4.4", - "pestphp/pest-plugin-laravel": "^4.1" + "pestphp/pest-plugin-laravel": "^4.1", + "phpunit/phpunit": "^11.0.1" }, "autoload": { "psr-4": { diff --git a/package-lock.json b/package-lock.json index 40b5dd4..ef15d56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,11 +5,25 @@ "packages": { "": { "dependencies": { + "@codemirror/commands": "^6.6.0", + "@codemirror/lang-css": "^6.2.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.10.1", + "@codemirror/state": "^6.4.1", + "@codemirror/theme-one-dark": "^6.1.2", + "@codemirror/view": "^6.33.0", + "alpinejs": "^3.14.0", + "axios": "^1.7.4" + }, + "devDependencies": { "@tailwindcss/vite": "^4.1.11", "autoprefixer": "^10.4.20", - "axios": "^1.7.4", "concurrently": "^9.0.1", "laravel-vite-plugin": "^2.0", + "postcss": "^8.4.38", "tailwindcss": "^4.0.7", "vite": "^7.0.4" }, @@ -19,6 +33,157 @@ "lightningcss-linux-x64-gnu": "^1.29.1" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", + "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.2.tgz", + "integrity": "sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.5.tgz", + "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz", + "integrity": "sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.4", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", @@ -26,6 +191,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -42,6 +208,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -58,6 +225,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -74,6 +242,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -90,6 +259,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -106,6 +276,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -122,6 +293,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -138,6 +310,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -154,6 +327,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -170,6 +344,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -186,6 +361,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -202,6 +378,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -218,6 +395,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -234,6 +412,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -250,6 +429,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -266,6 +446,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -282,6 +463,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -298,6 +480,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -314,6 +497,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -330,6 +514,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -346,6 +531,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -362,6 +548,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -378,6 +565,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -394,6 +582,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -410,6 +599,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -426,6 +616,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -439,6 +630,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -449,6 +641,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -459,6 +652,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -468,18 +662,105 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz", + "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", + "license": "MIT" + }, + "node_modules/@lezer/css": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.1.tgz", + "integrity": "sha512-PYAKeUVBo3HFThruRyp/iK91SwiZJnzXh8QzkQlwijB5y+N5iB28+iLk78o2zmKqqV0uolNhCwFqB8LA7b0Svg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", @@ -487,6 +768,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -500,6 +782,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -513,6 +796,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -526,6 +810,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -539,6 +824,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -552,6 +838,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -565,6 +852,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -578,6 +866,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -591,6 +880,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -604,6 +894,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -617,6 +908,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -630,6 +922,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -643,6 +936,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -656,6 +950,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -669,6 +964,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -682,6 +978,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -695,6 +992,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -721,6 +1019,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -734,6 +1033,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -747,6 +1047,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -760,6 +1061,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -773,6 +1075,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -786,6 +1089,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -799,6 +1103,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -809,6 +1114,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", @@ -824,6 +1130,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 20" @@ -850,6 +1157,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -866,6 +1174,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -882,6 +1191,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -898,6 +1208,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -914,6 +1225,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -930,6 +1242,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -946,6 +1259,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -978,6 +1292,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1002,6 +1317,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1023,6 +1339,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1039,6 +1356,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1052,6 +1370,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz", "integrity": "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==", + "dev": true, "license": "MIT", "dependencies": { "@tailwindcss/node": "4.2.1", @@ -1066,12 +1385,38 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", "license": "MIT" }, + "node_modules/alpinejs": { + "version": "3.15.8", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.8.tgz", + "integrity": "sha512-zxIfCRTBGvF1CCLIOMQOxAyBuqibxSEwS6Jm1a3HGA9rgrJVcjEWlwLcQTVGAWGS8YhAsTRLVrtQ5a5QT9bSSQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1081,6 +1426,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1102,6 +1448,7 @@ "version": "10.4.27", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -1149,6 +1496,7 @@ "version": "2.10.7", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -1161,6 +1509,7 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -1207,6 +1556,7 @@ "version": "1.0.30001778", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -1227,6 +1577,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -1243,6 +1594,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -1255,6 +1607,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -1269,6 +1622,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1281,6 +1635,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -1299,6 +1654,7 @@ "version": "9.2.1", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", @@ -1319,6 +1675,12 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1332,6 +1694,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -1355,18 +1718,21 @@ "version": "1.5.313", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/enhanced-resolve": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -1425,6 +1791,7 @@ "version": "0.27.4", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -1466,6 +1833,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1475,6 +1843,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1528,6 +1897,7 @@ "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -1541,6 +1911,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1564,6 +1935,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -1622,12 +1994,14 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1676,6 +2050,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1685,6 +2060,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -1694,6 +2070,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.1.0.tgz", "integrity": "sha512-z+ck2BSV6KWtYcoIzk9Y5+p4NEjqM+Y4i8/H+VZRLq0OgNjW2DqyADquwYu5j8qRvaXwzNmfCWl1KrMlV1zpsg==", + "dev": true, "license": "MIT", "dependencies": { "picocolors": "^1.0.0", @@ -1713,6 +2090,7 @@ "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -1745,6 +2123,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1765,6 +2144,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1785,6 +2165,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1805,6 +2186,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1825,6 +2207,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1845,6 +2228,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1865,6 +2249,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1905,6 +2290,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1925,6 +2311,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1945,6 +2332,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1965,6 +2353,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1982,6 +2371,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -2021,6 +2411,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -2039,18 +2430,21 @@ "version": "2.0.36", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2063,6 +2457,7 @@ "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2091,6 +2486,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, "license": "MIT" }, "node_modules/proxy-from-env": { @@ -2103,6 +2499,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2112,6 +2509,7 @@ "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -2159,6 +2557,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2169,6 +2568,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" @@ -2178,6 +2578,7 @@ "version": "1.8.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2190,6 +2591,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -2199,6 +2601,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -2213,6 +2616,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -2221,10 +2625,17 @@ "node": ">=8" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -2240,12 +2651,14 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2259,6 +2672,7 @@ "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2275,6 +2689,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, "license": "MIT", "bin": { "tree-kill": "cli.js" @@ -2284,12 +2699,14 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2320,6 +2737,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.27.0", @@ -2394,6 +2812,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, "license": "MIT", "dependencies": { "picocolors": "^1.0.0", @@ -2404,6 +2823,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -2412,10 +2832,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -2433,6 +2860,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -2442,6 +2870,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -2460,6 +2889,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" diff --git a/package.json b/package.json index 688bea8..f9f496e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,4 @@ { - "$schema": "https://www.schemastore.org/package.json", "private": true, "type": "module", "scripts": { @@ -7,11 +6,25 @@ "dev": "vite" }, "dependencies": { + "@codemirror/commands": "^6.6.0", + "@codemirror/lang-css": "^6.2.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.10.1", + "@codemirror/state": "^6.4.1", + "@codemirror/theme-one-dark": "^6.1.2", + "@codemirror/view": "^6.33.0", + "alpinejs": "^3.14.0", + "axios": "^1.7.4" + }, + "devDependencies": { "@tailwindcss/vite": "^4.1.11", "autoprefixer": "^10.4.20", - "axios": "^1.7.4", "concurrently": "^9.0.1", "laravel-vite-plugin": "^2.0", + "postcss": "^8.4.38", "tailwindcss": "^4.0.7", "vite": "^7.0.4" }, @@ -20,4 +33,4 @@ "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", "lightningcss-linux-x64-gnu": "^1.29.1" } -} +} \ No newline at end of file diff --git a/resources/js/file-manager.js b/resources/js/file-manager.js deleted file mode 100644 index ac93be9..0000000 --- a/resources/js/file-manager.js +++ /dev/null @@ -1,347 +0,0 @@ -/** - * file-manager.js - * Handles: CodeMirror 6 editor, file tree (Alpine), save/load, uploads, binary preview. - */ - -import { EditorView, keymap, lineNumbers, highlightActiveLine } from '@codemirror/view'; -import { EditorState } from '@codemirror/state'; -import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; -import { indentOnInput, bracketMatching } from '@codemirror/language'; -import { html } from '@codemirror/lang-html'; -import { css } from '@codemirror/lang-css'; -import { javascript } from '@codemirror/lang-javascript'; -import { json } from '@codemirror/lang-json'; -import { xml } from '@codemirror/lang-xml'; -import { oneDark } from '@codemirror/theme-one-dark'; -import Alpine from 'alpinejs'; - -// ── Globals ─────────────────────────────────────────────────────────────────── - -const SLUG = window.PAGE_SLUG; -const CSRF = window.CSRF_TOKEN; -const BASE = `/pages/${SLUG}`; - -const EDITABLE = new Set(['html','css','js','json','txt','xml','svg','md']); -const IMG_EXT = new Set(['png','jpg','jpeg','gif','webp','svg','ico']); -const VIDEO_EXT = new Set(['mp4','webm','ogg']); -const AUDIO_EXT = new Set(['mp3','wav','ogg']); - -// ── CodeMirror setup ────────────────────────────────────────────────────────── - -let editorView = null; -let currentFile = null; -let saveTimer = null; -let isDirty = false; - -function langExtension(ext) { - switch (ext) { - case 'html': return html(); - case 'css': return css(); - case 'js': return javascript(); - case 'json': return json(); - case 'xml': - case 'svg': return xml(); - default: return []; - } -} - -function createEditor(content, ext) { - if (editorView) { - editorView.destroy(); - editorView = null; - } - - const updateListener = EditorView.updateListener.of(update => { - if (update.docChanged) { - markDirty(); - } - }); - - const saveCmd = { - key: 'Mod-s', - run() { saveCurrentFile(); return true; } - }; - - editorView = new EditorView({ - state: EditorState.create({ - doc: content, - extensions: [ - oneDark, - lineNumbers(), - highlightActiveLine(), - history(), - indentOnInput(), - bracketMatching(), - keymap.of([saveCmd, ...defaultKeymap, ...historyKeymap]), - langExtension(ext), - updateListener, - EditorView.theme({ '&': { height: '100%' } }), - ], - }), - parent: document.getElementById('cm-editor'), - }); -} - -function markDirty() { - isDirty = true; - setStatus('Unsaved changes'); - - clearTimeout(saveTimer); - saveTimer = setTimeout(() => { - if (isDirty) saveCurrentFile(); - }, 1000); -} - -function setStatus(msg) { - document.getElementById('editor-status').textContent = msg; -} - -// ── File open / save ────────────────────────────────────────────────────────── - -async function openFile(path, name) { - const ext = name.split('.').pop().toLowerCase(); - - // Show/hide panels - document.getElementById('editor-empty').classList.add('hidden'); - document.getElementById('binary-preview').classList.add('hidden'); - document.getElementById('cm-editor').classList.remove('hidden'); - document.getElementById('btn-save').classList.add('hidden'); - document.getElementById('editor-filename').textContent = path; - - if (EDITABLE.has(ext)) { - // Text file – load into editor - try { - const res = await api(`${BASE}/files/read?path=${encodeURIComponent(path)}`); - const data = await res.json(); - createEditor(data.content, ext); - currentFile = path; - isDirty = false; - setStatus('Saved'); - document.getElementById('btn-save').classList.remove('hidden'); - } catch (e) { - setStatus('Error loading file'); - } - return; - } - - // Binary file – show preview - document.getElementById('cm-editor').classList.add('hidden'); - document.getElementById('binary-preview').classList.remove('hidden'); - showBinaryPreview(path, name, ext); -} - -function showBinaryPreview(path, name, ext) { - const url = `/${SLUG}/${path}`; - const img = document.getElementById('preview-img'); - const video = document.getElementById('preview-video'); - const audio = document.getElementById('preview-audio'); - const file = document.getElementById('preview-file'); - - [img, video, audio, file].forEach(el => el.classList.add('hidden')); - - if (IMG_EXT.has(ext)) { - img.src = url; - img.classList.remove('hidden'); - } else if (VIDEO_EXT.has(ext)) { - video.src = url; - video.classList.remove('hidden'); - } else if (AUDIO_EXT.has(ext)) { - audio.src = url; - audio.classList.remove('hidden'); - } else { - document.getElementById('preview-file-name').textContent = name; - file.classList.remove('hidden'); - } -} - -async function saveCurrentFile() { - if (!currentFile || !editorView) return; - - setStatus('Saving…'); - try { - await api(`${BASE}/files`, { - method: 'PUT', - body: JSON.stringify({ path: currentFile, content: editorView.state.doc.toString() }), - }); - isDirty = false; - setStatus('Saved ✓'); - } catch (e) { - setStatus('Save failed!'); - } -} - -document.getElementById('btn-save')?.addEventListener('click', saveCurrentFile); - -// ── Utility fetch wrapper ───────────────────────────────────────────────────── - -function api(url, opts = {}) { - return fetch(url, { - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-TOKEN': CSRF, - 'Accept': 'application/json', - ...(opts.headers || {}), - }, - ...opts, - }); -} - -// ── Settings panel toggle ───────────────────────────────────────────────────── - -window.togglePanel = function(name) { - const el = document.getElementById(`panel-${name}`); - el.classList.toggle('hidden'); -}; - -// ── Alpine file tree component ──────────────────────────────────────────────── - -Alpine.data('fileTree', (slug) => ({ - tree: [], - loading: true, - slug, - - async init() { - await this.refresh(); - }, - - async refresh() { - this.loading = true; - const res = await api(`${BASE}/files`); - const data = await res.json(); - this.tree = data; - this.loading = false; - }, - - openFile(path, name) { - openFile(path, name); - }, - - async promptNewFile() { - const name = prompt('File name (e.g. about.html):'); - if (!name) return; - - await api(`${BASE}/files/create`, { - method: 'POST', - body: JSON.stringify({ path: name }), - }); - await this.refresh(); - openFile(name, name); - }, - - async promptNewFolder() { - const name = prompt('Folder name:'); - if (!name) return; - - await api(`${BASE}/files/folder`, { - method: 'POST', - body: JSON.stringify({ path: name }), - }); - await this.refresh(); - }, - - async uploadFiles(event) { - const files = Array.from(event.target.files); - for (const file of files) { - const fd = new FormData(); - fd.append('file', file); - fd.append('_token', CSRF); - - await fetch(`${BASE}/files`, { method: 'POST', body: fd }); - } - await this.refresh(); - event.target.value = ''; - }, - - async deleteItem(path, type) { - const label = type === 'directory' ? 'folder and all its contents' : 'file'; - if (!confirm(`Delete this ${label}?`)) return; - - await api(`${BASE}/files`, { - method: 'DELETE', - body: JSON.stringify({ path }), - }); - await this.refresh(); - }, - - async renameItem(path, currentName) { - const newName = prompt('New name:', currentName); - if (!newName || newName === currentName) return; - - const dir = path.includes('/') ? path.rsplit('/', 1)[0] + '/' : ''; - const to = dir + newName; - - await api(`${BASE}/files/rename`, { - method: 'PUT', - body: JSON.stringify({ from: path, to }), - }); - await this.refresh(); - }, - - renderItem(item, depth) { - const indent = depth * 14; - const isIndex = item.name === 'index.html'; - const icon = item.type === 'directory' ? '📁' : fileIcon(item.ext); - - const highlight = isIndex - ? 'text-yellow-300 font-semibold' - : 'text-gray-300 hover:text-white'; - - if (item.type === 'directory') { - let children = ''; - if (item.children && item.children.length) { - children = item.children - .map(c => this.renderItem(c, depth + 1)) - .join(''); - } - return ` -
-
- ${icon} - ${item.name} - -
-
${children}
-
`; - } - - return ` -
- ${icon} - ${item.name} - -
`; - }, -})); - -// Bridge: called from rendered HTML strings -window._fileTree_openFile = openFile; - -// ── Helper: file icons ──────────────────────────────────────────────────────── - -function fileIcon(ext) { - const map = { - html: '🌐', css: '🎨', js: '⚡', json: '{}', txt: '📄', - xml: '📋', svg: '🖼', md: '📝', - png: '🖼', jpg: '🖼', jpeg: '🖼', gif: '🖼', webp: '🖼', ico: '🖼', - mp4: '🎬', webm: '🎬', mp3: '🎵', wav: '🎵', ogg: '🎵', - pdf: '📕', woff: '🔤', woff2: '🔤', - }; - return map[ext] ?? '📄'; -} - -// ── Boot Alpine ─────────────────────────────────────────────────────────────── -window.Alpine = Alpine; -Alpine.start(); diff --git a/resources/views/auth/confirm-password.blade.php b/resources/views/auth/confirm-password.blade.php new file mode 100644 index 0000000..8389090 --- /dev/null +++ b/resources/views/auth/confirm-password.blade.php @@ -0,0 +1,30 @@ + + Confirm Password + +

Confirm your password

+

+ This area requires you to confirm your password before continuing. +

+ +
+ @csrf + +
+ + + @error('password') +

{{ $message }}

+ @enderror +
+ + +
+
diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php new file mode 100644 index 0000000..9a58e51 --- /dev/null +++ b/resources/views/auth/forgot-password.blade.php @@ -0,0 +1,39 @@ + + Forgot Password + + ← Back to login + + +

Reset your password

+

+ Enter your email and we'll send you a reset link. +

+ + @if (session('status')) +
+ {{ session('status') }} +
+ @endif + +
+ @csrf + +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ + +
+
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..d212960 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,59 @@ + + Log In + + No account? Register + + +

Welcome back

+ + @if (session('status')) +
{{ session('status') }}
+ @endif + +
+ @csrf + +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ +
+
+ + @if (Route::has('password.request')) + + Forgot password? + + @endif +
+ + @error('password') +

{{ $message }}

+ @enderror +
+ +
+ + +
+ + +
+
diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000..1489fae --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,68 @@ + + Register + + Already have an account? Log in + + +

Create your account

+ +
+ @csrf + +
+ + + @error('name') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('password') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('password_confirmation') +

{{ $message }}

+ @enderror +
+ + +
+
diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php new file mode 100644 index 0000000..5552928 --- /dev/null +++ b/resources/views/auth/reset-password.blade.php @@ -0,0 +1,51 @@ + + Reset Password + +

Choose a new password

+ +
+ @csrf + + +
+ + + @error('email') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('password') +

{{ $message }}

+ @enderror +
+ +
+ + +
+ + +
+
diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php new file mode 100644 index 0000000..86f3870 --- /dev/null +++ b/resources/views/auth/verify-email.blade.php @@ -0,0 +1,35 @@ + + Verify Email + +

Verify your email

+

+ Thanks for signing up! Please check your email for a verification link. + If you didn't receive one, we can send another. +

+ + @if (session('status') === 'verification-link-sent') +
+ A new verification link has been sent to your email address. +
+ @endif + +
+
+ @csrf + +
+ +
+ @csrf + +
+
+
diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php new file mode 100644 index 0000000..8f6df09 --- /dev/null +++ b/resources/views/components/app-layout.blade.php @@ -0,0 +1,62 @@ +@props(['title' => config('app.name')]) + + + + + + + + {{ $title }} + @vite(['resources/css/app.css', 'resources/js/app.js']) + @livewireStyles + @stack('head') + + + + + +{{-- Flash messages --}} +@if (session('success')) +
+ {{ session('success') }} + +
+@endif + +@if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+@endif + +
{{ $slot }}
+ +@livewireScripts +@stack('scripts') + + diff --git a/resources/views/components/auth-layout.blade.php b/resources/views/components/auth-layout.blade.php new file mode 100644 index 0000000..3a6a1e2 --- /dev/null +++ b/resources/views/components/auth-layout.blade.php @@ -0,0 +1,33 @@ +@props(['title' => config('app.name'), 'footer' => null]) + + + + + + + + {{ $title }} — {{ config('app.name') }} + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+ + +
+ {{ $slot }} +
+ + @if ($footer) +

{{ $footer }}

+ @endif +
+ + + diff --git a/resources/views/layouts/auth.blade.php b/resources/views/layouts/auth.blade.php new file mode 100644 index 0000000..1cb412e --- /dev/null +++ b/resources/views/layouts/auth.blade.php @@ -0,0 +1,31 @@ + + + + + + + {{ $title ?? config('app.name') }} + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+ + +
+ {{ $slot }} +
+ + @isset($footer) +

{{ $footer }}

+ @endisset +
+ + + diff --git a/routes/auth.php b/routes/auth.php index 252fa71..71a5eb6 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -11,40 +11,60 @@ use App\Http\Controllers\Auth\VerifyEmailController; use Illuminate\Support\Facades\Route; -/* -|-------------------------------------------------------------------------- -| Auth Routes (generated by Laravel Breeze) -| Run: php artisan breeze:install blade -|-------------------------------------------------------------------------- -*/ +// ── Guest routes ────────────────────────────────────────────────────────────── Route::middleware('guest')->group(function () { - Route::get('register', [RegisteredUserController::class, 'create'])->name('register'); + + Route::get('register', [RegisteredUserController::class, 'create']) + ->name('register'); + Route::post('register', [RegisteredUserController::class, 'store']); - Route::get('login', [AuthenticatedSessionController::class, 'create'])->name('login'); + Route::get('login', [AuthenticatedSessionController::class, 'create']) + ->name('login'); + Route::post('login', [AuthenticatedSessionController::class, 'store']); - Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])->name('password.request'); - Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])->name('password.email'); + Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) + ->name('password.request'); + + Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) + ->name('password.email'); + + Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) + ->name('password.reset'); - Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])->name('password.reset'); - Route::post('reset-password', [NewPasswordController::class, 'store'])->name('password.store'); + Route::post('reset-password', [NewPasswordController::class, 'store']) + ->name('password.store'); }); +// ── Authenticated routes ────────────────────────────────────────────────────── + Route::middleware('auth')->group(function () { - Route::get('verify-email', EmailVerificationPromptController::class)->name('verification.notice'); + + // Email verification — invokable controllers (no method specified) + Route::get('verify-email', EmailVerificationPromptController::class) + ->name('verification.notice'); + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) ->middleware(['signed', 'throttle:6,1']) ->name('verification.verify'); + Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) ->middleware('throttle:6,1') ->name('verification.send'); - Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])->name('password.confirm'); + // Password confirmation + Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) + ->name('password.confirm'); + Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); - Route::put('password', [PasswordController::class, 'update'])->name('password.update'); + // Password update (from profile) + Route::put('password', [PasswordController::class, 'update']) + ->name('password.update'); - Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])->name('logout'); + // Logout + Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) + ->name('logout'); }); diff --git a/routes/console.php b/routes/console.php index 3c9adf1..c787e5f 100644 --- a/routes/console.php +++ b/routes/console.php @@ -2,7 +2,11 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Schedule; Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote'); + +// Scheduled task: clean up orphaned page storage directories once a day +Schedule::command('pages:cleanup-storage')->daily(); diff --git a/routes/web.php b/routes/web.php index 5a6a4a7..b856f24 100644 --- a/routes/web.php +++ b/routes/web.php @@ -66,4 +66,3 @@ ->name('page.path'); require __DIR__ . '/auth.php'; -require __DIR__.'/settings.php'; diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php deleted file mode 100644 index ded1b2b..0000000 --- a/tests/Feature/Auth/AuthenticationTest.php +++ /dev/null @@ -1,67 +0,0 @@ -get(route('login')); - - $response->assertOk(); -}); - -test('users can authenticate using the login screen', function () { - $user = User::factory()->create(); - - $response = $this->post(route('login.store'), [ - 'email' => $user->email, - 'password' => 'password', - ]); - - $response - ->assertSessionHasNoErrors() - ->assertRedirect(route('dashboard', absolute: false)); - - $this->assertAuthenticated(); -}); - -test('users can not authenticate with invalid password', function () { - $user = User::factory()->create(); - - $response = $this->post(route('login.store'), [ - 'email' => $user->email, - 'password' => 'wrong-password', - ]); - - $response->assertSessionHasErrorsIn('email'); - - $this->assertGuest(); -}); - -test('users with two factor enabled are redirected to two factor challenge', function () { - $this->skipUnlessFortifyFeature(Features::twoFactorAuthentication()); - - Features::twoFactorAuthentication([ - 'confirm' => true, - 'confirmPassword' => true, - ]); - - $user = User::factory()->withTwoFactor()->create(); - - $response = $this->post(route('login.store'), [ - 'email' => $user->email, - 'password' => 'password', - ]); - - $response->assertRedirect(route('two-factor.login')); - $this->assertGuest(); -}); - -test('users can logout', function () { - $user = User::factory()->create(); - - $response = $this->actingAs($user)->post(route('logout')); - - $response->assertRedirect(route('home')); - - $this->assertGuest(); -}); \ No newline at end of file diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php deleted file mode 100644 index 3203c45..0000000 --- a/tests/Feature/Auth/EmailVerificationTest.php +++ /dev/null @@ -1,72 +0,0 @@ -skipUnlessFortifyFeature(Features::emailVerification()); -}); - -test('email verification screen can be rendered', function () { - $user = User::factory()->unverified()->create(); - - $response = $this->actingAs($user)->get(route('verification.notice')); - - $response->assertOk(); -}); - -test('email can be verified', function () { - $user = User::factory()->unverified()->create(); - - Event::fake(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1($user->email)], - ); - - $response = $this->actingAs($user)->get($verificationUrl); - - Event::assertDispatched(Verified::class); - - expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); - $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); -}); - -test('email is not verified with invalid hash', function () { - $user = User::factory()->unverified()->create(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1('wrong-email')], - ); - - $this->actingAs($user)->get($verificationUrl); - - expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); -}); - -test('already verified user visiting verification link is redirected without firing event again', function () { - $user = User::factory()->create([ - 'email_verified_at' => now(), - ]); - - Event::fake(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1($user->email)], - ); - - $this->actingAs($user)->get($verificationUrl) - ->assertRedirect(route('dashboard', absolute: false).'?verified=1'); - - expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); - Event::assertNotDispatched(Verified::class); -}); \ No newline at end of file diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php deleted file mode 100644 index 514115a..0000000 --- a/tests/Feature/Auth/PasswordConfirmationTest.php +++ /dev/null @@ -1,11 +0,0 @@ -create(); - - $response = $this->actingAs($user)->get(route('password.confirm')); - - $response->assertOk(); -}); \ No newline at end of file diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php deleted file mode 100644 index 901c989..0000000 --- a/tests/Feature/Auth/PasswordResetTest.php +++ /dev/null @@ -1,65 +0,0 @@ -skipUnlessFortifyFeature(Features::resetPasswords()); -}); - -test('reset password link screen can be rendered', function () { - $response = $this->get(route('password.request')); - - $response->assertOk(); -}); - -test('reset password link can be requested', function () { - Notification::fake(); - - $user = User::factory()->create(); - - $this->post(route('password.request'), ['email' => $user->email]); - - Notification::assertSentTo($user, ResetPassword::class); -}); - -test('reset password screen can be rendered', function () { - Notification::fake(); - - $user = User::factory()->create(); - - $this->post(route('password.request'), ['email' => $user->email]); - - Notification::assertSentTo($user, ResetPassword::class, function ($notification) { - $response = $this->get(route('password.reset', $notification->token)); - - $response->assertOk(); - - return true; - }); -}); - -test('password can be reset with valid token', function () { - Notification::fake(); - - $user = User::factory()->create(); - - $this->post(route('password.request'), ['email' => $user->email]); - - Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { - $response = $this->post(route('password.update'), [ - 'token' => $notification->token, - 'email' => $user->email, - 'password' => 'password', - 'password_confirmation' => 'password', - ]); - - $response - ->assertSessionHasNoErrors() - ->assertRedirect(route('login', absolute: false)); - - return true; - }); -}); \ No newline at end of file diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php deleted file mode 100644 index c2bd3b2..0000000 --- a/tests/Feature/Auth/RegistrationTest.php +++ /dev/null @@ -1,27 +0,0 @@ -skipUnlessFortifyFeature(Features::registration()); -}); - -test('registration screen can be rendered', function () { - $response = $this->get(route('register')); - - $response->assertOk(); -}); - -test('new users can register', function () { - $response = $this->post(route('register.store'), [ - 'name' => 'John Doe', - 'email' => 'test@example.com', - 'password' => 'password', - 'password_confirmation' => 'password', - ]); - - $response->assertSessionHasNoErrors() - ->assertRedirect(route('dashboard', absolute: false)); - - $this->assertAuthenticated(); -}); \ No newline at end of file diff --git a/tests/Feature/Auth/TwoFactorChallengeTest.php b/tests/Feature/Auth/TwoFactorChallengeTest.php deleted file mode 100644 index 244f510..0000000 --- a/tests/Feature/Auth/TwoFactorChallengeTest.php +++ /dev/null @@ -1,28 +0,0 @@ -skipUnlessFortifyFeature(Features::twoFactorAuthentication()); -}); - -test('two factor challenge redirects to login when not authenticated', function () { - $response = $this->get(route('two-factor.login')); - - $response->assertRedirect(route('login')); -}); - -test('two factor challenge can be rendered', function () { - Features::twoFactorAuthentication([ - 'confirm' => true, - 'confirmPassword' => true, - ]); - - $user = User::factory()->withTwoFactor()->create(); - - $this->post(route('login.store'), [ - 'email' => $user->email, - 'password' => 'password', - ])->assertRedirect(route('two-factor.login')); -}); \ No newline at end of file diff --git a/tests/Feature/DashboardTest.php b/tests/Feature/DashboardTest.php deleted file mode 100644 index d59d2d7..0000000 --- a/tests/Feature/DashboardTest.php +++ /dev/null @@ -1,16 +0,0 @@ -get(route('dashboard')); - $response->assertRedirect(route('login')); -}); - -test('authenticated users can visit the dashboard', function () { - $user = User::factory()->create(); - $this->actingAs($user); - - $response = $this->get(route('dashboard')); - $response->assertOk(); -}); \ No newline at end of file diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php deleted file mode 100644 index a576279..0000000 --- a/tests/Feature/ExampleTest.php +++ /dev/null @@ -1,7 +0,0 @@ -get(route('home')); - - $response->assertOk(); -}); \ No newline at end of file diff --git a/tests/Feature/FileManagerApiTest.php b/tests/Feature/FileManagerApiTest.php new file mode 100644 index 0000000..f4e1e86 --- /dev/null +++ b/tests/Feature/FileManagerApiTest.php @@ -0,0 +1,187 @@ +user = User::factory()->create(); + $this->page = Page::factory()->for($this->user)->create(['slug' => 'testsite']); + Storage::disk('pages')->put('testsite/index.html', ''); + } + + // ── File listing ────────────────────────────────────────────────────────── + + public function test_can_list_files(): void + { + $this->actingAs($this->user) + ->getJson(route('pages.files.list', 'testsite')) + ->assertOk() + ->assertJsonFragment(['name' => 'index.html']); + } + + public function test_list_requires_ownership(): void + { + $other = User::factory()->create(); + $this->actingAs($other) + ->getJson(route('pages.files.list', 'testsite')) + ->assertNotFound(); + } + + // ── Read ────────────────────────────────────────────────────────────────── + + public function test_can_read_file_content(): void + { + $this->actingAs($this->user) + ->getJson(route('pages.files.read', 'testsite') . '?path=index.html') + ->assertOk() + ->assertJsonPath('path', 'index.html') + ->assertJsonStructure(['content', 'path']); + } + + public function test_read_nonexistent_file_returns_error(): void + { + $this->actingAs($this->user) + ->getJson(route('pages.files.read', 'testsite') . '?path=ghost.html') + ->assertStatus(422); + } + + // ── Save ────────────────────────────────────────────────────────────────── + + public function test_can_save_file_content(): void + { + $this->actingAs($this->user) + ->putJson(route('pages.files.save', 'testsite'), [ + 'path' => 'index.html', + 'content' => 'Updated', + ]) + ->assertOk() + ->assertJsonPath('ok', true); + + $this->assertStringContainsString( + 'Updated', + Storage::disk('pages')->get('testsite/index.html') + ); + } + + public function test_save_rejects_path_traversal(): void + { + $this->actingAs($this->user) + ->putJson(route('pages.files.save', 'testsite'), [ + 'path' => '../other-site/index.html', + 'content' => 'evil', + ]) + ->assertStatus(422); + } + + // ── Create ──────────────────────────────────────────────────────────────── + + public function test_can_create_new_file(): void + { + $this->actingAs($this->user) + ->postJson(route('pages.files.create', 'testsite'), ['path' => 'about.html']) + ->assertOk() + ->assertJsonPath('ok', true); + + $this->assertTrue(Storage::disk('pages')->exists('testsite/about.html')); + } + + public function test_creating_duplicate_file_returns_error(): void + { + $this->actingAs($this->user) + ->postJson(route('pages.files.create', 'testsite'), ['path' => 'index.html']) + ->assertStatus(422); + } + + // ── Upload ──────────────────────────────────────────────────────────────── + + public function test_can_upload_image(): void + { + $file = UploadedFile::fake()->image('logo.png'); + + $this->actingAs($this->user) + ->post(route('pages.files.upload', 'testsite'), ['file' => $file]) + ->assertOk() + ->assertJsonPath('ok', true); + + $this->assertTrue(Storage::disk('pages')->exists('testsite/logo.png')); + } + + public function test_upload_rejects_disallowed_extension(): void + { + $file = UploadedFile::fake()->create('shell.php', 10, 'text/plain'); + + $this->actingAs($this->user) + ->post(route('pages.files.upload', 'testsite'), ['file' => $file]) + ->assertStatus(422); + } + + // ── Delete ──────────────────────────────────────────────────────────────── + + public function test_can_delete_file(): void + { + Storage::disk('pages')->put('testsite/old.css', 'p{}'); + + $this->actingAs($this->user) + ->deleteJson(route('pages.files.delete', 'testsite'), ['path' => 'old.css']) + ->assertOk(); + + $this->assertFalse(Storage::disk('pages')->exists('testsite/old.css')); + } + + public function test_can_delete_folder(): void + { + Storage::disk('pages')->makeDirectory('testsite/old-dir'); + Storage::disk('pages')->put('testsite/old-dir/file.txt', 'x'); + + $this->actingAs($this->user) + ->deleteJson(route('pages.files.delete', 'testsite'), ['path' => 'old-dir']) + ->assertOk(); + + $this->assertFalse(Storage::disk('pages')->directoryExists('testsite/old-dir')); + } + + // ── Folder ──────────────────────────────────────────────────────────────── + + public function test_can_create_folder(): void + { + $this->actingAs($this->user) + ->postJson(route('pages.files.folder', 'testsite'), ['path' => 'assets']) + ->assertOk(); + + $this->assertTrue(Storage::disk('pages')->directoryExists('testsite/assets')); + } + + // ── Rename ──────────────────────────────────────────────────────────────── + + public function test_can_rename_file(): void + { + Storage::disk('pages')->put('testsite/old.txt', 'content'); + + $this->actingAs($this->user) + ->putJson(route('pages.files.rename', 'testsite'), [ + 'from' => 'old.txt', + 'to' => 'new.txt', + ]) + ->assertOk(); + + $this->assertFalse(Storage::disk('pages')->exists('testsite/old.txt')); + $this->assertTrue(Storage::disk('pages')->exists('testsite/new.txt')); + } +} diff --git a/tests/Feature/PageServeTest.php b/tests/Feature/PageServeTest.php new file mode 100644 index 0000000..da168d9 --- /dev/null +++ b/tests/Feature/PageServeTest.php @@ -0,0 +1,132 @@ +create(['slug' => 'hello', 'is_public' => true]); + Storage::disk('pages')->put('hello/index.html', 'Hi'); + + $this->get('/hello') + ->assertOk() + ->assertSee('Hi'); + } + + public function test_base_tag_is_injected_into_html(): void + { + $page = Page::factory()->create(['slug' => 'demo', 'is_public' => true]); + Storage::disk('pages')->put('demo/index.html', 'Test'); + + $response = $this->get('/demo'); + $content = $response->content(); + + $this->assertStringContainsString(' 'test', 'is_public' => true]); + Storage::disk('pages')->put('test/index.html', 'X'); + + $html = $this->get('/test')->content(); + // must come right after + $this->assertMatchesRegularExpression('/]*>\s*create(['slug' => 'assets', 'is_public' => true]); + Storage::disk('pages')->put('assets/style.css', 'body { color: red; }'); + + $this->get('/assets/style.css') + ->assertOk() + ->assertHeader('Content-Type', 'text/css'); + } + + public function test_nested_file_is_served(): void + { + Page::factory()->create(['slug' => 'nested', 'is_public' => true]); + Storage::disk('pages')->put('nested/js/app.js', 'console.log("hi")'); + + $this->get('/nested/js/app.js') + ->assertOk() + ->assertSee('console.log'); + } + + public function test_directory_path_resolves_to_index_html(): void + { + Page::factory()->create(['slug' => 'sub', 'is_public' => true]); + Storage::disk('pages')->put('sub/about/index.html', 'About'); + + $this->get('/sub/about') + ->assertOk() + ->assertSee('About'); + } + + public function test_missing_file_returns_404(): void + { + Page::factory()->create(['slug' => 'exists', 'is_public' => true]); + + $this->get('/exists/nope.html')->assertNotFound(); + } + + // ── Private pages ───────────────────────────────────────────────────────── + + public function test_private_page_returns_403_for_guests(): void + { + $page = Page::factory()->create(['slug' => 'secret', 'is_public' => false]); + Storage::disk('pages')->put('secret/index.html', 'Secret'); + + $this->get('/secret')->assertForbidden(); + } + + public function test_private_page_is_accessible_to_authenticated_user(): void + { + $user = User::factory()->create(); + Page::factory()->for($user)->create(['slug' => 'private', 'is_public' => false]); + Storage::disk('pages')->put('private/index.html', 'Private'); + + $this->actingAs($user) + ->get('/private') + ->assertOk(); + } + + // ── Path traversal protection ───────────────────────────────────────────── + + public function test_path_traversal_is_blocked(): void + { + Page::factory()->create(['slug' => 'pwned', 'is_public' => true]); + + // Try to escape the page directory via encoded traversal + $this->get('/pwned/../../../etc/passwd')->assertNotFound(); + } + + // ── HTML-less documents ─────────────────────────────────────────────────── + + public function test_base_tag_prepended_when_no_head_tag(): void + { + Page::factory()->create(['slug' => 'nohead', 'is_public' => true]); + Storage::disk('pages')->put('nohead/index.html', '

Minimal

'); + + $html = $this->get('/nohead')->content(); + $this->assertStringStartsWith('get(route('dashboard'))->assertRedirect(route('login')); + } + + public function test_dashboard_shows_user_pages(): void + { + $user = User::factory()->create(); + $other = User::factory()->create(); + + $mine = Page::factory()->for($user)->create(['name' => 'My Page']); + $theirs = Page::factory()->for($other)->create(['name' => 'Their Page']); + + $this->actingAs($user) + ->get(route('dashboard')) + ->assertOk() + ->assertSee('My Page') + ->assertDontSee('Their Page'); + } + + // ── Create ──────────────────────────────────────────────────────────────── + + public function test_create_page_form_renders(): void + { + $this->actingAs(User::factory()->create()) + ->get(route('pages.create')) + ->assertOk(); + } + + public function test_user_can_create_page_with_auto_slug(): void + { + $user = User::factory()->create(); + + $this->actingAs($user) + ->post(route('pages.store'), ['name' => 'My Awesome Site']) + ->assertRedirect(); + + $this->assertDatabaseHas('pages', [ + 'user_id' => $user->id, + 'name' => 'My Awesome Site', + 'slug' => 'my-awesome-site', + ]); + } + + public function test_user_can_create_page_with_custom_slug(): void + { + $user = User::factory()->create(); + + $this->actingAs($user) + ->post(route('pages.store'), ['name' => 'Test', 'slug' => 'custom-slug']) + ->assertRedirect(); + + $this->assertDatabaseHas('pages', ['slug' => 'custom-slug']); + } + + public function test_duplicate_slug_is_rejected(): void + { + $user = User::factory()->create(); + Page::factory()->for($user)->create(['slug' => 'taken']); + + $this->actingAs($user) + ->post(route('pages.store'), ['name' => 'Another', 'slug' => 'taken']) + ->assertSessionHasErrors('slug'); + } + + public function test_slug_with_invalid_chars_is_rejected(): void + { + $this->actingAs(User::factory()->create()) + ->post(route('pages.store'), ['name' => 'Test', 'slug' => 'invalid slug!']) + ->assertSessionHasErrors('slug'); + } + + // ── Update settings ─────────────────────────────────────────────────────── + + public function test_owner_can_update_page_settings(): void + { + $user = User::factory()->create(); + $page = Page::factory()->for($user)->create(['name' => 'Old', 'slug' => 'old-slug']); + + $this->actingAs($user) + ->put(route('pages.settings', 'old-slug'), [ + 'name' => 'New Name', + 'slug' => 'new-slug', + 'is_public' => true, + ]) + ->assertRedirect(route('pages.manager', 'new-slug')); + + $this->assertDatabaseHas('pages', ['name' => 'New Name', 'slug' => 'new-slug']); + } + + public function test_other_user_cannot_update_settings(): void + { + $owner = User::factory()->create(); + $other = User::factory()->create(); + $page = Page::factory()->for($owner)->create(['slug' => 'my-page']); + + $this->actingAs($other) + ->put(route('pages.settings', 'my-page'), [ + 'name' => 'Hacked', 'slug' => 'my-page', 'is_public' => true, + ]) + ->assertNotFound(); + } + + // ── Delete ──────────────────────────────────────────────────────────────── + + public function test_owner_can_delete_page(): void + { + $user = User::factory()->create(); + $page = Page::factory()->for($user)->create(['slug' => 'to-delete']); + + $this->actingAs($user) + ->delete(route('pages.destroy', 'to-delete')) + ->assertRedirect(route('dashboard')); + + $this->assertDatabaseMissing('pages', ['slug' => 'to-delete']); + } + + public function test_other_user_cannot_delete_page(): void + { + $owner = User::factory()->create(); + $other = User::factory()->create(); + Page::factory()->for($owner)->create(['slug' => 'my-page']); + + $this->actingAs($other) + ->delete(route('pages.destroy', 'my-page')) + ->assertNotFound(); + + $this->assertDatabaseHas('pages', ['slug' => 'my-page']); + } +} diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php deleted file mode 100644 index 6d30fee..0000000 --- a/tests/Feature/Settings/ProfileUpdateTest.php +++ /dev/null @@ -1,76 +0,0 @@ -actingAs($user = User::factory()->create()); - - $this->get('/settings/profile')->assertOk(); -}); - -test('profile information can be updated', function () { - $user = User::factory()->create(); - - $this->actingAs($user); - - $response = Livewire::test(Profile::class) - ->set('name', 'Test User') - ->set('email', 'test@example.com') - ->call('updateProfileInformation'); - - $response->assertHasNoErrors(); - - $user->refresh(); - - expect($user->name)->toEqual('Test User'); - expect($user->email)->toEqual('test@example.com'); - expect($user->email_verified_at)->toBeNull(); -}); - -test('email verification status is unchanged when email address is unchanged', function () { - $user = User::factory()->create(); - - $this->actingAs($user); - - $response = Livewire::test(Profile::class) - ->set('name', 'Test User') - ->set('email', $user->email) - ->call('updateProfileInformation'); - - $response->assertHasNoErrors(); - - expect($user->refresh()->email_verified_at)->not->toBeNull(); -}); - -test('user can delete their account', function () { - $user = User::factory()->create(); - - $this->actingAs($user); - - $response = Livewire::test('settings.delete-user-form') - ->set('password', 'password') - ->call('deleteUser'); - - $response - ->assertHasNoErrors() - ->assertRedirect('/'); - - expect($user->fresh())->toBeNull(); - expect(auth()->check())->toBeFalse(); -}); - -test('correct password must be provided to delete account', function () { - $user = User::factory()->create(); - - $this->actingAs($user); - - $response = Livewire::test('settings.delete-user-form') - ->set('password', 'wrong-password') - ->call('deleteUser'); - - $response->assertHasErrors(['password']); - - expect($user->fresh())->not->toBeNull(); -}); \ No newline at end of file diff --git a/tests/Feature/Settings/SecurityTest.php b/tests/Feature/Settings/SecurityTest.php deleted file mode 100644 index a31dfbf..0000000 --- a/tests/Feature/Settings/SecurityTest.php +++ /dev/null @@ -1,105 +0,0 @@ -skipUnlessFortifyFeature(Features::twoFactorAuthentication()); - - Features::twoFactorAuthentication([ - 'confirm' => true, - 'confirmPassword' => true, - ]); -}); - -test('security settings page can be rendered', function () { - $user = User::factory()->create(); - - $this->actingAs($user) - ->withSession(['auth.password_confirmed_at' => time()]) - ->get(route('security.edit')) - ->assertOk() - ->assertSee('Two-factor authentication') - ->assertSee('Enable 2FA'); -}); - -test('security settings page requires password confirmation when enabled', function () { - $user = User::factory()->create(); - - $response = $this->actingAs($user) - ->get(route('security.edit')); - - $response->assertRedirect(route('password.confirm')); -}); - -test('security settings page renders without two factor when feature is disabled', function () { - config(['fortify.features' => []]); - - $user = User::factory()->create(); - - $this->actingAs($user) - ->withSession(['auth.password_confirmed_at' => time()]) - ->get(route('security.edit')) - ->assertOk() - ->assertSee('Update password') - ->assertDontSee('Two-factor authentication'); -}); - -test('two factor authentication disabled when confirmation abandoned between requests', function () { - $user = User::factory()->create(); - - $user->forceFill([ - 'two_factor_secret' => encrypt('test-secret'), - 'two_factor_recovery_codes' => encrypt(json_encode(['code1', 'code2'])), - 'two_factor_confirmed_at' => null, - ])->save(); - - $this->actingAs($user); - - $component = Livewire::test(Security::class); - - $component->assertSet('twoFactorEnabled', false); - - $this->assertDatabaseHas('users', [ - 'id' => $user->id, - 'two_factor_secret' => null, - 'two_factor_recovery_codes' => null, - ]); -}); - -test('password can be updated', function () { - $user = User::factory()->create([ - 'password' => Hash::make('password'), - ]); - - $this->actingAs($user); - - $response = Livewire::test(Security::class) - ->set('current_password', 'password') - ->set('password', 'new-password') - ->set('password_confirmation', 'new-password') - ->call('updatePassword'); - - $response->assertHasNoErrors(); - - expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue(); -}); - -test('correct password must be provided to update password', function () { - $user = User::factory()->create([ - 'password' => Hash::make('password'), - ]); - - $this->actingAs($user); - - $response = Livewire::test(Security::class) - ->set('current_password', 'wrong-password') - ->set('password', 'new-password') - ->set('password_confirmation', 'new-password') - ->call('updatePassword'); - - $response->assertHasErrors(['current_password']); -}); \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index 3399054..ee63ad0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,14 +3,5 @@ namespace Tests; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; -use Laravel\Fortify\Features; -abstract class TestCase extends BaseTestCase -{ - protected function skipUnlessFortifyFeature(string $feature, ?string $message = null): void - { - if (! Features::enabled($feature)) { - $this->markTestSkipped($message ?? "Fortify feature [{$feature}] is not enabled."); - } - } -} +abstract class TestCase extends BaseTestCase {} diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index 27f3f87..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,5 +0,0 @@ -toBeTrue(); -}); \ No newline at end of file diff --git a/tests/Unit/FileManagerServiceTest.php b/tests/Unit/FileManagerServiceTest.php new file mode 100644 index 0000000..37e6b42 --- /dev/null +++ b/tests/Unit/FileManagerServiceTest.php @@ -0,0 +1,128 @@ +service = new FileManagerService(); + $this->page = Page::factory()->create(['slug' => 'mysite']); + Storage::disk('pages')->put('mysite/index.html', '

Hello

'); + } + + public function test_tree_returns_files(): void + { + $tree = $this->service->tree($this->page); + $names = array_column($tree, 'name'); + $this->assertContains('index.html', $names); + } + + public function test_tree_orders_dirs_before_files(): void + { + Storage::disk('pages')->makeDirectory('mysite/assets'); + Storage::disk('pages')->put('mysite/readme.txt', 'hi'); + + $tree = $this->service->tree($this->page); + $types = array_column($tree, 'type'); + + // Directories should precede files + $firstFile = array_search('file', $types); + $firstDir = array_search('directory', $types); + $this->assertLessThan($firstFile, $firstDir); + } + + public function test_can_read_file(): void + { + $content = $this->service->read($this->page, 'index.html'); + $this->assertStringContainsString('Hello', $content); + } + + public function test_read_missing_file_throws(): void + { + $this->expectException(RuntimeException::class); + $this->service->read($this->page, 'ghost.html'); + } + + public function test_can_save_file(): void + { + $this->service->save($this->page, 'index.html', '

Updated

'); + $this->assertStringContainsString('Updated', Storage::disk('pages')->get('mysite/index.html')); + } + + public function test_can_create_file(): void + { + $this->service->createFile($this->page, 'new.html'); + $this->assertTrue(Storage::disk('pages')->exists('mysite/new.html')); + } + + public function test_create_duplicate_file_throws(): void + { + $this->expectException(RuntimeException::class); + $this->service->createFile($this->page, 'index.html'); + } + + public function test_can_delete_file(): void + { + $this->service->delete($this->page, 'index.html'); + $this->assertFalse(Storage::disk('pages')->exists('mysite/index.html')); + } + + public function test_can_delete_directory(): void + { + Storage::disk('pages')->makeDirectory('mysite/subdir'); + Storage::disk('pages')->put('mysite/subdir/file.txt', 'x'); + + $this->service->delete($this->page, 'subdir'); + $this->assertFalse(Storage::disk('pages')->directoryExists('mysite/subdir')); + } + + public function test_can_create_folder(): void + { + $this->service->createFolder($this->page, 'media'); + $this->assertTrue(Storage::disk('pages')->directoryExists('mysite/media')); + } + + public function test_can_rename_file(): void + { + $this->service->rename($this->page, 'index.html', 'home.html'); + $this->assertFalse(Storage::disk('pages')->exists('mysite/index.html')); + $this->assertTrue(Storage::disk('pages')->exists('mysite/home.html')); + } + + public function test_path_traversal_is_blocked(): void + { + $this->expectException(RuntimeException::class); + $this->service->read($this->page, '../othersite/secret.txt'); + } + + public function test_upload_allowed_file(): void + { + $file = UploadedFile::fake()->image('photo.jpg'); + $this->service->upload($this->page, $file); + $this->assertTrue(Storage::disk('pages')->exists('mysite/photo.jpg')); + } + + public function test_upload_disallowed_extension_throws(): void + { + $this->expectException(RuntimeException::class); + $file = UploadedFile::fake()->create('evil.exe', 10); + $this->service->upload($this->page, $file); + } +} From 871cede7cf905b632afd9ab3fad4fa42b5075da2 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 11:57:01 +0100 Subject: [PATCH 4/9] fix: Livewire FileManager component --- app/Livewire/FileManager.php | 81 ++-- .../views/components/app-layout.blade.php | 1 + resources/views/layouts/app.blade.php | 1 + resources/views/layouts/auth.blade.php | 1 + .../views/livewire/file-manager.blade.php | 397 ++++++++++-------- 5 files changed, 275 insertions(+), 206 deletions(-) diff --git a/app/Livewire/FileManager.php b/app/Livewire/FileManager.php index db3f7f5..df9a267 100644 --- a/app/Livewire/FileManager.php +++ b/app/Livewire/FileManager.php @@ -15,16 +15,16 @@ class FileManager extends Component // ─── Public state ───────────────────────────────────────────────────────── - public Page $page; - public string $activeFile = ''; - public string $fileContent = ''; - public bool $isDirty = false; - public string $newFileName = ''; - public string $newFolderName = ''; - public string $currentDir = ''; // relative inside the page root - public ?string $renameTarget = null; - public string $renameTo = ''; - public mixed $upload = null; + public Page $page; + public string $activeFile = ''; + public string $fileContent = ''; + public bool $isDirty = false; + public string $newFileName = ''; + public string $newFolderName = ''; + public string $currentDir = ''; + public ?string $renameTarget = null; + public string $renameTo = ''; + public mixed $upload = null; // Settings panel public string $pageName = ''; @@ -32,9 +32,6 @@ class FileManager extends Component public bool $pageIsPublic = false; public bool $showSettings = false; - // Status bar - public string $flash = ''; - // ─── Lifecycle ──────────────────────────────────────────────────────────── public function mount(string $slug): void @@ -48,20 +45,18 @@ public function mount(string $slug): void $this->pageSlug = $this->page->slug; $this->pageIsPublic = $this->page->is_public; - // Auto-open index.html if ($this->disk()->exists($this->page->storagePath('index.html'))) { $this->openFile('index.html'); } } - // ─── Computed properties ────────────────────────────────────────────────── + // ─── Computed ───────────────────────────────────────────────────────────── #[Computed] public function fileTree(): array { $dir = $this->page->storagePath($this->currentDir ?: ''); $prefix = $this->page->storagePath() . '/'; - return $this->buildTree($dir, $prefix); } @@ -126,7 +121,7 @@ public function isPreviewable(): bool { return in_array( strtolower(pathinfo($this->activeFile, PATHINFO_EXTENSION)), - ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf'], + ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'ico', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf'], true ); } @@ -145,7 +140,7 @@ public function openFile(string $path): void $full = $this->page->storagePath($path); if (! $this->disk()->exists($full)) { - $this->flash = "File not found: {$path}"; + $this->showFlash("File not found: {$path}"); return; } @@ -168,19 +163,23 @@ public function saveFile(string $content): void $this->fileContent = $content; $this->isDirty = false; - $this->flash = 'Saved ✓'; + $this->showFlash('Saved ✓'); } public function createFile(): void { $name = $this->sanitiseName($this->newFileName); - if ($name === '') { $this->flash = 'File name cannot be empty.'; return; } + + if ($name === '') { + $this->showFlash('File name cannot be empty.'); + return; + } $relative = $this->buildRelative($name); $full = $this->page->storagePath($relative); if ($this->disk()->exists($full)) { - $this->flash = 'A file with that name already exists.'; + $this->showFlash('A file with that name already exists.'); return; } @@ -193,13 +192,17 @@ public function createFile(): void public function createFolder(): void { $name = $this->sanitiseName($this->newFolderName); - if ($name === '') { $this->flash = 'Folder name cannot be empty.'; return; } + + if ($name === '') { + $this->showFlash('Folder name cannot be empty.'); + return; + } $relative = $this->buildRelative($name); $this->disk()->makeDirectory($this->page->storagePath($relative)); $this->newFolderName = ''; - $this->flash = "Folder '{$name}' created."; + $this->showFlash("Folder '{$name}' created."); unset($this->fileTree); } @@ -217,9 +220,10 @@ public function deleteFile(string $path): void if ($this->activeFile === $path) { $this->activeFile = ''; $this->fileContent = ''; + $this->isDirty = false; } - $this->flash = "Deleted {$path}."; + $this->showFlash("Deleted {$path}."); unset($this->fileTree); } @@ -272,18 +276,14 @@ public function goUp(): void public function uploadFile(): void { $maxKb = (int) config('filesystems.max_upload_mb', 50) * 1024; - - $this->validate([ - 'upload' => "required|file|max:{$maxKb}", - ]); + $this->validate(['upload' => "required|file|max:{$maxKb}"]); $name = $this->sanitiseName($this->upload->getClientOriginalName()); $storeDir = $this->page->storagePath($this->currentDir ?: ''); - $this->disk()->putFileAs($storeDir, $this->upload, $name); $this->upload = null; - $this->flash = "Uploaded {$name}."; + $this->showFlash("Uploaded {$name}."); unset($this->fileTree); } @@ -297,7 +297,6 @@ public function saveSettings(): void 'pageIsPublic' => 'boolean', ]); - // The Page model's `updating` event handles directory rename automatically. $this->page->update([ 'name' => $this->pageName, 'slug' => $this->pageSlug, @@ -306,14 +305,30 @@ public function saveSettings(): void $this->page->refresh(); $this->showSettings = false; - $this->flash = 'Settings saved.'; + $this->showFlash('Settings saved.'); if ($this->page->wasChanged('slug')) { $this->redirect(route('pages.manager', $this->page->slug), navigate: true); } } - // ─── Security / helpers ─────────────────────────────────────────────────── + public function deletePage(): void + { + $this->page->delete(); + $this->redirect(route('dashboard'), navigate: true); + } + + // ─── Helpers ────────────────────────────────────────────────────────────── + + /** + * Set flash message AND dispatch a browser event for Alpine to catch. + * Using dispatch() avoids the @livewire Blade directive collision. + */ + private function showFlash(string $message): void + { + $this->flash = $message; + $this->dispatch('show-flash', message: $message); + } private function disk(): \Illuminate\Contracts\Filesystem\Filesystem { diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php index 8f6df09..b043afc 100644 --- a/resources/views/components/app-layout.blade.php +++ b/resources/views/components/app-layout.blade.php @@ -7,6 +7,7 @@ {{ $title }} + @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles @stack('head') diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 0391011..a7ddf7d 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -5,6 +5,7 @@ {{ $title ?? config('app.name') }} + @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles @stack('head') diff --git a/resources/views/layouts/auth.blade.php b/resources/views/layouts/auth.blade.php index 1cb412e..6c46d6f 100644 --- a/resources/views/layouts/auth.blade.php +++ b/resources/views/layouts/auth.blade.php @@ -5,6 +5,7 @@ {{ $title ?? config('app.name') }} + @vite(['resources/css/app.css', 'resources/js/app.js']) diff --git a/resources/views/livewire/file-manager.blade.php b/resources/views/livewire/file-manager.blade.php index 115a0c7..1f3cdd5 100644 --- a/resources/views/livewire/file-manager.blade.php +++ b/resources/views/livewire/file-manager.blade.php @@ -1,141 +1,164 @@ {{-- File Manager — Full-screen IDE layout --}} - -@push('head') -{{-- CodeMirror 6 via CDN --}} - -@endpush +{{-- + NOTE: Alpine event listeners use the x-on: prefix throughout this template. + Do NOT use the @ shorthand for Alpine here — Livewire registers @livewire + as a Blade directive, and other @ names can conflict in edge cases. + Livewire flash events use $this->dispatch('show-flash', message: '...') + and are caught by x-on:show-flash.window below. +--}}
- {{-- ── Flash notification ───────────────────────────────────────────── --}} + {{-- ── Flash toast ─────────────────────────────────────────────────────── --}} - {{-- ── Sidebar / File Tree ──────────────────────────────────────────── --}} + {{-- ── Left sidebar: file tree ─────────────────────────────────────────── --}} - {{-- ── Editor / Preview pane ───────────────────────────────────────── --}} -
+ {{-- ── Center: editor / preview pane ──────────────────────────────────── --}} +
{{-- Toolbar --}} -
- +
+ {{ $activeFile ?: 'No file open' }} @@ -145,39 +168,53 @@ class="text-xs bg-[#313244] hover:bg-[#45475a] px-2 rounded transition">+DisEditable && $activeFile) - @endif - Preview ↗
- {{-- Editor --}} + {{-- CodeMirror host --}} @if ($this->isEditable && $activeFile) -
+
+ + {{-- Binary / media preview --}} @elseif ($this->isPreviewable && $activeFile) @php $ext = strtolower(pathinfo($activeFile, PATHINFO_EXTENSION)); @endphp -
- @if (in_array($ext, ['png','jpg','jpeg','gif','webp','svg'])) - +
+ @if (in_array($ext, ['png','jpg','jpeg','gif','webp','svg','ico'])) + @elseif (in_array($ext, ['mp4','webm'])) - + @elseif (in_array($ext, ['mp3','wav','ogg'])) @elseif ($ext === 'pdf') - + + @else +
+

📎

+

{{ basename($activeFile) }}

+

Binary file — cannot be previewed

+
@endif
+ + {{-- Non-previewable binary --}} @elseif ($activeFile)
Binary file — not editable.
+ + {{-- Nothing open --}} @else

👈

@@ -187,53 +224,78 @@ class="text-xs bg-[#cba6f7] hover:bg-[#d6acff] text-[#1e1e2e] font-medium px-3 p
- {{-- ── Settings slide-over ─────────────────────────────────────────── --}} + {{-- ── Settings slide-over ─────────────────────────────────────────────── --}} @if ($showSettings) -
+
+
-

Page Settings

- +

Page Settings

+
-
+
+
- @error('pageName')

{{ $message }}

@enderror + class="w-full bg-[#313244] text-white text-sm rounded px-3 py-2 + focus:outline-none focus:ring-1 focus:ring-[#cba6f7]"> + @error('pageName') +

{{ $message }}

+ @enderror
- @error('pageSlug')

{{ $message }}

@enderror -

{{ config('app.base_domain') }}/{{ $pageSlug }}

+ class="w-full bg-[#313244] text-white text-sm rounded px-3 py-2 font-mono + focus:outline-none focus:ring-1 focus:ring-[#cba6f7]"> + @error('pageSlug') +

{{ $message }}

+ @enderror +

+ {{ config('app.base_domain') }}/{{ $pageSlug }} +

-
+
- + +
+ + +
-
- -
-
- @csrf @method('DELETE') - -
-
+
@@ -241,22 +303,27 @@ class="w-full bg-[#cba6f7] hover:bg-[#d6acff] text-[#1e1e2e] font-semibold text-
-{{-- ── CodeMirror 6 script ──────────────────────────────────────────────── --}} +{{-- ── CodeMirror 6 (loaded via ESM import map in manager.blade.php head) ── --}} From bcc6a423abd0ca145311f1290dc853d1fbe7a589 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 16:22:38 +0100 Subject: [PATCH 5/9] fix: tailwind stuff ... --- resources/css/app.css | 67 +++++++++++++++++-- .../views/components/app-layout.blade.php | 1 - resources/views/layouts/app.blade.php | 1 - resources/views/layouts/auth.blade.php | 1 - 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/resources/css/app.css b/resources/css/app.css index 86477d5..5b11777 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,8 +1,65 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import 'tailwindcss'; +@import '../../vendor/livewire/flux/dist/flux.css'; -/* CodeMirror 6 host element must fill its container */ +@source '../views'; +@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; +@source '../../vendor/livewire/flux-pro/stubs/**/*.blade.php'; +@source '../../vendor/livewire/flux/stubs/**/*.blade.php'; + +@custom-variant dark (&:where(.dark, .dark *)); + +@theme { + --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --color-zinc-50: #fafafa; + --color-zinc-100: #f5f5f5; + --color-zinc-200: #e5e5e5; + --color-zinc-300: #d4d4d4; + --color-zinc-400: #a3a3a3; + --color-zinc-500: #737373; + --color-zinc-600: #525252; + --color-zinc-700: #404040; + --color-zinc-800: #262626; + --color-zinc-900: #171717; + --color-zinc-950: #0a0a0a; + --color-accent: var(--color-neutral-800); + --color-accent-content: var(--color-neutral-800); + --color-accent-foreground: var(--color-white); +} + +@layer theme { + .dark { + --color-accent: var(--color-white); + --color-accent-content: var(--color-white); + --color-accent-foreground: var(--color-neutral-800); + } +} + +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } +} + +[data-flux-field]:not(ui-radio, ui-checkbox) { + @apply grid gap-2; +} + +[data-flux-label] { + @apply !mb-0 !leading-tight; +} + +input:focus[data-flux-control], +textarea:focus[data-flux-control], +select:focus[data-flux-control] { + @apply outline-hidden ring-2 ring-accent ring-offset-2 ring-offset-accent-foreground; +} + +/* ── CodeMirror 6 ─────────────────────────────────────────────────────────── */ .cm-editor { height: 100%; font-size: 0.85rem; @@ -10,5 +67,5 @@ .cm-scroller { overflow: auto; - font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; + font-family: ui-monospace, 'Cascadia Code', 'JetBrains Mono', monospace; } diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php index b043afc..8f6df09 100644 --- a/resources/views/components/app-layout.blade.php +++ b/resources/views/components/app-layout.blade.php @@ -7,7 +7,6 @@ {{ $title }} - @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles @stack('head') diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index a7ddf7d..0391011 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -5,7 +5,6 @@ {{ $title ?? config('app.name') }} - @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles @stack('head') diff --git a/resources/views/layouts/auth.blade.php b/resources/views/layouts/auth.blade.php index 6c46d6f..1cb412e 100644 --- a/resources/views/layouts/auth.blade.php +++ b/resources/views/layouts/auth.blade.php @@ -5,7 +5,6 @@ {{ $title ?? config('app.name') }} - @vite(['resources/css/app.css', 'resources/js/app.js']) From 6cae9b54328b0f61535773c3b10e85c7e7a44e46 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 16:26:10 +0100 Subject: [PATCH 6/9] [BROKEN, W.I.P] Views rework --- resources/views/.DS_Store | Bin 0 -> 6148 bytes .../views/auth/confirm-password.blade.php | 33 +++--- .../views/auth/forgot-password.blade.php | 46 ++++---- resources/views/auth/login.blade.php | 74 ++++++------- resources/views/auth/register.blade.php | 86 +++++++-------- resources/views/auth/reset-password.blade.php | 62 +++++------ resources/views/auth/verify-email.blade.php | 33 +++--- .../views/components/app-layout.blade.php | 100 ++++++++++-------- .../views/components/auth-layout.blade.php | 26 +++-- resources/views/layouts/app.blade.php | 56 ---------- resources/views/layouts/auth.blade.php | 31 ------ resources/views/pages/create.blade.php | 65 ++++++------ resources/views/pages/dashboard.blade.php | 71 +++++++------ resources/views/welcome.blade.php | 59 ++++++----- 14 files changed, 307 insertions(+), 435 deletions(-) create mode 100644 resources/views/.DS_Store delete mode 100644 resources/views/layouts/app.blade.php delete mode 100644 resources/views/layouts/auth.blade.php diff --git a/resources/views/.DS_Store b/resources/views/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cd1b8cfbaa11f1c468428a70d6a04fab2ee585bf GIT binary patch literal 6148 zcmeHKyG{c!5S)c8BGRO!^e^xSrzm^@KL8|16oErZ(qF}Q@oAZT2qijQNrPsk-Lcm@ zwmik{8Gx-%y9ZzeU_p1pw}+|uy8Fa#D&uIr$L_G*zYfEGQaPV+Zig3~aK!s1{|;LW z_`nzAC*J5s94-sy_tc6MkOERb3P=GdFs}f6w%cNvs3--bfE4&uz`qZT?${TOiSg-R zh$jHCV>*oEcuNqQCy0IFn8*yxl1faf)f2;#&U~x7zHm%TIxJ4+K6SFy3&rAe=3A7* z`b0%3AO)@!xXtOx`~M?-=KX(7(o70Sfq$idb(^QnnpdjcI=P(p+D3n-d(98sjpLv& mL^~!%JLbgO@qH9!UGp*5`@%6X=*$P5sGkAXMJ5IQT7ffr#T$$O literal 0 HcmV?d00001 diff --git a/resources/views/auth/confirm-password.blade.php b/resources/views/auth/confirm-password.blade.php index 8389090..fe7950a 100644 --- a/resources/views/auth/confirm-password.blade.php +++ b/resources/views/auth/confirm-password.blade.php @@ -1,30 +1,25 @@ - - Confirm Password + -

Confirm your password

-

- This area requires you to confirm your password before continuing. + Confirm your password +

+ Please confirm your password before continuing.

-
+ @csrf -
- - + + Password + @error('password') -

{{ $message }}

+ {{ $message }} @enderror -
+ - +
+
diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php index 9a58e51..6d2ad83 100644 --- a/resources/views/auth/forgot-password.blade.php +++ b/resources/views/auth/forgot-password.blade.php @@ -1,39 +1,35 @@ - - Forgot Password - - ← Back to login - + -

Reset your password

-

- Enter your email and we'll send you a reset link. + Reset your password +

+ Enter your email and we'll send a reset link.

@if (session('status')) -
+ {{ session('status') }} -
+ @endif -
+ @csrf -
- - + + Email address + @error('email') -

{{ $message }}

+ {{ $message }} @enderror -
+ - + + Send reset link +
+ +

+ ← Back to login +

+
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index d212960..bfb627e 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -1,59 +1,51 @@ - - Log In - - No account? Register - + -

Welcome back

+ Welcome back @if (session('status')) -
{{ session('status') }}
+ + {{ session('status') }} + @endif -
+ @csrf -
- - + + Email address + @error('email') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
-
- + +
+ Password @if (Route::has('password.request')) - + Forgot password? - + @endif
- + @error('password') -

{{ $message }}

+ {{ $message }} @enderror -
- -
- - -
- - + + + + + + Log in + + +

+ No account? + Register +

+ diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 1489fae..842ce4a 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -1,68 +1,54 @@ - - Register - - Already have an account? Log in - + -

Create your account

+ Create your account -
+ @csrf -
- - + + Name + @error('name') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- - + + Email address + @error('email') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- - + + Password + @error('password') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- - + + Confirm password + @error('password_confirmation') -

{{ $message }}

+ {{ $message }} @enderror -
+ - + + Create account +
+ +

+ Already have an account? + Log in +

+
diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php index 5552928..0df05cb 100644 --- a/resources/views/auth/reset-password.blade.php +++ b/resources/views/auth/reset-password.blade.php @@ -1,51 +1,39 @@ - - Reset Password + -

Choose a new password

+ Choose a new password -
+ @csrf -
- - + + Email address + @error('email') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- - + + New password + @error('password') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- - -
+ + Confirm new password + + - + + Reset password +
+
diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php index 86f3870..e0f9608 100644 --- a/resources/views/auth/verify-email.blade.php +++ b/resources/views/auth/verify-email.blade.php @@ -1,35 +1,30 @@ - - Verify Email + -

Verify your email

-

- Thanks for signing up! Please check your email for a verification link. - If you didn't receive one, we can send another. + Verify your email +

+ Check your inbox for a verification link. Didn't get one?

@if (session('status') === 'verification-link-sent') -
+ A new verification link has been sent to your email address. -
+ @endif -
+
@csrf - + + Resend verification email +
@csrf - + + Log out +
+ diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php index 8f6df09..9f81c79 100644 --- a/resources/views/components/app-layout.blade.php +++ b/resources/views/components/app-layout.blade.php @@ -1,7 +1,7 @@ @props(['title' => config('app.name')]) - + @@ -9,54 +9,62 @@ {{ $title }} @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles + @fluxStyles @stack('head') - - - - -{{-- Flash messages --}} -@if (session('success')) -
- {{ session('success') }} - -
-@endif - -@if ($errors->any()) -
-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
-@endif - -
{{ $slot }}
- -@livewireScripts -@stack('scripts') + + + @if (session('success')) +
+ {{ session('success') }} + +
+ @endif + + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
{{ $slot }}
+ + @livewireScripts + @fluxScripts + @stack('scripts') diff --git a/resources/views/components/auth-layout.blade.php b/resources/views/components/auth-layout.blade.php index 3a6a1e2..d627789 100644 --- a/resources/views/components/auth-layout.blade.php +++ b/resources/views/components/auth-layout.blade.php @@ -1,33 +1,31 @@ -@props(['title' => config('app.name'), 'footer' => null]) +@props(['title' => config('app.name')]) - + {{ $title }} — {{ config('app.name') }} @vite(['resources/css/app.css', 'resources/js/app.js']) + @fluxStyles - + -
-
- - {{ config('app.name') }} +
+ -
+
{{ $slot }}
- - @if ($footer) -

{{ $footer }}

- @endif
+ @fluxScripts diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php deleted file mode 100644 index 0391011..0000000 --- a/resources/views/layouts/app.blade.php +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - {{ $title ?? config('app.name') }} - @vite(['resources/css/app.css', 'resources/js/app.js']) - @livewireStyles - @stack('head') - - - - - -@if (session('success')) -
- {{ session('success') }} - -
-@endif - -@if ($errors->any()) -
-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
-@endif - -
- {{ $slot }} -
- -@livewireScripts -@stack('scripts') - - diff --git a/resources/views/layouts/auth.blade.php b/resources/views/layouts/auth.blade.php deleted file mode 100644 index 1cb412e..0000000 --- a/resources/views/layouts/auth.blade.php +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - {{ $title ?? config('app.name') }} - @vite(['resources/css/app.css', 'resources/js/app.js']) - - - -
- - -
- {{ $slot }} -
- - @isset($footer) -

{{ $footer }}

- @endisset -
- - - diff --git a/resources/views/pages/create.blade.php b/resources/views/pages/create.blade.php index 3cf1966..5e33c83 100644 --- a/resources/views/pages/create.blade.php +++ b/resources/views/pages/create.blade.php @@ -1,52 +1,49 @@ - - New Page — StaticHTMLSites + -
-

Create New Page

+
-
+ Create New Page + + @csrf -
- - + + Page Name + @error('name') -

{{ $message }}

+ {{ $message }} @enderror -
+ -
- -
- - statichtmlsites.mtex.dev/ + + + Slug + optional + +
+ + {{ config('app.base_domain') }}/ - +
+ Leave blank to auto-generate from the page name. @error('slug') -

{{ $message }}

+ {{ $message }} @enderror -
+
- - + + Create Page + + Cancel - +
+
+ diff --git a/resources/views/pages/dashboard.blade.php b/resources/views/pages/dashboard.blade.php index 3d42a6b..118a1f0 100644 --- a/resources/views/pages/dashboard.blade.php +++ b/resources/views/pages/dashboard.blade.php @@ -1,70 +1,71 @@ - - Dashboard — {{ config('app.name') }} +
@if ($pages->isEmpty()) -
+ @else
@foreach ($pages as $page) -
-
+
+ +
-

{{ $page->name }}

- + {{ $page->name }} + {{ $page->slug }}
- + {{ $page->is_public ? 'Public' : 'Private' }} - +
-

+

Updated {{ $page->updated_at->diffForHumans() }}

-
- +
+ Edit - - - Preview ↗ - + + + Preview +
@csrf @method('DELETE') - +
-
+
@endforeach
@endif +
+
diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index f56878f..451b93c 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -1,48 +1,51 @@ - + - StaticHTMLSites — Host your static pages instantly + {{ config('app.name') }} — Host your static pages instantly @vite(['resources/css/app.css']) + @fluxStyles - -
-

- StaticHTMLSites + + +
+ +

+ {{ config('app.name') }}

-

+ +

Create and host static HTML pages instantly.
Each page gets its own subdomain and path URL.

+ -
-
-
- Instant Deploy - Edit files in-browser, changes go live immediately. -
-
-
🌐
- Dual URLs - Subdomain + path-based access. Choose your style. -
-
-
🗂
- Full File Manager - Upload images, videos, code. Manage everything visually. +
+ @foreach ([ + ['⚡', 'Instant Deploy', 'Edit files in-browser — changes go live immediately.'], + ['🌐', 'Dual URLs', 'Subdomain + path-based access. Choose your style.'], + ['🗂', 'File Manager', 'Upload images, videos, code. Manage everything visually.'], + ] as [$icon, $title, $desc]) +
+
{{ $icon }}
+ {{ $title }} +

{{ $desc }}

+ @endforeach
+
+ + @fluxScripts From f189d3d535f3eed254449d27ab1d77c2f4802bbe Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 20:33:10 +0100 Subject: [PATCH 7/9] composer.jsobn broken, VIEWS UPDATED --- .../Controllers/PagePreviewController.php | 22 + app/Livewire/FileManager.php | 92 +- composer.json | 15 +- composer.lock | 9852 ----------------- package.json | 2 +- phpunit.xml | 9 +- resources/js/file-manager.js | 189 + resources/views/.DS_Store | Bin 6148 -> 0 bytes .../views/livewire/file-manager.blade.php | 938 +- .../livewire/partials/tree-item.blade.php | 136 + resources/views/pages/manager.blade.php | 34 +- resources/views/pages/preview.blade.php | 138 + routes/web.php | 16 +- vite.config.js | 25 +- 14 files changed, 1198 insertions(+), 10270 deletions(-) create mode 100644 app/Http/Controllers/PagePreviewController.php delete mode 100644 composer.lock create mode 100644 resources/js/file-manager.js delete mode 100644 resources/views/.DS_Store create mode 100644 resources/views/livewire/partials/tree-item.blade.php create mode 100644 resources/views/pages/preview.blade.php diff --git a/app/Http/Controllers/PagePreviewController.php b/app/Http/Controllers/PagePreviewController.php new file mode 100644 index 0000000..b3da9a8 --- /dev/null +++ b/app/Http/Controllers/PagePreviewController.php @@ -0,0 +1,22 @@ +firstOrFail(); + Gate::authorize('update', $page); + + return view('pages.preview', compact('page')); + } +} diff --git a/app/Livewire/FileManager.php b/app/Livewire/FileManager.php index df9a267..8112283 100644 --- a/app/Livewire/FileManager.php +++ b/app/Livewire/FileManager.php @@ -26,11 +26,15 @@ class FileManager extends Component public string $renameTo = ''; public mixed $upload = null; + // Delete modal state + public string $deleteTarget = ''; + public string $deleteTargetName = ''; + public string $deleteTargetType = 'file'; + // Settings panel public string $pageName = ''; public string $pageSlug = ''; public bool $pageIsPublic = false; - public bool $showSettings = false; // ─── Lifecycle ──────────────────────────────────────────────────────────── @@ -140,7 +144,7 @@ public function openFile(string $path): void $full = $this->page->storagePath($path); if (! $this->disk()->exists($full)) { - $this->showFlash("File not found: {$path}"); + $this->showFlash("File not found: {$path}", 'error'); return; } @@ -171,7 +175,7 @@ public function createFile(): void $name = $this->sanitiseName($this->newFileName); if ($name === '') { - $this->showFlash('File name cannot be empty.'); + $this->showFlash('File name cannot be empty.', 'error'); return; } @@ -179,7 +183,7 @@ public function createFile(): void $full = $this->page->storagePath($relative); if ($this->disk()->exists($full)) { - $this->showFlash('A file with that name already exists.'); + $this->showFlash('A file with that name already exists.', 'error'); return; } @@ -189,27 +193,71 @@ public function createFile(): void unset($this->fileTree); } - public function createFolder(): void + /** Create file and close the modal. */ + public function createFileAndClose(): void { - $name = $this->sanitiseName($this->newFolderName); + $name = $this->sanitiseName($this->newFileName); + if ($name === '') { $this->showFlash('File name cannot be empty.', 'error'); return; } - if ($name === '') { - $this->showFlash('Folder name cannot be empty.'); + $relative = $this->buildRelative($name); + $full = $this->page->storagePath($relative); + + if ($this->disk()->exists($full)) { + $this->showFlash('A file with that name already exists.', 'error'); return; } + $this->disk()->put($full, ''); + $this->newFileName = ''; + $this->dispatch('flux:modal.close', name: 'new-file'); + $this->openFile($relative); + unset($this->fileTree); + } + + public function createFolder(): void + { + $name = $this->sanitiseName($this->newFolderName); + if ($name === '') { $this->showFlash('Folder name cannot be empty.', 'error'); return; } + + $relative = $this->buildRelative($name); + $this->disk()->makeDirectory($this->page->storagePath($relative)); + + $this->newFolderName = ''; + $this->showFlash("Folder '{$name}' created."); + unset($this->fileTree); + } + + /** Create folder and close the modal. */ + public function createFolderAndClose(): void + { + $name = $this->sanitiseName($this->newFolderName); + if ($name === '') { $this->showFlash('Folder name cannot be empty.', 'error'); return; } + $relative = $this->buildRelative($name); $this->disk()->makeDirectory($this->page->storagePath($relative)); $this->newFolderName = ''; + $this->dispatch('flux:modal.close', name: 'new-folder'); $this->showFlash("Folder '{$name}' created."); unset($this->fileTree); } - public function deleteFile(string $path): void + /** Show delete confirmation modal. */ + public function prepareDelete(string $path, string $type = 'file'): void { $this->guardPath($path); - $full = $this->page->storagePath($path); + $this->deleteTarget = $path; + $this->deleteTargetName = basename($path); + $this->deleteTargetType = $type; + $this->dispatch('flux:modal.show', name: 'delete-confirm'); + } + + /** Execute the pending delete after modal confirmation. */ + public function executeDelete(): void + { + if ($this->deleteTarget === '') return; + + $full = $this->page->storagePath($this->deleteTarget); if ($this->disk()->directoryExists($full)) { $this->disk()->deleteDirectory($full); @@ -217,18 +265,23 @@ public function deleteFile(string $path): void $this->disk()->delete($full); } - if ($this->activeFile === $path) { + if ($this->activeFile === $this->deleteTarget) { $this->activeFile = ''; $this->fileContent = ''; $this->isDirty = false; } - $this->showFlash("Deleted {$path}."); + $this->showFlash("Deleted {$this->deleteTargetName}."); + $this->deleteTarget = ''; + $this->deleteTargetName = ''; + $this->dispatch('flux:modal.close', name: 'delete-confirm'); unset($this->fileTree); } - public function startRename(string $path): void + /** Prepare rename (shows inline form in sidebar). */ + public function prepareRename(string $path): void { + $this->guardPath($path); $this->renameTarget = $path; $this->renameTo = basename($path); } @@ -255,6 +308,7 @@ public function confirmRename(): void $this->renameTarget = null; $this->renameTo = ''; + $this->showFlash("Renamed to {$newName}."); unset($this->fileTree); } @@ -284,6 +338,7 @@ public function uploadFile(): void $this->upload = null; $this->showFlash("Uploaded {$name}."); + $this->dispatch('flux:modal.close', name: 'upload'); unset($this->fileTree); } @@ -304,7 +359,7 @@ public function saveSettings(): void ]); $this->page->refresh(); - $this->showSettings = false; + $this->dispatch('flux:modal.close', name: 'settings'); $this->showFlash('Settings saved.'); if ($this->page->wasChanged('slug')) { @@ -320,14 +375,9 @@ public function deletePage(): void // ─── Helpers ────────────────────────────────────────────────────────────── - /** - * Set flash message AND dispatch a browser event for Alpine to catch. - * Using dispatch() avoids the @livewire Blade directive collision. - */ - private function showFlash(string $message): void + private function showFlash(string $message, string $type = 'success'): void { - $this->flash = $message; - $this->dispatch('show-flash', message: $message); + $this->dispatch('show-flash', message: $message, type: $type); } private function disk(): \Illuminate\Contracts\Filesystem\Filesystem diff --git a/composer.json b/composer.json index 175ac39..2cfe04e 100644 --- a/composer.json +++ b/composer.json @@ -50,16 +50,9 @@ "Composer\\Config::disableProcessTimeout", "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" ], - "lint": [ - "pint --parallel" - ], - "lint:check": [ - "pint --parallel --test" - ], - "ci:check": [ - "Composer\\Config::disableProcessTimeout", - "@test" - ], + "lint": ["pint --parallel"], + "lint:check": ["pint --parallel --test"], + "ci:check": ["Composer\\Config::disableProcessTimeout", "@test"], "test": [ "@php artisan config:clear --ansi", "@lint:check", @@ -100,4 +93,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 6e9ba17..0000000 --- a/composer.lock +++ /dev/null @@ -1,9852 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "63028766c4e0986010161b1f1958f00d", - "packages": [ - { - "name": "bacon/bacon-qr-code", - "version": "v3.0.3", - "source": { - "type": "git", - "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563", - "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563", - "shasum": "" - }, - "require": { - "dasprid/enum": "^1.0.3", - "ext-iconv": "*", - "php": "^8.1" - }, - "require-dev": { - "phly/keep-a-changelog": "^2.12", - "phpunit/phpunit": "^10.5.11 || ^11.0.4", - "spatie/phpunit-snapshot-assertions": "^5.1.5", - "spatie/pixelmatch-php": "^1.2.0", - "squizlabs/php_codesniffer": "^3.9" - }, - "suggest": { - "ext-imagick": "to generate QR code images" - }, - "type": "library", - "autoload": { - "psr-4": { - "BaconQrCode\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", - "role": "Developer" - } - ], - "description": "BaconQrCode is a QR code generator for PHP.", - "homepage": "https://github.com/Bacon/BaconQrCode", - "support": { - "issues": "https://github.com/Bacon/BaconQrCode/issues", - "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3" - }, - "time": "2025-11-19T17:15:36+00:00" - }, - { - "name": "brick/math", - "version": "0.14.8", - "source": { - "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", - "shasum": "" - }, - "require": { - "php": "^8.2" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.2", - "phpstan/phpstan": "2.1.22", - "phpunit/phpunit": "^11.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Brick\\Math\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Arbitrary-precision arithmetic library", - "keywords": [ - "Arbitrary-precision", - "BigInteger", - "BigRational", - "arithmetic", - "bigdecimal", - "bignum", - "bignumber", - "brick", - "decimal", - "integer", - "math", - "mathematics", - "rational" - ], - "support": { - "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.8" - }, - "funding": [ - { - "url": "https://github.com/BenMorel", - "type": "github" - } - ], - "time": "2026-02-10T14:33:43+00:00" - }, - { - "name": "carbonphp/carbon-doctrine-types", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", - "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", - "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "conflict": { - "doctrine/dbal": "<4.0.0 || >=5.0.0" - }, - "require-dev": { - "doctrine/dbal": "^4.0.0", - "nesbot/carbon": "^2.71.0 || ^3.0.0", - "phpunit/phpunit": "^10.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "KyleKatarn", - "email": "kylekatarnls@gmail.com" - } - ], - "description": "Types to use Carbon in Doctrine", - "keywords": [ - "carbon", - "date", - "datetime", - "doctrine", - "time" - ], - "support": { - "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", - "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" - }, - "funding": [ - { - "url": "https://github.com/kylekatarnls", - "type": "github" - }, - { - "url": "https://opencollective.com/Carbon", - "type": "open_collective" - }, - { - "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", - "type": "tidelift" - } - ], - "time": "2024-02-09T16:56:22+00:00" - }, - { - "name": "dasprid/enum", - "version": "1.0.7", - "source": { - "type": "git", - "url": "https://github.com/DASPRiD/Enum.git", - "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", - "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", - "shasum": "" - }, - "require": { - "php": ">=7.1 <9.0" - }, - "require-dev": { - "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", - "squizlabs/php_codesniffer": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "DASPRiD\\Enum\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", - "role": "Developer" - } - ], - "description": "PHP 7.1 enum implementation", - "keywords": [ - "enum", - "map" - ], - "support": { - "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" - }, - "time": "2025-09-16T12:23:56+00:00" - }, - { - "name": "dflydev/dot-access-data", - "version": "v3.0.3", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", - "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.42", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", - "scrutinizer/ocular": "1.6.0", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Dflydev\\DotAccessData\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - }, - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "support": { - "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" - }, - "time": "2024-07-08T12:26:09+00:00" - }, - { - "name": "doctrine/inflector", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", - "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0 || ^13.0", - "phpstan/phpstan": "^1.12 || ^2.0", - "phpstan/phpstan-phpunit": "^1.4 || ^2.0", - "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", - "phpunit/phpunit": "^8.5 || ^12.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Inflector\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", - "homepage": "https://www.doctrine-project.org/projects/inflector.html", - "keywords": [ - "inflection", - "inflector", - "lowercase", - "manipulation", - "php", - "plural", - "singular", - "strings", - "uppercase", - "words" - ], - "support": { - "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.1.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" - } - ], - "time": "2025-08-10T19:31:58+00:00" - }, - { - "name": "doctrine/lexer", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2024-02-05T11:56:58+00:00" - }, - { - "name": "dragonmantank/cron-expression", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", - "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", - "shasum": "" - }, - "require": { - "php": "^8.2|^8.3|^8.4|^8.5" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.32|^2.1.31", - "phpunit/phpunit": "^8.5.48|^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Cron\\": "src/Cron/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" - } - ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": [ - "cron", - "schedule" - ], - "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2025-10-31T18:51:33+00:00" - }, - { - "name": "egulias/email-validator", - "version": "4.0.4", - "source": { - "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^2.0 || ^3.0", - "php": ">=8.1", - "symfony/polyfill-intl-idn": "^1.26" - }, - "require-dev": { - "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^5.12" - }, - "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Egulias\\EmailValidator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eduardo Gulias Davis" - } - ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", - "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" - ], - "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" - }, - "funding": [ - { - "url": "https://github.com/egulias", - "type": "github" - } - ], - "time": "2025-03-06T22:45:56+00:00" - }, - { - "name": "fruitcake/php-cors", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/fruitcake/php-cors.git", - "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", - "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", - "shasum": "" - }, - "require": { - "php": "^8.1", - "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" - }, - "require-dev": { - "phpstan/phpstan": "^2", - "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Fruitcake\\Cors\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fruitcake", - "homepage": "https://fruitcake.nl" - }, - { - "name": "Barryvdh", - "email": "barryvdh@gmail.com" - } - ], - "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", - "homepage": "https://github.com/fruitcake/php-cors", - "keywords": [ - "cors", - "laravel", - "symfony" - ], - "support": { - "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" - }, - "funding": [ - { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], - "time": "2025-12-03T09:33:47+00:00" - }, - { - "name": "graham-campbell/result-type", - "version": "v1.1.4", - "source": { - "type": "git", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", - "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.5" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "GrahamCampbell\\ResultType\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - } - ], - "description": "An Implementation Of The Result Type", - "keywords": [ - "Graham Campbell", - "GrahamCampbell", - "Result Type", - "Result-Type", - "result" - ], - "support": { - "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", - "type": "tidelift" - } - ], - "time": "2025-12-27T19:43:20+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "7.10.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2025-08-23T22:36:01+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2025-08-22T14:34:08+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.9.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2026-03-10T16:41:02+00:00" - }, - { - "name": "guzzlehttp/uri-template", - "version": "v1.0.5", - "source": { - "type": "git", - "url": "https://github.com/guzzle/uri-template.git", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "symfony/polyfill-php80": "^1.24" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25", - "uri-template/tests": "1.0.0" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\UriTemplate\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - } - ], - "description": "A polyfill class for uri_template of PHP", - "keywords": [ - "guzzlehttp", - "uri-template" - ], - "support": { - "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", - "type": "tidelift" - } - ], - "time": "2025-08-22T14:27:06+00:00" - }, - { - "name": "laravel/fortify", - "version": "v1.36.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/fortify.git", - "reference": "cad8bfeb63f6818f173d40090725c565c92651d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/cad8bfeb63f6818f173d40090725c565c92651d4", - "reference": "cad8bfeb63f6818f173d40090725c565c92651d4", - "shasum": "" - }, - "require": { - "bacon/bacon-qr-code": "^3.0", - "ext-json": "*", - "illuminate/console": "^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^10.0|^11.0|^12.0|^13.0", - "php": "^8.1", - "pragmarx/google2fa": "^9.0" - }, - "require-dev": { - "orchestra/testbench": "^8.36|^9.15|^10.8|^11.0", - "phpstan/phpstan": "^1.10" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Fortify\\FortifyServiceProvider" - ] - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\Fortify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Backend controllers and scaffolding for Laravel authentication.", - "keywords": [ - "auth", - "laravel" - ], - "support": { - "issues": "https://github.com/laravel/fortify/issues", - "source": "https://github.com/laravel/fortify" - }, - "time": "2026-03-10T19:59:49+00:00" - }, - { - "name": "laravel/framework", - "version": "v12.54.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/framework.git", - "reference": "325497463e7599cd14224c422c6e5dd2fe832868" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/325497463e7599cd14224c422c6e5dd2fe832868", - "reference": "325497463e7599cd14224c422c6e5dd2fe832868", - "shasum": "" - }, - "require": { - "brick/math": "^0.11|^0.12|^0.13|^0.14", - "composer-runtime-api": "^2.2", - "doctrine/inflector": "^2.0.5", - "dragonmantank/cron-expression": "^3.4", - "egulias/email-validator": "^3.2.1|^4.0", - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-session": "*", - "ext-tokenizer": "*", - "fruitcake/php-cors": "^1.3", - "guzzlehttp/guzzle": "^7.8.2", - "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.3.0", - "laravel/serializable-closure": "^1.3|^2.0", - "league/commonmark": "^2.8.1", - "league/flysystem": "^3.25.1", - "league/flysystem-local": "^3.25.1", - "league/uri": "^7.5.1", - "monolog/monolog": "^3.0", - "nesbot/carbon": "^3.8.4", - "nunomaduro/termwind": "^2.0", - "php": "^8.2", - "psr/container": "^1.1.1|^2.0.1", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", - "ramsey/uuid": "^4.7", - "symfony/console": "^7.2.0", - "symfony/error-handler": "^7.2.0", - "symfony/finder": "^7.2.0", - "symfony/http-foundation": "^7.2.0", - "symfony/http-kernel": "^7.2.0", - "symfony/mailer": "^7.2.0", - "symfony/mime": "^7.2.0", - "symfony/polyfill-php83": "^1.33", - "symfony/polyfill-php84": "^1.33", - "symfony/polyfill-php85": "^1.33", - "symfony/process": "^7.2.0", - "symfony/routing": "^7.2.0", - "symfony/uid": "^7.2.0", - "symfony/var-dumper": "^7.2.0", - "tijsverkoyen/css-to-inline-styles": "^2.2.5", - "vlucas/phpdotenv": "^5.6.1", - "voku/portable-ascii": "^2.0.2" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "provide": { - "psr/container-implementation": "1.1|2.0", - "psr/log-implementation": "1.0|2.0|3.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0" - }, - "replace": { - "illuminate/auth": "self.version", - "illuminate/broadcasting": "self.version", - "illuminate/bus": "self.version", - "illuminate/cache": "self.version", - "illuminate/collections": "self.version", - "illuminate/concurrency": "self.version", - "illuminate/conditionable": "self.version", - "illuminate/config": "self.version", - "illuminate/console": "self.version", - "illuminate/container": "self.version", - "illuminate/contracts": "self.version", - "illuminate/cookie": "self.version", - "illuminate/database": "self.version", - "illuminate/encryption": "self.version", - "illuminate/events": "self.version", - "illuminate/filesystem": "self.version", - "illuminate/hashing": "self.version", - "illuminate/http": "self.version", - "illuminate/json-schema": "self.version", - "illuminate/log": "self.version", - "illuminate/macroable": "self.version", - "illuminate/mail": "self.version", - "illuminate/notifications": "self.version", - "illuminate/pagination": "self.version", - "illuminate/pipeline": "self.version", - "illuminate/process": "self.version", - "illuminate/queue": "self.version", - "illuminate/redis": "self.version", - "illuminate/reflection": "self.version", - "illuminate/routing": "self.version", - "illuminate/session": "self.version", - "illuminate/support": "self.version", - "illuminate/testing": "self.version", - "illuminate/translation": "self.version", - "illuminate/validation": "self.version", - "illuminate/view": "self.version", - "spatie/once": "*" - }, - "require-dev": { - "ably/ably-php": "^1.0", - "aws/aws-sdk-php": "^3.322.9", - "ext-gmp": "*", - "fakerphp/faker": "^1.24", - "guzzlehttp/promises": "^2.0.3", - "guzzlehttp/psr7": "^2.4", - "laravel/pint": "^1.18", - "league/flysystem-aws-s3-v3": "^3.25.1", - "league/flysystem-ftp": "^3.25.1", - "league/flysystem-path-prefixing": "^3.25.1", - "league/flysystem-read-only": "^3.25.1", - "league/flysystem-sftp-v3": "^3.25.1", - "mockery/mockery": "^1.6.10", - "opis/json-schema": "^2.4.1", - "orchestra/testbench-core": "^10.9.0", - "pda/pheanstalk": "^5.0.6|^7.0.0", - "php-http/discovery": "^1.15", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", - "predis/predis": "^2.3|^3.0", - "resend/resend-php": "^0.10.0|^1.0", - "symfony/cache": "^7.2.0", - "symfony/http-client": "^7.2.0", - "symfony/psr-http-message-bridge": "^7.2.0", - "symfony/translation": "^7.2.0" - }, - "suggest": { - "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", - "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", - "ext-apcu": "Required to use the APC cache driver.", - "ext-fileinfo": "Required to use the Filesystem class.", - "ext-ftp": "Required to use the Flysystem FTP driver.", - "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", - "ext-memcached": "Required to use the memcache cache driver.", - "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", - "ext-pdo": "Required to use all database features.", - "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", - "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", - "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", - "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", - "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", - "mockery/mockery": "Required to use mocking (^1.6).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", - "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", - "predis/predis": "Required to use the predis connector (^2.3|^3.0).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "12.x-dev" - } - }, - "autoload": { - "files": [ - "src/Illuminate/Collections/functions.php", - "src/Illuminate/Collections/helpers.php", - "src/Illuminate/Events/functions.php", - "src/Illuminate/Filesystem/functions.php", - "src/Illuminate/Foundation/helpers.php", - "src/Illuminate/Log/functions.php", - "src/Illuminate/Reflection/helpers.php", - "src/Illuminate/Support/functions.php", - "src/Illuminate/Support/helpers.php" - ], - "psr-4": { - "Illuminate\\": "src/Illuminate/", - "Illuminate\\Support\\": [ - "src/Illuminate/Macroable/", - "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/", - "src/Illuminate/Reflection/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Laravel Framework.", - "homepage": "https://laravel.com", - "keywords": [ - "framework", - "laravel" - ], - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-03-10T20:25:56+00:00" - }, - { - "name": "laravel/prompts", - "version": "v0.3.14", - "source": { - "type": "git", - "url": "https://github.com/laravel/prompts.git", - "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/9f0e371244eedfe2ebeaa72c79c54bb5df6e0176", - "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.2", - "ext-mbstring": "*", - "php": "^8.1", - "symfony/console": "^6.2|^7.0|^8.0" - }, - "conflict": { - "illuminate/console": ">=10.17.0 <10.25.0", - "laravel/framework": ">=10.17.0 <10.25.0" - }, - "require-dev": { - "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", - "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3|^3.4|^4.0", - "phpstan/phpstan": "^1.12.28", - "phpstan/phpstan-mockery": "^1.1.3" - }, - "suggest": { - "ext-pcntl": "Required for the spinner to be animated." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "0.3.x-dev" - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Laravel\\Prompts\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Add beautiful and user-friendly forms to your command-line applications.", - "support": { - "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.14" - }, - "time": "2026-03-01T09:02:38+00:00" - }, - { - "name": "laravel/serializable-closure", - "version": "v2.0.10", - "source": { - "type": "git", - "url": "https://github.com/laravel/serializable-closure.git", - "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669", - "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "illuminate/support": "^10.0|^11.0|^12.0|^13.0", - "nesbot/carbon": "^2.67|^3.0", - "pestphp/pest": "^2.36|^3.0|^4.0", - "phpstan/phpstan": "^2.0", - "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\SerializableClosure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Nuno Maduro", - "email": "nuno@laravel.com" - } - ], - "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", - "keywords": [ - "closure", - "laravel", - "serializable" - ], - "support": { - "issues": "https://github.com/laravel/serializable-closure/issues", - "source": "https://github.com/laravel/serializable-closure" - }, - "time": "2026-02-20T19:59:49+00:00" - }, - { - "name": "laravel/tinker", - "version": "v2.11.1", - "source": { - "type": "git", - "url": "https://github.com/laravel/tinker.git", - "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", - "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", - "shasum": "" - }, - "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", - "php": "^7.2.5|^8.0", - "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" - }, - "require-dev": { - "mockery/mockery": "~1.3.3|^1.4.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" - }, - "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Tinker\\TinkerServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Tinker\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Powerful REPL for the Laravel framework.", - "keywords": [ - "REPL", - "Tinker", - "laravel", - "psysh" - ], - "support": { - "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.11.1" - }, - "time": "2026-02-06T14:12:35+00:00" - }, - { - "name": "league/commonmark", - "version": "2.8.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/commonmark.git", - "reference": "84b1ca48347efdbe775426f108622a42735a6579" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579", - "reference": "84b1ca48347efdbe775426f108622a42735a6579", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "league/config": "^1.1.1", - "php": "^7.4 || ^8.0", - "psr/event-dispatcher": "^1.0", - "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/polyfill-php80": "^1.16" - }, - "require-dev": { - "cebe/markdown": "^1.0", - "commonmark/cmark": "0.31.1", - "commonmark/commonmark.js": "0.31.1", - "composer/package-versions-deprecated": "^1.8", - "embed/embed": "^4.4", - "erusev/parsedown": "^1.0", - "ext-json": "*", - "github/gfm": "0.29.0", - "michelf/php-markdown": "^1.4 || ^2.0", - "nyholm/psr7": "^1.5", - "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", - "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", - "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" - }, - "suggest": { - "symfony/yaml": "v2.3+ required if using the Front Matter extension" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.9-dev" - } - }, - "autoload": { - "psr-4": { - "League\\CommonMark\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Lead Developer" - } - ], - "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", - "homepage": "https://commonmark.thephpleague.com", - "keywords": [ - "commonmark", - "flavored", - "gfm", - "github", - "github-flavored", - "markdown", - "md", - "parser" - ], - "support": { - "docs": "https://commonmark.thephpleague.com/", - "forum": "https://github.com/thephpleague/commonmark/discussions", - "issues": "https://github.com/thephpleague/commonmark/issues", - "rss": "https://github.com/thephpleague/commonmark/releases.atom", - "source": "https://github.com/thephpleague/commonmark" - }, - "funding": [ - { - "url": "https://www.colinodell.com/sponsor", - "type": "custom" - }, - { - "url": "https://www.paypal.me/colinpodell/10.00", - "type": "custom" - }, - { - "url": "https://github.com/colinodell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/league/commonmark", - "type": "tidelift" - } - ], - "time": "2026-03-05T21:37:03+00:00" - }, - { - "name": "league/config", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/config.git", - "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", - "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^3.0.1", - "nette/schema": "^1.2", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.5", - "scrutinizer/ocular": "^1.8.1", - "unleashedtech/php-coding-standard": "^3.1", - "vimeo/psalm": "^4.7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.2-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Lead Developer" - } - ], - "description": "Define configuration arrays with strict schemas and access values with dot notation", - "homepage": "https://config.thephpleague.com", - "keywords": [ - "array", - "config", - "configuration", - "dot", - "dot-access", - "nested", - "schema" - ], - "support": { - "docs": "https://config.thephpleague.com/", - "issues": "https://github.com/thephpleague/config/issues", - "rss": "https://github.com/thephpleague/config/releases.atom", - "source": "https://github.com/thephpleague/config" - }, - "funding": [ - { - "url": "https://www.colinodell.com/sponsor", - "type": "custom" - }, - { - "url": "https://www.paypal.me/colinpodell/10.00", - "type": "custom" - }, - { - "url": "https://github.com/colinodell", - "type": "github" - } - ], - "time": "2022-12-11T20:36:23+00:00" - }, - { - "name": "league/flysystem", - "version": "3.32.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/254b1595b16b22dbddaaef9ed6ca9fdac4956725", - "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725", - "shasum": "" - }, - "require": { - "league/flysystem-local": "^3.0.0", - "league/mime-type-detection": "^1.0.0", - "php": "^8.0.2" - }, - "conflict": { - "async-aws/core": "<1.19.0", - "async-aws/s3": "<1.14.0", - "aws/aws-sdk-php": "3.209.31 || 3.210.0", - "guzzlehttp/guzzle": "<7.0", - "guzzlehttp/ringphp": "<1.1.1", - "phpseclib/phpseclib": "3.0.15", - "symfony/http-client": "<5.2" - }, - "require-dev": { - "async-aws/s3": "^1.5 || ^2.0", - "async-aws/simple-s3": "^1.1 || ^2.0", - "aws/aws-sdk-php": "^3.295.10", - "composer/semver": "^3.0", - "ext-fileinfo": "*", - "ext-ftp": "*", - "ext-mongodb": "^1.3|^2", - "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.5", - "google/cloud-storage": "^1.23", - "guzzlehttp/psr7": "^2.6", - "microsoft/azure-storage-blob": "^1.1", - "mongodb/mongodb": "^1.2|^2", - "phpseclib/phpseclib": "^3.0.36", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.11|^10.0", - "sabre/dav": "^4.6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\Flysystem\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" - } - ], - "description": "File storage abstraction for PHP", - "keywords": [ - "WebDAV", - "aws", - "cloud", - "file", - "files", - "filesystem", - "filesystems", - "ftp", - "s3", - "sftp", - "storage" - ], - "support": { - "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.32.0" - }, - "time": "2026-02-25T17:01:41+00:00" - }, - { - "name": "league/flysystem-local", - "version": "3.31.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", - "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "league/flysystem": "^3.0.0", - "league/mime-type-detection": "^1.0.0", - "php": "^8.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\Flysystem\\Local\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" - } - ], - "description": "Local filesystem adapter for Flysystem.", - "keywords": [ - "Flysystem", - "file", - "files", - "filesystem", - "local" - ], - "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" - }, - "time": "2026-01-23T15:30:45+00:00" - }, - { - "name": "league/mime-type-detection", - "version": "1.16.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\MimeTypeDetection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" - } - ], - "description": "Mime-type detection for Flysystem", - "support": { - "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" - }, - "funding": [ - { - "url": "https://github.com/frankdejonge", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "type": "tidelift" - } - ], - "time": "2024-09-21T08:32:55+00:00" - }, - { - "name": "league/uri", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri.git", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76", - "shasum": "" - }, - "require": { - "league/uri-interfaces": "^7.8", - "php": "^8.1", - "psr/http-factory": "^1" - }, - "conflict": { - "league/uri-schemes": "^1.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-dom": "to convert the URI into an HTML anchor tag", - "ext-fileinfo": "to create Data URI from file contennts", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", - "league/uri-components": "to provide additional tools to manipulate URI objects components", - "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", - "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Uri\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" - } - ], - "description": "URI manipulation library", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "URN", - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "middleware", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc2141", - "rfc3986", - "rfc3987", - "rfc6570", - "rfc8141", - "uri", - "uri-template", - "url", - "ws" - ], - "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-01-14T17:24:56+00:00" - }, - { - "name": "league/uri-interfaces", - "version": "7.8.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^8.1", - "psr/http-message": "^1.1 || ^2.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Uri\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" - } - ], - "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc3986", - "rfc3987", - "rfc6570", - "uri", - "url", - "ws" - ], - "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-01-15T06:54:53+00:00" - }, - { - "name": "livewire/blaze", - "version": "v1.0.7", - "source": { - "type": "git", - "url": "https://github.com/livewire/blaze.git", - "reference": "20cb545a8f70a3325f17c97254bb2532a99c0731" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/livewire/blaze/zipball/20cb545a8f70a3325f17c97254bb2532a99c0731", - "reference": "20cb545a8f70a3325f17c97254bb2532a99c0731", - "shasum": "" - }, - "require": { - "illuminate/support": "^10.0|^11.0|^12.0", - "illuminate/view": "^10.0|^11.0|^12.0", - "nikic/php-parser": "^5.0", - "php": "^8.1" - }, - "conflict": { - "livewire/flux": "<2.12.1" - }, - "require-dev": { - "livewire/flux": "dev-main", - "orchestra/testbench": "^8.0|^9.0|^10.0", - "pestphp/pest": "^2.0|^3.0", - "pestphp/pest-plugin-laravel": "^2.0|^3.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Livewire\\Blaze\\BlazeServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Livewire\\Blaze\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Caleb Porzio" - } - ], - "description": "A tool for optimizing Blade component performance by folding them into parent templates", - "support": { - "issues": "https://github.com/livewire/blaze/issues", - "source": "https://github.com/livewire/blaze/tree/v1.0.7" - }, - "time": "2026-03-12T14:25:42+00:00" - }, - { - "name": "livewire/flux", - "version": "v2.13.0", - "source": { - "type": "git", - "url": "https://github.com/livewire/flux.git", - "reference": "741be2d4526e90b97c7a59e079a2f27ecdce2461" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/livewire/flux/zipball/741be2d4526e90b97c7a59e079a2f27ecdce2461", - "reference": "741be2d4526e90b97c7a59e079a2f27ecdce2461", - "shasum": "" - }, - "require": { - "illuminate/console": "^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^10.0|^11.0|^12.0|^13.0", - "illuminate/view": "^10.0|^11.0|^12.0|^13.0", - "laravel/prompts": "^0.1|^0.2|^0.3", - "livewire/livewire": "^3.7.4|^4.0", - "php": "^8.1", - "symfony/console": "^6.0|^7.0|^8.0" - }, - "conflict": { - "livewire/blaze": "<1.0.0-beta.2" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "Flux": "Flux\\Flux" - }, - "providers": [ - "Flux\\FluxServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Flux\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "proprietary" - ], - "authors": [ - { - "name": "Caleb Porzio", - "email": "calebporzio@gmail.com" - } - ], - "description": "The official UI component library for Livewire.", - "keywords": [ - "components", - "flux", - "laravel", - "livewire", - "ui" - ], - "support": { - "issues": "https://github.com/livewire/flux/issues", - "source": "https://github.com/livewire/flux/tree/v2.13.0" - }, - "time": "2026-03-03T03:32:35+00:00" - }, - { - "name": "livewire/livewire", - "version": "v4.2.1", - "source": { - "type": "git", - "url": "https://github.com/livewire/livewire.git", - "reference": "93e972fa42c1b34fff1550093ab94f778d81ea5a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/93e972fa42c1b34fff1550093ab94f778d81ea5a", - "reference": "93e972fa42c1b34fff1550093ab94f778d81ea5a", - "shasum": "" - }, - "require": { - "illuminate/database": "^10.0|^11.0|^12.0|^13.0", - "illuminate/routing": "^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^10.0|^11.0|^12.0|^13.0", - "illuminate/validation": "^10.0|^11.0|^12.0|^13.0", - "laravel/prompts": "^0.1.24|^0.2|^0.3", - "league/mime-type-detection": "^1.9", - "php": "^8.1", - "symfony/console": "^6.0|^7.0|^8.0", - "symfony/http-kernel": "^6.2|^7.0|^8.0" - }, - "require-dev": { - "calebporzio/sushi": "^2.1", - "laravel/framework": "^10.15.0|^11.0|^12.0|^13.0", - "mockery/mockery": "^1.3.1", - "orchestra/testbench": "^8.21.0|^9.0|^10.0|^11.0", - "orchestra/testbench-dusk": "^8.24|^9.1|^10.0|^11.0", - "phpunit/phpunit": "^10.4|^11.5|^12.5", - "psy/psysh": "^0.11.22|^0.12" - }, - "type": "library", - "extra": { - "laravel": { - "aliases": { - "Livewire": "Livewire\\Livewire" - }, - "providers": [ - "Livewire\\LivewireServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Livewire\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Caleb Porzio", - "email": "calebporzio@gmail.com" - } - ], - "description": "A front-end framework for Laravel.", - "support": { - "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v4.2.1" - }, - "funding": [ - { - "url": "https://github.com/livewire", - "type": "github" - } - ], - "time": "2026-02-28T00:01:19+00:00" - }, - { - "name": "monolog/monolog", - "version": "3.10.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^2.0 || ^3.0" - }, - "provide": { - "psr/log-implementation": "3.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7 || ^8", - "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2.0", - "guzzlehttp/guzzle": "^7.4.5", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8 || ^2.0", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.8", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.17 || ^11.0.7", - "predis/predis": "^1.1 || ^2", - "rollbar/rollbar": "^4.0", - "ruflin/elastica": "^7 || ^8", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "ext-openssl": "Required to send log messages using SSL", - "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.10.0" - }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2026-01-02T08:56:05+00:00" - }, - { - "name": "nesbot/carbon", - "version": "3.11.3", - "source": { - "type": "git", - "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "6a7e652845bb018c668220c2a545aded8594fbbf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6a7e652845bb018c668220c2a545aded8594fbbf", - "reference": "6a7e652845bb018c668220c2a545aded8594fbbf", - "shasum": "" - }, - "require": { - "carbonphp/carbon-doctrine-types": "<100.0", - "ext-json": "*", - "php": "^8.1", - "psr/clock": "^1.0", - "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" - }, - "provide": { - "psr/clock-implementation": "1.0" - }, - "require-dev": { - "doctrine/dbal": "^3.6.3 || ^4.0", - "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^v3.87.1", - "kylekatarnls/multi-tester": "^2.5.3", - "phpmd/phpmd": "^2.15.0", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.22", - "phpunit/phpunit": "^10.5.53", - "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" - }, - "bin": [ - "bin/carbon" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Carbon\\Laravel\\ServiceProvider" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Carbon\\": "src/Carbon/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "https://markido.com" - }, - { - "name": "kylekatarnls", - "homepage": "https://github.com/kylekatarnls" - } - ], - "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbonphp.github.io/carbon/", - "keywords": [ - "date", - "datetime", - "time" - ], - "support": { - "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", - "issues": "https://github.com/CarbonPHP/carbon/issues", - "source": "https://github.com/CarbonPHP/carbon" - }, - "funding": [ - { - "url": "https://github.com/sponsors/kylekatarnls", - "type": "github" - }, - { - "url": "https://opencollective.com/Carbon#sponsor", - "type": "opencollective" - }, - { - "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", - "type": "tidelift" - } - ], - "time": "2026-03-11T17:23:39+00:00" - }, - { - "name": "nette/schema", - "version": "v1.3.5", - "source": { - "type": "git", - "url": "https://github.com/nette/schema.git", - "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", - "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", - "shasum": "" - }, - "require": { - "nette/utils": "^4.0", - "php": "8.1 - 8.5" - }, - "require-dev": { - "nette/phpstan-rules": "^1.0", - "nette/tester": "^2.6", - "phpstan/extension-installer": "^1.4@stable", - "phpstan/phpstan": "^2.1.39@stable", - "tracy/tracy": "^2.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Nette\\": "src" - }, - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause", - "GPL-2.0-only", - "GPL-3.0-only" - ], - "authors": [ - { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" - } - ], - "description": "📐 Nette Schema: validating data structures against a given Schema.", - "homepage": "https://nette.org", - "keywords": [ - "config", - "nette" - ], - "support": { - "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.5" - }, - "time": "2026-02-23T03:47:12+00:00" - }, - { - "name": "nette/utils", - "version": "v4.1.3", - "source": { - "type": "git", - "url": "https://github.com/nette/utils.git", - "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", - "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", - "shasum": "" - }, - "require": { - "php": "8.2 - 8.5" - }, - "conflict": { - "nette/finder": "<3", - "nette/schema": "<1.2.2" - }, - "require-dev": { - "jetbrains/phpstorm-attributes": "^1.2", - "nette/phpstan-rules": "^1.0", - "nette/tester": "^2.5", - "phpstan/extension-installer": "^1.4@stable", - "phpstan/phpstan": "^2.1@stable", - "tracy/tracy": "^2.9" - }, - "suggest": { - "ext-gd": "to use Image", - "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", - "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", - "ext-json": "to use Nette\\Utils\\Json", - "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "psr-4": { - "Nette\\": "src" - }, - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause", - "GPL-2.0-only", - "GPL-3.0-only" - ], - "authors": [ - { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" - }, - { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" - } - ], - "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", - "homepage": "https://nette.org", - "keywords": [ - "array", - "core", - "datetime", - "images", - "json", - "nette", - "paginator", - "password", - "slugify", - "string", - "unicode", - "utf-8", - "utility", - "validation" - ], - "support": { - "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.1.3" - }, - "time": "2026-02-13T03:05:33+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v5.7.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" - }, - "time": "2025-12-06T11:56:16+00:00" - }, - { - "name": "nunomaduro/termwind", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "712a31b768f5daea284c2169a7d227031001b9a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", - "reference": "712a31b768f5daea284c2169a7d227031001b9a8", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.4.4 || ^8.0.4" - }, - "require-dev": { - "illuminate/console": "^11.47.0", - "laravel/pint": "^1.27.1", - "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", - "phpstan/phpstan": "^1.12.32", - "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.3.5 || ^8.0.4", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - }, - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "files": [ - "src/Functions.php" - ], - "psr-4": { - "Termwind\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "It's like Tailwind CSS, but for the console.", - "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" - ], - "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://github.com/xiCO2k", - "type": "github" - } - ], - "time": "2026-02-16T23:10:27+00:00" - }, - { - "name": "paragonie/constant_time_encoding", - "version": "v3.1.3", - "source": { - "type": "git", - "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", - "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", - "shasum": "" - }, - "require": { - "php": "^8" - }, - "require-dev": { - "infection/infection": "^0", - "nikic/php-fuzzer": "^0", - "phpunit/phpunit": "^9|^10|^11", - "vimeo/psalm": "^4|^5|^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "ParagonIE\\ConstantTime\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com", - "role": "Maintainer" - }, - { - "name": "Steve 'Sc00bz' Thomas", - "email": "steve@tobtu.com", - "homepage": "https://www.tobtu.com", - "role": "Original Developer" - } - ], - "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", - "keywords": [ - "base16", - "base32", - "base32_decode", - "base32_encode", - "base64", - "base64_decode", - "base64_encode", - "bin2hex", - "encoding", - "hex", - "hex2bin", - "rfc4648" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/constant_time_encoding/issues", - "source": "https://github.com/paragonie/constant_time_encoding" - }, - "time": "2025-09-24T15:06:41+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.9.5", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", - "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh" - }, - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2025-12-27T19:41:33+00:00" - }, - { - "name": "pragmarx/google2fa", - "version": "v9.0.0", - "source": { - "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/e6bc62dd6ae83acc475f57912e27466019a1f2cf", - "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf", - "shasum": "" - }, - "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "PragmaRX\\Google2FA\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" - } - ], - "description": "A One Time Password Authentication package, compatible with Google Authenticator.", - "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa" - ], - "support": { - "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v9.0.0" - }, - "time": "2025-09-19T22:51:08+00:00" - }, - { - "name": "psr/clock", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Psr\\Clock\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", - "keywords": [ - "clock", - "now", - "psr", - "psr-20", - "time" - ], - "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" - }, - "time": "2022-11-25T14:36:26+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "psr/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client" - }, - "time": "2023-09-23T14:17:50+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory" - }, - "time": "2024-04-15T12:06:14+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, - { - "name": "psr/simple-cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], - "support": { - "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" - }, - "time": "2021-10-29T13:26:27+00:00" - }, - { - "name": "psy/psysh", - "version": "v0.12.21", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/psysh.git", - "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", - "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-tokenizer": "*", - "nikic/php-parser": "^5.0 || ^4.0", - "php": "^8.0 || ^7.4", - "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" - }, - "conflict": { - "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "composer/class-map-generator": "^1.6" - }, - "suggest": { - "composer/class-map-generator": "Improved tab completion performance with better class discovery.", - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." - }, - "bin": [ - "bin/psysh" - ], - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": false, - "forward-command": false - }, - "branch-alias": { - "dev-main": "0.12.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Psy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info" - } - ], - "description": "An interactive shell for modern PHP.", - "homepage": "https://psysh.org", - "keywords": [ - "REPL", - "console", - "interactive", - "shell" - ], - "support": { - "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.21" - }, - "time": "2026-03-06T21:21:28+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "ramsey/collection", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", - "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.45", - "fakerphp/faker": "^1.24", - "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^2.1", - "mockery/mockery": "^1.6", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.4", - "phpspec/prophecy-phpunit": "^2.3", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5", - "ramsey/coding-standard": "^2.3", - "ramsey/conventional-commits": "^1.6", - "roave/security-advisories": "dev-latest" - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - }, - "ramsey/conventional-commits": { - "configFile": "conventional-commits.json" - } - }, - "autoload": { - "psr-4": { - "Ramsey\\Collection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" - } - ], - "description": "A PHP library for representing and manipulating collections.", - "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" - ], - "support": { - "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.1" - }, - "time": "2025-03-22T05:38:12+00:00" - }, - { - "name": "ramsey/uuid", - "version": "4.9.2", - "source": { - "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "8429c78ca35a09f27565311b98101e2826affde0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "reference": "8429c78ca35a09f27565311b98101e2826affde0", - "shasum": "" - }, - "require": { - "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", - "php": "^8.0", - "ramsey/collection": "^1.2 || ^2.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" - }, - "require-dev": { - "captainhook/captainhook": "^5.25", - "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "ergebnis/composer-normalize": "^2.47", - "mockery/mockery": "^1.6", - "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.6", - "php-mock/php-mock-mockery": "^1.5", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpbench/phpbench": "^1.2.14", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-mockery": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.6", - "slevomat/coding-standard": "^8.18", - "squizlabs/php_codesniffer": "^3.13" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." - }, - "type": "library", - "extra": { - "captainhook": { - "force-install": true - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Ramsey\\Uuid\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.2" - }, - "time": "2025-12-14T04:43:48+00:00" - }, - { - "name": "symfony/clock", - "version": "v8.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/clock.git", - "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", - "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "psr/clock": "^1.0" - }, - "provide": { - "psr/clock-implementation": "1.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/now.php" - ], - "psr-4": { - "Symfony\\Component\\Clock\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Decouples applications from the system clock", - "homepage": "https://symfony.com", - "keywords": [ - "clock", - "psr20", - "time" - ], - "support": { - "source": "https://github.com/symfony/clock/tree/v8.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-11-12T15:46:48+00:00" - }, - { - "name": "symfony/console", - "version": "v7.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v7.4.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-06T14:06:20+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v8.0.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "2a178bf80f05dbbe469a337730eba79d61315262" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a178bf80f05dbbe469a337730eba79d61315262", - "reference": "2a178bf80f05dbbe469a337730eba79d61315262", - "shasum": "" - }, - "require": { - "php": ">=8.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/v8.0.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-17T13:07:04+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/error-handler", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/8da531f364ddfee53e36092a7eebbbd0b775f6b8", - "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/polyfill-php85": "^1.32", - "symfony/var-dumper": "^6.4|^7.0|^8.0" - }, - "conflict": { - "symfony/deprecation-contracts": "<2.5", - "symfony/http-kernel": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0", - "symfony/webpack-encore-bundle": "^1.0|^2.0" - }, - "bin": [ - "Resources/bin/patch-type-declarations" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ErrorHandler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to manage errors and ease debugging PHP code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.4.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-20T16:42:42+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v8.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/security-http": "<7.4", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/error-handler": "^7.4|^8.0", - "symfony/expression-language": "^7.4|^8.0", - "symfony/framework-bundle": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-05T11:45:55+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/finder", - "version": "v7.4.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8655bf1076b7a3a346cb11413ffdabff50c7ffcf", - "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-29T09:40:50+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v7.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f94b3e7b7dafd40e666f0c9ff2084133bae41e81", - "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "^1.1" - }, - "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" - }, - "require-dev": { - "doctrine/dbal": "^3.6|^4", - "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Defines an object-oriented layer for the HTTP specification", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-06T13:15:18+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v7.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3b3fcf386c809be990c922e10e4c620d6367cab1", - "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^7.3|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", - "symfony/flex": "<2.10", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", - "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", - "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.12" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^7.1|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.1|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", - "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a structured process for converting a Request into a Response", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-06T16:33:18+00:00" - }, - { - "name": "symfony/mailer", - "version": "v7.4.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/mailer.git", - "reference": "b02726f39a20bc65e30364f5c750c4ddbf1f58e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/b02726f39a20bc65e30364f5c750c4ddbf1f58e9", - "reference": "b02726f39a20bc65e30364f5c750c4ddbf1f58e9", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.2", - "psr/event-dispatcher": "^1", - "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/mime": "^7.2|^8.0", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/twig-bridge": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/twig-bridge": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mailer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Helps sending emails", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/mailer/tree/v7.4.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-25T16:50:00+00:00" - }, - { - "name": "symfony/mime", - "version": "v7.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/da5ab4fde3f6c88ab06e96185b9922f48b677cd1", - "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<5.2|>=7", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1|^4", - "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^5.2|^6.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4.3|^7.0.3|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Mime\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows manipulating MIME messages", - "homepage": "https://symfony.com", - "keywords": [ - "mime", - "mime-type" - ], - "support": { - "source": "https://github.com/symfony/mime/tree/v7.4.7" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-03-05T15:24:09+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-27T09:58:17+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-10T14:38:51+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-01-02T08:10:11+00:00" - }, - { - "name": "symfony/polyfill-php83", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-08T02:45:35+00:00" - }, - { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-24T13:30:11+00:00" - }, - { - "name": "symfony/polyfill-php85", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-23T16:12:55+00:00" - }, - { - "name": "symfony/polyfill-uuid", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-uuid": "*" - }, - "suggest": { - "ext-uuid": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Uuid\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for uuid functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "uuid" - ], - "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/process", - "version": "v7.4.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "608476f4604102976d687c483ac63a79ba18cc97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", - "reference": "608476f4604102976d687c483ac63a79ba18cc97", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.4.5" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-26T15:07:59+00:00" - }, - { - "name": "symfony/routing", - "version": "v7.4.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "238d749c56b804b31a9bf3e26519d93b65a60938" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/238d749c56b804b31a9bf3e26519d93b65a60938", - "reference": "238d749c56b804b31a9bf3e26519d93b65a60938", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Maps an HTTP request to a set of configuration variables", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-25T16:50:00+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T11:30:57+00:00" - }, - { - "name": "symfony/string", - "version": "v8.0.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v8.0.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-09T10:14:57+00:00" - }, - { - "name": "symfony/translation", - "version": "v8.0.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b", - "reference": "13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-mbstring": "^1.0", - "symfony/translation-contracts": "^3.6.1" - }, - "conflict": { - "nikic/php-parser": "<5.0", - "symfony/http-client-contracts": "<2.5", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "symfony/translation-implementation": "2.3|3.0" - }, - "require-dev": { - "nikic/php-parser": "^5.0", - "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/console": "^7.4|^8.0", - "symfony/dependency-injection": "^7.4|^8.0", - "symfony/finder": "^7.4|^8.0", - "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^7.4|^8.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^7.4|^8.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/translation/tree/v8.0.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-17T13:07:04+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T13:41:35+00:00" - }, - { - "name": "symfony/uid", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/uid.git", - "reference": "7719ce8aba76be93dfe249192f1fbfa52c588e36" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/7719ce8aba76be93dfe249192f1fbfa52c588e36", - "reference": "7719ce8aba76be93dfe249192f1fbfa52c588e36", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-uuid": "^1.15" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Uid\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to generate and represent UIDs", - "homepage": "https://symfony.com", - "keywords": [ - "UID", - "ulid", - "uuid" - ], - "support": { - "source": "https://github.com/symfony/uid/tree/v7.4.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-03T23:30:35+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v7.4.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "045321c440ac18347b136c63d2e9bf28a2dc0291" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/045321c440ac18347b136c63d2e9bf28a2dc0291", - "reference": "045321c440ac18347b136c63d2e9bf28a2dc0291", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-15T10:53:20+00:00" - }, - { - "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", - "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^7.4 || ^8.0", - "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^8.5.21 || ^9.5.10" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "TijsVerkoyen\\CssToInlineStyles\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Tijs Verkoyen", - "email": "css_to_inline_styles@verkoyen.eu", - "role": "Developer" - } - ], - "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", - "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", - "support": { - "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" - }, - "time": "2025-12-02T11:56:42+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v5.6.3", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "955e7815d677a3eaa7075231212f2110983adecc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", - "reference": "955e7815d677a3eaa7075231212f2110983adecc", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.4", - "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.5", - "symfony/polyfill-ctype": "^1.26", - "symfony/polyfill-mbstring": "^1.26", - "symfony/polyfill-php80": "^1.26" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-filter": "*", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" - }, - "suggest": { - "ext-filter": "Required to use the boolean validator." - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "5.6-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "https://github.com/vlucas" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "support": { - "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], - "time": "2025-12-27T19:49:13+00:00" - }, - { - "name": "voku/portable-ascii", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "shasum": "" - }, - "require": { - "php": ">=7.0.0" - }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" - }, - "suggest": { - "ext-intl": "Use Intl for transliterator_transliterate() support" - }, - "type": "library", - "autoload": { - "psr-4": { - "voku\\": "src/voku/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Lars Moelleken", - "homepage": "https://www.moelleken.org/" - } - ], - "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "homepage": "https://github.com/voku/portable-ascii", - "keywords": [ - "ascii", - "clean", - "php" - ], - "support": { - "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" - }, - "funding": [ - { - "url": "https://www.paypal.me/moelleken", - "type": "custom" - }, - { - "url": "https://github.com/voku", - "type": "github" - }, - { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", - "type": "tidelift" - } - ], - "time": "2024-11-21T01:49:47+00:00" - } - ], - "packages-dev": [ - { - "name": "brianium/paratest", - "version": "v7.19.0", - "source": { - "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6", - "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.3.0", - "jean85/pretty-package-versions": "^2.1.1", - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", - "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1", - "phpunit/php-file-iterator": "^6.0.1 || ^7", - "phpunit/php-timer": "^8 || ^9", - "phpunit/phpunit": "^12.5.9 || ^13", - "sebastian/environment": "^8.0.3 || ^9", - "symfony/console": "^7.4.4 || ^8.0.4", - "symfony/process": "^7.4.5 || ^8.0.5" - }, - "require-dev": { - "doctrine/coding-standard": "^14.0.0", - "ext-pcntl": "*", - "ext-pcov": "*", - "ext-posix": "*", - "phpstan/phpstan": "^2.1.38", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.12", - "phpstan/phpstan-strict-rules": "^2.0.8", - "symfony/filesystem": "^7.4.0 || ^8.0.1" - }, - "bin": [ - "bin/paratest", - "bin/paratest_for_phpstorm" - ], - "type": "library", - "autoload": { - "psr-4": { - "ParaTest\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" - }, - { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" - } - ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", - "keywords": [ - "concurrent", - "parallel", - "phpunit", - "testing" - ], - "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.19.0" - }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2026-02-06T10:53:26+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=14" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^14", - "phpstan/phpstan": "1.4.10 || 2.1.30", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.6" - }, - "time": "2026-02-07T07:09:04+00:00" - }, - { - "name": "fakerphp/faker", - "version": "v1.24.1", - "source": { - "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "conflict": { - "fzaninotto/faker": "*" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "doctrine/persistence": "^1.3 || ^2.0", - "ext-intl": "*", - "phpunit/phpunit": "^9.5.26", - "symfony/phpunit-bridge": "^5.4.16" - }, - "suggest": { - "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." - }, - "type": "library", - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" - }, - "time": "2024-11-21T13:46:39+00:00" - }, - { - "name": "fidry/cpu-core-counter", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Tiny utility to get the number of CPU cores.", - "keywords": [ - "CPU", - "core" - ], - "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2025-08-14T07:29:31+00:00" - }, - { - "name": "filp/whoops", - "version": "2.18.4", - "source": { - "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Whoops\\": "src/Whoops/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" - } - ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], - "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" - }, - "funding": [ - { - "url": "https://github.com/denis-sokolov", - "type": "github" - } - ], - "time": "2025-08-08T12:00:00+00:00" - }, - { - "name": "hamcrest/hamcrest-php", - "version": "v2.1.1", - "source": { - "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", - "shasum": "" - }, - "require": { - "php": "^7.4|^8.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" - }, - "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "hamcrest" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "This is the PHP port of Hamcrest Matchers", - "keywords": [ - "test" - ], - "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" - }, - "time": "2025-04-30T06:54:44+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.1.0", - "php": "^7.4|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^7.5|^8.5|^9.6", - "rector/rector": "^2.0", - "vimeo/psalm": "^4.3 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" - }, - "time": "2025-03-19T14:43:43+00:00" - }, - { - "name": "laravel/pail", - "version": "v1.2.6", - "source": { - "type": "git", - "url": "https://github.com/laravel/pail.git", - "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pail/zipball/aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", - "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "illuminate/console": "^10.24|^11.0|^12.0|^13.0", - "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", - "illuminate/log": "^10.24|^11.0|^12.0|^13.0", - "illuminate/process": "^10.24|^11.0|^12.0|^13.0", - "illuminate/support": "^10.24|^11.0|^12.0|^13.0", - "nunomaduro/termwind": "^1.15|^2.0", - "php": "^8.2", - "symfony/console": "^6.0|^7.0|^8.0" - }, - "require-dev": { - "laravel/framework": "^10.24|^11.0|^12.0|^13.0", - "laravel/pint": "^1.13", - "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", - "pestphp/pest": "^2.20|^3.0|^4.0", - "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", - "phpstan/phpstan": "^1.12.27", - "symfony/var-dumper": "^6.3|^7.0|^8.0", - "symfony/yaml": "^6.3|^7.0|^8.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Pail\\PailServiceProvider" - ] - }, - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\Pail\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Easily delve into your Laravel application's log files directly from the command line.", - "homepage": "https://github.com/laravel/pail", - "keywords": [ - "dev", - "laravel", - "logs", - "php", - "tail" - ], - "support": { - "issues": "https://github.com/laravel/pail/issues", - "source": "https://github.com/laravel/pail" - }, - "time": "2026-02-09T13:44:54+00:00" - }, - { - "name": "laravel/pint", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "bdec963f53172c5e36330f3a400604c69bf02d39" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/bdec963f53172c5e36330f3a400604c69bf02d39", - "reference": "bdec963f53172c5e36330f3a400604c69bf02d39", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.2.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.94.2", - "illuminate/view": "^12.54.1", - "larastan/larastan": "^3.9.3", - "laravel-zero/framework": "^12.0.5", - "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.4.0", - "pestphp/pest": "^3.8.6", - "shipfastlabs/agent-detector": "^1.1.0" - }, - "bin": [ - "builds/pint" - ], - "type": "project", - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", - "keywords": [ - "dev", - "format", - "formatter", - "lint", - "linter", - "php" - ], - "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" - }, - "time": "2026-03-12T15:51:39+00:00" - }, - { - "name": "laravel/sail", - "version": "v1.53.0", - "source": { - "type": "git", - "url": "https://github.com/laravel/sail.git", - "reference": "e340eaa2bea9b99192570c48ed837155dbf24fbb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/e340eaa2bea9b99192570c48ed837155dbf24fbb", - "reference": "e340eaa2bea9b99192570c48ed837155dbf24fbb", - "shasum": "" - }, - "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "php": "^8.0", - "symfony/console": "^6.0|^7.0|^8.0", - "symfony/yaml": "^6.0|^7.0|^8.0" - }, - "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", - "phpstan/phpstan": "^2.0" - }, - "bin": [ - "bin/sail" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Sail\\SailServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Sail\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Docker files for running a basic Laravel application.", - "keywords": [ - "docker", - "laravel" - ], - "support": { - "issues": "https://github.com/laravel/sail/issues", - "source": "https://github.com/laravel/sail" - }, - "time": "2026-02-06T12:16:02+00:00" - }, - { - "name": "mockery/mockery", - "version": "1.6.12", - "source": { - "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", - "shasum": "" - }, - "require": { - "hamcrest/hamcrest-php": "^2.0.1", - "lib-pcre": ">=7.0", - "php": ">=7.3" - }, - "conflict": { - "phpunit/phpunit": "<8.0" - }, - "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.17", - "symplify/easy-coding-standard": "^12.1.14" - }, - "type": "library", - "autoload": { - "files": [ - "library/helpers.php", - "library/Mockery.php" - ], - "psr-4": { - "Mockery\\": "library/Mockery" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "https://github.com/padraic", - "role": "Author" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "https://davedevelopment.co.uk", - "role": "Developer" - }, - { - "name": "Nathanael Esayeas", - "email": "nathanael.esayeas@protonmail.com", - "homepage": "https://github.com/ghostwriter", - "role": "Lead Developer" - } - ], - "description": "Mockery is a simple yet flexible PHP mock object framework", - "homepage": "https://github.com/mockery/mockery", - "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", - "test", - "test double", - "testing" - ], - "support": { - "docs": "https://docs.mockery.io/", - "issues": "https://github.com/mockery/mockery/issues", - "rss": "https://github.com/mockery/mockery/releases.atom", - "security": "https://github.com/mockery/mockery/security/advisories", - "source": "https://github.com/mockery/mockery" - }, - "time": "2024-05-16T03:13:13+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, - { - "name": "nunomaduro/collision", - "version": "v8.9.1", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", - "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", - "shasum": "" - }, - "require": { - "filp/whoops": "^2.18.4", - "nunomaduro/termwind": "^2.4.0", - "php": "^8.2.0", - "symfony/console": "^7.4.4 || ^8.0.4" - }, - "conflict": { - "laravel/framework": "<11.48.0 || >=14.0.0", - "phpunit/phpunit": "<11.5.50 || >=14.0.0" - }, - "require-dev": { - "brianium/paratest": "^7.8.5", - "larastan/larastan": "^3.9.2", - "laravel/framework": "^11.48.0 || ^12.52.0", - "laravel/pint": "^1.27.1", - "orchestra/testbench-core": "^9.12.0 || ^10.9.0", - "pestphp/pest": "^3.8.5 || ^4.4.1 || ^5.0.0", - "sebastian/environment": "^7.2.1 || ^8.0.3 || ^9.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - }, - "branch-alias": { - "dev-8.x": "8.x-dev" - } - }, - "autoload": { - "files": [ - "./src/Adapters/Phpunit/Autoload.php" - ], - "psr-4": { - "NunoMaduro\\Collision\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Cli error handling for console/command-line PHP applications.", - "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "dev", - "error", - "handling", - "laravel", - "laravel-zero", - "php", - "symfony" - ], - "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2026-02-17T17:33:08+00:00" - }, - { - "name": "pestphp/pest", - "version": "v4.4.2", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest.git", - "reference": "5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701", - "reference": "5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701", - "shasum": "" - }, - "require": { - "brianium/paratest": "^7.19.0", - "nunomaduro/collision": "^8.9.1", - "nunomaduro/termwind": "^2.4.0", - "pestphp/pest-plugin": "^4.0.0", - "pestphp/pest-plugin-arch": "^4.0.0", - "pestphp/pest-plugin-mutate": "^4.0.1", - "pestphp/pest-plugin-profanity": "^4.2.1", - "php": "^8.3.0", - "phpunit/phpunit": "^12.5.12", - "symfony/process": "^7.4.5|^8.0.5" - }, - "conflict": { - "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">12.5.12", - "sebastian/exporter": "<7.0.0", - "webmozart/assert": "<1.11.0" - }, - "require-dev": { - "pestphp/pest-dev-tools": "^4.1.0", - "pestphp/pest-plugin-browser": "^4.3.0", - "pestphp/pest-plugin-type-coverage": "^4.0.3", - "psy/psysh": "^0.12.21" - }, - "bin": [ - "bin/pest" - ], - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Mutate\\Plugins\\Mutate", - "Pest\\Plugins\\Configuration", - "Pest\\Plugins\\Bail", - "Pest\\Plugins\\Cache", - "Pest\\Plugins\\Coverage", - "Pest\\Plugins\\Init", - "Pest\\Plugins\\Environment", - "Pest\\Plugins\\Help", - "Pest\\Plugins\\Memory", - "Pest\\Plugins\\Only", - "Pest\\Plugins\\Printer", - "Pest\\Plugins\\ProcessIsolation", - "Pest\\Plugins\\Profile", - "Pest\\Plugins\\Retry", - "Pest\\Plugins\\Snapshot", - "Pest\\Plugins\\Verbose", - "Pest\\Plugins\\Version", - "Pest\\Plugins\\Shard", - "Pest\\Plugins\\Parallel" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "files": [ - "src/Functions.php", - "src/Pest.php" - ], - "psr-4": { - "Pest\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "The elegant PHP Testing Framework.", - "keywords": [ - "framework", - "pest", - "php", - "test", - "testing", - "unit" - ], - "support": { - "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v4.4.2" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2026-03-10T21:09:12+00:00" - }, - { - "name": "pestphp/pest-plugin", - "version": "v4.0.0", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", - "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0.0", - "composer-runtime-api": "^2.2.2", - "php": "^8.3" - }, - "conflict": { - "pestphp/pest": "<4.0.0" - }, - "require-dev": { - "composer/composer": "^2.8.10", - "pestphp/pest": "^4.0.0", - "pestphp/pest-dev-tools": "^4.0.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Pest\\Plugin\\Manager" - }, - "autoload": { - "psr-4": { - "Pest\\Plugin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Pest plugin manager", - "keywords": [ - "framework", - "manager", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2025-08-20T12:35:58+00:00" - }, - { - "name": "pestphp/pest-plugin-arch", - "version": "v4.0.0", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", - "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", - "shasum": "" - }, - "require": { - "pestphp/pest-plugin": "^4.0.0", - "php": "^8.3", - "ta-tikoma/phpunit-architecture-test": "^0.8.5" - }, - "require-dev": { - "pestphp/pest": "^4.0.0", - "pestphp/pest-dev-tools": "^4.0.0" - }, - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Arch\\Plugin" - ] - } - }, - "autoload": { - "files": [ - "src/Autoload.php" - ], - "psr-4": { - "Pest\\Arch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Arch plugin for Pest PHP.", - "keywords": [ - "arch", - "architecture", - "framework", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2025-08-20T13:10:51+00:00" - }, - { - "name": "pestphp/pest-plugin-laravel", - "version": "v4.1.0", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-laravel.git", - "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/3057a36669ff11416cc0dc2b521b3aec58c488d0", - "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0", - "shasum": "" - }, - "require": { - "laravel/framework": "^11.45.2|^12.52.0|^13.0", - "pestphp/pest": "^4.4.1", - "php": "^8.3.0" - }, - "require-dev": { - "laravel/dusk": "^8.3.6", - "orchestra/testbench": "^9.13.0|^10.9.0|^11.0", - "pestphp/pest-dev-tools": "^4.1.0" - }, - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Laravel\\Plugin" - ] - }, - "laravel": { - "providers": [ - "Pest\\Laravel\\PestServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/Autoload.php" - ], - "psr-4": { - "Pest\\Laravel\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Pest Laravel Plugin", - "keywords": [ - "framework", - "laravel", - "pest", - "php", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.1.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2026-02-21T00:29:45+00:00" - }, - { - "name": "pestphp/pest-plugin-mutate", - "version": "v4.0.1", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-mutate.git", - "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", - "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.6.1", - "pestphp/pest-plugin": "^4.0.0", - "php": "^8.3", - "psr/simple-cache": "^3.0.0" - }, - "require-dev": { - "pestphp/pest": "^4.0.0", - "pestphp/pest-dev-tools": "^4.0.0", - "pestphp/pest-plugin-type-coverage": "^4.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Pest\\Mutate\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - }, - { - "name": "Sandro Gehri", - "email": "sandrogehri@gmail.com" - } - ], - "description": "Mutates your code to find untested cases", - "keywords": [ - "framework", - "mutate", - "mutation", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/gehrisandro", - "type": "github" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2025-08-21T20:19:25+00:00" - }, - { - "name": "pestphp/pest-plugin-profanity", - "version": "v4.2.1", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-profanity.git", - "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", - "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", - "shasum": "" - }, - "require": { - "pestphp/pest-plugin": "^4.0.0", - "php": "^8.3" - }, - "require-dev": { - "faissaloux/pest-plugin-inside": "^1.9", - "pestphp/pest": "^4.0.0", - "pestphp/pest-dev-tools": "^4.0.0" - }, - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Profanity\\Plugin" - ] - } - }, - "autoload": { - "psr-4": { - "Pest\\Profanity\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Pest Profanity Plugin", - "keywords": [ - "framework", - "pest", - "php", - "plugin", - "profanity", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" - }, - "time": "2025-12-08T00:13:17+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/897b5986ece6b4f9d8413fea345c7d49c757d6bf", - "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^2.0", - "phpstan/phpdoc-parser": "^2.0", - "webmozart/assert": "^1.9.1 || ^2" - }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26", - "shipmonk/dead-code-detector": "^0.5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.2" - }, - "time": "2026-03-01T18:43:49+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" - }, - "time": "2026-01-06T21:53:42+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "12.5.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b015312f28dd75b75d3422ca37dff2cd1a565e8d", - "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^5.7.0", - "php": ">=8.3", - "phpunit/php-file-iterator": "^6.0", - "phpunit/php-text-template": "^5.0", - "sebastian/complexity": "^5.0", - "sebastian/environment": "^8.0.3", - "sebastian/lines-of-code": "^4.0", - "sebastian/version": "^6.0", - "theseer/tokenizer": "^2.0.1" - }, - "require-dev": { - "phpunit/phpunit": "^12.5.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "12.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", - "type": "tidelift" - } - ], - "time": "2026-02-06T06:01:44+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", - "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", - "type": "tidelift" - } - ], - "time": "2026-02-02T14:04:18+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^12.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:58:58+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:59:16+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "8.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "8.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:59:38+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "12.5.12", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/418e06b3b46b0d54bad749ff4907fc7dfb530199", - "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.3", - "phpunit/php-code-coverage": "^12.5.3", - "phpunit/php-file-iterator": "^6.0.1", - "phpunit/php-invoker": "^6.0.0", - "phpunit/php-text-template": "^5.0.0", - "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.2.0", - "sebastian/comparator": "^7.1.4", - "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.3", - "sebastian/exporter": "^7.0.2", - "sebastian/global-state": "^8.0.2", - "sebastian/object-enumerator": "^7.0.0", - "sebastian/recursion-context": "^7.0.1", - "sebastian/type": "^6.0.3", - "sebastian/version": "^6.0.0", - "staabm/side-effects-detector": "^1.0.5" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "12.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.12" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2026-02-16T08:34:36+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "4.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", - "type": "tidelift" - } - ], - "time": "2025-09-14T09:36:45+00:00" - }, - { - "name": "sebastian/comparator", - "version": "7.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/diff": "^7.0", - "sebastian/exporter": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^12.2" - }, - "suggest": { - "ext-bcmath": "For comparing BcMath\\Number objects" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2026-01-24T09:28:48+00:00" - }, - { - "name": "sebastian/complexity", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:55:25+00:00" - }, - { - "name": "sebastian/diff", - "version": "7.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0", - "symfony/process": "^7.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:55:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "8.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "8.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", - "type": "tidelift" - } - ], - "time": "2025-08-12T14:11:56+00:00" - }, - { - "name": "sebastian/exporter", - "version": "7.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/recursion-context": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" - } - ], - "time": "2025-09-24T06:16:11+00:00" - }, - { - "name": "sebastian/global-state", - "version": "8.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d", - "shasum": "" - }, - "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "8.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", - "type": "tidelift" - } - ], - "time": "2025-08-29T11:29:25+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:57:28+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "7.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", - "shasum": "" - }, - "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:57:48+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T04:58:17+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "7.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], - "time": "2025-08-13T04:44:59+00:00" - }, - { - "name": "sebastian/type", - "version": "6.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "require-dev": { - "phpunit/phpunit": "^12.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/type", - "type": "tidelift" - } - ], - "time": "2025-08-09T06:57:12+00:00" - }, - { - "name": "sebastian/version", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", - "shasum": "" - }, - "require": { - "php": ">=8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-02-07T05:00:38+00:00" - }, - { - "name": "staabm/side-effects-detector", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/staabm/side-effects-detector.git", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.6", - "phpunit/phpunit": "^9.6.21", - "symfony/var-dumper": "^5.4.43", - "tomasvotruba/type-coverage": "1.0.0", - "tomasvotruba/unused-public": "1.0.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A static analysis tool to detect side effects in PHP code", - "keywords": [ - "static analysis" - ], - "support": { - "issues": "https://github.com/staabm/side-effects-detector/issues", - "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" - }, - "funding": [ - { - "url": "https://github.com/staabm", - "type": "github" - } - ], - "time": "2024-10-20T05:08:20+00:00" - }, - { - "name": "symfony/yaml", - "version": "v8.0.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/5f006c50a981e1630bbb70ad409c5d85f9a716e0", - "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0", - "shasum": "" - }, - "require": { - "php": ">=8.4", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<7.4" - }, - "require-dev": { - "symfony/console": "^7.4|^8.0" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v8.0.6" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-02-09T10:14:57+00:00" - }, - { - "name": "ta-tikoma/phpunit-architecture-test", - "version": "0.8.7", - "source": { - "type": "git", - "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", - "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8", - "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.18.0 || ^5.0.0", - "php": "^8.1.0", - "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", - "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0", - "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" - }, - "require-dev": { - "laravel/pint": "^1.13.7", - "phpstan/phpstan": "^1.10.52" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPUnit\\Architecture\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ni Shi", - "email": "futik0ma011@gmail.com" - }, - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Methods for testing application architecture", - "keywords": [ - "architecture", - "phpunit", - "stucture", - "test", - "testing" - ], - "support": { - "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", - "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7" - }, - "time": "2026-02-17T17:25:14+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", - "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^8.1" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2025-12-08T11:19:18+00:00" - }, - { - "name": "webmozart/assert", - "version": "2.1.6", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", - "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^8.2" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-feature/2-0": "2.0-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Woody Gilk", - "email": "woody.gilk@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.6" - }, - "time": "2026-02-27T10:28:38+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "^8.2" - }, - "platform-dev": {}, - "plugin-api-version": "2.9.0" -} diff --git a/package.json b/package.json index f9f496e..df56eeb 100644 --- a/package.json +++ b/package.json @@ -33,4 +33,4 @@ "@tailwindcss/oxide-linux-x64-gnu": "^4.0.1", "lightningcss-linux-x64-gnu": "^1.29.1" } -} \ No newline at end of file +} diff --git a/phpunit.xml b/phpunit.xml index e7f0a48..8863459 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,8 +2,7 @@ + colors="true"> tests/Unit @@ -21,16 +20,14 @@ - - + - - + diff --git a/resources/js/file-manager.js b/resources/js/file-manager.js new file mode 100644 index 0000000..15bbdbd --- /dev/null +++ b/resources/js/file-manager.js @@ -0,0 +1,189 @@ +/** + * file-manager.js + * Bundled via Vite — loaded only on the file manager page. + * Provides: CodeMirror 6 editor, Alpine data components. + */ + +import { EditorState, Compartment } from '@codemirror/state'; +import { + EditorView, keymap, lineNumbers, + highlightActiveLineGutter, highlightActiveLine, + drawSelection, dropCursor, rectangularSelection, + crosshairCursor, highlightSpecialChars, +} from '@codemirror/view'; +import { + defaultKeymap, history, historyKeymap, indentWithTab, +} from '@codemirror/commands'; +import { + indentOnInput, bracketMatching, foldGutter, + syntaxHighlighting, defaultHighlightStyle, +} from '@codemirror/language'; +import { html } from '@codemirror/lang-html'; +import { css } from '@codemirror/lang-css'; +import { javascript } from '@codemirror/lang-javascript'; +import { json } from '@codemirror/lang-json'; +import { xml } from '@codemirror/lang-xml'; +import { oneDark } from '@codemirror/theme-one-dark'; +import Alpine from 'alpinejs'; + +// ── CodeMirror state ────────────────────────────────────────────────────────── + +const langCompartment = new Compartment(); +let editorView = null; +let saveDebounce = null; + +/** Line/col published to Alpine via custom event */ +function publishCursor(state) { + const head = state.selection.main.head; + const line = state.doc.lineAt(head); + window.dispatchEvent(new CustomEvent('cm-cursor', { + detail: { line: line.number, col: head - line.from + 1, total: state.doc.lines }, + })); +} + +function langExt(lang) { + switch (lang) { + case 'html': return html({ matchClosingTags: true, autoCloseTags: true }); + case 'css': return css(); + case 'javascript': return javascript({ jsx: false }); + case 'json': return json(); + case 'xml': return xml(); + default: return []; + } +} + +function getWire() { + const el = document.querySelector('[wire\\:id]'); + return el ? window.Livewire?.find(el.getAttribute('wire:id')) : null; +} + +function triggerSave() { + if (!editorView) return; + getWire()?.call('saveFile', editorView.state.doc.toString()); +} + +export function createEditor(content, lang) { + const host = document.getElementById('cm-host'); + if (!host) return; + + editorView?.destroy(); + editorView = null; + + editorView = new EditorView({ + state: EditorState.create({ + doc: content, + extensions: [ + // Appearance + oneDark, + EditorView.theme({ + '&': { height: '100%' }, + '.cm-scroller': { overflow: 'auto', fontFamily: "ui-monospace, 'Cascadia Code', 'JetBrains Mono', Menlo, monospace" }, + '.cm-content': { padding: '8px 0' }, + '.cm-gutters': { background: 'transparent', border: 'none' }, + }), + // Gutter & highlights + lineNumbers(), + highlightActiveLineGutter(), + highlightActiveLine(), + highlightSpecialChars(), + drawSelection(), + dropCursor(), + // Editing + history(), + indentOnInput(), + bracketMatching(), + foldGutter(), + rectangularSelection(), + crosshairCursor(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + // Language + langCompartment.of(langExt(lang)), + // Keybindings + keymap.of([ + { key: 'Mod-s', run() { triggerSave(); return true; } }, + ...defaultKeymap, + ...historyKeymap, + indentWithTab, + ]), + // Listeners + EditorView.updateListener.of(update => { + if (update.selectionSet) publishCursor(update.state); + if (!update.docChanged) return; + getWire()?.set('isDirty', true); + clearTimeout(saveDebounce); + saveDebounce = setTimeout(triggerSave, 1200); + }), + ], + }), + parent: host, + }); + + publishCursor(editorView.state); +} + +// ── Alpine components ───────────────────────────────────────────────────────── + +/** + * fileManager() — root Alpine data for the manager page. + */ +window.fileManager = () => ({ + // Open folder paths (array for Alpine reactivity) + openDirs: [], + + // Context menu state + ctx: { show: false, x: 0, y: 0, item: null }, + + // Cursor info (updated by CodeMirror) + cursor: { line: 1, col: 1 }, + + isOpen(path) { + return this.openDirs.includes(path); + }, + + toggleDir(path) { + if (this.isOpen(path)) { + this.openDirs = this.openDirs.filter(p => p !== path); + } else { + this.openDirs = [...this.openDirs, path]; + } + }, + + openAllDirs(paths) { + this.openDirs = [...new Set([...this.openDirs, ...paths])]; + }, + + openContext(e, item) { + this.ctx = { show: true, x: e.clientX, y: e.clientY, item }; + }, + + closeContext() { + this.ctx = { show: false, x: 0, y: 0, item: null }; + }, + + init() { + // Listen for CodeMirror cursor events + window.addEventListener('cm-cursor', e => { + this.cursor = e.detail; + }); + + // Listen for Livewire editor load event + window.addEventListener('load-editor', e => { + createEditor(e.detail.content, e.detail.language); + }); + + // Close context menu on outside click + window.addEventListener('click', () => this.closeContext()); + window.addEventListener('keydown', e => { + if (e.key === 'Escape') this.closeContext(); + }); + }, + + save() { + triggerSave(); + }, +}); + +// ── Bootstrap ───────────────────────────────────────────────────────────────── + +window.Alpine = Alpine; +Alpine.start(); diff --git a/resources/views/.DS_Store b/resources/views/.DS_Store deleted file mode 100644 index cd1b8cfbaa11f1c468428a70d6a04fab2ee585bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKyG{c!5S)c8BGRO!^e^xSrzm^@KL8|16oErZ(qF}Q@oAZT2qijQNrPsk-Lcm@ zwmik{8Gx-%y9ZzeU_p1pw}+|uy8Fa#D&uIr$L_G*zYfEGQaPV+Zig3~aK!s1{|;LW z_`nzAC*J5s94-sy_tc6MkOERb3P=GdFs}f6w%cNvs3--bfE4&uz`qZT?${TOiSg-R zh$jHCV>*oEcuNqQCy0IFn8*yxl1faf)f2;#&U~x7zHm%TIxJ4+K6SFy3&rAe=3A7* z`b0%3AO)@!xXtOx`~M?-=KX(7(o70Sfq$idb(^QnnpdjcI=P(p+D3n-d(98sjpLv& mL^~!%JLbgO@qH9!UGp*5`@%6X=*$P5sGkAXMJ5IQT7ffr#T$$O diff --git a/resources/views/livewire/file-manager.blade.php b/resources/views/livewire/file-manager.blade.php index 1f3cdd5..13ffbda 100644 --- a/resources/views/livewire/file-manager.blade.php +++ b/resources/views/livewire/file-manager.blade.php @@ -1,398 +1,660 @@ -{{-- File Manager — Full-screen IDE layout --}} {{-- - NOTE: Alpine event listeners use the x-on: prefix throughout this template. - Do NOT use the @ shorthand for Alpine here — Livewire registers @livewire - as a Blade directive, and other @ names can conflict in edge cases. - Livewire flash events use $this->dispatch('show-flash', message: '...') - and are caught by x-on:show-flash.window below. + File Manager — full-screen IDE layout. + + Alpine root: fileManager() from resources/js/file-manager.js + Livewire: App\Livewire\FileManager + + Conventions: + - x-on: prefix everywhere (avoids @livewire Blade directive collision) + - Flux modals for new-file, new-folder, rename, delete-confirm, settings + - Context menu fully in Alpine --}}
- {{-- ── Flash toast ─────────────────────────────────────────────────────── --}} - - - {{-- ── Left sidebar: file tree ─────────────────────────────────────────── --}} - + - {{-- ── Center: editor / preview pane ──────────────────────────────────── --}} -
- {{-- Toolbar --}} -
- - {{ $activeFile ?: 'No file open' }} - + {{-- ── CENTER EDITOR PANE ──────────────────────────────────────── --}} +
-
- @if ($isDirty) - ● Unsaved + {{-- Editor toolbar --}} +
+ @if ($activeFile) + + + {{ $activeFile }} + + @else + No file open @endif - @if ($this->isEditable && $activeFile) - + @elseif ($activeFile && $this->isEditable) + + + Saved + + @endif + + @if ($activeFile && $this->isPreviewable && !$this->isEditable) + + + Open + + @endif +
+
+ + {{-- ── Content area ── --}} + + @if ($this->isEditable && $activeFile) + {{-- CodeMirror host --}} +
+ + @elseif ($this->isPreviewable && $activeFile) + {{-- Media / binary preview --}} + @php $ext = strtolower(pathinfo($activeFile, PATHINFO_EXTENSION)); @endphp +
+ @if (in_array($ext, ['png','jpg','jpeg','gif','webp','ico'])) + + @elseif ($ext === 'svg') + + @elseif (in_array($ext, ['mp4','webm'])) + + @elseif (in_array($ext, ['mp3','wav','ogg'])) +
+ +

{{ basename($activeFile) }}

+ +
+ @elseif ($ext === 'pdf') + + @endif +
+ + @elseif ($activeFile) + {{-- Non-previewable binary --}} +
+ +

{{ basename($activeFile) }}

+

Binary file — cannot be edited in browser

+
+ + @else + {{-- Empty state --}} +
+ +

No file open

+

Select a file from the explorer to start editing

+ - @endif +
+ @endif - - Preview ↗ - -
- {{-- CodeMirror host --}} - @if ($this->isEditable && $activeFile) -
- - {{-- Binary / media preview --}} - @elseif ($this->isPreviewable && $activeFile) - @php $ext = strtolower(pathinfo($activeFile, PATHINFO_EXTENSION)); @endphp -
- @if (in_array($ext, ['png','jpg','jpeg','gif','webp','svg','ico'])) - - @elseif (in_array($ext, ['mp4','webm'])) - - @elseif (in_array($ext, ['mp3','wav','ogg'])) - - @elseif ($ext === 'pdf') - - @else -
-

📎

-

{{ basename($activeFile) }}

-

Binary file — cannot be previewed

-
+
+ + + {{-- ═══════════════════════════════════════════════════════════════════════ + STATUS BAR + ═══════════════════════════════════════════════════════════════════════ --}} +
+
+ @if ($activeFile) + {{ basename($activeFile) }} + @if ($this->isEditable) + + Ln 1, + Col 1 + + {{ $this->editorLanguage }} @endif -
+ @else + {{ config('app.name') }} + @endif +
+
+ {{ $page->slug }} + + {{ $page->is_public ? '🌐 Public' : '🔒 Private' }} + +
+ + + + {{-- ═══════════════════════════════════════════════════════════════════════ + CONTEXT MENU (Alpine, fully client-side) + ═══════════════════════════════════════════════════════════════════════ --}} + + + + {{-- ═══════════════════════════════════════════════════════════════════════ + FLASH TOAST + ═══════════════════════════════════════════════════════════════════════ --}} + - {{-- ── Settings slide-over ─────────────────────────────────────────────── --}} - @if ($showSettings) -
-
-
-

Page Settings

- + {{-- ═══════════════════════════════════════════════════════════════════════ + MODALS + ═══════════════════════════════════════════════════════════════════════ --}} + + {{-- ── New File ────────────────────────────────────────────────────── --}} + +
+ New File + + Create a new file in + + {{ $currentDir ?: '/' }} + + + + + File name + + @error('newFileName') {{ $message }} @enderror + + +
+ + Create File + + + Cancel +
+
+
+ + {{-- ── New Folder ──────────────────────────────────────────────────── --}} + +
+ New Folder + Create a folder inside + + {{ $currentDir ?: '/' }} + + + + + Folder name + + @error('newFolderName') {{ $message }} @enderror + + +
+ + Create Folder + + + Cancel + +
+
+
+ + {{-- ── Upload ──────────────────────────────────────────────────────── --}} + +
+ Upload Files + + Upload to {{ $currentDir ?: '/' }} + — max {{ config('filesystems.max_upload_mb', 50) }} MB per file + + + -
+
+ + + + + Uploading… +
-
- - - @error('pageName') -

{{ $message }}

- @enderror -
+ @error('upload') + + {{ $message }} + + @enderror +
+ + Done + +
+
+ + + {{-- ── Delete Confirmation ─────────────────────────────────────────── --}} + +
+
+
+ +
- - - @error('pageSlug') -

{{ $message }}

- @enderror -

- {{ config('app.base_domain') }}/{{ $pageSlug }} + + Delete {{ $deleteTargetType === 'dir' ? 'folder' : 'file' }}? + +

+ + {{ $deleteTargetName }} + + will be permanently deleted. + @if ($deleteTargetType === 'dir') + All files inside will be lost. + @endif

+
-
- - -
+
+ + Delete + + + Cancel + +
+
+
+ + {{-- ── Page Settings ───────────────────────────────────────────────── --}} + +
+ Page Settings + + Configure your page's name, URL slug, and visibility. + + +
+ + Page Name + + @error('pageName') {{ $message }} @enderror + + + + Slug + + @error('pageSlug') {{ $message }} @enderror + + {{ config('app.base_domain') }}/{{ $pageSlug }} + + + + + + Publicly accessible + When off, only you can view this page. + +
-
-

Access URLs

+ {{-- URLs --}} + -
- + + + Cancel + +
- +
+ + + Delete this page +
-
- @endif +
- -{{-- ── CodeMirror 6 (loaded via ESM import map in manager.blade.php head) ── --}} - diff --git a/resources/views/livewire/partials/tree-item.blade.php b/resources/views/livewire/partials/tree-item.blade.php new file mode 100644 index 0000000..4c8bd83 --- /dev/null +++ b/resources/views/livewire/partials/tree-item.blade.php @@ -0,0 +1,136 @@ +{{-- + Recursive file-tree item. + Variables: $item (array), $depth (int, default 0) + Alpine context: fileManager() component on ancestor div. +--}} +@php +$depth ??= 0; +$pl = 8 + $depth * 16; + +// File-type icon + colour mapping (Flux/Heroicons) +$ext = strtolower(pathinfo($item['name'], PATHINFO_EXTENSION)); +$iconMap = [ + 'html' => ['name' => 'globe-alt', 'class' => 'text-orange-400'], + 'htm' => ['name' => 'globe-alt', 'class' => 'text-orange-400'], + 'css' => ['name' => 'swatch', 'class' => 'text-sky-400'], + 'js' => ['name' => 'bolt', 'class' => 'text-yellow-400'], + 'json' => ['name' => 'document-text', 'class' => 'text-amber-500'], + 'md' => ['name' => 'document-text', 'class' => 'text-zinc-400'], + 'txt' => ['name' => 'document-text', 'class' => 'text-zinc-400'], + 'xml' => ['name' => 'code-bracket', 'class' => 'text-green-400'], + 'svg' => ['name' => 'swatch', 'class' => 'text-pink-400'], + 'png' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'jpg' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'jpeg' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'gif' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'webp' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'ico' => ['name' => 'photo', 'class' => 'text-violet-400'], + 'mp4' => ['name' => 'film', 'class' => 'text-red-400'], + 'webm' => ['name' => 'film', 'class' => 'text-red-400'], + 'mp3' => ['name' => 'musical-note', 'class' => 'text-emerald-400'], + 'wav' => ['name' => 'musical-note', 'class' => 'text-emerald-400'], + 'ogg' => ['name' => 'musical-note', 'class' => 'text-emerald-400'], + 'pdf' => ['name' => 'document', 'class' => 'text-red-500'], + 'woff' => ['name' => 'document', 'class' => 'text-zinc-500'], + 'woff2' => ['name' => 'document', 'class' => 'text-zinc-500'], +]; +$fileIcon = $item['isIndex'] ?? false + ? ['name' => 'home', 'class' => 'text-emerald-400'] + : ($iconMap[$ext] ?? ['name' => 'document', 'class' => 'text-zinc-500']); +@endphp + +@if ($item['type'] === 'dir') +{{-- ── DIRECTORY ──────────────────────────────────────────────────────── --}} +
+
+ {{-- Chevron --}} + + + + + {{-- Folder icon --}} + + + + {{-- Name --}} + {{ $item['name'] }} + + {{-- Hover actions --}} + +
+ + {{-- Children --}} +
+ @forelse ($item['children'] ?? [] as $child) + @include('livewire.partials.tree-item', ['item' => $child, 'depth' => $depth + 1]) + @empty +

+ Empty +

+ @endforelse +
+
+ +@else +{{-- ── FILE ────────────────────────────────────────────────────────────── --}} +
+ {{-- File icon --}} + + + + + {{-- Name --}} + + {{ $item['name'] }} + + + {{-- Hover actions --}} + +
+@endif diff --git a/resources/views/pages/manager.blade.php b/resources/views/pages/manager.blade.php index 55b3c3b..2910b1b 100644 --- a/resources/views/pages/manager.blade.php +++ b/resources/views/pages/manager.blade.php @@ -1,35 +1,27 @@ - + {{ $page->name }} — File Manager - @vite(['resources/css/app.css']) + @vite(['resources/css/app.css', 'resources/js/file-manager.js']) @livewireStyles - - {{-- CodeMirror 6 via ESM import map (no bundler required for editor) --}} - + @fluxStyles + - + @livewireScripts + @fluxScripts diff --git a/resources/views/pages/preview.blade.php b/resources/views/pages/preview.blade.php new file mode 100644 index 0000000..7438348 --- /dev/null +++ b/resources/views/pages/preview.blade.php @@ -0,0 +1,138 @@ + + + + + + Preview — {{ $page->name }} + @vite(['resources/css/app.css']) + @fluxStyles + + + + + {{-- ── Toolbar ─────────────────────────────────────────────────────────── --}} +
+ + {{-- Back --}} + + + + + Editor + + +
+ + {{-- URL bar --}} +
+ + + + + +
+ + {{-- Device toggles --}} +
+ +
+ + {{-- Reload --}} + + + {{-- Open externally --}} + + + + + +
+ + {{-- ── Preview frame ───────────────────────────────────────────────────── --}} +
+
+ +
+
+ + @fluxScripts + + diff --git a/routes/web.php b/routes/web.php index b856f24..c32b862 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use App\Http\Controllers\DashboardController; use App\Http\Controllers\FileManagerController; use App\Http\Controllers\PageController; +use App\Http\Controllers\PagePreviewController; use App\Http\Controllers\PageServeController; use Illuminate\Support\Facades\Route; @@ -19,33 +20,34 @@ })->name('home'); Route::middleware(['auth', 'verified'])->group(function () { + + // Dashboard Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); // Page management Route::get('/pages/create', [PageController::class, 'create'])->name('pages.create'); Route::post('/pages', [PageController::class, 'store'])->name('pages.store'); Route::get('/pages/{slug}/manager', [FileManagerController::class, 'index'])->name('pages.manager'); + Route::get('/pages/{slug}/preview', [PagePreviewController::class, 'show'])->name('pages.preview'); Route::delete('/pages/{slug}', [PageController::class, 'destroy'])->name('pages.destroy'); + Route::put('/pages/{slug}/settings', [PageController::class, 'updateSettings'])->name('pages.settings'); - // File Manager API (JSON, consumed by Livewire / JS) + // File Manager JSON API Route::prefix('/pages/{slug}/files')->name('pages.files.')->group(function () { Route::get('/', [FileManagerController::class, 'list'])->name('list'); + Route::get('/read', [FileManagerController::class, 'read'])->name('read'); Route::post('/', [FileManagerController::class, 'upload'])->name('upload'); Route::post('/create', [FileManagerController::class, 'create'])->name('create'); Route::put('/', [FileManagerController::class, 'save'])->name('save'); Route::delete('/', [FileManagerController::class, 'delete'])->name('delete'); Route::post('/folder', [FileManagerController::class, 'createFolder'])->name('folder'); Route::put('/rename', [FileManagerController::class, 'rename'])->name('rename'); - Route::get('/read', [FileManagerController::class, 'read'])->name('read'); }); - - // Page settings - Route::put('/pages/{slug}/settings', [PageController::class, 'updateSettings'])->name('pages.settings'); }); /* |-------------------------------------------------------------------------- -| Subdomain Routes (must be defined before path-based catch-all) +| Subdomain Routes (must be defined before path catch-all) |-------------------------------------------------------------------------- */ Route::domain('{slug}.statichtmlsites.mtex.dev') @@ -57,7 +59,7 @@ /* |-------------------------------------------------------------------------- -| Path-based Page Serving (catch-all – keep last) +| Path-based Page Serving (catch-all – keep last) |-------------------------------------------------------------------------- */ Route::get('/{slug}/{path?}', [PageServeController::class, 'serve']) diff --git a/vite.config.js b/vite.config.js index f65249e..f28608b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,21 +1,20 @@ -import { - defineConfig -} from 'vite'; +import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; -import tailwindcss from "@tailwindcss/vite"; +import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ plugins: [ + tailwindcss(), laravel({ - input: ['resources/css/app.css', 'resources/js/app.js'], - refresh: true, + input: [ + 'resources/css/app.css', + 'resources/js/app.js', + 'resources/js/file-manager.js', + ], + refresh: [ + 'resources/views/**', + 'routes/**', + ], }), - tailwindcss(), ], - server: { - cors: true, - watch: { - ignored: ['**/storage/framework/views/**'], - }, - }, }); From c092dfa1da72cd6a990e6ff1030fbcb059a580b1 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 20:37:02 +0100 Subject: [PATCH 8/9] fix: composer.json --- composer.json | 3 +- composer.lock | 9913 +++++++++++++++++++++++++++++++++++++++++++++++++ phpunit.xml | 28 +- 3 files changed, 9930 insertions(+), 14 deletions(-) create mode 100644 composer.lock diff --git a/composer.json b/composer.json index 2cfe04e..8b11659 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,7 @@ "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", "pestphp/pest": "^4.4", - "pestphp/pest-plugin-laravel": "^4.1", - "phpunit/phpunit": "^11.0.1" + "pestphp/pest-plugin-laravel": "^4.1" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..8fcccde --- /dev/null +++ b/composer.lock @@ -0,0 +1,9913 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b33d07e938b3692a14088c8f0a47317d", + "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3" + }, + "time": "2025-11-19T17:15:36+00:00" + }, + { + "name": "brick/math", + "version": "0.14.8", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.14.8" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2026-02-10T14:33:43+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "shasum": "" + }, + "require": { + "php": "^8.2|^8.3|^8.4|^8.5" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2025-10-31T18:51:33+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:43:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.9.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-03-10T16:41:02+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "laravel/breeze", + "version": "v2.4.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/breeze.git", + "reference": "28cefeaf6af20177ddf5cc7b93e87e4ad79d533f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/breeze/zipball/28cefeaf6af20177ddf5cc7b93e87e4ad79d533f", + "reference": "28cefeaf6af20177ddf5cc7b93e87e4ad79d533f", + "shasum": "" + }, + "require": { + "illuminate/console": "^11.0|^12.0|^13.0", + "illuminate/filesystem": "^11.0|^12.0|^13.0", + "illuminate/support": "^11.0|^12.0|^13.0", + "illuminate/validation": "^11.0|^12.0|^13.0", + "php": "^8.2.0", + "symfony/console": "^7.0|^8.0" + }, + "require-dev": { + "laravel/framework": "^11.0|^12.0|^13.0", + "orchestra/testbench-core": "^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Breeze\\BreezeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Breeze\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/breeze/issues", + "source": "https://github.com/laravel/breeze" + }, + "time": "2026-03-10T19:59:01+00:00" + }, + { + "name": "laravel/fortify", + "version": "v1.36.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/fortify.git", + "reference": "cad8bfeb63f6818f173d40090725c565c92651d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/fortify/zipball/cad8bfeb63f6818f173d40090725c565c92651d4", + "reference": "cad8bfeb63f6818f173d40090725c565c92651d4", + "shasum": "" + }, + "require": { + "bacon/bacon-qr-code": "^3.0", + "ext-json": "*", + "illuminate/console": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "pragmarx/google2fa": "^9.0" + }, + "require-dev": { + "orchestra/testbench": "^8.36|^9.15|^10.8|^11.0", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Fortify\\FortifyServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Fortify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Backend controllers and scaffolding for Laravel authentication.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/fortify/issues", + "source": "https://github.com/laravel/fortify" + }, + "time": "2026-03-10T19:59:49+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.54.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "325497463e7599cd14224c422c6e5dd2fe832868" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/325497463e7599cd14224c422c6e5dd2fe832868", + "reference": "325497463e7599cd14224c422c6e5dd2fe832868", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12|^0.13|^0.14", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.8.1", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.9.0", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0|^1.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2026-03-10T20:25:56+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.14", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/9f0e371244eedfe2ebeaa72c79c54bb5df6e0176", + "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0|^8.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.14" + }, + "time": "2026-03-01T09:02:38+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.10", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669", + "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-02-20T19:59:49+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.11.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.11.1" + }, + "time": "2026-02-06T14:12:35+00:00" + }, + { + "name": "league/commonmark", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "84b1ca48347efdbe775426f108622a42735a6579" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579", + "reference": "84b1ca48347efdbe775426f108622a42735a6579", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.9-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2026-03-05T21:37:03+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.32.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/254b1595b16b22dbddaaef9ed6ca9fdac4956725", + "reference": "254b1595b16b22dbddaaef9ed6ca9fdac4956725", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.32.0" + }, + "time": "2026-02-25T17:01:41+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/uri", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "4436c6ec8d458e4244448b069cc572d088230b76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", + "reference": "4436c6ec8d458e4244448b069cc572d088230b76", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-01-14T17:24:56+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-01-15T06:54:53+00:00" + }, + { + "name": "livewire/blaze", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/livewire/blaze.git", + "reference": "b7d8976e540f792aaf52c4955c3576fcc7eb5c0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/blaze/zipball/b7d8976e540f792aaf52c4955c3576fcc7eb5c0e", + "reference": "b7d8976e540f792aaf52c4955c3576fcc7eb5c0e", + "shasum": "" + }, + "require": { + "illuminate/support": "^10.0|^11.0|^12.0", + "illuminate/view": "^10.0|^11.0|^12.0", + "nikic/php-parser": "^5.0", + "php": "^8.1" + }, + "conflict": { + "livewire/flux": "<2.12.1" + }, + "require-dev": { + "livewire/flux": "dev-main", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "pestphp/pest": "^2.0|^3.0", + "pestphp/pest-plugin-laravel": "^2.0|^3.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Livewire\\Blaze\\BlazeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Livewire\\Blaze\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio" + } + ], + "description": "A tool for optimizing Blade component performance by folding them into parent templates", + "support": { + "issues": "https://github.com/livewire/blaze/issues", + "source": "https://github.com/livewire/blaze/tree/v1.0.8" + }, + "time": "2026-03-14T15:06:24+00:00" + }, + { + "name": "livewire/flux", + "version": "v2.13.0", + "source": { + "type": "git", + "url": "https://github.com/livewire/flux.git", + "reference": "741be2d4526e90b97c7a59e079a2f27ecdce2461" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/flux/zipball/741be2d4526e90b97c7a59e079a2f27ecdce2461", + "reference": "741be2d4526e90b97c7a59e079a2f27ecdce2461", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "illuminate/view": "^10.0|^11.0|^12.0|^13.0", + "laravel/prompts": "^0.1|^0.2|^0.3", + "livewire/livewire": "^3.7.4|^4.0", + "php": "^8.1", + "symfony/console": "^6.0|^7.0|^8.0" + }, + "conflict": { + "livewire/blaze": "<1.0.0-beta.2" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Flux": "Flux\\Flux" + }, + "providers": [ + "Flux\\FluxServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Flux\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "proprietary" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "The official UI component library for Livewire.", + "keywords": [ + "components", + "flux", + "laravel", + "livewire", + "ui" + ], + "support": { + "issues": "https://github.com/livewire/flux/issues", + "source": "https://github.com/livewire/flux/tree/v2.13.0" + }, + "time": "2026-03-03T03:32:35+00:00" + }, + { + "name": "livewire/livewire", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "93e972fa42c1b34fff1550093ab94f778d81ea5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/93e972fa42c1b34fff1550093ab94f778d81ea5a", + "reference": "93e972fa42c1b34fff1550093ab94f778d81ea5a", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0|^11.0|^12.0|^13.0", + "illuminate/routing": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^10.0|^11.0|^12.0|^13.0", + "laravel/prompts": "^0.1.24|^0.2|^0.3", + "league/mime-type-detection": "^1.9", + "php": "^8.1", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/http-kernel": "^6.2|^7.0|^8.0" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^10.15.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^8.21.0|^9.0|^10.0|^11.0", + "orchestra/testbench-dusk": "^8.24|^9.1|^10.0|^11.0", + "phpunit/phpunit": "^10.4|^11.5|^12.5", + "psy/psysh": "^0.11.22|^0.12" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Livewire": "Livewire\\Livewire" + }, + "providers": [ + "Livewire\\LivewireServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v4.2.1" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2026-02-28T00:01:19+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8 || ^2.0", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2026-01-02T08:56:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.11.3", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "6a7e652845bb018c668220c2a545aded8594fbbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6a7e652845bb018c668220c2a545aded8594fbbf", + "reference": "6a7e652845bb018c668220c2a545aded8594fbbf", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbonphp.github.io/carbon/", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2026-03-11T17:23:39+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.5" + }, + "time": "2026-02-23T03:47:12+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.3" + }, + "time": "2026-02-13T03:05:33+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "require-dev": { + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "It's like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2026-02-16T23:10:27+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v9.0.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/e6bc62dd6ae83acc475f57912e27466019a1f2cf", + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v9.0.0" + }, + "time": "2025-09-19T22:51:08+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.21", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", + "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" + }, + "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.21" + }, + "time": "2026-03-06T21:21:28+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "8429c78ca35a09f27565311b98101e2826affde0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.2" + }, + "time": "2025-12-14T04:43:48+00:00" + }, + { + "name": "symfony/clock", + "version": "v8.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v8.0.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-12T15:46:48+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-06T14:06:20+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v8.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "2a178bf80f05dbbe469a337730eba79d61315262" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a178bf80f05dbbe469a337730eba79d61315262", + "reference": "2a178bf80f05dbbe469a337730eba79d61315262", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v8.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-17T13:07:04+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8da531f364ddfee53e36092a7eebbbd0b775f6b8", + "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-20T16:42:42+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v8.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-05T11:45:55+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "reference": "8655bf1076b7a3a346cb11413ffdabff50c7ffcf", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-29T09:40:50+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f94b3e7b7dafd40e666f0c9ff2084133bae41e81", + "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-06T13:15:18+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3b3fcf386c809be990c922e10e4c620d6367cab1", + "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-06T16:33:18+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "b02726f39a20bc65e30364f5c750c4ddbf1f58e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/b02726f39a20bc65e30364f5c750c4ddbf1f58e9", + "reference": "b02726f39a20bc65e30364f5c750c4ddbf1f58e9", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-25T16:50:00+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/da5ab4fde3f6c88ab06e96185b9922f48b677cd1", + "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-05T15:24:09+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "608476f4604102976d687c483ac63a79ba18cc97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", + "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-26T15:07:59+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "238d749c56b804b31a9bf3e26519d93b65a60938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/238d749c56b804b31a9bf3e26519d93b65a60938", + "reference": "238d749c56b804b31a9bf3e26519d93b65a60938", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-25T16:50:00+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:30:57+00:00" + }, + { + "name": "symfony/string", + "version": "v8.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-09T10:14:57+00:00" + }, + { + "name": "symfony/translation", + "version": "v8.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b", + "reference": "13ff19bcf2bea492d3c2fbeaa194dd6f4599ce1b", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/http-client-contracts": "<2.5", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v8.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-17T13:07:04+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "7719ce8aba76be93dfe249192f1fbfa52c588e36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/7719ce8aba76be93dfe249192f1fbfa52c588e36", + "reference": "7719ce8aba76be93dfe249192f1fbfa52c588e36", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-03T23:30:35+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "045321c440ac18347b136c63d2e9bf28a2dc0291" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/045321c440ac18347b136c63d2e9bf28a2dc0291", + "reference": "045321c440ac18347b136c63d2e9bf28a2dc0291", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-15T10:53:20+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" + }, + "time": "2025-12-02T11:56:42+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.3", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "955e7815d677a3eaa7075231212f2110983adecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.4", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:49:13+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + } + ], + "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.19.0", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6", + "reference": "7c6c29af7c4b406b49ce0c6b0a3a81d3684474e6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.5.3 || ^13.0.1", + "phpunit/php-file-iterator": "^6.0.1 || ^7", + "phpunit/php-timer": "^8 || ^9", + "phpunit/phpunit": "^12.5.9 || ^13", + "sebastian/environment": "^8.0.3 || ^9", + "symfony/console": "^7.4.4 || ^8.0.4", + "symfony/process": "^7.4.5 || ^8.0.5" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.38", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.12", + "phpstan/phpstan-strict-rules": "^2.0.8", + "symfony/filesystem": "^7.4.0 || ^8.0.1" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.19.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2026-02-06T10:53:26+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2026-02-09T13:44:54+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "bdec963f53172c5e36330f3a400604c69bf02d39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/bdec963f53172c5e36330f3a400604c69bf02d39", + "reference": "bdec963f53172c5e36330f3a400604c69bf02d39", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.94.2", + "illuminate/view": "^12.54.1", + "larastan/larastan": "^3.9.3", + "laravel-zero/framework": "^12.0.5", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2026-03-12T15:51:39+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.53.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "e340eaa2bea9b99192570c48ed837155dbf24fbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/e340eaa2bea9b99192570c48ed837155dbf24fbb", + "reference": "e340eaa2bea9b99192570c48ed837155dbf24fbb", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2026-02-06T12:16:02+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", + "reference": "a1ed3fa530fd60bc515f9303e8520fcb7d4bd935", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.2", + "laravel/framework": "^11.48.0 || ^12.52.0", + "laravel/pint": "^1.27.1", + "orchestra/testbench-core": "^9.12.0 || ^10.9.0", + "pestphp/pest": "^3.8.5 || ^4.4.1 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.3 || ^9.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-02-17T17:33:08+00:00" + }, + { + "name": "pestphp/pest", + "version": "v4.4.2", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701", + "reference": "5d42e8fe3ae1d9fdf7c9f73ee88138fd30265701", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.19.0", + "nunomaduro/collision": "^8.9.1", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest-plugin": "^4.0.0", + "pestphp/pest-plugin-arch": "^4.0.0", + "pestphp/pest-plugin-mutate": "^4.0.1", + "pestphp/pest-plugin-profanity": "^4.2.1", + "php": "^8.3.0", + "phpunit/phpunit": "^12.5.12", + "symfony/process": "^7.4.5|^8.0.5" + }, + "conflict": { + "filp/whoops": "<2.18.3", + "phpunit/phpunit": ">12.5.12", + "sebastian/exporter": "<7.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^4.1.0", + "pestphp/pest-plugin-browser": "^4.3.0", + "pestphp/pest-plugin-type-coverage": "^4.0.3", + "psy/psysh": "^0.12.21" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Shard", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v4.4.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-03-10T21:09:12+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "reference": "9d4b93d7f73d3f9c3189bb22c220fef271cdf568", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.3" + }, + "conflict": { + "pestphp/pest": "<4.0.0" + }, + "require-dev": { + "composer/composer": "^2.8.10", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-08-20T12:35:58+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "reference": "25bb17e37920ccc35cbbcda3b00d596aadf3e58d", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "ta-tikoma/phpunit-architecture-test": "^0.8.5" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T13:10:51+00:00" + }, + { + "name": "pestphp/pest-plugin-laravel", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-laravel.git", + "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-laravel/zipball/3057a36669ff11416cc0dc2b521b3aec58c488d0", + "reference": "3057a36669ff11416cc0dc2b521b3aec58c488d0", + "shasum": "" + }, + "require": { + "laravel/framework": "^11.45.2|^12.52.0|^13.0", + "pestphp/pest": "^4.4.1", + "php": "^8.3.0" + }, + "require-dev": { + "laravel/dusk": "^8.3.6", + "orchestra/testbench": "^9.13.0|^10.9.0|^11.0", + "pestphp/pest-dev-tools": "^4.1.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Laravel\\Plugin" + ] + }, + "laravel": { + "providers": [ + "Pest\\Laravel\\PestServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Laravel Plugin", + "keywords": [ + "framework", + "laravel", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-laravel/tree/v4.1.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2026-02-21T00:29:45+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/d9b32b60b2385e1688a68cc227594738ec26d96c", + "reference": "d9b32b60b2385e1688a68cc227594738ec26d96c", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.6.1", + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v4.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-21T20:19:25+00:00" + }, + { + "name": "pestphp/pest-plugin-profanity", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-profanity.git", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "reference": "343cfa6f3564b7e35df0ebb77b7fa97039f72b27", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^4.0.0", + "php": "^8.3" + }, + "require-dev": { + "faissaloux/pest-plugin-inside": "^1.9", + "pestphp/pest": "^4.0.0", + "pestphp/pest-dev-tools": "^4.0.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Profanity\\Plugin" + ] + } + }, + "autoload": { + "psr-4": { + "Pest\\Profanity\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest Profanity Plugin", + "keywords": [ + "framework", + "pest", + "php", + "plugin", + "profanity", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.1" + }, + "time": "2025-12-08T00:13:17+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/897b5986ece6b4f9d8413fea345c7d49c757d6bf", + "reference": "897b5986ece6b4f9d8413fea345c7d49c757d6bf", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.2" + }, + "time": "2026-03-01T18:43:49+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + }, + "time": "2026-01-25T14:56:51+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.5.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b015312f28dd75b75d3422ca37dff2cd1a565e8d", + "reference": "b015312f28dd75b75d3422ca37dff2cd1a565e8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-02-06T06:01:44+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T14:04:18+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.5.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/418e06b3b46b0d54bad749ff4907fc7dfb530199", + "reference": "418e06b3b46b0d54bad749ff4907fc7dfb530199", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.3", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.4", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.3", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.12" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2026-02-16T08:34:36+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2025-09-14T09:36:45+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6", + "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:28:48+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-08-12T14:11:56+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:16:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-29T11:29:25+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:57:12+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/yaml", + "version": "v8.0.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/5f006c50a981e1630bbb70ad409c5d85f9a716e0", + "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<7.4" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v8.0.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-02-09T10:14:57+00:00" + }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.7", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/1248f3f506ca9641d4f68cebcd538fa489754db8", + "reference": "1248f3f506ca9641d4f68cebcd538fa489754db8", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18.0 || ^5.0.0", + "php": "^8.1.0", + "phpdocumentor/reflection-docblock": "^5.3.0 || ^6.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0 || ^13.0.0", + "symfony/finder": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "require-dev": { + "laravel/pint": "^1.13.7", + "phpstan/phpstan": "^1.10.52" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPUnit\\Architecture\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ni Shi", + "email": "futik0ma011@gmail.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Methods for testing application architecture", + "keywords": [ + "architecture", + "phpunit", + "stucture", + "test", + "testing" + ], + "support": { + "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.7" + }, + "time": "2026-02-17T17:25:14+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.1.6", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.1.6" + }, + "time": "2026-02-27T10:28:38+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/phpunit.xml b/phpunit.xml index 8863459..95d5cc8 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -3,6 +3,7 @@ xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true"> + tests/Unit @@ -11,23 +12,26 @@ tests/Feature + app + - - - - - - - - - - - - + + + + + + + + + + + + + From 52d4ec475da8bc5c890384fd436f2b2e30d8c3b3 Mon Sep 17 00:00:00 2001 From: Fabian ternis Date: Sat, 14 Mar 2026 20:53:11 +0100 Subject: [PATCH 9/9] fix: Flux (v2) works now --- app/Livewire/FileManager.php | 13 +- bootstrap/providers.php | 6 +- resources/js/app.js | 14 +- resources/js/file-manager.js | 137 +++++++----------- .../views/components/app-layout.blade.php | 10 +- .../views/components/auth-layout.blade.php | 2 - .../views/livewire/file-manager.blade.php | 26 ++-- resources/views/pages/manager.blade.php | 2 - resources/views/pages/preview.blade.php | 2 - resources/views/welcome.blade.php | 2 - 10 files changed, 88 insertions(+), 126 deletions(-) diff --git a/app/Livewire/FileManager.php b/app/Livewire/FileManager.php index 8112283..81ff2e6 100644 --- a/app/Livewire/FileManager.php +++ b/app/Livewire/FileManager.php @@ -3,6 +3,7 @@ namespace App\Livewire; use App\Models\Page; +use Flux\Flux; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; use Livewire\Attributes\Computed; @@ -209,7 +210,7 @@ public function createFileAndClose(): void $this->disk()->put($full, ''); $this->newFileName = ''; - $this->dispatch('flux:modal.close', name: 'new-file'); + Flux::modal('new-file')->close(); $this->openFile($relative); unset($this->fileTree); } @@ -237,7 +238,7 @@ public function createFolderAndClose(): void $this->disk()->makeDirectory($this->page->storagePath($relative)); $this->newFolderName = ''; - $this->dispatch('flux:modal.close', name: 'new-folder'); + Flux::modal('new-folder')->close(); $this->showFlash("Folder '{$name}' created."); unset($this->fileTree); } @@ -249,7 +250,7 @@ public function prepareDelete(string $path, string $type = 'file'): void $this->deleteTarget = $path; $this->deleteTargetName = basename($path); $this->deleteTargetType = $type; - $this->dispatch('flux:modal.show', name: 'delete-confirm'); + Flux::modal('delete-confirm')->show(); } /** Execute the pending delete after modal confirmation. */ @@ -274,7 +275,7 @@ public function executeDelete(): void $this->showFlash("Deleted {$this->deleteTargetName}."); $this->deleteTarget = ''; $this->deleteTargetName = ''; - $this->dispatch('flux:modal.close', name: 'delete-confirm'); + Flux::modal('delete-confirm')->close(); unset($this->fileTree); } @@ -338,7 +339,7 @@ public function uploadFile(): void $this->upload = null; $this->showFlash("Uploaded {$name}."); - $this->dispatch('flux:modal.close', name: 'upload'); + Flux::modal('upload')->close(); unset($this->fileTree); } @@ -359,7 +360,7 @@ public function saveSettings(): void ]); $this->page->refresh(); - $this->dispatch('flux:modal.close', name: 'settings'); + Flux::modal('settings')->close(); $this->showFlash('Settings saved.'); if ($this->page->wasChanged('slug')) { diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 5ffd769..38b258d 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -1,9 +1,5 @@ { if (update.selectionSet) publishCursor(update.state); if (!update.docChanged) return; @@ -121,69 +117,48 @@ export function createEditor(content, lang) { publishCursor(editorView.state); } -// ── Alpine components ───────────────────────────────────────────────────────── - -/** - * fileManager() — root Alpine data for the manager page. - */ -window.fileManager = () => ({ - // Open folder paths (array for Alpine reactivity) - openDirs: [], - - // Context menu state - ctx: { show: false, x: 0, y: 0, item: null }, - - // Cursor info (updated by CodeMirror) - cursor: { line: 1, col: 1 }, - - isOpen(path) { - return this.openDirs.includes(path); - }, - - toggleDir(path) { - if (this.isOpen(path)) { - this.openDirs = this.openDirs.filter(p => p !== path); - } else { - this.openDirs = [...this.openDirs, path]; - } - }, - - openAllDirs(paths) { - this.openDirs = [...new Set([...this.openDirs, ...paths])]; - }, - - openContext(e, item) { - this.ctx = { show: true, x: e.clientX, y: e.clientY, item }; - }, - - closeContext() { - this.ctx = { show: false, x: 0, y: 0, item: null }; - }, - - init() { - // Listen for CodeMirror cursor events - window.addEventListener('cm-cursor', e => { - this.cursor = e.detail; - }); - - // Listen for Livewire editor load event - window.addEventListener('load-editor', e => { - createEditor(e.detail.content, e.detail.language); - }); - - // Close context menu on outside click - window.addEventListener('click', () => this.closeContext()); - window.addEventListener('keydown', e => { - if (e.key === 'Escape') this.closeContext(); - }); - }, - - save() { - triggerSave(); - }, +// ── Alpine component — registered on Flux/Livewire's Alpine instance ────────── +// +// We use alpine:init so our data component is registered BEFORE Alpine +// processes the DOM, but AFTER Flux has registered its own plugins +// (including x-flux-modal and $dispatch helpers). + +document.addEventListener('alpine:init', () => { + // Safety guard: window.Alpine is set by @fluxScripts before this fires. + if (!window.Alpine) return; + + window.Alpine.data('fileManager', () => ({ + openDirs: [], + ctx: { show: false, x: 0, y: 0, item: null }, + cursor: { line: 1, col: 1 }, + + isOpen(path) { return this.openDirs.includes(path); }, + toggleDir(path) { + this.openDirs = this.isOpen(path) + ? this.openDirs.filter(p => p !== path) + : [...this.openDirs, path]; + }, + + openContext(e, item) { + this.ctx = { show: true, x: e.clientX, y: e.clientY, item }; + }, + closeContext() { + this.ctx = { show: false, x: 0, y: 0, item: null }; + }, + + init() { + window.addEventListener('cm-cursor', e => { this.cursor = e.detail; }); + + window.addEventListener('load-editor', e => { + createEditor(e.detail.content, e.detail.language); + }); + + window.addEventListener('click', () => this.closeContext()); + window.addEventListener('keydown', e => { + if (e.key === 'Escape') this.closeContext(); + }); + }, + + save() { triggerSave(); }, + })); }); - -// ── Bootstrap ───────────────────────────────────────────────────────────────── - -window.Alpine = Alpine; -Alpine.start(); diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php index 9f81c79..eb49845 100644 --- a/resources/views/components/app-layout.blade.php +++ b/resources/views/components/app-layout.blade.php @@ -9,10 +9,9 @@ {{ $title }} @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles - @fluxStyles @stack('head') - + @@ -41,12 +40,14 @@ @if (session('success')) -
{{ session('success') }} - +
@endif @@ -64,7 +65,6 @@ class="border-b border-green-200 bg-green-50 dark:bg-green-950 dark:border-green
{{ $slot }}
@livewireScripts - @fluxScripts @stack('scripts') diff --git a/resources/views/components/auth-layout.blade.php b/resources/views/components/auth-layout.blade.php index d627789..6bc5859 100644 --- a/resources/views/components/auth-layout.blade.php +++ b/resources/views/components/auth-layout.blade.php @@ -8,7 +8,6 @@ {{ $title }} — {{ config('app.name') }} @vite(['resources/css/app.css', 'resources/js/app.js']) - @fluxStyles @@ -26,6 +25,5 @@
- @fluxScripts diff --git a/resources/views/livewire/file-manager.blade.php b/resources/views/livewire/file-manager.blade.php index 13ffbda..8c247e2 100644 --- a/resources/views/livewire/file-manager.blade.php +++ b/resources/views/livewire/file-manager.blade.php @@ -77,7 +77,7 @@ class="hidden sm:flex items-center gap-1.5 text-xs px-2.5 py-1.5 rounded-md {{-- Settings --}} @@ -274,7 +274,7 @@ class="w-full h-full rounded-lg border-0">

No file open

Select a file from the explorer to start editing