diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bae70ec..595d121 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,21 +1,30 @@ -name: Tests +name: tests on: + push: + branches: [main] pull_request: - workflow_call: - workflow_dispatch: jobs: - - just-pass: + tests: runs-on: ubuntu-latest + strategy: + matrix: + php: ['8.4'] steps: - - run: echo "Just pass" + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, intl, bcmath + coverage: none + + - name: Install dependencies + run: composer update --prefer-dist --no-interaction --no-progress - pest-tests: - if: false - uses: ./.github/workflows/run-pest-tests.yml + - name: Check code style + run: composer format:test - e2e-tests: - if: false - uses: ./.github/workflows/run-e2e-tests.yml \ No newline at end of file + - name: Run tests + run: composer test diff --git a/.gitignore b/.gitignore index abaa940..ce0d33a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /vendor /node_modules -.env \ No newline at end of file +composer.lock +.phpunit.result.cache +.phpunit.cache +.DS_Store diff --git a/LICENSE.md b/LICENSE.md index 3b2b6d0..f438173 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,9 @@ -The MIT License (MIT) +# The MIT License (MIT) -Copyright (c) komma-softhouse software development +Copyright (c) Komma SoftHouse / Elias Olivtradet -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..54acbc0 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# Skeleton Plugin + +๐Ÿš€ Skeleton template for building Filament 5.x plugins | Komma SoftHouse plugin standard + +This repository replaces the old `skeleton-module` (Laravel Modules) template for everything that ships as a **redistributable Filament plugin**. Internal app modules keep their own standard; anything installed via Composer into third-party Laravel apps starts here. + +Built on the official Filament 5.x plugin guidelines: + +- [Getting started](https://filamentphp.com/docs/5.x/plugins/getting-started) +- [Panel plugins](https://filamentphp.com/docs/5.x/plugins/panel-plugins) +- [Building a panel plugin](https://filamentphp.com/docs/5.x/plugins/building-a-panel-plugin) +- [Building a standalone plugin](https://filamentphp.com/docs/5.x/plugins/building-a-standalone-plugin) +- [Configurable resources and pages](https://filamentphp.com/docs/5.x/plugins/configurable-resources-and-pages) + +## Requirements + +- PHP >= 8.4 +- Laravel >= 13.0 +- Filament >= 5.0 + +## Creating a new plugin + +1. On GitHub, click **Use this template** to create the new plugin repository. +2. Clone it and run the configurator: + +```bash +php ./configure.php +``` + +Answer the prompts (plugin name, vendor, namespace, description). The script rewrites the package name, namespaces, class names and file names, optionally resets git history, and removes itself โ€” the same workflow as the official Filament plugin skeleton. + +3. Install and verify: + +```bash +composer install +composer test +``` + +4. Read [`docs/how-to-build-a-filament-plugin.md`](docs/how-to-build-a-filament-plugin.md) โ€” the Komma plugin standard โ€” and start building. + +## Using a plugin built from this skeleton + +Register it in the host panel provider: + +```php +use Komma\SkeletonPlugin\SkeletonPlugin; + +public function panel(Panel $panel): Panel +{ + return $panel + ->plugin( + SkeletonPlugin::make() + ->exampleFeature(), + ); +} +``` + +Every feature toggle ships **disabled by default**: a host that enables nothing gets zero extra dependencies. + +## What's inside + +| Path | Purpose | +| ---------------------------------------- | -------------------------------------------------------------------------------------- | +| `src/SkeletonPlugin.php` | Panel plugin object (`getId` / `register` / `boot`, `make()`, `get()`, fluent toggles) | +| `src/SkeletonPluginServiceProvider.php` | `PackageServiceProvider` (spatie/laravel-package-tools) with asset registration notes | +| `config/skeleton-plugin.php` | Publishable config โ€” toggles off by default | +| `resources/lang/*.json` | JSON translations keyed by English literals (`__('English literal')`) | +| `tests/` | Pest + Testbench with the full Filament 5 provider set | +| `.github/workflows/tests.yml` | CI: Pint (style) + Pest on PHP 8.4 | +| `docs/how-to-build-a-filament-plugin.md` | The Komma ecosystem plugin standard | + +## Testing + +```bash +composer test +``` + +## Credits + +- [Elias Olivtradet](https://github.com/edeoliv) +- [All Contributors](../../contributors) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/app/Console/Commands/ModuleBuildCommand.php b/app/Console/Commands/ModuleBuildCommand.php deleted file mode 100644 index f8f6e57..0000000 --- a/app/Console/Commands/ModuleBuildCommand.php +++ /dev/null @@ -1,444 +0,0 @@ -info('Creating a new module'); - - if ($this->option('name')) { - $moduleName = $this->option('name'); - } else { - $moduleName = $this->ask('What is the name of the module?'); - } - - if (empty($moduleName)) { - $this->error('Module name is required!'); - - return 1; - } - - // Standardize the name (first letter uppercase) - $moduleName = ucfirst($moduleName); - $moduleNameLower = Str::lower($moduleName); - - // Check if module already exists - $modulePath = base_path('Modules/' . $moduleName); - - if (File::exists($modulePath)) { - $this->error("Module {$moduleName} already exists!"); - - return 1; - } - - // Skeleton module path - $skeletonPath = base_path('Modules/Skeleton'); - - $this->info("Copying Skeleton module to {$moduleName}..."); - - // Copy files from Skeleton to new module - File::copyDirectory($skeletonPath, $modulePath); - - // Remove .git directory from new module if it exists - if (File::exists($modulePath . '/.git')) { - File::deleteDirectory($modulePath . '/.git'); - $this->info("Removing .git directory from new module..."); - } - - // Remove /vendor directory from new module if it exists - if (File::exists($modulePath . '/vendor')) { - File::deleteDirectory($modulePath . '/vendor'); - $this->info("Removing vendor directory from new module..."); - } - - // Now we need to rename all files containing "Skeleton" in the name - $this->info("Renaming files..."); - $this->renameFiles($modulePath, 'Skeleton', $moduleName); - - // Replace all occurrences of "Skeleton" and "skeleton" in files - $this->info("Replacing references in files..."); - $this->replaceInFiles($modulePath, 'Skeleton', $moduleName, 'skeleton', $moduleNameLower); - - // Update specific files - $this->info("Updating specific configurations..."); - $this->updateComposerJson($modulePath, $moduleNameLower); - $this->updateModuleJson($modulePath, $moduleName, $moduleNameLower); - $this->updateServiceProvider($modulePath, $moduleName, $moduleNameLower); - - // Remove this command from the new module - $this->info("Removing ModuleBuildCommand from the new module..."); - $commandPath = $modulePath . '/app/Console/Commands/ModuleBuildCommand.php'; - - if (File::exists($commandPath)) { - File::delete($commandPath); - } - - // Remove Console directory if empty - $consoleDir = $modulePath . '/app/Console'; - $commandsDir = $modulePath . '/app/Console/Commands'; - - if (File::exists($commandsDir) && count(File::files($commandsDir)) === 0 && count(File::directories($commandsDir)) === 0) { - File::deleteDirectory($commandsDir); - } - - if (File::exists($consoleDir) && count(File::files($consoleDir)) === 0 && count(File::directories($consoleDir)) === 0) { - File::deleteDirectory($consoleDir); - } - - // Run composer dump-autoload - $this->info("Running composer dump-autoload..."); - $process = new Process(['composer', 'dump-autoload']); - $process->run(); - - if ($process->isSuccessful()) { - $this->info($process->getOutput()); - } else { - $this->error("Failed to run composer dump-autoload"); - $this->error($process->getErrorOutput()); - } - - $this->newLine(); - $this->info("Module {$moduleName} created successfully!"); - $this->info("Path: {$modulePath}"); - $this->newLine(); - - // Enable the new module - $this->info("Enabling the new module..."); - - $modulesFile = base_path('modules_statuses.json'); - - if (File::exists($modulesFile)) { - $modules = json_decode(File::get($modulesFile), true); - $modules[$moduleName] = true; - File::put($modulesFile, json_encode($modules, JSON_PRETTY_PRINT)); - $this->info("Module {$moduleName} enabled successfully!"); - } else { - $this->warn("modules_statuses.json file not found!"); - } - - return self::SUCCESS; - } - - /** - * Update the composer.json file - */ - private function updateComposerJson($modulePath, $moduleNameLower) - { - $filePath = $modulePath . '/composer.json'; - - if (! File::exists($filePath)) { - $this->warn("composer.json file not found!"); - - return; - } - - $this->line("Updating composer.json..."); - - $content = json_decode(File::get($filePath), true); - - // Update name - $content['name'] = "e2tmk/{$moduleNameLower}-module"; - - // Update namespaces in autoload and autoload-dev - $content['autoload']['psr-4'] = $this->updateNamespaces($content['autoload']['psr-4'], 'Skeleton', ucfirst($moduleNameLower)); - - if (isset($content['autoload-dev']['psr-4'])) { - $content['autoload-dev']['psr-4'] = $this->updateNamespaces($content['autoload-dev']['psr-4'], 'Skeleton', ucfirst($moduleNameLower)); - } - - // Save the file - File::put($filePath, json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - - $this->line("composer.json updated."); - } - - /** - * Update namespace keys in an associative array - */ - private function updateNamespaces($namespaces, $oldName, $newName) - { - $updatedNamespaces = []; - - foreach ($namespaces as $namespace => $path) { - $newNamespace = str_replace("\\{$oldName}\\", "\\{$newName}\\", $namespace); - $updatedNamespaces[$newNamespace] = $path; - } - - return $updatedNamespaces; - } - - /** - * Update the module.json file - */ - private function updateModuleJson($modulePath, $moduleName, $moduleNameLower) - { - $filePath = $modulePath . '/module.json'; - - if (! File::exists($filePath)) { - $this->warn("module.json file not found!"); - - return; - } - - $this->line("Updating module.json..."); - - $content = json_decode(File::get($filePath), true); - - // Update name and alias - $content['name'] = $moduleName; - $content['alias'] = $moduleNameLower; - - // Update providers - if (isset($content['providers'])) { - $updatedProviders = []; - - foreach ($content['providers'] as $provider) { - $updatedProviders[] = str_replace('Skeleton', $moduleName, $provider); - } - $content['providers'] = $updatedProviders; - } - - // Update Filament plugins if they exist - if (isset($content['filament_plugins'])) { - $updatedPlugins = []; - - foreach ($content['filament_plugins'] as $plugin) { - $updatedPlugins[] = str_replace('Skeleton', $moduleName, $plugin); - } - $content['filament_plugins'] = $updatedPlugins; - } - - // Save the file - File::put($filePath, json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - - $this->line("module.json updated."); - } - - /** - * Update the ServiceProvider file - */ - private function updateServiceProvider($modulePath, $moduleName, $moduleNameLower) - { - $providerPath = $modulePath . '/app/Providers/' . $moduleName . 'ServiceProvider.php'; - - if (! File::exists($providerPath)) { - $this->warn("ServiceProvider not found at {$providerPath}!"); - - return; - } - - $this->line("Updating ServiceProvider..."); - - $content = File::get($providerPath); - - // Update namespace - $content = str_replace( - 'namespace Modules\\Skeleton\\Providers;', - 'namespace Modules\\' . $moduleName . '\\Providers;', - $content - ); - - // Update the name and nameLower properties - $content = preg_replace( - '/protected string \$name = \'Skeleton\';/', - "protected string \$name = '{$moduleName}';", - $content - ); - - $content = preg_replace( - '/protected string \$nameLower = \'skeleton\';/', - "protected string \$nameLower = '{$moduleNameLower}';", - $content - ); - - // Update the class name - $content = preg_replace( - '/class SkeletonServiceProvider extends ServiceProvider/', - "class {$moduleName}ServiceProvider extends ServiceProvider", - $content - ); - - // Save the file - File::put($providerPath, $content); - - $this->line("ServiceProvider updated."); - } - - /** - * Rename files containing the pattern in their names. - */ - private function renameFiles($directory, $search, $replace) - { - $searchLower = strtolower($search); - $replaceLower = strtolower($replace); - - // Process directories first (bottom-up to avoid issues with renaming) - $directories = array_reverse(File::directories($directory)); - - foreach ($directories as $dir) { - $this->renameFiles($dir, $search, $replace); - - // Rename the directory itself if needed - $dirName = basename($dir); - - if (Str::contains($dirName, $search) || Str::contains($dirName, $searchLower)) { - $newDirName = str_replace( - [$search, $searchLower], - [$replace, $replaceLower], - $dirName - ); - - $newDirPath = str_replace($dirName, $newDirName, $dir); - - if (File::exists($dir) && $dirName !== $newDirName) { - File::move($dir, $newDirPath); - $this->line("Directory renamed: {$dirName} -> {$newDirName}"); - } - } - } - - // Now process files - $files = File::files($directory); - - foreach ($files as $file) { - $filePath = $file->getPathname(); - $fileName = $file->getFilename(); - - // Check if the filename contains the pattern - if (Str::contains($fileName, $search) || Str::contains($fileName, $searchLower)) { - $newFileName = str_replace( - [$search, $searchLower], - [$replace, $replaceLower], - $fileName - ); - - $newFilePath = str_replace($fileName, $newFileName, $filePath); - - // Rename the file - if (File::exists($filePath) && $fileName !== $newFileName) { - File::move($filePath, $newFilePath); - $this->line("File renamed: {$fileName} -> {$newFileName}"); - } - } - } - } - - /** - * Replace all occurrences of patterns in files. - */ - private function replaceInFiles($directory, $search, $replace, $searchLower, $replaceLower) - { - $files = File::allFiles($directory); - - foreach ($files as $file) { - $filePath = $file->getPathname(); - - // Skip binary files or specific files that will be handled separately - if ($this->isBinaryFile($filePath) || $this->shouldSkipFile($filePath)) { - continue; - } - - // Read file content - $content = File::get($filePath); - - // Replace occurrences - $newContent = str_replace( - [$search, $searchLower], - [$replace, $replaceLower], - $content - ); - - // If changes were made, save the file - if ($content !== $newContent) { - File::put($filePath, $newContent); - $this->line("Content updated: " . $file->getRelativePathname()); - } - } - } - - /** - * Check if a file should be skipped in general replacement - */ - private function shouldSkipFile($filePath) - { - // Files that will be processed separately - $skipFiles = [ - 'composer.json', - 'module.json', - ]; - - $fileName = basename($filePath); - - if (in_array($fileName, $skipFiles)) { - return true; - } - - // Check if it's a ServiceProvider - if (Str::endsWith($fileName, 'ServiceProvider.php')) { - return true; - } - - return false; - } - - /** - * Check if a file is binary. - */ - private function isBinaryFile($filePath) - { - // Common binary file extensions - $binaryExtensions = ['png', 'jpg', 'jpeg', 'gif', 'ico', 'zip', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'bin', 'exe', 'dll', 'so', 'dat', 'cache']; - $extension = pathinfo($filePath, PATHINFO_EXTENSION); - - if (in_array(strtolower($extension), $binaryExtensions)) { - return true; - } - - // Try to open the file - if it contains binary characters, return true - $f = fopen($filePath, 'r'); - - if (! $f) { - return true; // If can't open, consider it binary for safety - } - - $blocksize = 512; - $binary = false; - - // Read the beginning of the file to detect binary content - $data = fread($f, $blocksize); - fclose($f); - - // If there's a null byte or other specific control characters, it's considered binary - if (strpos($data, "\0") !== false) { - return true; - } - - return false; - } -} diff --git a/app/Filament/Plugin/SkeletonPlugin.php b/app/Filament/Plugin/SkeletonPlugin.php deleted file mode 100644 index 5bb2380..0000000 --- a/app/Filament/Plugin/SkeletonPlugin.php +++ /dev/null @@ -1,37 +0,0 @@ -discoverResources(in: module_path('Skeleton', 'app/Filament/Resources'), for: 'Modules\\Skeleton\\Filament\\Resources') - ->discoverPages(in: module_path('Skeleton', 'app/Filament/Pages'), for: 'Modules\\Skeleton\\Filament\\Pages') - ->discoverWidgets(in: module_path('Skeleton', 'app/Filament/Widgets'), for: 'Modules\\Skeleton\\Filament\\Widgets'); - } - - public function boot(Panel $panel): void - { - \Event::listen(ServingFilament::class, function () use ($panel): void { - // ... - }); - } - - public static function make(): static - { - return app(static::class); - } -} diff --git a/app/Providers/SkeletonServiceProvider.php b/app/Providers/SkeletonServiceProvider.php deleted file mode 100644 index 0ff5890..0000000 --- a/app/Providers/SkeletonServiceProvider.php +++ /dev/null @@ -1,37 +0,0 @@ -bootConfigViewTranslationAndMigrations(); - - if ($this->app->runningInConsole()) { - $this->commands([ - ModuleBuildCommand::class, - ]); - } - } - - #[\Override] - public function register(): void - { - // ... - } -} diff --git a/composer.json b/composer.json index 6c41647..2001beb 100644 --- a/composer.json +++ b/composer.json @@ -1,34 +1,56 @@ { - "name": "komma-softhouse/skeleton-module", - "description": "", - "type": "laravel-module", + "name": "komma-softhouse/skeleton-plugin", + "description": "๐Ÿš€ Skeleton template for building Filament 5.x plugins | Komma SoftHouse plugin standard", + "keywords": ["komma-softhouse", "qbit-dynamics","e2tmk", "laravel", "filament", "filamentphp-5", "filament-plugin", "skeleton"], + "homepage": "https://github.com/komma-softhouse/skeleton-plugin", "license": "MIT", "authors": [ { "name": "Elias Olivtradet", - "email": "elias.olivtradet@kommasofthouse.com" + "role": "Developer" } ], - "extra": { - "laravel": { - "providers": [], - "aliases": {} - } - }, "require": { - "codeat3/blade-phosphor-icons": "*" + "php": "^8.4", + "filament/filament": "^5.7", + "spatie/laravel-package-tools": "^1.16", + "illuminate/contracts": "^13.0" + }, + "require-dev": { + "laravel/pint": "^1.14", + "nunomaduro/collision": "^8.0", + "orchestra/testbench": "^11.0", + "pestphp/pest": "^4.0", + "pestphp/pest-plugin-laravel": "^4.0" }, "autoload": { "psr-4": { - "Modules\\Skeleton\\": "app/", - "Modules\\Skeleton\\Database\\Factories\\": "database/factories/", - "Modules\\Skeleton\\Database\\Seeders\\": "database/seeders/" + "Komma\\SkeletonPlugin\\": "src/" } }, "autoload-dev": { "psr-4": { - "Modules\\Skeleton\\Tests\\": "tests/" + "Komma\\SkeletonPlugin\\Tests\\": "tests/" + } + }, + "scripts": { + "test": "vendor/bin/pest", + "format": "vendor/bin/pint", + "format:test": "vendor/bin/pint --test" + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "extra": { + "laravel": { + "providers": [ + "Komma\\SkeletonPlugin\\SkeletonPluginServiceProvider" + ] } }, - "repositories": {} + "minimum-stability": "stable", + "prefer-stable": true } diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 63aad9a..0000000 --- a/composer.lock +++ /dev/null @@ -1,2584 +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": "efde226c5285c6795981e0b80e6de7d1", - "packages": [ - { - "name": "blade-ui-kit/blade-icons", - "version": "1.10.1", - "source": { - "type": "git", - "url": "https://github.com/driesvints/blade-icons.git", - "reference": "6e072d021ea6249986c330b93293c33d0c4f0e34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/driesvints/blade-icons/zipball/6e072d021ea6249986c330b93293c33d0c4f0e34", - "reference": "6e072d021ea6249986c330b93293c33d0c4f0e34", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", - "illuminate/filesystem": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", - "illuminate/view": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", - "php": "^7.4|^8.0", - "symfony/console": "^5.3|^6.0|^7.0|^8.0", - "symfony/finder": "^5.3|^6.0|^7.0|^8.0" - }, - "require-dev": { - "mockery/mockery": "^1.5.1", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "phpunit/phpunit": "^9.0|^10.5|^11.0" - }, - "bin": [ - "bin/blade-icons-generate" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "BladeUI\\Icons\\BladeIconsServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "BladeUI\\Icons\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dries Vints", - "homepage": "https://driesvints.com" - } - ], - "description": "A package to easily make use of icons in your Laravel Blade views.", - "homepage": "https://github.com/driesvints/blade-icons", - "keywords": [ - "blade", - "icons", - "laravel", - "svg" - ], - "support": { - "issues": "https://github.com/driesvints/blade-icons/issues", - "source": "https://github.com/driesvints/blade-icons" - }, - "funding": [ - { - "url": "https://github.com/sponsors/driesvints", - "type": "github" - }, - { - "url": "https://www.paypal.com/paypalme/driesvints", - "type": "paypal" - } - ], - "time": "2026-06-30T09:44:12+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": "codeat3/blade-phosphor-icons", - "version": "2.4.0", - "source": { - "type": "git", - "url": "https://github.com/codeat3/blade-phosphor-icons.git", - "reference": "39a40c44908729d20f03f9903f4abcc165b1b02b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/codeat3/blade-phosphor-icons/zipball/39a40c44908729d20f03f9903f4abcc165b1b02b", - "reference": "39a40c44908729d20f03f9903f4abcc165b1b02b", - "shasum": "" - }, - "require": { - "blade-ui-kit/blade-icons": "^1.1", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", - "php": "^7.4|^8.0" - }, - "require-dev": { - "codeat3/blade-icon-generation-helpers": "^0.12", - "codeat3/phpcs-styles": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "phpunit/phpunit": "^9.0|^10.5|^11.0|^12.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Codeat3\\BladePhosphorIcons\\BladePhosphorIconsServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Codeat3\\BladePhosphorIcons\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Swapnil Sarwe", - "homepage": "https://swapnilsarwe.com" - }, - { - "name": "Dries Vints", - "homepage": "https://driesvints.com" - } - ], - "description": "A package to easily make use of \"Phosphor Icons\" in your Laravel Blade views.", - "homepage": "https://github.com/codeat3/blade-phosphor-icons", - "keywords": [ - "blade", - "laravel", - "phosphor-icons" - ], - "support": { - "issues": "https://github.com/codeat3/blade-phosphor-icons/issues", - "source": "https://github.com/codeat3/blade-phosphor-icons/tree/2.4.0" - }, - "funding": [ - { - "url": "https://github.com/swapnilsarwe", - "type": "github" - } - ], - "time": "2026-03-21T13:21:02+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": "illuminate/bus", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/bus.git", - "reference": "03bc86fe5800c6e0e16aaea6c08df96540a68220" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/bus/zipball/03bc86fe5800c6e0e16aaea6c08df96540a68220", - "reference": "03bc86fe5800c6e0e16aaea6c08df96540a68220", - "shasum": "" - }, - "require": { - "illuminate/collections": "^13.0", - "illuminate/conditionable": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/pipeline": "^13.0", - "illuminate/support": "^13.0", - "php": "^8.3" - }, - "suggest": { - "illuminate/queue": "Required to use closures when chaining jobs (^13.0)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Bus\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Bus package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-04T14:04:36+00:00" - }, - { - "name": "illuminate/collections", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/collections.git", - "reference": "e89523328db4947d0a239de5607823402c414825" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/e89523328db4947d0a239de5607823402c414825", - "reference": "e89523328db4947d0a239de5607823402c414825", - "shasum": "" - }, - "require": { - "illuminate/conditionable": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/macroable": "^13.0", - "php": "^8.3", - "symfony/polyfill-php84": "^1.36", - "symfony/polyfill-php85": "^1.36", - "symfony/polyfill-php86": "^1.36" - }, - "suggest": { - "illuminate/http": "Required to convert collections to API resources (^13.0).", - "symfony/var-dumper": "Required to use the dump method (^7.4 || ^8.0)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php", - "helpers.php" - ], - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Collections package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-07T13:46:56+00:00" - }, - { - "name": "illuminate/conditionable", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/conditionable.git", - "reference": "7f1ef52d9a346f829421b296adfb7644a951b216" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/7f1ef52d9a346f829421b296adfb7644a951b216", - "reference": "7f1ef52d9a346f829421b296adfb7644a951b216", - "shasum": "" - }, - "require": { - "php": "^8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Conditionable package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-02-25T16:07:55+00:00" - }, - { - "name": "illuminate/container", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/container.git", - "reference": "8d28410dcefce414b2f159a23c59c28f63362520" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/8d28410dcefce414b2f159a23c59c28f63362520", - "reference": "8d28410dcefce414b2f159a23c59c28f63362520", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^13.0", - "illuminate/reflection": "^13.0", - "php": "^8.3", - "psr/container": "^1.1.1 || ^2.0.1", - "symfony/polyfill-php84": "^1.36", - "symfony/polyfill-php85": "^1.36" - }, - "provide": { - "psr/container-implementation": "1.1 || 2.0" - }, - "suggest": { - "illuminate/auth": "Required to use the Auth attribute", - "illuminate/cache": "Required to use the Cache attribute", - "illuminate/config": "Required to use the Config attribute", - "illuminate/database": "Required to use the DB attribute", - "illuminate/filesystem": "Required to use the Storage attribute", - "illuminate/log": "Required to use the Log or Context attributes" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Container\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Container package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-05T13:59:39+00:00" - }, - { - "name": "illuminate/contracts", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "52fd3118b71254dd083eca6acba39b3f7325ec9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/52fd3118b71254dd083eca6acba39b3f7325ec9a", - "reference": "52fd3118b71254dd083eca6acba39b3f7325ec9a", - "shasum": "" - }, - "require": { - "php": "^8.3", - "psr/container": "^1.1.1 || ^2.0.1", - "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Contracts\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Contracts package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-10T17:21:54+00:00" - }, - { - "name": "illuminate/events", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/events.git", - "reference": "f1c94930eb75b7aa5206eda4e0c89a22166006a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/events/zipball/f1c94930eb75b7aa5206eda4e0c89a22166006a6", - "reference": "f1c94930eb75b7aa5206eda4e0c89a22166006a6", - "shasum": "" - }, - "require": { - "illuminate/bus": "^13.0", - "illuminate/collections": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/macroable": "^13.0", - "illuminate/reflection": "^13.0", - "illuminate/support": "^13.0", - "php": "^8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Illuminate\\Events\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Events package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-13T15:21:29+00:00" - }, - { - "name": "illuminate/filesystem", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/filesystem.git", - "reference": "b42bc51118a4041dd243cab78340dcdc7c5e06c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/b42bc51118a4041dd243cab78340dcdc7c5e06c8", - "reference": "b42bc51118a4041dd243cab78340dcdc7c5e06c8", - "shasum": "" - }, - "require": { - "illuminate/collections": "^13.0", - "illuminate/conditionable": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/macroable": "^13.0", - "illuminate/support": "^13.0", - "php": "^8.3", - "symfony/finder": "^7.4.0 || ^8.0.0" - }, - "suggest": { - "ext-fileinfo": "Required to use the Filesystem class.", - "ext-ftp": "Required to use the Flysystem FTP driver.", - "ext-hash": "Required to use the Filesystem class.", - "illuminate/http": "Required for handling uploaded files (^13.0).", - "illuminate/image": "Required to create images from stored files (^13.0).", - "league/flysystem": "Required to use the Flysystem local driver (^3.25.1).", - "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-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0 || ^2.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", - "symfony/mime": "Required to enable support for guessing extensions (^7.4 || ^8.0)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php" - ], - "psr-4": { - "Illuminate\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Filesystem package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-18T06:38:02+00:00" - }, - { - "name": "illuminate/macroable", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/macroable.git", - "reference": "59b5b5f3cf290a91db8cf6cd3d35ff56978bc057" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/59b5b5f3cf290a91db8cf6cd3d35ff56978bc057", - "reference": "59b5b5f3cf290a91db8cf6cd3d35ff56978bc057", - "shasum": "" - }, - "require": { - "php": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Macroable package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-04-29T09:35:06+00:00" - }, - { - "name": "illuminate/pipeline", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/pipeline.git", - "reference": "9ee04593b932a6d7dca255e3217584b896621b99" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/pipeline/zipball/9ee04593b932a6d7dca255e3217584b896621b99", - "reference": "9ee04593b932a6d7dca255e3217584b896621b99", - "shasum": "" - }, - "require": { - "illuminate/conditionable": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/macroable": "^13.0", - "illuminate/support": "^13.0", - "php": "^8.3" - }, - "suggest": { - "illuminate/database": "Required to use database transactions (^13.0)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Pipeline\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Pipeline package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-04T14:02:13+00:00" - }, - { - "name": "illuminate/reflection", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/reflection.git", - "reference": "2b6ba33970dd84849322a6a9857647ca96c1bb66" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/reflection/zipball/2b6ba33970dd84849322a6a9857647ca96c1bb66", - "reference": "2b6ba33970dd84849322a6a9857647ca96c1bb66", - "shasum": "" - }, - "require": { - "illuminate/collections": "^13.0", - "illuminate/contracts": "^13.0", - "php": "^8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "files": [ - "helpers.php" - ], - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Reflection package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-03T17:56:24+00:00" - }, - { - "name": "illuminate/support", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "1f963e021a251710ce81aba663311e94957c68f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/1f963e021a251710ce81aba663311e94957c68f2", - "reference": "1f963e021a251710ce81aba663311e94957c68f2", - "shasum": "" - }, - "require": { - "doctrine/inflector": "^2.0", - "ext-ctype": "*", - "ext-filter": "*", - "ext-mbstring": "*", - "illuminate/collections": "^13.0", - "illuminate/conditionable": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/macroable": "^13.0", - "illuminate/reflection": "^13.0", - "nesbot/carbon": "^3.8.4", - "php": "^8.3", - "symfony/polyfill-php85": "^1.36", - "voku/portable-ascii": "^2.0.2" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "replace": { - "spatie/once": "*" - }, - "suggest": { - "illuminate/filesystem": "Required to use the Composer class (^13.0).", - "laravel/serializable-closure": "Required to use the once function (^2.0.10).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.7).", - "league/uri": "Required to use the Uri class (^7.5.1).", - "ramsey/uuid": "Required to use Str::uuid() (^4.7).", - "symfony/process": "Required to use the Composer class (^7.4 || ^8.0).", - "symfony/uid": "Required to use Str::ulid() (^7.4 || ^8.0).", - "symfony/var-dumper": "Required to use the dd function (^7.4 || ^8.0).", - "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "files": [ - "functions.php", - "helpers.php" - ], - "psr-4": { - "Illuminate\\Support\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Support package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-14T21:23:27+00:00" - }, - { - "name": "illuminate/view", - "version": "v13.26.1", - "source": { - "type": "git", - "url": "https://github.com/illuminate/view.git", - "reference": "687f525c6de1878d08cd494757cdc8062fef7f90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/view/zipball/687f525c6de1878d08cd494757cdc8062fef7f90", - "reference": "687f525c6de1878d08cd494757cdc8062fef7f90", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "illuminate/collections": "^13.0", - "illuminate/conditionable": "^13.0", - "illuminate/container": "^13.0", - "illuminate/contracts": "^13.0", - "illuminate/events": "^13.0", - "illuminate/filesystem": "^13.0", - "illuminate/macroable": "^13.0", - "illuminate/reflection": "^13.0", - "illuminate/support": "^13.0", - "php": "^8.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "13.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\View\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate View package.", - "homepage": "https://laravel.com", - "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" - }, - "time": "2026-08-04T14:04:36+00:00" - }, - { - "name": "nesbot/carbon", - "version": "3.13.2", - "source": { - "type": "git", - "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/a1c54919f5fff9800cd03c32bd01defd5a4061cb", - "reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb", - "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-08-08T11:40:35+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/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": "symfony/clock", - "version": "v8.1.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/clock.git", - "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/701ef4de9705d6c32292ebee5e8044094a09fbf6", - "reference": "701ef4de9705d6c32292ebee5e8044094a09fbf6", - "shasum": "" - }, - "require": { - "php": ">=8.4.1", - "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.1.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": "2026-05-29T05:06:50+00:00" - }, - { - "name": "symfony/console", - "version": "v8.1.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "d07c06839e33047e2c894a6793248f3fb66c8129" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d07c06839e33047e2c894a6793248f3fb66c8129", - "reference": "d07c06839e33047e2c894a6793248f3fb66c8129", - "shasum": "" - }, - "require": { - "php": ">=8.4.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php85": "^1.32", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4.6|^8.0.6" - }, - "conflict": { - "symfony/dependency-injection": "<8.1", - "symfony/event-dispatcher": "<8.1" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^8.1", - "symfony/event-dispatcher": "^8.1", - "symfony/filesystem": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/uid": "^7.4|^8.0", - "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^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/v8.1.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-08-21T14:29:57+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", - "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.7-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.7.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": "2026-06-05T06:23:12+00:00" - }, - { - "name": "symfony/finder", - "version": "v8.1.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "8d7acede2b2ae07605783d1c43e49b5767036474" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8d7acede2b2ae07605783d1c43e49b5767036474", - "reference": "8d7acede2b2ae07605783d1c43e49b5767036474", - "shasum": "" - }, - "require": { - "php": ">=8.4.1" - }, - "require-dev": { - "symfony/filesystem": "^7.4|^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/v8.1.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-08-21T12:16:08+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.37.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", - "reference": "141046a8f9477948ff284fa65be2095baafb94f2", - "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.37.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": "2026-04-10T16:19:22+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", - "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.41.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": "2026-07-28T08:25:59+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.42.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502", - "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502", - "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.42.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": "2026-08-07T06:33:24+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.38.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", - "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.38.2" - }, - "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-05-27T06:59:30+00:00" - }, - { - "name": "symfony/polyfill-php84", - "version": "v1.38.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", - "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", - "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.38.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": "2026-05-26T12:51:13+00:00" - }, - { - "name": "symfony/polyfill-php85", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", - "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.41.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": "2026-07-01T12:47:55+00:00" - }, - { - "name": "symfony/polyfill-php86", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php86.git", - "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/6bc356ed3d8dbfeea8f0de235e34d670704e880e", - "reference": "6bc356ed3d8dbfeea8f0de235e34d670704e880e", - "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\\Php86\\": "" - }, - "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.6+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php86/tree/v1.41.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": "2026-07-02T13:42:24+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", - "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", - "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.7-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.7.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": "2026-06-16T09:55:08+00:00" - }, - { - "name": "symfony/string", - "version": "v8.1.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", - "shasum": "" - }, - "require": { - "php": ">=8.4.1", - "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.1.2" - }, - "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-07-28T07:35:25+00:00" - }, - { - "name": "symfony/translation", - "version": "v8.1.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "d9e1caba0d6b6f9a26710af8a2f88d37f001215a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d9e1caba0d6b6f9a26710af8a2f88d37f001215a", - "reference": "d9e1caba0d6b6f9a26710af8a2f88d37f001215a", - "shasum": "" - }, - "require": { - "php": ">=8.4.1", - "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.1.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-08-21T17:47:34+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v3.7.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621", - "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.7-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.7.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": "2026-06-05T06:23:12+00:00" - }, - { - "name": "voku/portable-ascii", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "8e1051fe39379367aecf014f41744ce7539a856f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", - "reference": "8e1051fe39379367aecf014f41744ce7539a856f", - "shasum": "" - }, - "require": { - "php": ">=7.1.0" - }, - "require-dev": { - "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" - }, - "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.1.1" - }, - "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": "2026-04-26T05:33:54+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, - "prefer-lowest": false, - "platform": {}, - "platform-dev": {}, - "plugin-api-version": "2.9.0" -} diff --git a/config/config.php b/config/config.php deleted file mode 100644 index b45cf02..0000000 --- a/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ - 'Skeleton', - - 'table_prefix' => fromModuleManager('skeleton.table_prefix', 'skeleton_'), -]; diff --git a/config/skeleton-plugin.php b/config/skeleton-plugin.php new file mode 100644 index 0000000..a8dcb2a --- /dev/null +++ b/config/skeleton-plugin.php @@ -0,0 +1,13 @@ + env('SKELETON_PLUGIN_EXAMPLE_FEATURE', false), + +]; diff --git a/configure.php b/configure.php new file mode 100644 index 0000000..dacc909 --- /dev/null +++ b/configure.php @@ -0,0 +1,117 @@ +#!/usr/bin/env php + "{$vendorSlug}/filament-{$kebabName}", + 'Komma\\SkeletonPlugin' => "{$vendorNamespace}\\{$studlyName}", + 'Komma\\\\SkeletonPlugin' => "{$vendorNamespace}\\\\{$studlyName}", + 'SkeletonPlugin' => "{$studlyName}Plugin", + 'skeleton-plugin' => "filament-{$kebabName}", + 'SKELETON_PLUGIN' => strtoupper(str_replace('-', '_', $kebabName)), + '๐Ÿš€ Skeleton template for building Filament 5.x plugins | Komma SoftHouse plugin standard' => $description, +]; + +$directory = new RecursiveIteratorIterator( + new RecursiveCallbackFilterIterator( + new RecursiveDirectoryIterator(__DIR__, FilesystemIterator::SKIP_DOTS), + fn (SplFileInfo $current): bool => ! in_array($current->getFilename(), ['.git', 'vendor', 'node_modules'], true), + ), +); + +$files = []; + +foreach ($directory as $file) { + if ($file->isFile() && $file->getRealPath() !== __FILE__) { + $files[] = $file->getRealPath(); + } +} + +foreach ($files as $path) { + $contents = file_get_contents($path); + $updated = str_replace(array_keys($replacements), array_values($replacements), $contents); + + if ($updated !== $contents) { + file_put_contents($path, $updated); + } + + $newPath = str_replace('SkeletonPlugin', "{$studlyName}Plugin", basename($path)); + + if ($newPath !== basename($path)) { + rename($path, dirname($path) . DIRECTORY_SEPARATOR . $newPath); + } +} + +// Rename config and lang artifacts that carry the kebab name. +$configFile = __DIR__ . '/config/skeleton-plugin.php'; + +if (file_exists($configFile)) { + rename($configFile, __DIR__ . "/config/filament-{$kebabName}.php"); +} + +if (confirm('Reset git history (rm -rf .git && git init)?')) { + exec('rm -rf ' . escapeshellarg(__DIR__ . '/.git')); + exec('git -C ' . escapeshellarg(__DIR__) . ' init --quiet'); +} + +echo "\nPlugin \"{$vendorSlug}/filament-{$kebabName}\" configured. Next steps:\n"; +echo " 1. composer install\n"; +echo " 2. composer test\n"; +echo " 3. Read docs/how-to-build-a-filament-plugin.md and start building.\n"; + +unlink(__FILE__); diff --git a/docs/how-to-build-a-filament-plugin.md] b/docs/how-to-build-a-filament-plugin.md] new file mode 100644 index 0000000..fb93a2a --- /dev/null +++ b/docs/how-to-build-a-filament-plugin.md] @@ -0,0 +1,117 @@ +# How to Build a Filament Plugin โ€” Komma SoftHouse Standard + +The reference for every redistributable Filament 5.x plugin in the Komma ecosystem (Verifactu, TicketBAI drivers, Print Agent SDK, and everything that follows). Internal app modules have their own standard and are **out of scope** here: this document covers only Composer packages installed into third-party Laravel apps. + +Grounded in the official Filament 5.x docs โ€” read them once before your first plugin: +[Getting started](https://filamentphp.com/docs/5.x/plugins/getting-started) ยท +[Panel plugins](https://filamentphp.com/docs/5.x/plugins/panel-plugins) ยท +[Building a panel plugin](https://filamentphp.com/docs/5.x/plugins/building-a-panel-plugin) ยท +[Building a standalone plugin](https://filamentphp.com/docs/5.x/plugins/building-a-standalone-plugin) ยท +[Configurable resources and pages](https://filamentphp.com/docs/5.x/plugins/configurable-resources-and-pages) + +--- + +## 1. The two plugin contexts + +- **Panel plugin** โ€” adds resources, pages, widgets or whole features to a panel. Uses the `Plugin` object. Most Komma products are this. +- **Standalone plugin** โ€” adds form fields, table columns, schema components usable outside panels. **No `Plugin` object**: all configuration lives in the service provider. + +One package may be both. When in doubt, ship the `Plugin` object and keep standalone pieces registered in the provider. + +## 2. Bootstrapping โ€” always from the skeleton + +1. GitHub โ†’ `komma-softhouse/skeleton-plugin` โ†’ **Use this template**. +2. `php ./configure.php` โ†’ answer prompts โ†’ the script renames everything and deletes itself. +3. `composer install && composer test` โ†’ green before writing a line. + +Never hand-roll the package layer. The skeleton already encodes: spatie/laravel-package-tools provider, PHP 8.4 / Laravel 13 / Filament 5 constraints, Pest + Testbench, Pint, CI, MIT scaffold. + +## 3. The package layer + +- Service provider extends `Spatie\LaravelPackageTools\PackageServiceProvider` with a **static `$name`** (kebab-case). That name is the config file name, the views/translations namespace, and must match `Plugin::getId()`. +- Declare only what you ship: `hasConfigFile()`, `hasViews()`, `hasTranslations()`, `hasMigrations([...])`, `hasCommands([...])`. +- Composer package naming: `{vendor}/filament-{plugin}` (e.g. `komma-softhouse/filament-verifactu`). Namespace: `Komma\{Plugin}`. + +## 4. The Plugin object + +Required trio: `getId()` (unique, kebab), `register(Panel $panel)` (resources / pages / widgets / render hooks โ€” runs after the host configured the plugin, so read your toggles here), `boot(Panel $panel)` (runs only when that panel is in use). + +House additions, always: + +```php +public static function make(): static +{ + return app(static::class); // container-resolved: host can swap it +} + +public static function get(): static +{ + return filament(app(static::class)->getId()); // typed access to the configured instance +} +``` + +### Per-option configuration + +One **setter + getter pair per option**, setter returns `static`: + +```php +->gotenbergPdf() / hasGotenbergPdf() +->escPos() / hasEscPos() +``` + +Rules: +- **Every toggle ships `false`.** A host that enables nothing gets zero extra dependencies, zero extra UI, zero surprises. Infrastructure (Docker, Gotenberg, print agents, AI providers) is always opt-in. +- Mirror each fluent toggle with a config key (`config/{name}.php` + env var) so non-panel contexts (jobs, CLI) can read the same switch. +- Conditional registrations (`if ($this->hasX())`) live inside `register()`. + +## 5. Configurable resources and pages (v5) + +When a resource must be registrable N times with different behavior (e.g. "Active" vs "Archived"), do **not** clone resource classes: + +1. Create a configuration class extending `Filament\Resources\ResourceConfiguration` (or `PageConfiguration`) with the fluent setter/getter pairs. +2. Point the resource at it: `protected static ?string $configurationClass = XConfiguration::class;` +3. Register with keys: `XResource::make('active')->...`, `XResource::make('archived')->archived()`. +4. Read at runtime with `static::getConfiguration()` / `static::hasConfiguration()`; URLs via `XResource::getUrl(..., configuration: 'archived')`. + +Expose the configurations to hosts through a plugin setter that stores an array and spreads it inside `register()`. + +## 6. Assets + +- On-demand assets (async Alpine components, lazy CSS) โ†’ `FilamentAsset::register()` in the provider's `packageBooted()`, with `package: '{vendor}/{package}'`. +- Assets that must load on **every page of a panel** โ†’ `$panel->assets()` inside `Plugin::register()`. Registering these in `packageBooted()` leaks them into every panel of the host app โ€” never do it. +- Prefer no CSS at all: hosts style plugins through their own Filament theme. + +## 7. Filament 5 code conventions (Komma) + +- Layout components from `Filament\Schemas\Components` (Section, Grid, Tabs); actions from `Filament\Actions`; form signature `form(Schema $schema): Schema`; reactivity with `->live()`. +- `Section->columnSpanFull()` with an inner `Grid` for multi-column layouts. +- Separate classes per concern: Form / Table / Infolist / Pages โ€” no god-resources. +- Read-only data (fiscal records, audit trails) โ†’ resources with **list + view only**; `create/update/delete` policies return `false`; model guards throw on mutation. +- Money: **bcmath end to end, never `round()`**. +- i18n: `__('English literal')` with JSON lang files (`resources/lang/es.json`, โ€ฆ). `en.json` stays `{}` โ€” the literals are the English. +- External engines (josemmo, barnetik clones, โ€ฆ) stay behind a Komma contract; **product code never imports vendor classes**. + +## 8. Testing & CI + +- Pest on Testbench; the skeleton's `TestCase` already boots the full Filament 5 provider set. +- Minimum bar per plugin: plugin id, toggles-off-by-default, each fluent option, each conditional registration, policies of read-only resources. +- CI (shipped): Pint `--test` + Pest on PHP 8.4. Red CI never tags. +- Products with a regulatory surface add their own release gate on top (e.g. the Verifactu homologation battery) โ€” vendor-clone updates re-run it entirely. + +## 9. Repo, release & distribution + +- Commits, PR titles and bodies, and all git artifacts: **English**. Conventional, imperative commit subjects. +- SemVer + `CHANGELOG.md`. The version must be traceable from anything the plugin emits (e.g. `ComputerSystem` in fiscal records). +- License per product: MIT for skeletons/OSS, commercial license (Anystack / Filament marketplace) for paid plugins. Cloned-engine licensing is handled at company level โ€” not in the plugin repo. +- README must contain: requirements, install, the exact `->plugin(X::make())` snippet, every fluent option, and publishable assets. + +## 10. Checklist before the first tag + +- [ ] Bootstrapped from `skeleton-plugin` via `configure.php` +- [ ] `Plugin::getId()` === provider `$name` === config file name +- [ ] `make()` + `get()` present; every option has setter + getter; **all toggles default off** +- [ ] Assets follow ยง6 (nothing leaking into foreign panels) +- [ ] v5 conventions of ยง7 (namespaces, Schema signature, bcmath, i18n literals) +- [ ] No vendor-engine class imported outside its contract +- [ ] Pest green, Pint clean, CI green +- [ ] README + CHANGELOG + license correct for the product type diff --git a/module.json b/module.json deleted file mode 100644 index e22665f..0000000 --- a/module.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Skeleton", - "alias": "skeleton", - "description": "", - "keywords": [], - "priority": 0, - "providers": ["Modules\\Skeleton\\Providers\\SkeletonServiceProvider"], - "filament_plugins": ["Modules\\Skeleton\\Filament\\Plugin\\SkeletonPlugin"], - "files": [], - "npm-packages": [] -} diff --git a/package.json b/package.json deleted file mode 100644 index 946ad99..0000000 --- a/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build" - }, - "devDependencies": { - "axios": "^1.1.2", - "laravel-vite-plugin": "^0.7.5", - "sass": "^1.69.5", - "postcss": "^8.3.7", - "vite": "^4.0.0" - } -} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..49cfe8d --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,11 @@ + + + + + tests + + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..4b4f940 --- /dev/null +++ b/pint.json @@ -0,0 +1,6 @@ +{ + "preset": "laravel", + "rules": { + "declare_strict_types": true + } +} diff --git a/readme.md b/readme.md deleted file mode 100644 index 8687a82..0000000 --- a/readme.md +++ /dev/null @@ -1,128 +0,0 @@ -# Skeleton Module - -[![Latest Version on Packagist](https://img.shields.io/packagist/v/komma-softhouse/skeleton-module.svg?style=flat-square)](https://packagist.org/packages/komma-softhouse/skeleton-module) - - -The Skeleton module serves as a template for creating new modules in the system. It provides a streamlined structure and a convenient command-line tool to quickly generate new modules based on this skeleton. - -## Installation - -You can install the package via composer: - -```bash -composer require komma-softhouse/skeleton-module -``` - -## Requirements - -- PHP >= 8.4 -- Laravel >= 13.0 -- nwidart/laravel-modules >= 10.0 - -## Module Structure - -The Skeleton module contains a basic structure that follows best practices for module development: - -- Configuration files -- Service provider setup -- Basic directory structure -- Composer configuration -- Module manifest - -## Module Builder Command - -The Skeleton module includes a powerful command-line tool called `module-build` that helps you quickly create new modules based on this template. - -### Usage - -You can create a new module using the command: - -```bash -php artisan module-build -``` - -Or specify the module name directly with the `--name` option: - -```bash -php artisan module-build --name=MyNewModule -``` - -### What it does - -The `module-build` command performs the following operations: - -1. **Creates a new module directory** based on the provided name -2. **Copies all files** from the Skeleton module to the new module -3. **Renames files and directories** by replacing "Skeleton" with your module name - - PascalCase substitution: "Skeleton" โ†’ "YourModuleName" - - Lowercase substitution: "skeleton" โ†’ "yourmodulename" -4. **Updates file contents** to replace all occurrences of "Skeleton" with your module name -5. **Updates specific files**: - - `composer.json`: Updates package name and namespaces - - `module.json`: Updates name, alias, and provider paths - - Service Provider: Updates namespace, class name, and properties -6. **Cleans up**: - - Removes `.git` directory if it exists - - Removes `vendor` directory if it exists - - Removes the `ModuleBuildCommand` from the new module -7. **Registers the module**: - - Updates `modules_statuses.json` to enable the new module -8. **Updates autoloader**: - - Runs `composer dump-autoload` to register the new namespaces - -### After Creation - -Once your new module is created, it will be: - -1. Properly registered in the system -2. Ready for development -3. Accessible through its namespace (`Modules\YourModuleName`) - -You can then start adding your specific functionality to the new module! - -## Best Practices - -- Use the module-build command to ensure consistency across all modules -- Maintain the standard directory structure for new modules -- Follow the naming conventions established by the Skeleton module - -## Example - -To create a new "Reports" module: - -```bash -php artisan module-build --name=Reports -``` - -This will create a fully functional Reports module with all necessary files and configurations. - -## Testing - -```bash -composer test -``` - -## Changelog - -Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. - -## Contributing - -Please see [CONTRIBUTING](CONTRIBUTING.md) for details. - -## Security Vulnerabilities - -Please review [our security policy](../../security/policy) on how to report security vulnerabilities. - -## Credits - -- [Elias Olivtradet](https://github.com/edeoliv) -- [All Contributors](../../contributors) - -## License - -The MIT License (MIT). Please see [License File](LICENSE) for more information. - -## Support - -If you discover any issues or have questions, please [open an issue](https://github.com/e2tmk/skeleton-module/issues) on GitHub. diff --git a/resources/lang/en.json b/resources/lang/en.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/resources/lang/en.json @@ -0,0 +1 @@ +{} diff --git a/resources/lang/es.json b/resources/lang/es.json new file mode 100644 index 0000000..7d744a4 --- /dev/null +++ b/resources/lang/es.json @@ -0,0 +1,3 @@ +{ + "Example": "Ejemplo" +} diff --git a/resources/.gitkeep b/resources/views/.gitkeep similarity index 100% rename from resources/.gitkeep rename to resources/views/.gitkeep diff --git a/src/Commands/.gitkeep b/src/Commands/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Pages/.gitkeep b/src/Pages/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Resources/.gitkeep b/src/Resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/SkeletonPlugin.php b/src/SkeletonPlugin.php new file mode 100644 index 0000000..14cac61 --- /dev/null +++ b/src/SkeletonPlugin.php @@ -0,0 +1,82 @@ +plugin(SkeletonPlugin::make()->exampleFeature()); + * + * @see https://filamentphp.com/docs/5.x/plugins/panel-plugins + */ +class SkeletonPlugin implements Plugin +{ + /** + * Feature toggles are ALWAYS off by default (Komma standard): a host + * that enables nothing gets zero extra dependencies and zero surprises. + */ + protected bool $hasExampleFeature = false; + + public static function make(): static + { + // Resolved through the container so hosts can swap the implementation. + return app(static::class); + } + + public static function get(): static + { + // Typed access to the configured instance from anywhere in the code: + // SkeletonPlugin::get()->hasExampleFeature() + return filament(app(static::class)->getId()); + } + + public function getId(): string + { + return 'skeleton-plugin'; + } + + /** Setter + getter pair per option โ€” the Filament fluent standard. */ + public function exampleFeature(bool $condition = true): static + { + $this->hasExampleFeature = $condition; + + return $this; + } + + public function hasExampleFeature(): bool + { + return $this->hasExampleFeature; + } + + public function register(Panel $panel): void + { + $panel + ->resources([ + // Resources\ExampleResource::class, + + // v5 configurable registrations (N registrations, one class): + // Resources\ExampleResource::make('active')->navigationLabel(__('Active')), + // @see https://filamentphp.com/docs/5.x/plugins/configurable-resources-and-pages + ]) + ->pages([ + // Pages\SkeletonPluginSettings::class, + ]) + ->widgets([ + // Widgets\SkeletonPluginStats::class, + ]); + + if ($this->hasExampleFeature()) { + // Conditional registrations live here, driven by the toggles above. + } + } + + public function boot(Panel $panel): void + { + // Runs only when the panel this plugin is registered to is in use. + } +} diff --git a/src/SkeletonPluginServiceProvider.php b/src/SkeletonPluginServiceProvider.php new file mode 100644 index 0000000..480608f --- /dev/null +++ b/src/SkeletonPluginServiceProvider.php @@ -0,0 +1,52 @@ +name(static::$name) + ->hasConfigFile() + ->hasViews() + ->hasTranslations(); + + // Migrations: uncomment and list them when the plugin ships tables. + // ->hasMigrations(['create_skeleton_plugin_table']) + // ->runsMigrations() + + // Commands: uncomment when the plugin ships artisan commands. + // ->hasCommands([Commands\SkeletonPluginCommand::class]) + } + + public function packageBooted(): void + { + /** + * Register assets ONLY when they are loaded on demand (async Alpine + * components, lazy CSS). Assets that must load on every page of a + * panel belong in SkeletonPlugin::register() via $panel->assets(), + * otherwise they leak into every panel of the host app. + * + * @see https://filamentphp.com/docs/5.x/plugins/getting-started + * @see https://filamentphp.com/docs/5.x/advanced/assets + */ + // FilamentAsset::register( + // assets: [ + // AlpineComponent::make('skeleton-plugin', __DIR__ . '/../resources/dist/skeleton-plugin.js'), + // ], + // package: 'komma-softhouse/skeleton-plugin', + // ); + } +} diff --git a/src/Widgets/.gitkeep b/src/Widgets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php new file mode 100644 index 0000000..7ed3181 --- /dev/null +++ b/tests/ExampleTest.php @@ -0,0 +1,17 @@ +getId())->toBe('skeleton-plugin'); +}); + +it('keeps feature toggles off by default', function () { + expect(SkeletonPlugin::make()->hasExampleFeature())->toBeFalse(); +}); + +it('enables features fluently', function () { + expect(SkeletonPlugin::make()->exampleFeature()->hasExampleFeature())->toBeTrue(); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..49e9076 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,7 @@ +in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..cd80893 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,47 @@ +set('database.default', 'testing'); + } +} diff --git a/vite.config.js b/vite.config.js deleted file mode 100644 index bf59e37..0000000 --- a/vite.config.js +++ /dev/null @@ -1,12 +0,0 @@ -function getModuleName() { - const currentPath = new URL(import.meta.url).pathname; - const pathSegments = currentPath.split('/'); - const modulesIndex = pathSegments.findIndex(segment => segment === 'Modules'); - return modulesIndex !== -1 && modulesIndex + 1 < pathSegments.length - ? pathSegments[modulesIndex + 1] - : ''; -} - -const dirName = 'Modules/' + getModuleName(); - -export const paths = [];