diff --git a/.env.ci b/.env.ci new file mode 100644 index 0000000..4410eec --- /dev/null +++ b/.env.ci @@ -0,0 +1,106 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=mariadb +DB_PORT=3306 +DB_DATABASE=testing +DB_USERNAME=sail +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=database +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https +PUSHER_APP_CLUSTER=mt1 + +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + + +PLAID_ENVIRONMENT=sandbox +PLAID_CLIENT_ID= +PLAID_DEVELOPMENT_SECRET= +PLAID_PRODUCTION_SECRET=prod_super_secret +PLAID_SANDBOX_SECRET= + +SCOUT_DRIVER=meilisearch +MEILISEARCH_HOST=http://meilisearch:7700 + +WEATHER_API_KEY= +OPEN_WEATHER_KEY= + +REDIS_QUEUE= + +FTP_HOST= +FTP_USERNAME= +FTP_PASSWORD= + +SPORK_ADMIN_EMAILS= + +MATRIX_USERNAME="" +MATRIX_PASSWORD="" +MATRIX_HOST="" +MATRIX_ACCESS_TOKEN="" +MATRIX_DEVICE="" +MATRIX_JWT_TOKEN="" + +GITHUB_TOKEN="" + +SMTP_USERNAME=@proton.me +IMAP_USERNAME=@proton.me +SMTP_PASSWORD= +IMAP_PASSWORD= +SMTP_HOST=proton-bridge +IMAP_HOST=proton-bridge +IMAP_PORT=143 +SMTP_PORT=1025 + +SPORK_DEFAULT_FILESYSTEM= +LINK_SHORTENING_DOMAIN= +RUST_BACKTRACE= + +FORGE_CLOUDFLARE_TOKEN= + +MATRIX_CLIENT_FOR_COMMUNICATION_VENTURES= +ADMIN_MATRIX_CLIENT_FOR_COMMUNICATION_VENTURES= diff --git a/.env.example b/.env.example index 8c207b9..73e8edd 100644 --- a/.env.example +++ b/.env.example @@ -56,3 +56,52 @@ VITE_PUSHER_HOST="${PUSHER_HOST}" VITE_PUSHER_PORT="${PUSHER_PORT}" VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + + +PLAID_ENVIRONMENT=sandbox +PLAID_CLIENT_ID= +PLAID_DEVELOPMENT_SECRET= +PLAID_PRODUCTION_SECRET=prod_super_secret +PLAID_SANDBOX_SECRET= + +SCOUT_DRIVER=meilisearch +MEILISEARCH_HOST=http://meilisearch:7700 + +WEATHER_API_KEY= +OPEN_WEATHER_KEY= + +REDIS_QUEUE= + +FTP_HOST= +FTP_USERNAME= +FTP_PASSWORD= + +SPORK_ADMIN_EMAILS= + +MATRIX_USERNAME="" +MATRIX_PASSWORD="" +MATRIX_HOST="" +MATRIX_ACCESS_TOKEN="" +MATRIX_DEVICE="" +MATRIX_JWT_TOKEN="" + +GITHUB_TOKEN="" + +SMTP_USERNAME=@proton.me +IMAP_USERNAME=@proton.me +SMTP_PASSWORD= +IMAP_PASSWORD= +SMTP_HOST=proton-bridge +IMAP_HOST=proton-bridge +IMAP_PORT=143 +SMTP_PORT=1025 + +SPORK_DEFAULT_FILESYSTEM= +LINK_SHORTENING_DOMAIN= +RUST_BACKTRACE= + +FORGE_CLOUDFLARE_TOKEN= + +MATRIX_CLIENT_FOR_COMMUNICATION_VENTURES= +ADMIN_MATRIX_CLIENT_FOR_COMMUNICATION_VENTURES= +#BUGSNAG_API_KEY= diff --git a/.github/workflows/cs-checks.yml b/.github/workflows/cs-checks.yml index 35244ed..c629b82 100644 --- a/.github/workflows/cs-checks.yml +++ b/.github/workflows/cs-checks.yml @@ -1,4 +1,4 @@ -name: PHP Linting (Pint) +name: Linting (Pint) on: workflow_dispatch: push: @@ -15,9 +15,4 @@ jobs: uses: aglipanci/laravel-pint-action@0.1.0 with: preset: laravel - - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: PHP Linting (Pint) - skip_fetch: true + diff --git a/.github/workflows/php-unit.yml b/.github/workflows/php-unit.yml index 825a350..5dd3a6d 100644 --- a/.github/workflows/php-unit.yml +++ b/.github/workflows/php-unit.yml @@ -1,4 +1,4 @@ -name: Checks +name: PHP Unit on: [ pull_request ] jobs: phpunit: @@ -10,23 +10,25 @@ jobs: fetch-depth: 0 # important! - name: setup php run: | - sudo apt update - sudo apt install -y software-properties-common - sudo add-apt-repository ppa:ondrej/php -y - sudo apt install -y php8.2-{common,cli,gd,curl,mysql,mbstring,dom,xml,simplexml} - curl -s https://getcomposer.org/installer | php - sudo mv composer.phar /usr/local/bin/composer git submodule init git submodule update --remote --merge - name: Install dependencies run: | - sudo systemctl start mysql.service - mysql -uroot -h127.0.0.1 -proot -e 'CREATE DATABASE IF NOT EXISTS testing;' - sudo apt update && sudo apt install -y php-mysql - composer install --prefer-dist --no-progress --no-suggest + sudo apt update && sudo apt remove mysql* -y + touch storage/logs/laravel.log + touch storage/logs/crontab.log + touch storage/logs/horizon.log + touch database/database.sqlite + npm install + cp .env.ci .env + ./bin/sail up -d + npm run build - name: Run tests run: | - cp .env.ci .env - touch database/database.sqlite - php artisan key:generate - php vendor/bin/phpunit + ./bin/sail art key:generate + export $(cat .env | xargs) + ./bin/sail exec mariadb /docker-entrypoint-initdb.d/10-create-testing-database.sh + ./bin/sail test + - name: tear down containers + run: | + ./bin/sail down diff --git a/.gitignore b/.gitignore index 85b1ca4..63b37ff 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ yarn-error.log /.vscode /*.log /*.pid +storage/*.bin diff --git a/README.md b/README.md index 35e52ef..bbbaad6 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ $ ./sail up -d - [x] DNS Validation/Verification - [x] Projects, with relations to domains, servers, rss feeds, pages (and redirects), and people in a one to many association for organization. - [-] Automatically adding and configuring purchased domains to Cloudflare & Laravel Forge, and provision out the routing automatically. (Still in progress) + - [x] Automatic SSL configuration via forge. ## Planned - - [ ] Automatic SSL configuration via forge. - [ ] IFTTT inspired Dynamic Automations - [ ] Plaid integration for asset syncing - [ ] Budgeting per project @@ -32,6 +32,32 @@ $ ./sail up -d - [ ] List tasks from all projects - [ ] List events from all projects - [ ] List budget usage -- if applicable. + - [ ] Domain Purchasing and Renewals -### Happily built with Laravel Jetstream Inertia + - Single Chat interface + - Email interface +# Domain Feature Details +This presently uses Cloudflare DNS. + +We can configure and manage any DNS records that Cloudflare supports. + +We also want to be able to update the NS of registrars to point to Cloudflare + +## Domain Syncing +This will sync domains from Namecheap to Cloudflare. It will also sync domains from Cloudflare to Laravel Forge. + +# Server Feature Details +We can manage any server listed in our database as long as there is at least an SSH server configured. + +Servers house code or perform jobs. They are not necessarily web servers, but can be. When accessed via SSH, you have full access to everything that user has access to. + +## Server Feature Details +Laravel Forge, and Digital Ocean are both supported providers, but any server can be added manually and accessed via SSH. + +# RSS Feature Details +RSS feeds are synced and updated on a schedule. This is done via a job that runs every 15 minutes. + +# Page Feature Details +Pages are dynamic routes that can be configured to point to any domain, or server. They can also be configured to redirect to another page, or domain. + diff --git a/app/Actions/Spork/CustomAction.php b/app/Actions/Spork/CustomAction.php new file mode 100644 index 0000000..9ef4778 --- /dev/null +++ b/app/Actions/Spork/CustomAction.php @@ -0,0 +1,22 @@ +validate([ + 'domains' => 'required|array', + 'nameservers' => 'required', + ]); + + $domains = request()->get('domains'); + + $nameservers = explode(',', request()->get('nameservers', '')); + + foreach ($domains as $domain) { + $this->service->updateDomainNs($domain, $nameservers); + } + + return 'OK'; + } + + public function show(): bool + { + return auth()->check(); + } +} diff --git a/app/Actions/Spork/SyncDataFromCredential.php b/app/Actions/Spork/SyncDataFromCredential.php new file mode 100644 index 0000000..9bb9f84 --- /dev/null +++ b/app/Actions/Spork/SyncDataFromCredential.php @@ -0,0 +1,37 @@ +user()->id)->whereIn('id', $request->get('items'))->get(); + + foreach ($credentials as $credential) { + $dispatcher->dispatch(match ($credential->type) { + Credential::TYPE_REGISTRAR => new FetchRegistrarForCredential($credential), + Credential::TYPE_DOMAIN => new FetchDomainsForCredential($credential), + Credential::TYPE_SERVER => new FetchServersForCredential($credential), + Credential::TYPE_DEVELOPMENT, 'forge' => new LaravelForgeServersSyncJob($credential), + Credential::TYPE_FINANCE => new SyncPlaidTransactionsJob($credential, now()->subWeek(), now(), false), + }); + } + } +} diff --git a/app/Console/Commands/CompileNewInstanceOfClass.php b/app/Console/Commands/CompileNewInstanceOfClass.php new file mode 100644 index 0000000..50e84ad --- /dev/null +++ b/app/Console/Commands/CompileNewInstanceOfClass.php @@ -0,0 +1,62 @@ +argument('sourceClass'); + $destinationClass = $this->argument('destination'); + + if (! str_contains($destinationClass, '\\')) { + $namespace = str_replace(class_basename($sourceClass), '', $sourceClass); + + $destinationClass = $namespace.$destinationClass; + } + + if (! str_starts_with($sourceClass, 'App\\')) { + throw new \Exception('Not setup for '.$sourceClass); + } + + $newEvent = LaravelProgrammingStyle::for($sourceClass) + ->renameClass($filename = class_basename($destinationClass), trim(str_replace(class_basename($destinationClass), '', $destinationClass), '\\')) + ->modifyMethod('__construct', '//', parameters: [ + (new Parameter('model')) + ->setType(Model::class), + ]) + ->import(AbstractLogicalEvent::class) + ->import(Model::class) + ->toFile(Code::RETURN_CONTENTS); + + $newFileName = str_replace('\\', '/', lcfirst($destinationClass)).'.php'; + + file_put_contents(base_path($newFileName), $newEvent); + } +} diff --git a/app/Console/Commands/Generate.php b/app/Console/Commands/Generate.php new file mode 100644 index 0000000..735cc7c --- /dev/null +++ b/app/Console/Commands/Generate.php @@ -0,0 +1,33 @@ + true], $this->getOutput()); + \Artisan::call('filament:assets', [], $this->getOutput()); + } +} diff --git a/app/Console/Commands/MakeUser.php b/app/Console/Commands/MakeUser.php new file mode 100644 index 0000000..fc567c7 --- /dev/null +++ b/app/Console/Commands/MakeUser.php @@ -0,0 +1,43 @@ + $this->ask('What is your name?'), + 'email' => $this->ask('What is your email address?'), + 'password' => bcrypt($this->ask('What password would you like to use?')), + ]); + + $user->ownedTeams()->create([ + 'name' => config('app.name'), + 'personal_team' => true, + 'settings' => [], + ]); + } +} diff --git a/app/Console/Commands/Messaging/MatrixBeeperRequestCode.php b/app/Console/Commands/Messaging/MatrixBeeperRequestCode.php new file mode 100644 index 0000000..b0469c6 --- /dev/null +++ b/app/Console/Commands/Messaging/MatrixBeeperRequestCode.php @@ -0,0 +1,37 @@ +argument('email'), $this->option('host')); + + $client->requestCodeForBeeper($this->argument('email')); + $this->info('Please check your email, and return within 30 minutes'); + $this->info('When you\'re ready please run the following command'); + $this->warn(' sail art matrix:beeper-verify-code '.$this->argument('email').' {code}'); + } +} diff --git a/app/Console/Commands/Messaging/MatrixBeeperRequestTokenCode.php b/app/Console/Commands/Messaging/MatrixBeeperRequestTokenCode.php new file mode 100644 index 0000000..ecc8be5 --- /dev/null +++ b/app/Console/Commands/Messaging/MatrixBeeperRequestTokenCode.php @@ -0,0 +1,37 @@ +argument('email'), $this->option('host')); + + $response = $client->loginWithJwt($this->argument('code')); + + $this->info('Login successful'); + dd($response); + } +} diff --git a/app/Console/Commands/Messaging/MatrixBeeperVerifyCode.php b/app/Console/Commands/Messaging/MatrixBeeperVerifyCode.php new file mode 100644 index 0000000..d6865ba --- /dev/null +++ b/app/Console/Commands/Messaging/MatrixBeeperVerifyCode.php @@ -0,0 +1,37 @@ +argument('email'), $this->option('host')); + + $response = $client->loginWithBeeperCode($this->argument('email'), $this->argument('code')); + + $this->info('Login successful'); + dd($response); + } +} diff --git a/app/Console/Commands/Operations/MakeOperationCommand.php b/app/Console/Commands/Operations/MakeOperationCommand.php new file mode 100644 index 0000000..616b84b --- /dev/null +++ b/app/Console/Commands/Operations/MakeOperationCommand.php @@ -0,0 +1,68 @@ +option('migration')) { + $this->createMigration(); + } + } + + protected function createMigration() + { + $table = Str::snake(Str::pluralStudly(class_basename($this->argument('name')))); + + $this->call('make:operation-migration', [ + 'name' => $this->argument('name'), + ]); + } + + protected function getStub() + { + return __DIR__.'/../../stubs/operation.stub'; + } + + protected function getDefaultNamespace($rootNamespace) + { + return $rootNamespace.'\Operations'; + } + + protected function replaceClass($stub, $name) + { + $stub = parent::replaceClass($stub, $name); + + return str_replace('DummyOperation', $this->argument('name'), $stub); + } + + protected function getArguments() + { + return [ + ['name', InputArgument::REQUIRED, 'The name of the operation'], + ]; + } + + protected function getOptions() + { + return [ + ['migration', 'm', InputOption::VALUE_NONE, 'Create a new migration file for the operation'], + ]; + } +} diff --git a/app/Console/Commands/Operations/MakeOperationMigrationCommand.php b/app/Console/Commands/Operations/MakeOperationMigrationCommand.php new file mode 100644 index 0000000..980bf4c --- /dev/null +++ b/app/Console/Commands/Operations/MakeOperationMigrationCommand.php @@ -0,0 +1,38 @@ +creator = $creator; + $this->composer = $composer; + } + + public function handle() + { + $name = Str::snake(Str::plural($this->input->getArgument('name'))); + $table = Str::snake(Str::pluralStudly(class_basename($this->input->getArgument('name')))); + + $file = $this->creator->create('create_'.$name.'_table', $this->getMigrationPath(), $table, true); + + $this->line("Created Migration: {$file}"); + } +} diff --git a/app/Console/Commands/Operations/MigrationCreator.php b/app/Console/Commands/Operations/MigrationCreator.php new file mode 100644 index 0000000..2013e1b --- /dev/null +++ b/app/Console/Commands/Operations/MigrationCreator.php @@ -0,0 +1,15 @@ +files->get(__DIR__.'/../../stubs/migration.stub'); + } +} diff --git a/app/Console/Commands/Operations/QueueCommand.php b/app/Console/Commands/Operations/QueueCommand.php new file mode 100644 index 0000000..589229e --- /dev/null +++ b/app/Console/Commands/Operations/QueueCommand.php @@ -0,0 +1,20 @@ +browse(function (Browser $browser) { + $browser->visit('https://www.facebook.com/newsreview')->assertSee('Facebook'); + }); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ee3a05f..410c28f 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -5,6 +5,8 @@ namespace App\Console; use App\Jobs\FetchCloudflareAnalytics; +use App\Jobs\FetchResourcesFromCredentials; +use App\Jobs\News\UpdateAllFeeds; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -15,8 +17,10 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule): void { - // $schedule->command('inspire')->hourly(); + $schedule->job(UpdateAllFeeds::class)->hourly(); + $schedule->job(FetchResourcesFromCredentials::class)->everyOddHour(); $schedule->job(FetchCloudflareAnalytics::class)->everyFourHours(); + $schedule->command('operations:queue')->everyFiveMinutes(); } /** diff --git a/app/Contracts/ActionInterface.php b/app/Contracts/ActionInterface.php new file mode 100644 index 0000000..d577e01 --- /dev/null +++ b/app/Contracts/ActionInterface.php @@ -0,0 +1,12 @@ + + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/app/Events/Admin/Composer/ActionFinished.php b/app/Events/Admin/Composer/ActionFinished.php new file mode 100644 index 0000000..2b2db51 --- /dev/null +++ b/app/Events/Admin/Composer/ActionFinished.php @@ -0,0 +1,35 @@ + + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/app/Events/Admin/Composer/ActionLoggedToConsole.php b/app/Events/Admin/Composer/ActionLoggedToConsole.php new file mode 100644 index 0000000..ced3fac --- /dev/null +++ b/app/Events/Admin/Composer/ActionLoggedToConsole.php @@ -0,0 +1,35 @@ + + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/app/Events/Domains/DnsRecordVerified.php b/app/Events/Domains/DnsRecordVerified.php index 5f875ab..b4d1ebe 100644 --- a/app/Events/Domains/DnsRecordVerified.php +++ b/app/Events/Domains/DnsRecordVerified.php @@ -4,12 +4,13 @@ namespace App\Events\Domains; +use App\Events\AbstractLogicalEvent; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class DnsRecordVerified +class DnsRecordVerified extends AbstractLogicalEvent { use Dispatchable, InteractsWithSockets, SerializesModels; diff --git a/app/Events/Domains/DomainCreated.php b/app/Events/Domains/DomainCreated.php index b4b8f7c..6a8d0d9 100644 --- a/app/Events/Domains/DomainCreated.php +++ b/app/Events/Domains/DomainCreated.php @@ -4,6 +4,7 @@ namespace App\Events\Domains; +use App\Events\AbstractLogicalEvent; use App\Models\Credential; use App\Models\Domain; use Illuminate\Broadcasting\InteractsWithSockets; @@ -11,7 +12,7 @@ use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; -class DomainCreated +class DomainCreated extends AbstractLogicalEvent { use Dispatchable, InteractsWithSockets, SerializesModels; diff --git a/app/Events/Domains/DomainLinkedToNewProject.php b/app/Events/Domains/DomainLinkedToNewProject.php new file mode 100644 index 0000000..e8d8e06 --- /dev/null +++ b/app/Events/Domains/DomainLinkedToNewProject.php @@ -0,0 +1,17 @@ + + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/app/Exceptions/OperationCanceledException.php b/app/Exceptions/OperationCanceledException.php new file mode 100644 index 0000000..f4fe90a --- /dev/null +++ b/app/Exceptions/OperationCanceledException.php @@ -0,0 +1,11 @@ +schema([ + Forms\Components\TextInput::make('name'), + Forms\Components\TextInput::make('api_key'), + Forms\Components\TextInput::make('access_token'), + Forms\Components\TextInput::make('refresh_token'), + Forms\Components\TextInput::make('access_token'), + Forms\Components\Select::make('type')->options([ + 'ssh', + 'domain', + 'registrar', + 'development', + ]), + Forms\Components\Select::make('service')->options([ + 'cloudflare', + 'namecheap', + 'forge', + ]), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name'), + Tables\Columns\TextColumn::make('type'), + Tables\Columns\TextColumn::make('service'), + Tables\Columns\TextColumn::make('enabled_on'), + + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListCredentials::route('/'), + 'create' => Pages\CreateCredential::route('/create'), + 'edit' => Pages\EditCredential::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/CredentialResource/Pages/CreateCredential.php b/app/Filament/Resources/CredentialResource/Pages/CreateCredential.php new file mode 100644 index 0000000..a0d24ca --- /dev/null +++ b/app/Filament/Resources/CredentialResource/Pages/CreateCredential.php @@ -0,0 +1,13 @@ + [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getFooterWidgets(): array + { + return [ + 'list' => [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getColumnsOverwrite(): array + { + return [ + 'table' => [ + // + ], + 'form' => [ + // + ], + 'infolist' => [ + // + ], + ]; + } + + public static function getExtraPages(): array + { + return [ + // + ]; + } +} diff --git a/app/Filament/Resources/PageResource.php b/app/Filament/Resources/PageResource.php new file mode 100644 index 0000000..97e332d --- /dev/null +++ b/app/Filament/Resources/PageResource.php @@ -0,0 +1,88 @@ + [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getFooterWidgets(): array + { + return [ + 'list' => [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getColumnsOverwrite(): array + { + return [ + 'table' => [ + // + ], + 'form' => [ + // + ], + 'infolist' => [ + // + ], + ]; + } + + public static function getExtraPages(): array + { + return [ + // + ]; + } +} diff --git a/app/Filament/Resources/PersonResource.php b/app/Filament/Resources/PersonResource.php new file mode 100644 index 0000000..5f0a3e2 --- /dev/null +++ b/app/Filament/Resources/PersonResource.php @@ -0,0 +1,88 @@ + [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getFooterWidgets(): array + { + return [ + 'list' => [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getColumnsOverwrite(): array + { + return [ + 'table' => [ + // + ], + 'form' => [ + // + ], + 'infolist' => [ + // + ], + ]; + } + + public static function getExtraPages(): array + { + return [ + // + ]; + } +} diff --git a/app/Filament/Resources/ProjectResource.php b/app/Filament/Resources/ProjectResource.php new file mode 100644 index 0000000..7e1c5b2 --- /dev/null +++ b/app/Filament/Resources/ProjectResource.php @@ -0,0 +1,88 @@ + [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getFooterWidgets(): array + { + return [ + 'list' => [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getColumnsOverwrite(): array + { + return [ + 'table' => [ + // + ], + 'form' => [ + // + ], + 'infolist' => [ + // + ], + ]; + } + + public static function getExtraPages(): array + { + return [ + // + ]; + } +} diff --git a/app/Filament/Resources/Shield/RoleResource.php b/app/Filament/Resources/Shield/RoleResource.php new file mode 100644 index 0000000..8149f3b --- /dev/null +++ b/app/Filament/Resources/Shield/RoleResource.php @@ -0,0 +1,501 @@ +schema([ + Forms\Components\Grid::make() + ->schema([ + Forms\Components\Section::make() + ->schema([ + Forms\Components\TextInput::make('name') + ->label(__('filament-shield::filament-shield.field.name')) + ->unique(ignoreRecord: true) + ->required() + ->maxLength(255), + Forms\Components\TextInput::make('guard_name') + ->label(__('filament-shield::filament-shield.field.guard_name')) + ->default(Utils::getFilamentAuthGuard()) + ->nullable() + ->maxLength(255), + Forms\Components\Toggle::make('select_all') + ->onIcon('heroicon-s-shield-check') + ->offIcon('heroicon-s-shield-exclamation') + ->label(__('filament-shield::filament-shield.field.select_all.name')) + ->helperText(fn (): HtmlString => new HtmlString(__('filament-shield::filament-shield.field.select_all.message'))) + ->live() + ->afterStateUpdated(function ($livewire, Forms\Set $set, $state) { + static::toggleEntitiesViaSelectAll($livewire, $set, $state); + }) + ->dehydrated(fn ($state): bool => $state), + ]) + ->columns([ + 'sm' => 2, + 'lg' => 3, + ]), + ]), + Forms\Components\Tabs::make('Permissions') + ->contained() + ->tabs([ + Forms\Components\Tabs\Tab::make(__('filament-shield::filament-shield.resources')) + ->visible(fn (): bool => (bool) Utils::isResourceEntityEnabled()) + ->badge(static::getResourceTabBadgeCount()) + ->schema([ + Forms\Components\Grid::make() + ->schema(static::getResourceEntitiesSchema()) + ->columns(FilamentShieldPlugin::get()->getGridColumns()), + ]), + Forms\Components\Tabs\Tab::make(__('filament-shield::filament-shield.pages')) + ->visible(fn (): bool => (bool) Utils::isPageEntityEnabled() && (count(FilamentShield::getPages()) > 0 ? true : false)) + ->badge(count(static::getPageOptions())) + ->schema([ + Forms\Components\CheckboxList::make('pages_tab') + ->label('') + ->options(fn (): array => static::getPageOptions()) + ->searchable() + ->live() + ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) { + static::setPermissionStateForRecordPermissions( + component: $component, + operation: $operation, + permissions: static::getPageOptions(), + record: $record + ); + static::toggleSelectAllViaEntities($livewire, $set); + }) + ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) + ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set + )) + ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set, + resetState: true + )) + ->dehydrated(fn ($state) => blank($state) ? false : true) + ->bulkToggleable() + ->gridDirection('row') + ->columns(FilamentShieldPlugin::get()->getCheckboxListColumns()) + ->columnSpan(FilamentShieldPlugin::get()->getCheckboxListColumnSpan()), + ]), + Forms\Components\Tabs\Tab::make(__('filament-shield::filament-shield.widgets')) + ->visible(fn (): bool => (bool) Utils::isWidgetEntityEnabled() && (count(FilamentShield::getWidgets()) > 0 ? true : false)) + ->badge(count(static::getWidgetOptions())) + ->schema([ + Forms\Components\CheckboxList::make('widgets_tab') + ->label('') + ->options(fn (): array => static::getWidgetOptions()) + ->searchable() + ->live() + ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) { + static::setPermissionStateForRecordPermissions( + component: $component, + operation: $operation, + permissions: static::getWidgetOptions(), + record: $record + ); + + static::toggleSelectAllViaEntities($livewire, $set); + }) + ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) + ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set + )) + ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set, + resetState: true + )) + ->dehydrated(fn ($state) => blank($state) ? false : true) + ->bulkToggleable() + ->gridDirection('row') + ->columns(FilamentShieldPlugin::get()->getCheckboxListColumns()) + ->columnSpan(FilamentShieldPlugin::get()->getCheckboxListColumnSpan()), + ]), + Forms\Components\Tabs\Tab::make(__('filament-shield::filament-shield.custom')) + ->visible(fn (): bool => (bool) Utils::isCustomPermissionEntityEnabled() && (count(static::getCustomEntities()) > 0 ? true : false)) + ->badge(count(static::getCustomPermissionOptions())) + ->schema([ + Forms\Components\CheckboxList::make('custom_permissions') + ->label('') + ->options(fn (): array => static::getCustomPermissionOptions()) + ->searchable() + ->live() + ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) { + static::setPermissionStateForRecordPermissions( + component: $component, + operation: $operation, + permissions: static::getCustomPermissionOptions(), + record: $record + ); + static::toggleSelectAllViaEntities($livewire, $set); + }) + ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) + ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set + )) + ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set, + resetState: true + )) + ->dehydrated(fn ($state) => blank($state) ? false : true) + ->bulkToggleable() + ->gridDirection('row') + ->columns(FilamentShieldPlugin::get()->getCheckboxListColumns()) + ->columnSpan(FilamentShieldPlugin::get()->getCheckboxListColumnSpan()), + ]), + ]) + ->columnSpan('full'), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->badge() + ->label(__('filament-shield::filament-shield.column.name')) + ->formatStateUsing(fn ($state): string => Str::headline($state)) + ->colors(['primary']) + ->searchable(), + Tables\Columns\TextColumn::make('guard_name') + ->badge() + ->label(__('filament-shield::filament-shield.column.guard_name')), + Tables\Columns\TextColumn::make('permissions_count') + ->badge() + ->label(__('filament-shield::filament-shield.column.permissions')) + ->counts('permissions') + ->colors(['success']), + Tables\Columns\TextColumn::make('updated_at') + ->label(__('filament-shield::filament-shield.column.updated_at')) + ->dateTime(), + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\DeleteBulkAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListRoles::route('/'), + 'create' => Pages\CreateRole::route('/create'), + 'view' => Pages\ViewRole::route('/{record}'), + 'edit' => Pages\EditRole::route('/{record}/edit'), + ]; + } + + public static function getModel(): string + { + return Utils::getRoleModel(); + } + + public static function getModelLabel(): string + { + return __('filament-shield::filament-shield.resource.label.role'); + } + + public static function getPluralModelLabel(): string + { + return __('filament-shield::filament-shield.resource.label.roles'); + } + + public static function shouldRegisterNavigation(): bool + { + return Utils::isResourceNavigationRegistered(); + } + + public static function getNavigationGroup(): ?string + { + return Utils::isResourceNavigationGroupEnabled() + ? __('filament-shield::filament-shield.nav.group') + : ''; + } + + public static function getNavigationLabel(): string + { + return __('filament-shield::filament-shield.nav.role.label'); + } + + public static function getNavigationIcon(): string + { + return __('filament-shield::filament-shield.nav.role.icon'); + } + + public static function getNavigationSort(): ?int + { + return Utils::getResourceNavigationSort(); + } + + public static function getSlug(): string + { + return Utils::getResourceSlug(); + } + + public static function getNavigationBadge(): ?string + { + return Utils::isResourceNavigationBadgeEnabled() + ? (string) static::getModel()::count() + : null; + } + + public static function isScopedToTenant(): bool + { + return Utils::isScopedToTenant(); + } + + public static function canGloballySearch(): bool + { + return Utils::isResourceGloballySearchable() && count(static::getGloballySearchableAttributes()) && static::canViewAny(); + } + + public static function getResourceEntitiesSchema(): ?array + { + if (blank(static::$permissionsCollection)) { + static::$permissionsCollection = Utils::getPermissionModel()::all(); + } + + return collect(FilamentShield::getResources())->sortKeys()->reduce(function ($entities, $entity) { + + $entities[] = Forms\Components\Section::make(FilamentShield::getLocalizedResourceLabel($entity['fqcn'])) + ->description(fn () => new HtmlString(''.Utils::showModelPath($entity['fqcn']).'')) + ->compact() + ->schema([ + Forms\Components\CheckboxList::make($entity['resource']) + ->label('') + ->options(fn (): array => static::getResourcePermissionOptions($entity)) + ->live() + ->afterStateHydrated(function (Component $component, $livewire, string $operation, ?Model $record, Forms\Set $set) use ($entity) { + static::setPermissionStateForRecordPermissions( + component: $component, + operation: $operation, + permissions: static::getResourcePermissionOptions($entity), + record: $record + ); + + static::toggleSelectAllViaEntities($livewire, $set); + }) + ->afterStateUpdated(fn ($livewire, Forms\Set $set) => static::toggleSelectAllViaEntities($livewire, $set)) + ->selectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set + )) + ->deselectAllAction(fn (FormAction $action, Component $component, $livewire, Forms\Set $set) => static::bulkToggleableAction( + action: $action, + component: $component, + livewire: $livewire, + set: $set, + resetState: true + )) + ->dehydrated(fn ($state) => blank($state) ? false : true) + ->bulkToggleable() + ->gridDirection('row') + ->columns(FilamentShieldPlugin::get()->getResourceCheckboxListColumns()), + ]) + ->columnSpan(FilamentShieldPlugin::get()->getSectionColumnSpan()) + ->collapsible(); + + return $entities; + }, collect()) + ?->toArray() ?? []; + } + + public static function getResourceTabBadgeCount(): ?int + { + return collect(FilamentShield::getResources()) + ->map(fn ($resource) => count(static::getResourcePermissionOptions($resource))) + ->sum(); + } + + public static function getResourcePermissionOptions(array $entity): array + { + return collect(Utils::getResourcePermissionPrefixes($entity['fqcn'])) + ->flatMap(fn ($permission) => [ + $permission.'_'.$entity['resource'] => FilamentShield::getLocalizedResourcePermissionLabel($permission), + ]) + ->toArray(); + } + + public static function setPermissionStateForRecordPermissions(Component $component, string $operation, array $permissions, ?Model $record): void + { + + if (in_array($operation, ['edit', 'view'])) { + + if (blank($record)) { + return; + } + if ($component->isVisible() && count($permissions) > 0) { + $component->state( + collect($permissions) + /** @phpstan-ignore-next-line */ + ->filter(fn ($value, $key) => $record->checkPermissionTo($key)) + ->keys() + ->toArray() + ); + } + } + } + + public static function toggleEntitiesViaSelectAll($livewire, Forms\Set $set, bool $state): void + { + $entitiesComponents = collect($livewire->form->getFlatComponents()) + ->filter(fn (Component $component) => $component instanceof Forms\Components\CheckboxList); + + if ($state) { + $entitiesComponents + ->each( + function (Forms\Components\CheckboxList $component) use ($set) { + $set($component->getName(), array_keys($component->getOptions())); + } + ); + } else { + $entitiesComponents + ->each(fn (Forms\Components\CheckboxList $component) => $component->state([])); + } + } + + public static function toggleSelectAllViaEntities($livewire, Forms\Set $set): void + { + $entitiesStates = collect($livewire->form->getFlatComponents()) + ->reduce(function ($counts, $component) { + if ($component instanceof Forms\Components\CheckboxList) { + $counts[$component->getName()] = count(array_keys($component->getOptions())) == count(collect($component->getState())->values()->unique()->toArray()); + } + + return $counts; + }, collect()) + ->values(); + if ($entitiesStates->containsStrict(false)) { + $set('select_all', false); + } else { + $set('select_all', true); + } + } + + public static function getPageOptions(): array + { + return collect(FilamentShield::getPages()) + ->flatMap(fn ($pagePermission) => [ + $pagePermission => FilamentShield::getLocalizedPageLabel($pagePermission), + ]) + ->toArray(); + } + + public static function getWidgetOptions(): array + { + return collect(FilamentShield::getWidgets()) + ->flatMap(fn ($widgetPermission) => [ + $widgetPermission => FilamentShield::getLocalizedWidgetLabel($widgetPermission), + ]) + ->toArray(); + } + + public static function getCustomPermissionOptions(): array + { + return collect(static::getCustomEntities()) + ->flatMap(fn ($customPermission) => [ + $customPermission => str($customPermission)->headline()->toString(), + ]) + ->toArray(); + } + + protected static function getCustomEntities(): ?Collection + { + $resourcePermissions = collect(); + collect(FilamentShield::getResources())->each(function ($entity) use ($resourcePermissions) { + collect(Utils::getResourcePermissionPrefixes($entity['fqcn']))->map(function ($permission) use ($resourcePermissions, $entity) { + $resourcePermissions->push((string) Str::of($permission.'_'.$entity['resource'])); + }); + }); + + $entitiesPermissions = $resourcePermissions + ->merge(FilamentShield::getPages()) + ->merge(FilamentShield::getWidgets()) + ->values(); + + return static::$permissionsCollection->whereNotIn('name', $entitiesPermissions)->pluck('name'); + } + + public static function bulkToggleableAction(FormAction $action, Component $component, $livewire, Forms\Set $set, bool $resetState = false): void + { + $action + ->livewireClickHandlerEnabled(true) + ->action(function () use ($component, $livewire, $set, $resetState) { + /** @phpstan-ignore-next-line */ + $component->state($resetState ? [] : array_keys($component->getOptions())); + static::toggleSelectAllViaEntities($livewire, $set); + }); + } +} diff --git a/app/Filament/Resources/Shield/RoleResource/Pages/CreateRole.php b/app/Filament/Resources/Shield/RoleResource/Pages/CreateRole.php new file mode 100644 index 0000000..21991f3 --- /dev/null +++ b/app/Filament/Resources/Shield/RoleResource/Pages/CreateRole.php @@ -0,0 +1,44 @@ +permissions = collect($data) + ->filter(function ($permission, $key) { + return ! in_array($key, ['name', 'guard_name', 'select_all']); + }) + ->values() + ->flatten(); + + return Arr::only($data, ['name', 'guard_name']); + } + + protected function afterCreate(): void + { + $permissionModels = collect(); + $this->permissions->each(function ($permission) use ($permissionModels) { + $permissionModels->push(Utils::getPermissionModel()::firstOrCreate([ + /** @phpstan-ignore-next-line */ + 'name' => $permission, + 'guard_name' => $this->data['guard_name'], + ])); + }); + + $this->record->syncPermissions($permissionModels); + } +} diff --git a/app/Filament/Resources/Shield/RoleResource/Pages/EditRole.php b/app/Filament/Resources/Shield/RoleResource/Pages/EditRole.php new file mode 100644 index 0000000..6db7599 --- /dev/null +++ b/app/Filament/Resources/Shield/RoleResource/Pages/EditRole.php @@ -0,0 +1,51 @@ +permissions = collect($data) + ->filter(function ($permission, $key) { + return ! in_array($key, ['name', 'guard_name', 'select_all']); + }) + ->values() + ->flatten(); + + return Arr::only($data, ['name', 'guard_name']); + } + + protected function afterSave(): void + { + $permissionModels = collect(); + $this->permissions->each(function ($permission) use ($permissionModels) { + $permissionModels->push(Utils::getPermissionModel()::firstOrCreate([ + 'name' => $permission, + 'guard_name' => $this->data['guard_name'], + ])); + }); + + $this->record->syncPermissions($permissionModels); + } +} diff --git a/app/Filament/Resources/Shield/RoleResource/Pages/ListRoles.php b/app/Filament/Resources/Shield/RoleResource/Pages/ListRoles.php new file mode 100644 index 0000000..319fecf --- /dev/null +++ b/app/Filament/Resources/Shield/RoleResource/Pages/ListRoles.php @@ -0,0 +1,21 @@ + [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getFooterWidgets(): array + { + return [ + 'list' => [ + // + ], + 'view' => [ + // + ], + ]; + } + + public static function getColumnsOverwrite(): array + { + return [ + 'table' => [ + // + ], + 'form' => [ + // + ], + 'infolist' => [ + // + ], + ]; + } + + public static function getExtraPages(): array + { + return [ + // + ]; + } +} diff --git a/app/Forecast.php b/app/Forecast.php new file mode 100644 index 0000000..7902bd3 --- /dev/null +++ b/app/Forecast.php @@ -0,0 +1,47 @@ +filter(function ($service, $key) { + return ! empty($service['client_id']) + && ! empty($service['client_secret']) + && ! empty($service['redirect']); + })->reduce(function ($result, $config, $service) use ($installedNotInstalled) { + + try { + $installedServiceThatMatchesInstalledDriver = array_values(array_filter($installedNotInstalled['installed'], fn ($value) => in_array($service, $value['drivers'] ?? []))); + $driver = Arr::first($installedServiceThatMatchesInstalledDriver) ?? []; + foreach ($driver['drivers'] ?? [] as $eventListener => $driverName) { + $foundListener = Code::with(LaravelProgrammingStyle::class) + ->for(EventServiceProvider::class) + ->propertyContainsValue('listen', $eventListener); + + if ($foundListener) { + return array_merge($result, [ + 'enabled' => array_merge($result['enabled'] ?? [], [ + $service => $config, + ]), + ]); + } + + return array_merge($result, [ + 'disabled' => array_merge($result['disabled'] ?? [], [ + $service => $config, + ]), + ]); + } + } catch (\Throwable $e) { + return array_merge($result, [ + 'disabled' => array_merge($result['disabled'] ?? [], [ + $service => $config, + ]), + ]); + } + + return array_merge($result, [ + 'disabled' => array_merge($result['disabled'] ?? [], [ + $service => $config, + ]), + ]); + }, [ + 'enabled' => [], + 'disabled' => [], + ]); + + return Inertia::render('Admin/Index', array_merge($installedNotInstalled, $serviceWithOauthish)); + } + + public function email() + { + $imapService = new ImapService(); + + dd($imapService->findAllFromDate(now()->subDay())); + + return Inertia::render('Admin/Emails', [ + + 'mail' => '', + ]); + } +} diff --git a/app/Http/Controllers/Api/Actions/RunActionController.php b/app/Http/Controllers/Api/Actions/RunActionController.php new file mode 100644 index 0000000..858f239 --- /dev/null +++ b/app/Http/Controllers/Api/Actions/RunActionController.php @@ -0,0 +1,21 @@ +validate([ + 'id' => 'integer', + ]); + + $imap->markAsRead(request('id')); + } +} diff --git a/app/Http/Controllers/Api/Mail/MarkAsSpamAndMoveController.php b/app/Http/Controllers/Api/Mail/MarkAsSpamAndMoveController.php new file mode 100644 index 0000000..326eb6f --- /dev/null +++ b/app/Http/Controllers/Api/Mail/MarkAsSpamAndMoveController.php @@ -0,0 +1,15 @@ +validate([ + 'id' => 'integer', + ]); + + $imap->markAsUnread(request('id')); + } +} diff --git a/app/Http/Controllers/Api/Mail/ReplyAllController.php b/app/Http/Controllers/Api/Mail/ReplyAllController.php new file mode 100644 index 0000000..65571d8 --- /dev/null +++ b/app/Http/Controllers/Api/Mail/ReplyAllController.php @@ -0,0 +1,15 @@ +createLinkToken((string) $request->user()->id); + } +} diff --git a/app/Http/Controllers/Api/Plaid/ExchangeTokenController.php b/app/Http/Controllers/Api/Plaid/ExchangeTokenController.php new file mode 100644 index 0000000..5ab740a --- /dev/null +++ b/app/Http/Controllers/Api/Plaid/ExchangeTokenController.php @@ -0,0 +1,44 @@ +validate($request, [ + 'institution' => 'required', + 'public_token' => 'required', + ]); + + $service = app(PlaidServiceContract::class); + $exchangedToken = $service->getAccessToken((string) $request->get('public_token')); + + /** @var User $user */ + $user = auth()->user(); + + $institution = $service->getInstitutionsById($request->get('institution')); + $token = $user->credentials()->firstOrCreate([ + 'name' => $institution->get('name'), + 'type' => Credential::TYPE_FINANCE, + 'service' => 'plaid', + 'settings' => [ + 'institution' => $request->get('institution'), + 'item_id' => $exchangedToken['item_id'], + ], + ], [ + 'api_key' => $exchangedToken['access_token'], + 'access_token' => $exchangedToken['access_token'], + ]); + + return $token; + } +} diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php new file mode 100644 index 0000000..87fc1b7 --- /dev/null +++ b/app/Http/Controllers/BudgetController.php @@ -0,0 +1,10 @@ +validate([ + 'name' => 'required|string', + ]); + $name = request()->get('name'); + + $jobId = Str::uuid(); + + $queuedInstalledProcess = function () use ($jobId, $name) { + // enabling is based on if the driver is supported, so we need to remove support from the event service provider. + + $providers = json_decode(file_get_contents(storage_path('provider-information.json')), true); + $driversToEnable = array_values(array_filter($providers['installed'], fn ($package) => $package['name'] === $name)); + + abort_if(count($driversToEnable) === 0, 404, 'No drivers installed by this composer vendor name'); + + broadcast(new ActionLoggedToConsole($jobId, "Attempting to identify the driver needed\r\n")); + + $configuredServices = collect(config('services'))->filter(function ($service, $key) { + return ! empty($service['client_id']) + && ! empty($service['client_secret']) + && ! empty($service['redirect']); + }); + + broadcast(new ActionLoggedToConsole($jobId, "\e[01;32mThere are {$configuredServices->count()} possible oauth/socialite services.\r\n")); + + $errors = collect([]); + $code = LaravelProgrammingStyle::for(EventServiceProvider::class); + foreach ($driversToEnable as $vendor) { + $drivers = $vendor['drivers']; + + foreach ($drivers as $class => $driverName) { + try { + if (! $configuredServices->has($driverName)) { + broadcast(new ActionLoggedToConsole($jobId, "\e[01;31mThere are no services configured for driver [".$driverName."]. Please update your config/services.php config file.\r\n")); + + continue; + } + + $code->removeListenerFromEvent(SocialiteWasCalled::class, $class); + + broadcast(new ActionLoggedToConsole($jobId, "\e[01;32mLooks like we are able to disable the event listener automatically.\r\n" + ."Attempting to save file...\r\n")); + + $code->toFile(Code::REPLACE_FILE); + broadcast(new ActionLoggedToConsole($jobId, "\e[01;33mFile: app/Providers/EventServiceProvider.php updated successfully.\r\n")); + } catch (\Throwable $e) { + broadcast(new ActionLoggedToConsole($jobId, $e->getMessage())); + $errors->push($e->getMessage()); + } finally { + // We don't want to close the modal in the UI, so don't launch an event to say the job completed. + } + } + } + + broadcast(new ActionLoggedToConsole($jobId, "\e[01;33mDisabling complete, you can close this window.\r\n")); + }; + + dispatch($queuedInstalledProcess)->delay(5); + } +} diff --git a/app/Http/Controllers/EnableProviderController.php b/app/Http/Controllers/EnableProviderController.php new file mode 100644 index 0000000..398d6b0 --- /dev/null +++ b/app/Http/Controllers/EnableProviderController.php @@ -0,0 +1,77 @@ +validate([ + 'name' => 'required|string', + 'client_id' => 'required', + 'client_secret' => 'required', + 'redirect' => 'required', + ]); + $name = request()->get('name'); + + $jobId = Str::uuid(); + + $queuedInstalledProcess = function () use ($jobId, $name) { + // enabling is based on if the driver is supported, so we need to remove support from the event service provider. + $providers = json_decode(file_get_contents(storage_path('provider-information.json')), true); + $driversToEnable = array_values(array_filter($providers['installed'], fn ($package) => $package['name'] === $name)); + + abort_if(count($driversToEnable) === 0, 404, 'No drivers installed by this composer vendor name'); + + broadcast(new ComposerActionLoggedToConsole($jobId, "Attempting to identify the driver needed\r\n")); + + $configuredServices = collect(config('services'))->filter(function ($service, $key) { + return ! empty($service['client_id']) + && ! empty($service['client_secret']) + && ! empty($service['redirect']); + }); + + broadcast(new ComposerActionLoggedToConsole($jobId, "\e[01;32mThere are {$configuredServices->count()} possible oauth/socialite services.\r\n")); + + $errors = collect([]); + $code = Code::for(EventServiceProvider::class); + foreach ($driversToEnable as $vendor) { + $drivers = $vendor['drivers']; + + foreach ($drivers as $class => $driverName) { + try { + if (! $configuredServices->has($driverName)) { + broadcast(new ComposerActionLoggedToConsole($jobId, "\e[01;31mThere are no services configured for driver [".$driverName."]. Please update your config/services.php config file.\r\n")); + + continue; + } + + $code->addValueToProperty('listen', SocialiteWasCalled::class, $class); + + broadcast(new ComposerActionLoggedToConsole($jobId, "\e[01;32mLooks like we are able to add the event listener automatically.\r\nAttempting to save file...\r\n")); + + $code->toFile(Code::REPLACE_FILE); + broadcast(new ComposerActionLoggedToConsole($jobId, "\e[01;33mFile: app/Providers/EventServiceProvider.php updated\r\n")); + } catch (\Throwable $e) { + broadcast(new ComposerActionLoggedToConsole($jobId, $e->getMessage())); + $errors->push($e->getMessage()); + } finally { + // We don't want to close the modal in the UI, so don't launch an event to say the job completed. + } + } + } + }; + + broadcast(new SubscribeToJobEvent(request()->user()->id, $jobId)); + + dispatch($queuedInstalledProcess)->delay(5); + } +} diff --git a/app/Http/Controllers/InstallNewProvider.php b/app/Http/Controllers/InstallNewProvider.php new file mode 100644 index 0000000..f0e0e6d --- /dev/null +++ b/app/Http/Controllers/InstallNewProvider.php @@ -0,0 +1,32 @@ +validate([ + 'name' => 'required|string', + ]); + + $jobId = Str::uuid(); + + $package = request()->get('name'); + abort_if(empty($package), 404); + + $queuedInstalledProcess = function () use ($jobId, $package) { + $process = new Process(['composer', 'require', $package], base_path(), ['COMPOSER_HOME' => '~/.composer']); + + $this->runProcess($jobId, $process); + }; + + dispatch($queuedInstalledProcess)->delay(5); + } +} diff --git a/app/Http/Controllers/LocalAdminController.php b/app/Http/Controllers/LocalAdminController.php deleted file mode 100644 index e384565..0000000 --- a/app/Http/Controllers/LocalAdminController.php +++ /dev/null @@ -1,192 +0,0 @@ - $query->Field, DB::select('describe '.(new $model)->getTable())); - - $returnTypes = array_reduce(get_class_methods($model), function ($allClassMethods, $method) use ($model) { - $ref = new \ReflectionMethod($model, $method); - - $type = $ref->getReturnType(); - if (empty($type)) { - return $allClassMethods; - } - - return array_merge($allClassMethods, - [ - $method => $type, - ]); - }, []); - - $methodsThatReturnAClass = array_filter($returnTypes, fn (\ReflectionNamedType $type) => class_exists($type->getName())); - $relations = array_filter($methodsThatReturnAClass, function ($type) { - $c = new \ReflectionClass($type->getName()); - - if (! empty($parentClass = $c->getParentClass())) { - if (! empty($parentParentClass = $parentClass->getParentClass())) { - if ($parentParentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { - return true; - } - } - if ($parentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { - return true; - } - } - - return false; - }); - - return response()->json([ - 'fields' => $fields, - 'includes' => array_keys($relations), - 'sorts' => [], - 'filters' => [], - 'actions' => [ - 'get', - 'paginate:14', - 'first', - ], - ]); - } - - /** - * @throws Exception - */ - public function index(IndexRequest $request, ModelQuery $model) - { - $action = new ActionFilter(request()->get('action', 'paginate:14')); - - $returnTypes = array_reduce(get_class_methods($model), function ($allClassMethods, $method) use ($model) { - $ref = new \ReflectionMethod($model, $method); - - $type = $ref->getReturnType(); - if (empty($type)) { - return $allClassMethods; - } - - return array_merge($allClassMethods, [ - $method => $type, - ]); - }, []); - - $methodsThatReturnAClass = array_filter($returnTypes, fn (\ReflectionNamedType $type) => class_exists($type->getName())); - $relations = array_filter($methodsThatReturnAClass, function ($type) { - $c = new \ReflectionClass($type->getName()); - - if (! empty($parentClass = $c->getParentClass())) { - if (! empty($parentParentClass = $parentClass->getParentClass())) { - if ($parentParentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { - return true; - } - } - if ($parentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { - return true; - } - - if ($parentClass->getName() === \Illuminate\Database\Eloquent\Relations\MorphOneOrMany::class) { - return true; - } - } - - return false; - }); - - $query = QueryBuilder::for(get_class($model)) - ->allowedFields(array_map(fn ($query) => $query->Field, DB::select('describe '.(new $model)->getTable()))) - ->allowedFilters(array_merge([ - Filter::scope('q'), - ])) - ->allowedIncludes(array_keys($relations)) - ->allowedSorts([ - 'id', 'updated_at', 'created_at', - 'name', - ]); - - return $action->execute($query); - } - - public function store(CreateRequest $request, ModelQuery $model) - { - /** @var ModelQuery $resource */ - $resource = new $model; - $resource->fill($request->all()); - $resource->save(); - - return $resource->refresh(); - } - - public function show(ViewRequest $request, ModelQuery $model, $abstractEloquentModel = null) - { - $query = QueryBuilder::for(get_class($model)); - - return $query->find($abstractEloquentModel) ?? response([ - 'message' => 'No resource found by that id.', - ], 404); - } - - public function update(UpdateRequest $request, ModelQuery $model, $abstractEloquentModel = null) - { - $abstractEloquentModel->update($request->all()); - - return $abstractEloquentModel->refresh(); - } - - public function destroy(DeleteRequest $request, ModelQuery $model, ModelQuery $abstractEloquentModel) - { - $abstractEloquentModel->delete(); - - return response('', 204); - } - - public function forceDestroy(ForceDeleteRequest $request, ModelQuery $model, ModelQuery $abstractEloquentModel) - { - if (! $model->usesSoftdeletes()) { - abort(404, 'You cannot force delete an item of this type.'); - - return; - } - - $abstractEloquentModel->forceDelete(); - - return response('', 204); - } - - public function restore(RestoreRequest $request, ModelQuery $model, ModelQuery $abstractEloquentModel) - { - if (! $model->usesSoftdeletes()) { - abort(404, 'You cannot restore an item of this type.'); - - return; - } - - $abstractEloquentModel->restore(); - - return $abstractEloquentModel->refresh(); - } -} diff --git a/app/Http/Controllers/Logic/AddListenerForEventController.php b/app/Http/Controllers/Logic/AddListenerForEventController.php new file mode 100644 index 0000000..a64b50f --- /dev/null +++ b/app/Http/Controllers/Logic/AddListenerForEventController.php @@ -0,0 +1,29 @@ +validate([ + 'event' => 'required', + 'listener' => 'required', + ]); + + LaravelProgrammingStyle::for(EventServiceProvider::class) + ->addListenerToEvent($request->get('event'), $request->get('listener')) + ->toFile(Code::REPLACE_FILE); + } +} diff --git a/app/Http/Controllers/Logic/RemoveListenerForEventController.php b/app/Http/Controllers/Logic/RemoveListenerForEventController.php new file mode 100644 index 0000000..6dfa199 --- /dev/null +++ b/app/Http/Controllers/Logic/RemoveListenerForEventController.php @@ -0,0 +1,29 @@ +validate([ + 'event' => 'required', + 'listener' => 'required', + ]); + + LaravelProgrammingStyle::for(EventServiceProvider::class) + ->removeListenerFromEvent($request->get('event'), $request->get('listener')) + ->toFile(Code::REPLACE_FILE); + } +} diff --git a/app/Http/Controllers/MessageController.php b/app/Http/Controllers/MessageController.php new file mode 100644 index 0000000..48aefbb --- /dev/null +++ b/app/Http/Controllers/MessageController.php @@ -0,0 +1,10 @@ + Arr::first((new OpenWeatherService)->query('Petoskey, MI')), + 'articles' => Article::query() + ->with('author:id,name') + ->where('author_type', ExternalRssFeed::class) + ->whereIn('author_id', ExternalRssFeed::query() + ->whereHas('tags', fn ($query) => $query->where('id', Tag::findOrCreate('Petoskey')->id)) + ->pluck('id') + ) + ->where('last_modified', '>=', now()->subDays(7)) + ->orderByDesc('last_modified') + ->paginate(10, ['*'], 'articles'), + ]); + + return view('petoskey.today', [ + 'articles' => Article::query() + ->with('author:id,name') + ->where('author_type', ExternalRssFeed::class) + ->whereIn('author_id', ExternalRssFeed::query() + ->where('name', 'Petoskey Area') + ->orWhere('name', 'Petoskey Downtown on Facebook') + ->orWhere('name', 'Petoskey Library on Facebook') + ->pluck('id') + ) + ->where('last_modified', '>=', now()->subDays(7)) + ->orderByDesc('last_modified') + ->paginate(5, ['*'], 'articles'), + 'weather' => Arr::first((new OpenWeatherService)->query('Petoskey, MI')), + 'news' => Article::query() + ->with('author:id,name') + ->where('author_type', ExternalRssFeed::class) + ->where('last_modified', '>=', now()->subDays(7)) + ->whereIn('author_id', ExternalRssFeed::query() + ->where('name', 'Petoskey News on Facebook') + ->pluck('id') + ) + ->paginate(5, ['*'], 'news'), + ]); + } +} diff --git a/app/Http/Controllers/ResearchController.php b/app/Http/Controllers/ResearchController.php deleted file mode 100644 index 5fad990..0000000 --- a/app/Http/Controllers/ResearchController.php +++ /dev/null @@ -1,52 +0,0 @@ - \App\Models\Project::count(), + 'server_count' => \App\Models\Server::count(), + 'domain_count' => \App\Models\Domain::count(), + 'credential_count' => \App\Models\Credential::count(), + 'user_count' => \App\Models\User::count(), + 'activity_logs' => \Spatie\Activitylog\Models\Activity::query() + ->with('causer') + ->orderBy('created_at', 'desc') + ->paginate(20), + ]); + } +} diff --git a/app/Http/Controllers/DomainController.php b/app/Http/Controllers/Spork/Domains/DomainController.php similarity index 60% rename from app/Http/Controllers/DomainController.php rename to app/Http/Controllers/Spork/Domains/DomainController.php index 8ff94c3..ce584cd 100644 --- a/app/Http/Controllers/DomainController.php +++ b/app/Http/Controllers/Spork/Domains/DomainController.php @@ -2,11 +2,10 @@ declare(strict_types=1); -namespace App\Http\Controllers; +namespace App\Http\Controllers\Spork\Domains; -use App\Http\Requests\StoreDomainRequest; -use App\Http\Requests\UpdateDomainRequest; -use App\Models\Domain; +use App\Http\Controllers\Controller; +use Illuminate\Http\Request; class DomainController extends Controller { @@ -21,7 +20,7 @@ public function index() /** * Store a newly created resource in storage. */ - public function store(StoreDomainRequest $request) + public function store(Request $request) { // } @@ -29,7 +28,7 @@ public function store(StoreDomainRequest $request) /** * Display the specified resource. */ - public function show(Domain $domain) + public function show(string $id) { // } @@ -37,7 +36,7 @@ public function show(Domain $domain) /** * Update the specified resource in storage. */ - public function update(UpdateDomainRequest $request, Domain $domain) + public function update(Request $request, string $id) { // } @@ -45,7 +44,7 @@ public function update(UpdateDomainRequest $request, Domain $domain) /** * Remove the specified resource from storage. */ - public function destroy(Domain $domain) + public function destroy(string $id) { // } diff --git a/app/Http/Controllers/Spork/DomainsController.php b/app/Http/Controllers/Spork/DomainsController.php new file mode 100644 index 0000000..1db331c --- /dev/null +++ b/app/Http/Controllers/Spork/DomainsController.php @@ -0,0 +1,30 @@ + Domain::query() + ->withCount('records', 'domainAnalytics') + ->paginate(request('limit'), ['*'], 'page', request('page')), + ]); + } + + public function show(Domain $domain) + { + $domain->load('domainAnalytics', 'records'); + + return Inertia::render('Domain', [ + 'domain' => $domain, + ]); + } +} diff --git a/app/Http/Controllers/Spork/LocalAdminController.php b/app/Http/Controllers/Spork/LocalAdminController.php new file mode 100644 index 0000000..bcfa280 --- /dev/null +++ b/app/Http/Controllers/Spork/LocalAdminController.php @@ -0,0 +1,166 @@ + App\Models\Finance\Account::class, + 'articles' => App\Models\Article::class, + 'budgets' => App\Models\Finance\Budget::class, + 'conditions' => App\Models\Condition::class, + 'credentials' => App\Models\Credential::class, + 'domains' => App\Models\Domain::class, + 'external_rss_feeds' => App\Models\ExternalRssFeed::class, + 'messages' => App\Models\Message::class, + 'navigations' => App\Models\Navigation::class, + 'pages' => App\Models\Page::class, + 'people' => App\Models\Person::class, + 'projects' => App\Models\Project::class, + 'research' => App\Models\Research::class, + 'scripts' => App\Models\Spork\Script::class, + 'servers' => App\Models\Server::class, + 'tags' => App\Models\Tag::class, + 'threads' => App\Models\Thread::class, + 'transactions' => App\Models\Finance\Transaction::class, + 'users' => App\Models\User::class, + ]; + + public function fields(IndexRequest $request) + { + return response()->json((new DescribeTableService)->describe($this->getModel($request))); + } + + protected function getModel(Request $request) + { + $parts = $request->path(); + $split = array_filter(explode('/', $parts), fn ($part) => ! is_numeric($part)); + + $tableFromUrl = end($split); + + return static::MODELS[$tableFromUrl]; + } + + /** + * @throws Exception + */ + public function index(IndexRequest $request) + { + $class = $this->getModel($request); + $model = new $class; + $action = new ActionFilter(request()->get('action', 'paginate:14')); + + $description = (new DescribeTableService)->describe($model); + + $query = QueryBuilder::for($class) + ->allowedFields($description['fields']) + ->allowedFilters(array_merge([ + Filter::scope('q'), + ], $description['filters'])) + ->allowedIncludes($description['includes']) + ->allowedSorts($description['sorts']); + + return $action->execute($query); + } + + public function store(CreateRequest $request) + { + $model = $this->getModel($request); + $description = (new DescribeTableService)->describe(new $model); + + $request->validate(array_reduce($description['required'], fn ($all, $field) => array_merge( + $all, + [$field => 'required'] + ), [])); + /** @var ModelQuery $resource */ + $resource = new $model; + $resource->fill($request->all()); + $resource->save(); + + return $resource->refresh(); + } + + public function show(ViewRequest $request, $abstractEloquentModel = null) + { + $query = QueryBuilder::for($this->getModel($request)); + + return $query->find($abstractEloquentModel) ?? response([ + 'message' => 'No resource found by that id.', + ], 414); + } + + public function update(UpdateRequest $request, $abstractEloquentModel = null) + { + $modelClass = $this->getModel($request); + $abstractEloquentModel = $modelClass::findOrFail($abstractEloquentModel); + $abstractEloquentModel->update($request->all()); + + return $abstractEloquentModel->refresh(); + } + + public function destroy(DeleteRequest $request, $abstractEloquentModel) + { + $modelClass = $this->getModel($request); + $abstractEloquentModel = $modelClass::findOrFail($abstractEloquentModel); + $abstractEloquentModel->delete(); + + return response('', 204); + } + + public function forceDestroy(ForceDeleteRequest $request, $abstractEloquentModel) + { + $modelClass = $this->getModel($request); + $abstractEloquentModel = $modelClass::findOrFail($abstractEloquentModel); + + $model = new $modelClass; + + if (! $model->usesSoftdeletes()) { + abort(404, 'You cannot force delete an item of this type.'); + + return; + } + + $abstractEloquentModel->forceDelete(); + + return response('', 204); + } + + public function restore(RestoreRequest $request, $model, $abstractEloquentModel) + { + $modelClass = $this->getModel($request); + $abstractEloquentModel = $modelClass::findOrFail($abstractEloquentModel); + + if (! $abstractEloquentModel->usesSoftdeletes()) { + abort(404, 'You cannot restore an item of this type.'); + + return; + } + + $abstractEloquentModel->restore(); + + return $abstractEloquentModel->refresh(); + } +} diff --git a/app/Http/Controllers/Spork/PagesController.php b/app/Http/Controllers/Spork/PagesController.php new file mode 100644 index 0000000..d7b8817 --- /dev/null +++ b/app/Http/Controllers/Spork/PagesController.php @@ -0,0 +1,21 @@ +load([ + 'servers.tags', + 'domains.records', + 'pages.domain', + 'research', + 'credentials', + 'domains' => function ($domainQuery) { + $domainQuery->with([ + 'domainAnalytics' => function ($analyticsQuery) { + $analyticsQuery + ->select([ + \DB::raw('sum(query_count) as query_count'), + \DB::raw('sum(uncached_count) as uncached_count'), + \DB::raw('sum(stale_count) as stale_count'), + \DB::raw('min(date) as min_date'), + \DB::raw('max(date) as max_date'), + 'domain_id', + ]) + ->where('date', '>=', now()->subHours(24)) + ->where('date', '<=', now()) + ->groupBy('domain_id', DB::raw('date(date)')) + ->orderBy('query_count', 'desc'); + }, + ]); + }, + ]); + + return Inertia::render('Projects/Project', [ + 'project' => $project, + 'project_analytics' => [ + [ + 'name' => 'Total Queries', + 'stat' => $project->domains->reduce(fn ($carry, $domain) => $carry + $domain->domainAnalytics->sum('query_count'), 0), + 'duration' => $project->domains->reduce(function (int $carry, $domain) { + $maxDate = $domain->domainAnalytics->map->min_date->min(); + $minDate = $domain->domainAnalytics->map->max_date->max(); + + return max(\Carbon\Carbon::parse($maxDate)->diffInHours(\Carbon\Carbon::parse($minDate)), $carry); + }, 0).' hours', + ], + [ + 'name' => 'Total Uncached', + 'stat' => $project->domains->reduce(fn ($carry, $domain) => $carry + $domain->domainAnalytics->sum('uncached_count'), 0), + 'duration' => $project->domains->reduce(function (int $carry, $domain) { + $maxDate = $domain->domainAnalytics->map->min_date->min(); + $minDate = $domain->domainAnalytics->map->max_date->max(); + + return max(\Carbon\Carbon::parse($maxDate)->diffInHours(\Carbon\Carbon::parse($minDate)), $carry); + }, 0).' hours', + ], + [ + 'name' => 'Total Stale', + 'stat' => $project->domains->reduce(fn ($carry, $domain) => $carry + $domain->domainAnalytics->sum('stale_count'), 0), + 'duration' => $project->domains->reduce(function (int $carry, $domain) { + $maxDate = $domain->domainAnalytics->map->min_date->min(); + $minDate = $domain->domainAnalytics->map->max_date->max(); + + return max(\Carbon\Carbon::parse($maxDate)->diffInHours(\Carbon\Carbon::parse($minDate)), $carry); + }, 0).' hours', + + ], + ], + ]); + } + + public function deploy(Project $project) + { + $project->load([ + 'servers.tags', 'domains', + ]); + + $forgeCredential = $project->credentials()->where('service', 'forge')->first(); + $cloudflareCredential = $project->credentials()->where('service', 'cloudflare')->first(); + $namecheapCredential = $project->credentials()->where('service', 'namecheap')->first(); + + /** @var \App\Models\Server $server */ + foreach ($project->servers as $server) { + $tags = array_map(fn ($tag) => $tag->name->en, $server->tags); + if (in_array('loadbalancer', $tags)) { + // Link the load balancer's network to all the other servers, only add servers that are labeled `web` + } + foreach ($project->domains as $domain) { + if (in_array('loadbalancer', $tags)) { + // Each one of these jobs should look to see if the configuration is already where we want it. + dispatch_sync(new \App\Jobs\Deployment\Steps\SetupCloudflareDns($domain, $cloudflareCredential, $namecheapCredential)); + dispatch_sync(new \App\Jobs\Deployment\Steps\SetupLoadBalancerJob($server, $domain, $project)); + dispatch_sync(new \App\Jobs\Deployment\Steps\SetupLoadBalancerDnsRecordJob($server, $domain, $project)); + dispatch_sync(new \App\Jobs\Deployment\Steps\DeploySslCertificateJob($server, $domain, $forgeCredential)); + } + if (in_array('web', $tags)) { + // Setup domain on server + // Setup project for server (setup git, setup deployment webhook, etc etc...) + // Update the environment variables with share values. + // Configure jobs/queues for server + // configure cron schedules/daemons + } + // Basically a queue worker, or a project with a prod env that isn't _the_ prod server. + if (in_array('app', $tags)) { + // Queue workers are not setup to handle traffic from the load balancer + // Setup domain on server + // Setup project for server (setup git, setup deployment webhook, etc etc...) + // Update the environment variables with share values. + // Configure jobs/queues for server + // configure cron schedules/daemons + } + } + } + + } + + public function attach(Project $project) + { + // request() + request()->validate([ + 'resource_type' => \Illuminate\Validation\Rule::in([ + \App\Models\Server::class, + \App\Models\Domain::class, + \App\Models\Credential::class, + \App\Models\Page::class, + ]), + ]); + + if (\DB::table('project_resources')->where([ + 'resource_type' => request()->get('resource_type'), + 'resource_id' => request()->get('resource_id'), + 'project_id' => $project->id, + ])->exists()) { + return response([ + 'message' => 'Already exists', + ], 422); + } + + \DB::table('project_resources')->insert([ + 'resource_type' => request()->get('resource_type'), + 'resource_id' => request()->get('resource_id'), + 'project_id' => $project->id, + 'settings' => '{}', + ]); + } + + public function detach(Project $project) + { + // request() + request()->validate([ + 'resource_type' => \Illuminate\Validation\Rule::in([ + \App\Models\Server::class, + \App\Models\Domain::class, + \App\Models\Credential::class, + \App\Models\Page::class, + ]), + ]); + + \DB::table('project_resources')->where([ + 'resource_type' => request()->get('resource_type'), + 'resource_id' => request()->get('resource_id'), + 'project_id' => $project->id, + ])->delete(); + } +} diff --git a/app/Http/Controllers/Spork/ServersController.php b/app/Http/Controllers/Spork/ServersController.php new file mode 100644 index 0000000..be1359c --- /dev/null +++ b/app/Http/Controllers/Spork/ServersController.php @@ -0,0 +1,24 @@ + $project, + ]); + } +} diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php deleted file mode 100644 index 827c641..0000000 --- a/app/Http/Controllers/TagController.php +++ /dev/null @@ -1,52 +0,0 @@ -validate([ + 'name' => 'required|string', + ]); + + $jobId = Str::uuid(); + $package = request()->get('name'); + abort_if(empty($package), 404); + + $queuedInstalledProcess = function () use ($jobId, $package) { + $process = new Process(['composer', 'remove', $package], base_path(), ['COMPOSER_HOME' => '~/.composer']); + + $this->runProcess($jobId, $process); + }; + + dispatch($queuedInstalledProcess)->delay(5); + } +} diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index dafb4ad..50aafaf 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -4,6 +4,8 @@ namespace App\Http\Middleware; +use App\Models\Thread; +use App\Services\ConditionService; use Illuminate\Http\Request; use Inertia\Middleware; @@ -36,7 +38,16 @@ public function version(Request $request): ?string public function share(Request $request): array { return array_merge(parent::share($request), [ - // + 'navigation' => $navigation = (new ConditionService)->navigation(), + 'current_navigation' => $navigation->where('current', true)->first(), + 'conversations' => Thread::query() + ->orderByDesc('origin_server_ts') + ->paginate( + request('limit'), + ['*'], + 'page', + request('page') + ), ]); } } diff --git a/app/Http/Middleware/OnlyHost.php b/app/Http/Middleware/OnlyHost.php new file mode 100644 index 0000000..cdf4d78 --- /dev/null +++ b/app/Http/Middleware/OnlyHost.php @@ -0,0 +1,20 @@ +user(), 404); + + abort_unless(in_array($request->user()->email, config('auth.admin_emails')), 404); + + return $next($request); + } +} diff --git a/app/Http/Middleware/OnlyInDevelopment.php b/app/Http/Middleware/OnlyInDevelopment.php new file mode 100644 index 0000000..08346ce --- /dev/null +++ b/app/Http/Middleware/OnlyInDevelopment.php @@ -0,0 +1,24 @@ +|string|null */ - protected $proxies; + protected $proxies = '*'; /** * The headers that should be used to detect proxies. diff --git a/app/Http/Requests/Condition/StoreRequest.php b/app/Http/Requests/Condition/StoreRequest.php new file mode 100644 index 0000000..fdcb7af --- /dev/null +++ b/app/Http/Requests/Condition/StoreRequest.php @@ -0,0 +1,68 @@ +check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array|string> + */ + public function rules(): array + { + return [ + 'comparator' => [ + 'required', + Rule::in([ + ContainsValueOperator::class, + ContainsValueStrictOperator::class, + DoesntContainValueOperator::class, + DoesntEqualValueOperator::class, + EndsWithOperator::class, + EqualsValueOperator::class, + FilterIn::class, + GreaterThanOperator::class, + GreaterThanOrEqualToOperator::class, + LessThanOperator::class, + LessThanOrEqualToOperator::class, + StartsWithOperator::class, + ]), + ], + 'parameter' => 'required', + 'value' => 'required', + 'conditionable_id' => '', + 'conditionable_type' => [ + 'required', + Rule::in(LaravelProgrammingStyle::instancesOf(Conditionable::class)->getClasses()), + ], + ]; + } +} diff --git a/app/Http/Requests/StoreScriptRequest.php b/app/Http/Requests/StoreScriptRequest.php new file mode 100644 index 0000000..b759186 --- /dev/null +++ b/app/Http/Requests/StoreScriptRequest.php @@ -0,0 +1,30 @@ + + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/UpdateScriptRequest.php b/app/Http/Requests/UpdateScriptRequest.php new file mode 100644 index 0000000..8c52362 --- /dev/null +++ b/app/Http/Requests/UpdateScriptRequest.php @@ -0,0 +1,30 @@ + + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Jobs/AbstractSyncResourceJob.php b/app/Jobs/AbstractSyncResourceJob.php index d66d40a..d535407 100644 --- a/app/Jobs/AbstractSyncResourceJob.php +++ b/app/Jobs/AbstractSyncResourceJob.php @@ -16,7 +16,7 @@ abstract class AbstractSyncResourceJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected ModelQuery $model; diff --git a/app/Jobs/Chunks/BulkFindOrCreateJob.php b/app/Jobs/Chunks/BulkFindOrCreateJob.php new file mode 100644 index 0000000..bd63dd9 --- /dev/null +++ b/app/Jobs/Chunks/BulkFindOrCreateJob.php @@ -0,0 +1,49 @@ +model::query() + ->select('name', 'primary_address') + ->get() + ->map(fn ($row) => md5($row->name.$row->primary_address)); + + foreach ($this->chunks as $chunk) { + if ($existingThings->contains($hash = md5($chunk['name'].$chunk['primary_address']))) { + continue; + } + + $existingThings->push($hash); + + $this->model::create($chunk); + } + } +} diff --git a/app/Jobs/Chunks/ChunkFindOrCreateJob.php b/app/Jobs/Chunks/ChunkFindOrCreateJob.php new file mode 100644 index 0000000..c88d80d --- /dev/null +++ b/app/Jobs/Chunks/ChunkFindOrCreateJob.php @@ -0,0 +1,33 @@ +model; + + $instance::firstOrCreate($this->attributes, $this->values); + } +} diff --git a/app/Jobs/CloudflareSyncAndPurgeJob.php b/app/Jobs/CloudflareSyncAndPurgeJob.php index 77bdf85..682b1e0 100644 --- a/app/Jobs/CloudflareSyncAndPurgeJob.php +++ b/app/Jobs/CloudflareSyncAndPurgeJob.php @@ -48,6 +48,7 @@ public function sync(): void 'domain' => $domain['domain'], 'credential' => $localDomain, ]); + // If we don't have the domain in question synced via registrars we don't want to touch it. continue; } diff --git a/app/Jobs/RefreshForgeServersJob.php b/app/Jobs/Deployment/Steps/AddSSHKeyToServerJob.php similarity index 50% rename from app/Jobs/RefreshForgeServersJob.php rename to app/Jobs/Deployment/Steps/AddSSHKeyToServerJob.php index 643da85..7d07118 100644 --- a/app/Jobs/RefreshForgeServersJob.php +++ b/app/Jobs/Deployment/Steps/AddSSHKeyToServerJob.php @@ -2,29 +2,31 @@ declare(strict_types=1); -namespace App\Jobs; +namespace App\Jobs\Deployment\Steps; use App\Models\Credential; use App\Models\Server; -use App\Services\LaravelForgeService; +use App\Services\Development\ForgeDevelopmentService; +use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class RefreshForgeServersJob implements ShouldQueue +class AddSSHKeyToServerJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Create a new job instance. */ public function __construct( + public Server $server, public Credential $credential, - public ?LaravelForgeService $service + public Credential $forgeCredential, ) { - $this->service = new LaravelForgeService($credential); + // } /** @@ -32,10 +34,8 @@ public function __construct( */ public function handle(): void { - $servers = $this->service->getServers(); + $laravelForgeService = new ForgeDevelopmentService($this->forgeCredential); - foreach ($servers as $server) { - // $localServer = Server::where() - } + $laravelForgeService->addSSHKeyToServer($this->server, $this->credential); } } diff --git a/app/Jobs/Deployment/Steps/CompileAndUploadAssetsToStorage.php b/app/Jobs/Deployment/Steps/CompileAndUploadAssetsToStorage.php new file mode 100644 index 0000000..7c0cbfc --- /dev/null +++ b/app/Jobs/Deployment/Steps/CompileAndUploadAssetsToStorage.php @@ -0,0 +1,37 @@ +server, $this->domain, $this->project), + new CompileNpmAssetsJob($this->server, $this->domain, $this->project), + new UploadAssetsToStorageJob($this->server, $this->domain, $this->project), + ])->dispatch(); + } +} diff --git a/app/Jobs/Deployment/Steps/CompileNpmAssetsJob.php b/app/Jobs/Deployment/Steps/CompileNpmAssetsJob.php new file mode 100644 index 0000000..e3b4483 --- /dev/null +++ b/app/Jobs/Deployment/Steps/CompileNpmAssetsJob.php @@ -0,0 +1,50 @@ +project->credentialFor(Credential::TYPE_SSH); + + try { + $service = new SshService( + host: $this->server->internal_ip_address, + username: 'forge', + publicKeyFile: $sshCredential->getPublicKey(), + privateKeyFile: $sshCredential->getPrivateKey(), + passKey: $sshCredential->getPasskey() + ); // replace with your server details + + $installLog = $service->execute('npm install', '/home/forge/'.$this->domain->name); + $buildLog = $service->execute('npm run build', '/home/forge/'.$this->domain->name); + + } catch (\Exception $e) { + dd($e); + } + } +} diff --git a/app/Jobs/Deployment/Steps/DeploySslCertificateJob.php b/app/Jobs/Deployment/Steps/DeploySslCertificateJob.php index f8c9e72..0062599 100644 --- a/app/Jobs/Deployment/Steps/DeploySslCertificateJob.php +++ b/app/Jobs/Deployment/Steps/DeploySslCertificateJob.php @@ -6,15 +6,25 @@ use App\Models\Credential; use App\Models\Domain; +use App\Models\Project; use App\Models\Server; use App\Services\Development\ForgeDevelopmentService; - -class DeploySslCertificateJob +use Illuminate\Bus\Batchable; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; +use Laravel\Forge\Resources\Site; + +class DeploySslCertificateJob implements ShouldQueue { + use Batchable, DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels; + public function __construct( public Server $server, public Domain $domain, - public Credential $credential + public Project $project ) { } @@ -22,15 +32,26 @@ public function handle() { // Configure the domain for the server // DNS, Setup SSL, Verify - // Register the domain to forge. - $service = new ForgeDevelopmentService($this->credential); + $forgeCredential = $this->project->credentialFor(Credential::FORGE_DEVELOPMENT); + $service = new ForgeDevelopmentService($forgeCredential); + $domainAliases = $this->project->domains->filter(fn (Domain $domain) => $domain->id !== $this->domain->id)->values(); + + /** @var Site $site */ + $site = $service->createDomainIfNotExists($this->domain, $domainAliases, $this->server); - $site = $service->createDomainIfNotExists($this->domain, $this->server); + if ($this->aRecordForOurServerDoesntAlreadyExist($this->domain, $this->server, (array) $site)) { + // If we dont have all our servers pointing at the server in question fetching an SSL cert will fail + return; + } - $service->setupSslCertificate($this->domain, $this->server, $site); + $service->setupSslCertificate($this->domain, $domainAliases, $this->server, (array) $site); + } + + protected function aRecordForOurServerDoesntAlreadyExist(Domain $domain, Server $server, array $site): bool + { + $aRecordValues = array_map(fn (array $record) => $record['ip'], dns_get_record($domain->name, DNS_A)); - $service->setupSsl($this->domain, $this->server); - dd(dns_get_record($this->domain->name, DNS_A)); + return ! in_array($server->ip_address, $aRecordValues); } } diff --git a/app/Jobs/Deployment/Steps/InstallComposerAssetsJob.php b/app/Jobs/Deployment/Steps/InstallComposerAssetsJob.php new file mode 100644 index 0000000..8977fe7 --- /dev/null +++ b/app/Jobs/Deployment/Steps/InstallComposerAssetsJob.php @@ -0,0 +1,49 @@ +project->credentialFor(Credential::TYPE_SSH); + // Run npm install, then npm run production + // upload assets to s3 + + try { + $service = new SshService( + $this->server->internal_ip_address, + 'forge', + $sshCredential->getPublicKey(), + $sshCredential->getPrivateKey(), + passKey: $sshCredential->getPasskey(), + ); // replace with your server details + + $installLog = $service->execute('composer install', '/home/forge/'.$this->domain->name); + } catch (\Exception $e) { + dd($e); + } + } +} diff --git a/app/Jobs/Deployment/Steps/SetupCloudflareDns.php b/app/Jobs/Deployment/Steps/SetupCloudflareDns.php index 977a920..2d58e13 100644 --- a/app/Jobs/Deployment/Steps/SetupCloudflareDns.php +++ b/app/Jobs/Deployment/Steps/SetupCloudflareDns.php @@ -9,9 +9,17 @@ use App\Models\Domain; use App\Services\Factories\DomainServiceFactory; use App\Services\Factories\RegistrarServiceFactory; - -class SetupCloudflareDns +use Illuminate\Bus\Batchable; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; + +class SetupCloudflareDns implements ShouldQueue { + use Batchable, DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels; + public function __construct( public Domain $domain, public Credential $cloudflareDns, @@ -31,14 +39,16 @@ public function handle(DomainServiceFactory $factory) $domainPaginator = $service->getDomains(100, $page++); foreach ($domainPaginator->items() as $domain) { if ($domain['domain'] === $this->domain->name) { - $registrar->updateDomainNs($this->domain->name, $domain['']); + // $registrar->updateDomainNs($this->domain->name, $domain['name_servers']); break 2; } } } while ($domainPaginator->hasMorePages()); + /// Create domain if not exists $servers = $service->createDomain($this->domain->name); + // Update the NS with registrar if not already set.. $registrar->updateDomainNs($this->domain->name, $servers); dispatch(new NameServerVerificationJob($this->domain->name, $servers)); diff --git a/app/Jobs/Deployment/Steps/SetupCronSchedulerJob.php b/app/Jobs/Deployment/Steps/SetupCronSchedulerJob.php new file mode 100644 index 0000000..ec31bfb --- /dev/null +++ b/app/Jobs/Deployment/Steps/SetupCronSchedulerJob.php @@ -0,0 +1,59 @@ +project->credentialFor(Credential::FORGE_DEVELOPMENT); + + $laravelForgeService = new ForgeDevelopmentService($forgeCredential); + + $laravelForgeService->createDomainIfNotExists( + $this->domain, + $this->project->domains->filter(fn (Domain $domain) => $domain->id !== $this->domain->id)->values(), + $this->server + ); + + $laravelForgeService->createCronIfNotExists( + $this->domain, + $this->server, + [ + 'command' => 'php /home/forge/'.$this->domain->name.'/artisan schedule:run', + 'frequency' => 'minutely', + 'user' => 'forge', + 'minute' => '*', + 'hour' => '*', + 'day' => '*', + 'month' => '*', + 'weekday' => '*', + ] + ); + } +} diff --git a/app/Jobs/Deployment/Steps/SetupHorizonSchedulerJob.php b/app/Jobs/Deployment/Steps/SetupHorizonSchedulerJob.php new file mode 100644 index 0000000..aa33e51 --- /dev/null +++ b/app/Jobs/Deployment/Steps/SetupHorizonSchedulerJob.php @@ -0,0 +1,54 @@ +project->credentialFor(Credential::FORGE_DEVELOPMENT); + + $laravelForgeService = new ForgeDevelopmentService($forgeCredential); + + $laravelForgeService->createDomainIfNotExists( + $this->domain, + $this->project->domains->filter(fn (Domain $domain) => $domain->id !== $this->domain->id)->values(), + $this->server + ); + + $laravelForgeService->createDaemonIfNotExists( + $this->domain, + $this->server, + [ + 'command' => 'php artisan horizon', + 'user' => 'forge', + 'directory' => '/home/forge/'.$this->domain->name, + ] + ); + } +} diff --git a/app/Jobs/Deployment/Steps/SetupLoadBalancerDnsRecordJob.php b/app/Jobs/Deployment/Steps/SetupLoadBalancerDnsRecordJob.php index 4dccb34..576c435 100644 --- a/app/Jobs/Deployment/Steps/SetupLoadBalancerDnsRecordJob.php +++ b/app/Jobs/Deployment/Steps/SetupLoadBalancerDnsRecordJob.php @@ -7,31 +7,68 @@ use App\Jobs\Domains\VerifyDnsValue; use App\Models\Credential; use App\Models\Domain; +use App\Models\Project; use App\Models\Server; use App\Services\Factories\DomainServiceFactory; -use Illuminate\Support\Collection; +use Illuminate\Bus\Batchable; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; -class SetupLoadBalancerDnsRecordJob +class SetupLoadBalancerDnsRecordJob implements ShouldQueue { + use Batchable, DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels; + public function __construct( public Server $server, public Domain $domain, - public Collection $domains, - public Credential $credential + public Project $project ) { } public function handle() { - $dnsService = (new DomainServiceFactory)->make(Credential::find(4)); - - $dnsService->createDnsRecord($this->domain, [ - 'type' => 'A', - 'name' => '@', - 'content' => $this->server->ip_address, - 'ttl' => 'auto', + $this->project->load([ + 'domains' => function ($query) { + $query->whereNotNull('cloudflare_id'); + }, ]); - - dispatch(new VerifyDnsValue($this->domain->name, 'A', $this->server->ip_address)); + $dnsService = (new DomainServiceFactory)->make($this->project->credentialFor(Credential::CLOUDFLARE)); + $this->project->domains->each(function (Domain $domain) use ($dnsService) { + $page = 1; + // do { + // $records = $dnsService->getDns($domain->cloudflare_id, 'A', 100, $page ++); + // + // foreach ($records as $record) { + // if ( + // $record['name'] === $domain->name && + // $record['type'] === 'A' && + // $record['content'] === $this->server->ip_address + // ) { + // return; + // } + // } + // } while ($records->hasMorePages()); + // + // $dnsService->createDnsRecord($domain->cloudflare_id, [ + // 'type' => 'A', + // 'name' => '@', + // 'content' => $this->server->ip_address, + // 'ttl' => 3600, + // // For forge, we can't have the dang SSL thing proxied + // 'proxied' => false, + // ]); + $dnsService->createDnsRecord($domain->cloudflare_id, [ + 'type' => 'CNAME', + 'name' => '*', + 'content' => $domain->name, + 'ttl' => 3600, + // For forge, we can't have the dang SSL thing proxied + 'proxied' => false, + ]); + }); + // dispatch_sync(new VerifyDnsValue($this->domain->name, 'A', $this->server->ip_address)); } } diff --git a/app/Jobs/Deployment/Steps/SetupLoadBalancerJob.php b/app/Jobs/Deployment/Steps/SetupLoadBalancerJob.php index be945cf..842e1f2 100644 --- a/app/Jobs/Deployment/Steps/SetupLoadBalancerJob.php +++ b/app/Jobs/Deployment/Steps/SetupLoadBalancerJob.php @@ -6,17 +6,25 @@ use App\Models\Credential; use App\Models\Domain; +use App\Models\Project; use App\Models\Server; use App\Services\Development\ForgeDevelopmentService; -use Illuminate\Support\Collection; +use Illuminate\Bus\Batchable; +use Illuminate\Bus\Queueable; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\DispatchesJobs; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\SerializesModels; +use Laravel\Forge\Resources\Site; -class SetupLoadBalancerJob +class SetupLoadBalancerJob implements ShouldQueue { + use Batchable, DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels; + public function __construct( public Server $server, public Domain $domain, - public Collection $domains, - public Credential $credential + public Project $project ) { } @@ -24,11 +32,17 @@ public function handle() { // Configure the domain for the server // Register the domain to forge. - $service = new ForgeDevelopmentService($this->credential); - $domains = collect([]); + $forgeCredential = $this->project->credentialFor(Credential::FORGE_DEVELOPMENT); + + $service = new ForgeDevelopmentService($forgeCredential); - $site = $service->createDomainIfNotExists($this->domain, $domains, $this->server); + /** @var Site $site */ + $site = $service->createDomainIfNotExists( + $this->domain, + $this->project->domains->filter(fn (Domain $domain) => $domain->id !== $this->domain->id)->values(), + $this->server + ); - $service->setupSslCertificate($this->domain, $domains, $this->server, $site); + $service->updateLoadBalancer($this->domain, $this->project, (array) $site); } } diff --git a/app/Jobs/Deployment/Steps/SetupWebServerJob.php b/app/Jobs/Deployment/Steps/SetupWebServerJob.php new file mode 100644 index 0000000..9d71978 --- /dev/null +++ b/app/Jobs/Deployment/Steps/SetupWebServerJob.php @@ -0,0 +1,44 @@ +project->credentialFor(Credential::FORGE_DEVELOPMENT); + + $laravelForgeService = new ForgeDevelopmentService($forgeCredential); + + $laravelForgeService->createDomainIfNotExists( + $this->domain, + $this->project->domains->filter(fn (Domain $domain) => $domain->id !== $this->domain->id)->values(), + $this->server + ); + } +} diff --git a/app/Jobs/Deployment/Steps/UploadAssetsToStorageJob.php b/app/Jobs/Deployment/Steps/UploadAssetsToStorageJob.php new file mode 100644 index 0000000..9969a40 --- /dev/null +++ b/app/Jobs/Deployment/Steps/UploadAssetsToStorageJob.php @@ -0,0 +1,63 @@ +open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + // Add the compiled assets to the zip file + $compiledAssetsFiles = Storage::files($compiledAssetsPath); + foreach ($compiledAssetsFiles as $file) { + $zip->addFile(public_path($file), 'assets/'.$file); + } + + // Add the vendor files to the zip file + $vendorFiles = Storage::allFiles($vendorPath); + foreach ($vendorFiles as $file) { + $zip->addFile(base_path($file), 'vendor/'.$file); + } + + // Close the zip file + $zip->close(); + + // Upload the zip file to the S3 storage + $s3Path = 'assets/assets.zip'; // Replace 'assets/assets.zip' with your desired S3 path + Storage::disk('s3')->put($s3Path, file_get_contents($zipPath)); + + // Delete the temporary zip file + unlink($zipPath); + } +} diff --git a/app/Jobs/Deployment/Strategies/LaravelWithDatabase.php b/app/Jobs/Deployment/Strategies/LaravelWithDatabase.php index b6394c3..6f46f27 100644 --- a/app/Jobs/Deployment/Strategies/LaravelWithDatabase.php +++ b/app/Jobs/Deployment/Strategies/LaravelWithDatabase.php @@ -5,6 +5,7 @@ namespace App\Jobs\Deployment\Strategies; use App\Models\Credential; +use App\Services\Development\ForgeDevelopmentService; use GuzzleHttp\Client; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -51,7 +52,13 @@ public function handle() 'project_type' => 'php', 'directory' => '/public', ]); + $forgeCredential = $this->project->credentialFor(Credential::FORGE_DEVELOPMENT); + $laravelForgeService = new ForgeDevelopmentService($forgeCredential); + + $laravelForgeService->createRedirectIfNotExists($this->domain, + $this->server, + $this->redirectTo); $client->createRedirectRule($this->server, $site->id, [ 'from' => '/', 'to' => $this->redirectTo, diff --git a/app/Jobs/Domains/CloudflareSyncAndPurgeJob.php b/app/Jobs/Domains/CloudflareSyncAndPurgeJob.php index 8d9cd3f..4113bcf 100644 --- a/app/Jobs/Domains/CloudflareSyncAndPurgeJob.php +++ b/app/Jobs/Domains/CloudflareSyncAndPurgeJob.php @@ -16,7 +16,7 @@ public function handle(DomainServiceFactory $serviceFactory) if ($this->batch()?->cancelled()) { return; } - $credentials = Credential::ownedBy($this->credential->owner)->where('uuid', $this->credential->uuid)->get(); + $credentials = Credential::where('service', 'cloudflare')->get(); foreach ($credentials as $credential) { try { $this->service = $serviceFactory->make($credential); @@ -45,6 +45,7 @@ public function sync(): void 'domain' => $domain['domain'], 'credential' => $localDomain, ]); + // If we don't have the domain in question synced via registrars we don't want to touch it. continue; } diff --git a/app/Jobs/ExecuteSequence.php b/app/Jobs/ExecuteSequence.php index c76f28d..6e45ca3 100644 --- a/app/Jobs/ExecuteSequence.php +++ b/app/Jobs/ExecuteSequence.php @@ -225,12 +225,12 @@ protected function handleSynchronizationStep(Step $step) $this->updateProperty($modelToUpdateBecauseItIsRelatedToThePrimaryKey, $key, $standardDataForOurModel[$key], $synchronization); } elseif ( $synchronization->on_update === 'changed' - && $modelToUpdateBecauseItIsRelatedToThePrimaryKey->$key !== $standardDataForOurModel[$key] + && $standardDataForOurModel[$key] !== $modelToUpdateBecauseItIsRelatedToThePrimaryKey->$key ) { $this->updateProperty($modelToUpdateBecauseItIsRelatedToThePrimaryKey, $key, $standardDataForOurModel[$key], $synchronization); } elseif ( $synchronization->on_update === 'changed' - && $modelToUpdateBecauseItIsRelatedToThePrimaryKey->$key === $standardDataForOurModel[$key] + && $standardDataForOurModel[$key] === $modelToUpdateBecauseItIsRelatedToThePrimaryKey->$key ) { continue; } else { diff --git a/app/Jobs/FetchCloudflareAnalytics.php b/app/Jobs/FetchCloudflareAnalytics.php index bf1a884..eb6ea2f 100644 --- a/app/Jobs/FetchCloudflareAnalytics.php +++ b/app/Jobs/FetchCloudflareAnalytics.php @@ -11,7 +11,7 @@ class FetchCloudflareAnalytics implements ShouldQueue { - use Queueable, InteractsWithQueue; + use InteractsWithQueue, Queueable; public function handle() { @@ -19,7 +19,7 @@ public function handle() $domains = \App\Models\Domain::whereNotNull('cloudflare_id')->get(); foreach ($domains as $domain) { - $service->getAnalytics($domain, now()->subDay()->startOfDay(), now()->subDay()->endOfDay()); + $service->getAnalytics($domain, now()->subDay()->startOfDay(), now()->endOfDay()); } } } diff --git a/app/Jobs/FetchDomainsForCredential.php b/app/Jobs/FetchDomainsForCredential.php index a57a82c..23eb71f 100644 --- a/app/Jobs/FetchDomainsForCredential.php +++ b/app/Jobs/FetchDomainsForCredential.php @@ -17,7 +17,7 @@ class FetchDomainsForCredential implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Create a new job instance. @@ -26,7 +26,7 @@ class FetchDomainsForCredential implements ShouldQueue */ public function __construct( public Credential $credential, - public ?User $user + public ?User $user = null ) { $this->user = $user ?? auth()->user(); } @@ -38,7 +38,7 @@ public function __construct( */ public function handle(Dispatcher $dispatcher) { - if ($this->batch()->cancelled()) { + if ($this->batch()?->cancelled()) { return; } if ($this->credential->type !== Credential::TYPE_DOMAIN) { diff --git a/app/Jobs/FetchResourcesFromCredentials.php b/app/Jobs/FetchResourcesFromCredentials.php new file mode 100644 index 0000000..50122f0 --- /dev/null +++ b/app/Jobs/FetchResourcesFromCredentials.php @@ -0,0 +1,55 @@ +type === 'ssh') { + continue; + } + + $dispatcher->dispatchSync(match ($credential->type) { + Credential::TYPE_REGISTRAR => new FetchRegistrarForCredential($credential), + Credential::TYPE_DOMAIN => new FetchDomainsForCredential($credential), + Credential::TYPE_SERVER => new FetchServersForCredential($credential), + Credential::TYPE_DEVELOPMENT, 'forge' => new LaravelForgeServersSyncJob($credential), + Credential::TYPE_FINANCE => new SyncPlaidTransactionsJob($credential, now()->subWeek(), now(), false), + }); + } + } +} diff --git a/app/Jobs/FetchServersForCredential.php b/app/Jobs/FetchServersForCredential.php index 6a66b2f..6b05fe2 100644 --- a/app/Jobs/FetchServersForCredential.php +++ b/app/Jobs/FetchServersForCredential.php @@ -17,7 +17,7 @@ class FetchServersForCredential implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** * Create a new job instance. @@ -26,7 +26,7 @@ class FetchServersForCredential implements ShouldQueue */ public function __construct( public Credential $credential, - public ?User $user + public ?User $user = null, ) { $this->user = $user ?? auth()->user(); } diff --git a/app/Jobs/Finance/SyncPlaidTransactionsJob.php b/app/Jobs/Finance/SyncPlaidTransactionsJob.php new file mode 100644 index 0000000..cd82725 --- /dev/null +++ b/app/Jobs/Finance/SyncPlaidTransactionsJob.php @@ -0,0 +1,137 @@ +accessToken = $access; + $this->startDate = $startDate; + $this->endDate = $endDate; + $this->shouldSendAlerts = $shouldSendAlerts; + } + + public function handle(PlaidServiceContract $plaid): void + { + $transactionsResponse = $plaid->getTransactions($this->accessToken->api_key, $this->startDate, $this->endDate); + + $accounts = $transactionsResponse->get('accounts'); + + foreach ($accounts as $account) { + + /** @var Account $localAccount */ + $localAccount = $this->accessToken->accounts()->firstOrCreate([ + 'account_id' => $account->account_id, + ], $data = [ + 'account_id' => $account->account_id, + 'name' => $account->name, + 'mask' => $account->mask, + 'balance' => $account->balances->current ?? 0, + 'available' => $account->balances->available ?? 0, + 'type' => $account->subtype ?? $account->type, + ]); + + if (! $localAccount->wasRecentlyCreated) { + $localAccount->update($data); + } + } + + $transactions = $transactionsResponse->get('transactions'); + + foreach ($transactions as $transaction) { + $localTransactions = Transaction::where(function ($query) use ($transaction): void { + $query->where('transaction_id', $transaction->transaction_id); + + /** + * Due to how Plaid handles pending transactions, we need to delete the transaction with a pending transaction id, + * and then create a new transaction + * + * @see https://plaid.com/docs/transactions/transactions-data/#reconciling-transactions + */ + if ($transaction->pending_transaction_id) { + $query->orWhere('transaction_id', $transaction->pending_transaction_id); + } + })->get(); + + $localTransactions->map(function ($localTransaction) use ($transaction): void { + if ($transaction->pending_transaction_id === $localTransaction->transaction_id) { + $localTransaction->delete(); + $localTransaction = null; + } + + if (empty($localTransaction)) { + $localTransaction = $this->createLocalTransaction($transaction); + } else { + $localTransaction->update([ + 'account_id' => $transaction->account_id, + 'amount' => $transaction->amount, + 'category_id' => $transaction->category_id, + 'date' => Carbon::parse($transaction->date), + 'name' => $transaction->name, + 'pending' => $transaction->pending, + 'transaction_id' => $transaction->transaction_id, + 'transaction_type' => $transaction->transaction_type, + 'pending_transaction_id' => $transaction->pending_transaction_id, + ]); + } + + $this->syncTransactions($transaction, $localTransaction); + }); + + if ($localTransactions->isEmpty()) { + $this->createLocalTransaction($transaction); + } + } + } + + protected function syncTransactions($transaction, Transaction $localTransaction): void + { + $categoriesToSync = []; + $categories = $transaction->category ?? []; + + $localTransaction->attachTags($categories, 'finance'); + } + + protected function createLocalTransaction($transaction) + { + $localTransaction = Transaction::firstOrCreate([ + 'transaction_id' => $transaction->transaction_id, + ], [ + 'account_id' => $transaction->account_id, + 'amount' => $transaction->amount, + 'category_id' => $transaction->category_id, + 'date' => Carbon::parse($transaction->date), + 'name' => $transaction->name, + 'pending' => $transaction->pending, + 'transaction_id' => $transaction->transaction_id, + 'transaction_type' => $transaction->transaction_type, + 'pending_transaction_id' => $transaction->pending_transaction_id, + ]); + + return $localTransaction; + } +} diff --git a/app/Jobs/News/PopulateExternalRssFeeds.php b/app/Jobs/News/PopulateExternalRssFeeds.php new file mode 100644 index 0000000..6a6ee7a --- /dev/null +++ b/app/Jobs/News/PopulateExternalRssFeeds.php @@ -0,0 +1,42 @@ +feed = $feed; + } + + public function handle(RssFeedService $service) + { + $rssFeed = $service->fetchRssFeed($this->feed->url); + + if ($rssFeed === null) { + // TODO: dispatch some event or something to alert the system that this feed is dead. + return; + } + + /** @var FeedItem $item */ + foreach ($rssFeed->getData() as $item) { + Article::fromFeedItem($this->feed, $item); + } + } +} diff --git a/app/Jobs/News/UpdateAllFeeds.php b/app/Jobs/News/UpdateAllFeeds.php index 453379a..58ee199 100644 --- a/app/Jobs/News/UpdateAllFeeds.php +++ b/app/Jobs/News/UpdateAllFeeds.php @@ -4,8 +4,7 @@ namespace App\Jobs\News; -use App\Events\BatchFinishedRunningEvent; -use App\Models\FeatureList; +use App\Models\ExternalRssFeed; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -19,17 +18,11 @@ class UpdateAllFeeds implements ShouldQueue public function handle() { - $jobs = FeatureList::query() - ->where('feature', 'rss') + $jobs = ExternalRssFeed::query() + ->select('id') ->get() - ->map(fn ($feed) => new UpdateFeed($feed)); + ->map(fn (ExternalRssFeed $feed) => new \App\Jobs\News\UpdateFeed($feed)); - Bus::batch($jobs) - ->allowFailures() - ->name('Update All Feeds') - ->finally(function () { - broadcast(new BatchFinishedRunningEvent(...func_get_args())); - }) - ->dispatch(); + Bus::batch($jobs)->allowFailures()->name('Update All Feeds')->dispatch(); } } diff --git a/app/Jobs/News/UpdateFeed.php b/app/Jobs/News/UpdateFeed.php index 9b5ec4c..ad49bd7 100644 --- a/app/Jobs/News/UpdateFeed.php +++ b/app/Jobs/News/UpdateFeed.php @@ -5,11 +5,9 @@ namespace App\Jobs\News; use App\Models\Article; -use App\Models\FeatureList; +use App\Models\ExternalRssFeed; use App\Services\News\Feeds\AbstractFeed; use App\Services\News\Feeds\FeedItem; -use App\Services\News\RssFeedService; -use Carbon\Carbon; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -21,75 +19,57 @@ class UpdateFeed implements ShouldQueue { use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - protected FeatureList $feed; + protected ExternalRssFeed $feed; - public function __construct(FeatureList $feed) + public function __construct(ExternalRssFeed $feed) { $this->feed = $feed; } - public function handle(RssFeedService $service) + public function handle(\App\Services\News\RssFeedService $service) { + if ($this->batch() && $this->batch()->cancelled()) { + return; + } + /** @var AbstractFeed $rssFeed */ - $rssFeed = $service->fetchRssFeed($this->feed->settings['url'] ?? 'https://fake.tools'); + $rssFeed = $service->fetchRssFeed($this->feed->url); - if (empty($rssFeed)) { - dd($rssFeed, $this->feed); - } /** @var FeedItem $feedItem */ - try { - info("Fetching feed from {$this->feed->settings['url']}"); - foreach ($rssFeed->getData() as $feedItem) { - if ( - $this->feed->articles() - ->where('external_guid', $feedItem->getUuidIfExists()) - ->exists() - ) { - break; - } - - if (str_contains($feedItem->title, '#shorts')) { - $item = Article::firstWhere('external_guid', $feedItem->getExternalId()); - - if (! empty($item)) { - $item->delete(); - } - - continue; - } - - Article::fromFeedItem($this->feed, $feedItem); + foreach ($rssFeed->getData() as $feedItem) { + // If we already have the item's GUID, we must already have this item so we should stop, + // as any items afterwards are probably already in our system as well. + if ($this->feed->articles()->where('external_guid', $feedItem->getUuidIfExists())->exists()) { + break; } - } catch (\Throwable $e) { - info($e->getMessage(), ['error' => $e, 'feed' => $rssFeed]); - dd($e); + + // As a second check, if the item is older than the newest item we already have, we + // probably don't want to add it since it is most likely already in our database. + // This should hopefully never actually be catching anything as the GUID should cover + // everything, but just in case a feed doesn't provide GUIDs or something this is a + // decent back-up protection against duplicate articles. + // Sub one minute just in case the time parsing doesn't quite match up. + $mostRecentPost = $mostRecentPost ?? $this->feed->articles()->first(); + + if (! empty($mostRecentPost) && $feedItem->getPublishedAt()->lessThanOrEqualTo($mostRecentPost->created_at->subMinute())) { + break; + } + + Article::fromFeedItem($this->feed, $feedItem); } -// $this->feed->name = $rssFeed->getName(); + $this->feed->name = $rssFeed->getName(); if (! empty($rssFeed->getPhoto())) { - $this->feed->settings = array_merge(['profile_photo_path' => $rssFeed->getPhoto()], $this->feed->settings); + $this->feed->profile_photo_path = $rssFeed->getPhoto(); } if (! empty($rssFeed->getLastModified())) { - $this->feed->settings = array_merge(['last_modified' => $rssFeed->getLastModified()], $this->feed->settings); + // $this->feed->last_modified = $rssFeed->getLastModified(); } if (! empty($rssFeed->getEtag())) { - $this->feed->settings = array_merge(['etag' => $rssFeed->getEtag()], $this->feed->settings); - } - - $lastArticle = $this->feed->articles() - ->select('last_modified') - ->orderByDesc('last_modified') - ->limit(1) - ->pluck('last_modified') - ->first(); - - if (! empty($lastArticle)) { - $lastArticle = Carbon::parse($lastArticle); - $this->feed->settings = array_merge(['last_published_at' => $lastArticle], $this->feed->settings); - $this->feed->updated_at = $lastArticle; + // $this->feed->etag = $rssFeed->getEtag(); } if ($this->feed->isDirty()) { diff --git a/app/Jobs/OperationJob.php b/app/Jobs/OperationJob.php new file mode 100644 index 0000000..f6ba821 --- /dev/null +++ b/app/Jobs/OperationJob.php @@ -0,0 +1,66 @@ +operation, 'run')) { + throw new OperationCanceledException(); + } + + App::call([$this->operation, 'run']); + } catch (OperationStoppedException $e) { + $this->operation->started_run_at = null; + $this->operation->save(); + + return; + } catch (OperationCanceledException $e) { + $this->operation->delete(); + + return; + } finally { + $this->operation->finished_run_at = Carbon::now(); + $this->operation->save(); + } + } + + public function getOperation(): Operation + { + return $this->operation; + } + + public function displayName() + { + return get_class($this->operation); + } + + public function tags() + { + return $this->operation->tags(); + } +} diff --git a/app/Jobs/Registrar/CloudflareSyncJob.php b/app/Jobs/Registrar/CloudflareSyncJob.php index 078d860..d465bf6 100644 --- a/app/Jobs/Registrar/CloudflareSyncJob.php +++ b/app/Jobs/Registrar/CloudflareSyncJob.php @@ -4,8 +4,8 @@ namespace App\Jobs\Registrar; +use App\Events\Domains\DomainCreated; use App\Models\Domain; -use App\Models\Registrar; use Carbon\Carbon; use Illuminate\Support\Str; @@ -14,9 +14,6 @@ class CloudflareSyncJob extends AbstractSyncRegistrarResourceJob public function sync(): void { $page = 1; - $registrar = Registrar::query()->firstOrCreate([ - 'name' => 'Cloudflare', - ]); do { $domains = $this->service->getDomains(100, $page++); foreach ($domains as $domain) { @@ -29,7 +26,6 @@ public function sync(): void 'domain_id' => $domain['id'], 'registered_at' => $domain['created_at'], 'expires_at' => $domain['expires_at'], - 'registrar_id' => $registrar->id, ]; if (empty($localDomain)) { @@ -45,7 +41,7 @@ public function sync(): void } elseif ($localDomain->$key instanceof Carbon && ! $localDomain->$key->equalTo($value)) { $localDomain->$key = $value; } - } elseif ($localDomain->$key !== $value) { + } elseif ($value !== $localDomain->$key) { // Only set the new value if its different $localDomain->$key = $value; } @@ -53,6 +49,9 @@ public function sync(): void if ($localDomain->isDirty() || ! $localDomain->exists()) { $localDomain->save(); + if ($localDomain->wasRecentlyCreated) { + event(new DomainCreated($localDomain, $this->credential, $this->credential)); + } } } } while ($domains->hasMorePages()); diff --git a/app/Jobs/Registrar/NamecheapSyncJob.php b/app/Jobs/Registrar/NamecheapSyncJob.php index 98fb22d..62bb84a 100644 --- a/app/Jobs/Registrar/NamecheapSyncJob.php +++ b/app/Jobs/Registrar/NamecheapSyncJob.php @@ -4,8 +4,6 @@ namespace App\Jobs\Registrar; -use App\Events\Domains\DomainCreated; -use App\Models\Credential; use App\Models\Domain; use Illuminate\Support\Str; @@ -37,7 +35,7 @@ public function sync(): void } foreach ($data as $key => $value) { - if ($localDomain->$key !== $value) { + if ($value !== $localDomain->$key) { // Only set the new value if its different $localDomain->$key = $value; } @@ -45,9 +43,6 @@ public function sync(): void if ($localDomain->isDirty() || ! $localDomain->exists()) { $localDomain->save(); - if ($localDomain->wasRecentlyCreated) { - event(new DomainCreated($localDomain, $this->credential, Credential::find(4))); - } } } } while ($domains->hasMorePages()); diff --git a/app/Jobs/RunDeployment.php b/app/Jobs/RunDeployment.php index 7091628..aac006b 100644 --- a/app/Jobs/RunDeployment.php +++ b/app/Jobs/RunDeployment.php @@ -4,46 +4,89 @@ namespace App\Jobs; -use App\Models\Domain; +use App\Jobs\Deployment\Steps\AddSSHKeyToServerJob; +use App\Jobs\Deployment\Steps\CompileNpmAssetsJob; +use App\Jobs\Deployment\Steps\DeploySslCertificateJob; +use App\Jobs\Deployment\Steps\SetupCloudflareDns; +use App\Jobs\Deployment\Steps\SetupCronSchedulerJob; +use App\Jobs\Deployment\Steps\SetupHorizonSchedulerJob; +use App\Jobs\Deployment\Steps\SetupLoadBalancerDnsRecordJob; +use App\Jobs\Deployment\Steps\SetupLoadBalancerJob; +use App\Jobs\Deployment\Steps\SetupWebServerJob; +use App\Models\Credential; use App\Models\Project; -use App\Models\Server; +use Illuminate\Bus\Batch; +use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; +use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Bus; class RunDeployment implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - - /** - * Create a new job instance. - * - * @return void - */ - public function __construct( - public Project $project, - ) { + use Batchable, DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels; + + public function __construct(public Project $project) + { } public function handle() { - $this->project->load('servers.tags', 'domains'); - // .... Do stuff?... - $this->project->domains->each(function (Domain $domain) { - $this->project->servers->each(function (Server $server) { - - }); - }); - // Connect to related servers - // Ensure there is routing between servers - // perform various actions for it based on the tags of the servers. - // NPM install if needed - // composer install if needed - // one server should run migrations - // restart horizon on the jobs server - // publish vendor assets - // clean up unneeded files + $servers = $this->project->servers; + $domains = $this->project->domains; + + $jobs = []; + $jobsByDomain = []; + + foreach ($domains as $domain) { + if ($domain->cloudflare_id === null) { + continue; + } + if (! isset($jobsByDomain[$domain->name])) { + $jobsByDomain[$domain->name] = []; + } + $jobsByDomain[$domain->name][] = new SetupCloudflareDns($domain, $this->project->credentialFor(Credential::CLOUDFLARE), $this->project->credentialFor(Credential::NAMECHEAP)); + } + + $primaryDomain = $domains->first(); + $otherDomains = $domains->slice(1); + foreach ($servers as $server) { + $serverJobs = []; + if (! isset($jobsByDomain[$primaryDomain->name])) { + $jobsByDomain[$primaryDomain->name] = []; + } + $tags = $server->tags->map->name; + + $serverJobs[] = new AddSSHKeyToServerJob($server, $this->project->credentialFor(Credential::TYPE_SSH), $this->project->credentialFor(Credential::FORGE_DEVELOPMENT)); + + if ($tags->contains('loadbalancer')) { + $serverJobs[] = new SetupLoadBalancerDnsRecordJob($server, $primaryDomain, $this->project); + $serverJobs[] = new SetupLoadBalancerJob($server, $primaryDomain, $this->project); + $serverJobs[] = new DeploySslCertificateJob($server, $primaryDomain, $this->project); + } + + if ($tags->contains('app') || $tags->contains('web')) { + // $serverJobs[] = new CompileNpmAssetsJob($server, $primaryDomain, $this->project); + $serverJobs[] = new SetupWebServerJob($server, $primaryDomain, $this->project); + // $serverJobs[] = new UploadAssetsJob($server); + } + + if ($tags->contains('jobs')) { + $serverJobs[] = new SetupCronSchedulerJob($server, $primaryDomain, $this->project); + $serverJobs[] = new SetupHorizonSchedulerJob($server, $primaryDomain, $this->project); + } + + // if ($server->tags->contains('database')) { + // $serverJobs[] = new CreateDatabaseJob($server, $this->project); + // } + + array_push($jobsByDomain[$primaryDomain->name], ...$serverJobs); + } + + Bus::batch(array_values(array_filter($jobsByDomain)))->then(function (Batch $batch) { + echo 'Done";'; + })->dispatch(); } } diff --git a/app/Jobs/Servers/DigitalOceanSyncJob.php b/app/Jobs/Servers/DigitalOceanSyncJob.php index 66d0d3d..e1d3113 100644 --- a/app/Jobs/Servers/DigitalOceanSyncJob.php +++ b/app/Jobs/Servers/DigitalOceanSyncJob.php @@ -49,7 +49,7 @@ public function sync(): void ]; foreach ($data as $key => $value) { - if ($localServer->$key !== $value) { + if ($value !== $localServer->$key) { // Only set the new value if its different $localServer->$key = $value; } diff --git a/app/Jobs/Servers/LaravelForgeServersSyncJob.php b/app/Jobs/Servers/LaravelForgeServersSyncJob.php index f63c11d..5c5d981 100644 --- a/app/Jobs/Servers/LaravelForgeServersSyncJob.php +++ b/app/Jobs/Servers/LaravelForgeServersSyncJob.php @@ -4,9 +4,8 @@ namespace App\Jobs\Servers; -use App\Models\Server; +use App\Services\Development\ForgeDevelopmentService; use App\Services\Factories\ServerServiceFactory; -use App\Services\LaravelForgeService; class LaravelForgeServersSyncJob extends AbstractSyncServerResourceJob { @@ -18,14 +17,15 @@ public function handle(ServerServiceFactory $serviceFactory) public function sync(): void { // this means all servers need to respond with the keys. - $servers = (new LaravelForgeService($this->credential))->getServers(); + $servers = (new ForgeDevelopmentService($this->credential))->findAllServers(); foreach ($servers as $server) { - $localServer = Server::where('server_id', $server['id']) + $localServer = $this->credential->servers() + ->where('server_id', $server['id']) ->first(); if (empty($localServer)) { - Server::create([ + $localServer = $this->credential->servers()->create([ 'server_id' => $server['id'], 'name' => $server['name'], 'vcpu' => 1, @@ -35,17 +35,14 @@ public function sync(): void 'os' => 'Ubuntu', 'internal_ip_address' => $server['private_ip_address'], ]); - info('Noserver found for forge'); - - continue; } if ($localServer->isDirty() || ! $localServer->exists()) { $localServer->save(); } - $localServer->attachTag($server['type']); - $localServer->attachTag($server['php_version']); + $localServer->attachTag($server['type'], 'server'); + $localServer->attachTag($server['php_version'], 'server'); } } } diff --git a/app/Listeners/DebugEventListener.php b/app/Listeners/DebugEventListener.php new file mode 100644 index 0000000..ffbbb6f --- /dev/null +++ b/app/Listeners/DebugEventListener.php @@ -0,0 +1,23 @@ + $event, + ]); + } +} diff --git a/app/Listeners/Finance/ApplyUserAutomatedTagsToTransaction.php b/app/Listeners/Finance/ApplyUserAutomatedTagsToTransaction.php new file mode 100644 index 0000000..991a9a5 --- /dev/null +++ b/app/Listeners/Finance/ApplyUserAutomatedTagsToTransaction.php @@ -0,0 +1,77 @@ +model->load('account.credential.user'); + $event->model->refresh(); + $transaction = $event->model; + /** @var Account $account */ + $account = $transaction->account; + $credential = $account->credential; + + $user = $credential->user; + + + if (empty($user)) { + $this->logger->warning('No user found for account', [ + 'account' => $account->id, + 'transaction' => $transaction->id, + 'credential' => $account->credential?->user_id + ]); + return; + } + + $tags = $user->tags()->with('conditions')->where('type', 'automatic')->get(); + + foreach ($tags as $tag) { + $conditions = $tag->conditions; + $conditionsMet = false; + foreach ($conditions as $condition) { + /** @var AbstractLogicalOperator $operator */ + $operator = ConditionService::AVAILABLE_CONDITIONS[$condition->comparator]; + + /** @var AbstractLogicalOperator $operatorInstance */ + $operatorInstance = new $operator; + + $value = Arr::get(compact('transaction'), $condition->parameter); + + if ($operatorInstance->compute($condition->value, $value)) { + $conditionsMet = true; + } + } + + if ($conditionsMet) { + $transaction->tags()->attach($tag); + } + } + } +} diff --git a/app/Listeners/Finance/CreateDefaultAutomatedTags.php b/app/Listeners/Finance/CreateDefaultAutomatedTags.php new file mode 100644 index 0000000..324a57a --- /dev/null +++ b/app/Listeners/Finance/CreateDefaultAutomatedTags.php @@ -0,0 +1,296 @@ + 'subscriptions', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'hulu', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'disney', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'HBO', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'twitch', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'github', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'plex', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'protonmail', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'youtube', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Subscription', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Discord', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'netflix', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'WASABI TECHNOLOGIES', + ], + ], + ], + + [ + 'name' => 'games', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'ORIGIN', + ], + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Steampowered', + ], + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Steam', + ], + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'UBISOFT', + ], + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'gamestop', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Video Games', + ], + [ + 'parameter' => 'transaction.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'game store', + ], + ], + ], + [ + 'name' => 'bills', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'tag.name', + 'comparator' => Condition::COMPARATOR_LIKE, + // anything that's a utility should automatically be a bill + 'value' => 'utilities', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Loans and Mortgages', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Billpay', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'USAA P&C INT AUTOPAY', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Car Dealers and Leasing', + ], + ], + ], + [ + 'name' => 'utilities', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Cable', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Telecommunication Services', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Utilities', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Sanitary and Waste Management', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + // This is for people who get their power/water from the city (like those in petoskey) + 'value' => 'Government Departments and Agencies', + ], + ], + ], + [ + 'name' => 'fast food/restaurants', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'Restaurants', + ], + [ + 'parameter' => 'category.name', + 'comparator' => Condition::COMPARATOR_EQUALS, + 'value' => 'Fast Food', + ], + ], + ], + [ + 'name' => 'fees', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'fee', + ], + ], + ], + [ + 'name' => 'via Privacy.com', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_STARTS_WITH, + 'value' => 'PWP*', + ], + ], + ], + [ + 'name' => 'transfer', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_LIKE, + 'value' => 'transfer', + ], + ], + ], + [ + 'name' => 'credit/income', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_NOT_LIKE, + 'value' => 'transfer', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_NOT_LIKE, + 'value' => 'fee', + ], + [ + 'parameter' => 'amount', + 'comparator' => Condition::COMPARATOR_LESS_THAN, + 'value' => 0, + ], + ], + ], + [ + 'name' => 'debit/expense', + 'type' => 'automatic', + 'conditions' => [ + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_NOT_LIKE, + 'value' => 'transfer', + ], + [ + 'parameter' => 'name', + 'comparator' => Condition::COMPARATOR_NOT_LIKE, + 'value' => 'fee', + ], + [ + 'parameter' => 'amount', + 'comparator' => Condition::COMPARATOR_GREATER_THAN, + 'value' => 0, + ], + ], + ], + ]; + + public function handle(UserCreated $event): void + { + /** @var User $user */ + $user = $event->model; + + foreach (static::TAGS as $tagInfo) { + $conditions = $tagInfo['conditions']; + unset($tagInfo['conditions']); + + /** @var Tag $tag */ + $tag = $user->tags()->create($tagInfo); + foreach ($conditions as $condition) { + $tag->conditions()->create($condition); + } + } + } +} diff --git a/app/Models/Article.php b/app/Models/Article.php new file mode 100644 index 0000000..cb5d064 --- /dev/null +++ b/app/Models/Article.php @@ -0,0 +1,86 @@ + 'datetime', + ]; + + public $dispatchesEvents = [ + 'created' => ArticleCreated::class, + 'creating' => ArticleCreating::class, + 'deleting' => ArticleDeleting::class, + 'deleted' => ArticleDeleted::class, + 'updating' => ArticleUpdating::class, + 'updated' => ArticleUpdated::class, + ]; + + public static function fromFeedItem(ExternalRssFeed $feed, FeedItem $item): self + { + if ($post = self::firstWhere('external_guid', $item->getExternalId())) { + return $post; + } + + $post = new Article(); + // If the item's GUID is a v4 UUID, we may as well use it as our UUID. + $post->uuid = $item->getUuidIfExists(); + $post->external_guid = $item->getExternalId(); + $post->author_id = $feed->id; + $post->author_type = get_class($feed); + $post->headline = $item->getTitle(); + $post->content = $item->getContent(); + $post->attachment = $item->getUrl(); + $post->url = $item->getUrl(); + $post->created_at = $item->getPublishedAt(); + $post->last_modified = $item->getPublishedAt(); + $post->save(); + + return $post; + } + + public function author() + { + return $this->morphTo(); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['headline', 'content', 'attachment', 'url']) + ->useLogName('article') + ->logFillable() + ->logOnlyDirty(); + } +} diff --git a/app/Models/Condition.php b/app/Models/Condition.php new file mode 100644 index 0000000..ff449b4 --- /dev/null +++ b/app/Models/Condition.php @@ -0,0 +1,65 @@ + ConditionCreated::class, + 'creating' => ConditionCreating::class, + 'deleting' => ConditionDeleting::class, + 'deleted' => ConditionDeleted::class, + 'updating' => ConditionUpdating::class, + 'updated' => ConditionUpdated::class, + ]; +} diff --git a/app/Models/Credential.php b/app/Models/Credential.php index bcbc1ce..6705753 100644 --- a/app/Models/Credential.php +++ b/app/Models/Credential.php @@ -4,15 +4,26 @@ namespace App\Models; +use App\Actions\Spork\SyncDataFromCredential; use App\Contracts\ModelQuery; +use App\Events\Models\Credential\CredentialCreated; +use App\Events\Models\Credential\CredentialCreating; +use App\Events\Models\Credential\CredentialDeleted; +use App\Events\Models\Credential\CredentialDeleting; +use App\Events\Models\Credential\CredentialUpdated; +use App\Events\Models\Credential\CredentialUpdating; +use App\Models\Finance\Account; +use App\Models\Traits\HasProjectResource; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; -class Credential extends Model implements ModelQuery +class Credential extends Model implements Crud, ModelQuery { use HasFactory; - - public $guarded = []; + use HasProjectResource; + use LogsActivity; public const DIGITAL_OCEAN = 'digital-ocean'; @@ -46,6 +57,10 @@ class Credential extends Model implements ModelQuery public const TYPE_SOURCE = 'source'; + public const TYPE_FINANCE = 'finance'; + + public const TYPE_SSH = 'ssh'; + public const ALL_DOMAIN_PROVIDERS = [ self::DIGITAL_OCEAN, self::CLOUDFLARE, @@ -81,6 +96,8 @@ class Credential extends Model implements ModelQuery self::GITHUB_SOURCE, ]; + public $guarded = []; + public $hidden = [ 'api_key', 'access_token', @@ -90,4 +107,81 @@ class Credential extends Model implements ModelQuery public $casts = [ 'settings' => 'json', ]; + + public $fillable = [ + 'name', + 'type', + 'service', + 'api_key', + 'secret_key', + 'access_token', + 'refresh_token', + 'settings', + 'enabled_on', + ]; + + public $dispatchesEvents = [ + 'created' => CredentialCreated::class, + 'creating' => CredentialCreating::class, + 'deleting' => CredentialDeleting::class, + 'deleted' => CredentialDeleted::class, + 'updating' => CredentialUpdating::class, + 'updated' => CredentialUpdated::class, + ]; + + public $actions = [ + SyncDataFromCredential::class, + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + public function getPublicKey(): string + { + $publicKeyFile = $this->settings['pub_key_file']; + + if (! file_exists($publicKeyFile)) { + file_put_contents($publicKeyFile, $this->settings['pub_key'] ?? ''); + chmod($publicKeyFile, 0600); + } + + return $publicKeyFile; + } + + public function getPrivateKey(): string + { + $privateKeyFile = $this->settings['private_key_file']; + + if (! file_exists($privateKeyFile)) { + file_put_contents($privateKeyFile, $this->settings['private_key'] ?? ''); + chmod($privateKeyFile, 0600); + } + + return $privateKeyFile; + } + + public function getPasskey(): string + { + return decrypt($this->settings['pass_key'] ?? ''); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'type', 'service', 'enabled_on']) + ->useLogName('credential') + ->logOnlyDirty(); + } + + public function servers() + { + return $this->hasMany(Server::class); + } + + public function accounts() + { + return $this->hasMany(Account::class); + } } diff --git a/app/Models/Crud.php b/app/Models/Crud.php new file mode 100644 index 0000000..4a66393 --- /dev/null +++ b/app/Models/Crud.php @@ -0,0 +1,9 @@ + DomainCreated::class, + 'creating' => DomainCreating::class, + 'deleting' => DomainDeleting::class, + 'deleted' => DomainDeleted::class, + 'updating' => DomainUpdating::class, + 'updated' => DomainUpdated::class, + ]; public function records(): HasMany { @@ -26,12 +52,16 @@ public function domainAnalytics(): HasMany return $this->hasMany(DomainAnalytics::class); } - public function projects(): MorphToMany + public function credential(): BelongsTo + { + return $this->belongsTo(Credential::class); + } + + public function getActivitylogOptions(): LogOptions { - return $this->morphToMany( - Project::class, - 'resource', - 'project_resources' - ); + return LogOptions::defaults() + ->logOnly(['name', 'domain_id', 'registered_at']) + ->useLogName('domain') + ->logOnlyDirty(); } } diff --git a/app/Models/DomainAnalytics.php b/app/Models/DomainAnalytics.php index e31d7d7..91f89dd 100644 --- a/app/Models/DomainAnalytics.php +++ b/app/Models/DomainAnalytics.php @@ -4,12 +4,41 @@ namespace App\Models; +use App\Events\Models\DomainAnalytics\DomainAnalyticsCreated; +use App\Events\Models\DomainAnalytics\DomainAnalyticsCreating; +use App\Events\Models\DomainAnalytics\DomainAnalyticsDeleted; +use App\Events\Models\DomainAnalytics\DomainAnalyticsDeleting; +use App\Events\Models\DomainAnalytics\DomainAnalyticsUpdated; +use App\Events\Models\DomainAnalytics\DomainAnalyticsUpdating; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class DomainAnalytics extends Model { use HasFactory; - public $guarded = []; + public $fillable = [ + 'query_name', + 'response_code', + 'origin', + 'query_count', + 'uncached_count', + 'stale_count', + 'date', + ]; + + public $dispatchesEvents = [ + 'created' => DomainAnalyticsCreated::class, + 'creating' => DomainAnalyticsCreating::class, + 'deleting' => DomainAnalyticsDeleting::class, + 'deleted' => DomainAnalyticsDeleted::class, + 'updating' => DomainAnalyticsUpdating::class, + 'updated' => DomainAnalyticsUpdated::class, + ]; + + public function domain(): BelongsTo + { + return $this->belongsTo(Domain::class); + } } diff --git a/app/Models/DomainRecord.php b/app/Models/DomainRecord.php index 6533d96..3123fb4 100644 --- a/app/Models/DomainRecord.php +++ b/app/Models/DomainRecord.php @@ -5,12 +5,21 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\DomainRecord\DomainRecordCreated; +use App\Events\Models\DomainRecord\DomainRecordCreating; +use App\Events\Models\DomainRecord\DomainRecordDeleted; +use App\Events\Models\DomainRecord\DomainRecordDeleting; +use App\Events\Models\DomainRecord\DomainRecordUpdated; +use App\Events\Models\DomainRecord\DomainRecordUpdating; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; class DomainRecord extends Model implements ModelQuery { use HasFactory; + use LogsActivity; protected $fillable = [ 'name', @@ -23,8 +32,25 @@ class DomainRecord extends Model implements ModelQuery 'timeout', ]; + public $dispatchesEvents = [ + 'created' => DomainRecordCreated::class, + 'creating' => DomainRecordCreating::class, + 'deleting' => DomainRecordDeleting::class, + 'deleted' => DomainRecordDeleted::class, + 'updating' => DomainRecordUpdating::class, + 'updated' => DomainRecordUpdated::class, + ]; + public function domain() { return $this->belongsTo(Domain::class); } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logFillable() + ->logOnlyDirty() + ->useLogName('domain-record'); + } } diff --git a/app/Models/ExternalRssFeed.php b/app/Models/ExternalRssFeed.php new file mode 100644 index 0000000..d78fe59 --- /dev/null +++ b/app/Models/ExternalRssFeed.php @@ -0,0 +1,53 @@ + ExternalRssFeedCreated::class, + 'creating' => ExternalRssFeedCreating::class, + 'deleting' => ExternalRssFeedDeleting::class, + 'deleted' => ExternalRssFeedDeleted::class, + 'updating' => ExternalRssFeedUpdating::class, + 'updated' => ExternalRssFeedUpdated::class, + ]; + + public function articles() + { + return $this->morphMany(Article::class, 'author'); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['uuid', 'url', 'name', 'profile_photo_path']) + ->useLogName('external-rss-feed') + ->logOnlyDirty(); + } +} diff --git a/app/Models/Finance/Account.php b/app/Models/Finance/Account.php new file mode 100644 index 0000000..3188a72 --- /dev/null +++ b/app/Models/Finance/Account.php @@ -0,0 +1,47 @@ + AccountCreated::class, + 'creating' => AccountCreating::class, + 'deleting' => AccountDeleting::class, + 'deleted' => AccountDeleted::class, + 'updating' => AccountUpdating::class, + 'updated' => AccountUpdated::class, + ]; + + public function credential() + { + return $this->belongsTo(Credential::class); + } +} diff --git a/app/Models/Finance/Budget.php b/app/Models/Finance/Budget.php new file mode 100644 index 0000000..e88926a --- /dev/null +++ b/app/Models/Finance/Budget.php @@ -0,0 +1,29 @@ + BudgetCreated::class, + 'creating' => BudgetCreating::class, + 'deleting' => BudgetDeleting::class, + 'deleted' => BudgetDeleted::class, + 'updating' => BudgetUpdating::class, + 'updated' => BudgetUpdated::class, + ]; +} diff --git a/app/Models/Finance/Transaction.php b/app/Models/Finance/Transaction.php new file mode 100644 index 0000000..05ccc15 --- /dev/null +++ b/app/Models/Finance/Transaction.php @@ -0,0 +1,62 @@ + 'float', + 'date' => 'datetime', + 'pending' => 'boolean', + 'data' => 'json', + ]; + + public $dispatchesEvents = [ + 'created' => TransactionCreated::class, + 'creating' => TransactionCreating::class, + 'deleting' => TransactionDeleting::class, + 'deleted' => TransactionDeleted::class, + 'updating' => TransactionUpdating::class, + 'updated' => TransactionUpdated::class, + ]; + + public function account() + { + return $this->belongsTo(Account::class, 'account_id', 'account_id'); + } + + public function user() + { + return $this->hasManyThrough(User::class, Account::class); + } +} diff --git a/app/Models/Membership.php b/app/Models/Membership.php index 2a1515e..57213b1 100644 --- a/app/Models/Membership.php +++ b/app/Models/Membership.php @@ -4,6 +4,12 @@ namespace App\Models; +use App\Events\Models\Membership\MembershipCreated; +use App\Events\Models\Membership\MembershipCreating; +use App\Events\Models\Membership\MembershipDeleted; +use App\Events\Models\Membership\MembershipDeleting; +use App\Events\Models\Membership\MembershipUpdated; +use App\Events\Models\Membership\MembershipUpdating; use Laravel\Jetstream\Membership as JetstreamMembership; class Membership extends JetstreamMembership @@ -14,4 +20,13 @@ class Membership extends JetstreamMembership * @var bool */ public $incrementing = true; + + public $dispatchesEvents = [ + 'created' => MembershipCreated::class, + 'creating' => MembershipCreating::class, + 'deleting' => MembershipDeleting::class, + 'deleted' => MembershipDeleted::class, + 'updating' => MembershipUpdating::class, + 'updated' => MembershipUpdated::class, + ]; } diff --git a/app/Models/Message.php b/app/Models/Message.php new file mode 100644 index 0000000..8725c94 --- /dev/null +++ b/app/Models/Message.php @@ -0,0 +1,35 @@ + MessageCreated::class, + 'creating' => MessageCreating::class, + 'deleting' => MessageDeleting::class, + 'deleted' => MessageDeleted::class, + 'updating' => MessageUpdating::class, + 'updated' => MessageUpdated::class, + ]; + + public function getIsUserAttribute() + { + return auth()->id() === $this->from_person; + } +} diff --git a/app/Models/Navigation.php b/app/Models/Navigation.php new file mode 100644 index 0000000..50e0066 --- /dev/null +++ b/app/Models/Navigation.php @@ -0,0 +1,56 @@ + 'boolean', + 'settings' => 'json', + ]; + + public $hidden = [ + 'id', 'created_at', 'updated_at', + ]; + + public $dispatchesEvents = [ + 'created' => NavigationCreated::class, + 'creating' => NavigationCreating::class, + 'deleting' => NavigationDeleting::class, + 'deleted' => NavigationDeleted::class, + 'updating' => NavigationUpdating::class, + 'updated' => NavigationUpdated::class, + ]; + + public function children() + { + return $this->hasMany(Navigation::class, 'parent_id'); + } + + public function parent() + { + return $this->belongsTo(Navigation::class); + } + + public function domain() + { + return $this->belongsTo(Domain::class); + } +} diff --git a/app/Models/Page.php b/app/Models/Page.php index 45fbf3e..9eaff75 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -5,12 +5,55 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\Page\PageCreated; +use App\Events\Models\Page\PageCreating; +use App\Events\Models\Page\PageDeleted; +use App\Events\Models\Page\PageDeleting; +use App\Events\Models\Page\PageUpdated; +use App\Events\Models\Page\PageUpdating; +use App\Models\Traits\HasProjectResource; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class Page extends Model implements ModelQuery +class Page extends Model implements Crud, ModelQuery { use HasFactory; + use HasProjectResource; - public $guarded = []; + public $fillable = [ + 'title', + 'uri', + 'slug', + 'route', + 'middleware', + 'subtitle', + 'excerpt', + 'content', + 'view', + 'redirect', + 'is_active', + 'sort_order', + 'published_at', + ]; + + public $casts = [ + 'is_active' => 'boolean', + 'middleware' => 'json', + 'settings' => 'json', + ]; + + public $dispatchesEvents = [ + 'created' => PageCreated::class, + 'creating' => PageCreating::class, + 'deleting' => PageDeleting::class, + 'deleted' => PageDeleted::class, + 'updating' => PageUpdating::class, + 'updated' => PageUpdated::class, + ]; + + public function domain(): BelongsTo + { + return $this->belongsTo(Domain::class); + } } diff --git a/app/Models/Person.php b/app/Models/Person.php index c60c251..1b80f71 100644 --- a/app/Models/Person.php +++ b/app/Models/Person.php @@ -5,12 +5,45 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\Person\PersonCreated; +use App\Events\Models\Person\PersonCreating; +use App\Events\Models\Person\PersonDeleted; +use App\Events\Models\Person\PersonDeleting; +use App\Events\Models\Person\PersonUpdated; +use App\Events\Models\Person\PersonUpdating; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class Person extends Model implements ModelQuery +class Person extends Model implements Crud, ModelQuery { use HasFactory; public $guarded = []; + + public $casts = [ + 'birthdate' => 'date', + 'phone_numbers' => 'array', + 'addresses' => 'array', + 'emails' => 'array', + 'names' => 'array', + 'identifiers' => 'array', + 'locality' => 'array', + 'jobs' => 'array', + 'education' => 'array', + ]; + + public $dispatchesEvents = [ + 'created' => PersonCreated::class, + 'creating' => PersonCreating::class, + 'deleting' => PersonDeleting::class, + 'deleted' => PersonDeleted::class, + 'updating' => PersonUpdating::class, + 'updated' => PersonUpdated::class, + ]; + + public function scopeQ(Builder $query, string $string): void + { + $query->where('name', 'like', '%'.$string.'%'); + } } diff --git a/app/Models/Project.php b/app/Models/Project.php index 4c5639b..48737e6 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -5,19 +5,42 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\Project\ProjectCreated; +use App\Events\Models\Project\ProjectCreating; +use App\Events\Models\Project\ProjectDeleted; +use App\Events\Models\Project\ProjectDeleting; +use App\Events\Models\Project\ProjectUpdated; +use App\Events\Models\Project\ProjectUpdating; +use App\Services\SshKeyGeneratorService; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphToMany; +use Illuminate\Support\Str; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; +use Spatie\Tags\HasTags; -class Project extends Model implements ModelQuery +class Project extends Model implements Crud, ModelQuery { use HasFactory; + use HasTags; + use LogsActivity; public $guarded = []; protected $casts = ['settings' => 'json']; + public $dispatchesEvents = [ + 'created' => ProjectCreated::class, + 'creating' => ProjectCreating::class, + 'deleting' => ProjectDeleting::class, + 'deleted' => ProjectDeleted::class, + 'updating' => ProjectUpdating::class, + 'updated' => ProjectUpdated::class, + ]; + public function scopeQ(Builder $query, string $string): void { $query->where('name', 'like', '%'.$string.'%'); @@ -26,8 +49,7 @@ public function scopeQ(Builder $query, string $string): void public function domains(): MorphToMany { return $this->morphedByMany( - Domain::class, - 'resource', + Domain::class, 'resource', 'project_resources' ); } @@ -58,4 +80,63 @@ public function pages(): MorphToMany 'project_resources' ); } + + public function credentials(): MorphToMany + { + return $this->morphedByMany( + Credential::class, + 'resource', + 'project_resources' + ); + } + + public function team(): BelongsTo + { + return $this->belongsTo(Team::class); + } + + public function credentialFor(string $service): ?Credential + { + $credential = $this->credentials()->where('service', $service)->first(); + + if (! $credential) { + if ($service === Credential::TYPE_SSH) { + $randomName = Str::random(16); + + $generatorService = new SshKeyGeneratorService( + privateKeyFile: $privateKeyFile = storage_path('app/keys/'.$randomName.'.key'), + publicKeyFile: $publicKeyFile = storage_path('app/keys/'.$randomName.'.pub'), + passKey: $passKey = ''//''tr::random(16), + ); + + $credential = $this->credentials()->create([ + 'service' => Credential::TYPE_SSH, + 'type' => Credential::TYPE_SSH, + 'name' => 'Forge', + 'user_id' => auth()->id(), + 'settings' => [ + 'pub_key' => $generatorService->getPublicKey(), + 'pub_key_file' => $publicKeyFile, + 'private_key' => $generatorService->getPrivateKey(), + 'private_key_file' => $privateKeyFile, + 'pass_key' => encrypt($passKey), + ], + ]); + + return $credential; + } + + throw new \Exception('No credential found for '.$service); + } + + return $credential; + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'team_id']) + ->useLogName('project') + ->logOnlyDirty(); + } } diff --git a/app/Models/Research.php b/app/Models/Research.php index 20b4728..66964b9 100644 --- a/app/Models/Research.php +++ b/app/Models/Research.php @@ -4,16 +4,56 @@ namespace App\Models; +use App\Events\Models\Research\ResearchCreated; +use App\Events\Models\Research\ResearchCreating; +use App\Events\Models\Research\ResearchDeleted; +use App\Events\Models\Research\ResearchDeleting; +use App\Events\Models\Research\ResearchUpdated; +use App\Events\Models\Research\ResearchUpdating; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphToMany; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; -class Research extends Model +class Research extends Model implements Crud { use HasFactory; + use LogsActivity; public $casts = [ 'sources' => 'json', ]; - public $guarded = []; + public $fillable = [ + 'topic', + 'notes', + 'sources', + ]; + + public $dispatchesEvents = [ + 'created' => ResearchCreated::class, + 'creating' => ResearchCreating::class, + 'deleting' => ResearchDeleting::class, + 'deleted' => ResearchDeleted::class, + 'updating' => ResearchUpdating::class, + 'updated' => ResearchUpdated::class, + ]; + + public function projects(): MorphToMany + { + return $this->morphToMany( + Project::class, + 'resource', + 'project_resources' + ); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['topic', 'notes', 'sources']) + ->useLogName('research') + ->logOnlyDirty(); + } } diff --git a/app/Models/Server.php b/app/Models/Server.php index ef313d1..4eca21e 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -5,13 +5,72 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\Server\ServerCreated; +use App\Events\Models\Server\ServerCreating; +use App\Events\Models\Server\ServerDeleted; +use App\Events\Models\Server\ServerDeleting; +use App\Events\Models\Server\ServerUpdated; +use App\Events\Models\Server\ServerUpdating; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphToMany; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; use Spatie\Tags\HasTags; -class Server extends Model implements ModelQuery +class Server extends Model implements Crud, ModelQuery { - use HasFactory, HasTags; + use HasFactory; + use HasTags; + use LogsActivity; - public $guarded = []; + public $fillable = [ + 'server_id', + 'name', + 'vcpu', + 'memory', + 'disk', + 'cost_per_hour', + 'ip_address', + 'ip_address_v6', + 'internal_ip_address', + 'internal_ip_address_v6', + 'internal_url', + 'last_ping_at', + 'booted_at', + 'turned_off_at', + 'os', + ]; + + public $dispatchesEvents = [ + 'created' => ServerCreated::class, + 'creating' => ServerCreating::class, + 'deleting' => ServerDeleting::class, + 'deleted' => ServerDeleted::class, + 'updating' => ServerUpdating::class, + 'updated' => ServerUpdated::class, + ]; + + public function credential(): BelongsTo + { + return $this->belongsTo(Credential::class); + } + + public function projects(): MorphToMany + { + return $this->morphToMany( + Project::class, + 'resource', + 'project_resources' + ); + } + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'vcpu', 'memory', 'disk', 'cost_per_hour', 'internal_url', 'last_ping_at', 'booted_at', 'turned_off_at', 'os']) + ->useLogName('server') + ->logOnlyDirty(); + } } diff --git a/app/Models/ShortCode.php b/app/Models/ShortCode.php new file mode 100644 index 0000000..970b6ec --- /dev/null +++ b/app/Models/ShortCode.php @@ -0,0 +1,48 @@ + 'bool', + ]; + + public $dispatchesEvents = [ + 'created' => ShortCodeCreated::class, + 'creating' => ShortCodeCreating::class, + 'deleting' => ShortCodeDeleting::class, + 'deleted' => ShortCodeDeleted::class, + 'updating' => ShortCodeUpdating::class, + 'updated' => ShortCodeUpdated::class, + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + public function generate(string $link) + { + } +} diff --git a/app/Models/Spork/Script.php b/app/Models/Spork/Script.php new file mode 100644 index 0000000..1657f13 --- /dev/null +++ b/app/Models/Spork/Script.php @@ -0,0 +1,29 @@ + ScriptCreated::class, + 'creating' => ScriptCreating::class, + 'deleting' => ScriptDeleting::class, + 'deleted' => ScriptDeleted::class, + 'updating' => ScriptUpdating::class, + 'updated' => ScriptUpdated::class, + ]; +} diff --git a/app/Models/Tag.php b/app/Models/Tag.php index a751646..4cbff84 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -4,13 +4,78 @@ namespace App\Models; +use App\Contracts\Conditionable; use App\Contracts\ModelQuery; +use App\Events\Models\Tag\TagCreated; +use App\Events\Models\Tag\TagCreating; +use App\Events\Models\Tag\TagDeleted; +use App\Events\Models\Tag\TagDeleting; +use App\Events\Models\Tag\TagUpdated; +use App\Events\Models\Tag\TagUpdating; +use App\Models\Finance\Account; +use App\Models\Finance\Budget; +use App\Models\Finance\Transaction; +use App\Models\Traits\HasConditions; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; -class Tag extends Model implements ModelQuery +class Tag extends \Spatie\Tags\Tag implements Conditionable, Crud, ModelQuery { - use HasFactory; + // Tags with conditions will essentially only be applied if the conditions pass. + use HasConditions, HasFactory; public $guarded = []; + + public $dispatchesEvents = [ + 'created' => TagCreated::class, + 'creating' => TagCreating::class, + 'deleting' => TagDeleting::class, + 'deleted' => TagDeleted::class, + 'updating' => TagUpdating::class, + 'updated' => TagUpdated::class, + ]; + + public function articles() + { + return $this->morphedByMany(Article::class, 'taggable'); + } + + public function feeds() + { + return $this->morphedByMany(ExternalRssFeed::class, 'taggable'); + } + + public function servers() + { + return $this->morphedByMany(Server::class, 'taggable'); + } + + public function transactions() + { + return $this->morphedByMany(Transaction::class, 'taggable'); + } + + public function projects() + { + return $this->morphedByMany(Project::class, 'taggable'); + } + + public function budgets() + { + return $this->morphedByMany(Budget::class, 'taggable'); + } + + public function accounts() + { + return $this->morphedByMany(Account::class, 'taggable'); + } + + public function domains() + { + return $this->morphedByMany(Domain::class, 'taggable'); + } + + public function people() + { + return $this->morphedByMany(Person::class, 'taggable'); + } } diff --git a/app/Models/Thread.php b/app/Models/Thread.php new file mode 100644 index 0000000..f4af2f3 --- /dev/null +++ b/app/Models/Thread.php @@ -0,0 +1,48 @@ + 'json', 'origin_server_ts' => 'datetime']; + + public $appends = ['human_timestamp']; + + public $dispatchesEvents = [ + 'created' => ThreadCreated::class, + 'creating' => ThreadCreating::class, + 'deleting' => ThreadDeleting::class, + 'deleted' => ThreadDeleted::class, + 'updating' => ThreadUpdating::class, + 'updated' => ThreadUpdated::class, + ]; + + public function messages() + { + return $this->hasMany(Message::class); + } + + public function participants() + { + return $this->belongsToMany(Person::class, 'thread_participants'); + } + + public function getHumanTimestampAttribute() + { + return $this->origin_server_ts->diffForHumans(now(), CarbonInterface::DIFF_RELATIVE_TO_NOW, false); + } +} diff --git a/app/Models/Traits/HasArticles.php b/app/Models/Traits/HasArticles.php new file mode 100644 index 0000000..7f1b5eb --- /dev/null +++ b/app/Models/Traits/HasArticles.php @@ -0,0 +1,19 @@ +morphMany(Article::class, 'author')->orderByDesc('created_at'); + } +} diff --git a/app/Models/Traits/HasConditions.php b/app/Models/Traits/HasConditions.php new file mode 100644 index 0000000..f35707b --- /dev/null +++ b/app/Models/Traits/HasConditions.php @@ -0,0 +1,19 @@ +morphMany(Condition::class, 'conditionable')->orderByDesc('created_at'); + } +} diff --git a/app/Models/Traits/HasProjectResource.php b/app/Models/Traits/HasProjectResource.php new file mode 100644 index 0000000..92f23a3 --- /dev/null +++ b/app/Models/Traits/HasProjectResource.php @@ -0,0 +1,20 @@ +morphToMany( + Project::class, + 'resource', + 'project_resources' + ); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 970c243..0f076d2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,6 +5,13 @@ namespace App\Models; use App\Contracts\ModelQuery; +use App\Events\Models\User\UserCreated; +use App\Events\Models\User\UserCreating; +use App\Events\Models\User\UserDeleted; +use App\Events\Models\User\UserDeleting; +use App\Events\Models\User\UserUpdated; +use App\Events\Models\User\UserUpdating; +use App\Models\Finance\Account; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -12,15 +19,22 @@ use Laravel\Jetstream\HasProfilePhoto; use Laravel\Jetstream\HasTeams; use Laravel\Sanctum\HasApiTokens; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; +use Spatie\Permission\Traits\HasRoles; +use Spatie\Tags\HasTags; class User extends Authenticatable implements ModelQuery { use HasApiTokens; use HasFactory; use HasProfilePhoto; + use HasRoles; use HasTeams; + use LogsActivity; use Notifiable; use TwoFactorAuthenticatable; + use HasTags; /** * The attributes that are mass assignable. @@ -60,4 +74,36 @@ class User extends Authenticatable implements ModelQuery protected $appends = [ 'profile_photo_url', ]; + + public $dispatchesEvents = [ + 'created' => UserCreated::class, + 'creating' => UserCreating::class, + 'deleting' => UserDeleting::class, + 'deleted' => UserDeleted::class, + 'updating' => UserUpdating::class, + 'updated' => UserUpdated::class, + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['name', 'email']) + ->useLogName('user') + ->logOnlyDirty(); + } + + public function codes() + { + return $this->hasMany(ShortCode::class); + } + + public function credentials() + { + return $this->hasMany(Credential::class); + } + + public function accounts() + { + return $this->hasManyThrough(Account::class, Credential::class); + } } diff --git a/app/Operations/Operation.php b/app/Operations/Operation.php new file mode 100644 index 0000000..d5bebb2 --- /dev/null +++ b/app/Operations/Operation.php @@ -0,0 +1,98 @@ + $shouldRunAt], $attributes)); + } + + /** + * @param array $attributes + * @return static + */ + public static function dispatch($attributes = []) + { + return static::schedule(Carbon::now(), $attributes); + } + + /** + * @param array $attributes + * @return static + */ + public static function dispatchNow($attributes = []) + { + $operation = static::dispatch($attributes); + + $operation->started_run_at = Carbon::now(); + + if (method_exists($operation, 'queue')) { + $operation->queue(); + } + + (new OperationJob($operation))->handle(); + + return $operation; + } + + /** + * @return array + */ + public function tags() + { + return [ + 'operation', + static::class.':'.$this->id, + ]; + } +} diff --git a/app/Operations/Operator.php b/app/Operations/Operator.php new file mode 100644 index 0000000..3edc11e --- /dev/null +++ b/app/Operations/Operator.php @@ -0,0 +1,38 @@ +where('should_run_at', '<=', Carbon::now()) + ->get() + ->each(function (Operation $operation) { + $operation->started_run_at = Carbon::now(); + $operation->save(); + + if (method_exists($operation, 'queue')) { + $operation->queue(); + } + + $operationJob = OperationJob::dispatch($operation); + + if (property_exists($operation, 'queue')) { + $operationJob->onQueue($operation->queue); + } + + if (property_exists($operation, 'queueConnection')) { + $operationJob->onConnection($operation->queueConnection); + } + }); + } + } +} diff --git a/app/Operations/ServerAction.php b/app/Operations/ServerAction.php new file mode 100644 index 0000000..6da88e5 --- /dev/null +++ b/app/Operations/ServerAction.php @@ -0,0 +1,57 @@ +server->internal_ip_address, + username: $this->credential->settings['username'] ?? 'forge', + publicKeyFile: $this->credential->getPublicKey(), + privateKeyFile: $this->credential->getPrivateKey(), + passKey: $this->credential->getPasskey() + ); // replace with your server details + + [ + 'stdout' => $out, + 'stderr' => $err, + ] = $service->run($this->script->script); + + $this->error = $err; + $this->output = $out; + // modifying the Action will update the model by reference, and then when we execute, it should save. + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function credential(): BelongsTo + { + return $this->belongsTo(Credential::class); + } + + public function script(): BelongsTo + { + return $this->belongsTo(Script::class); + } + + public function server(): BelongsTo + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Policies/CredentialPolicy.php b/app/Policies/CredentialPolicy.php index 7f4966b..ab03e66 100644 --- a/app/Policies/CredentialPolicy.php +++ b/app/Policies/CredentialPolicy.php @@ -6,15 +6,18 @@ use App\Models\Credential; use App\Models\User; +use Illuminate\Auth\Access\HandlesAuthorization; class CredentialPolicy { + use HandlesAuthorization; + /** * Determine whether the user can view any models. */ public function viewAny(User $user): bool { - // + return $user->can('view_any_credential'); } /** @@ -22,7 +25,7 @@ public function viewAny(User $user): bool */ public function view(User $user, Credential $credential): bool { - // + return $user->can('view_credential'); } /** @@ -30,7 +33,7 @@ public function view(User $user, Credential $credential): bool */ public function create(User $user): bool { - // + return $user->can('create_credential'); } /** @@ -38,7 +41,7 @@ public function create(User $user): bool */ public function update(User $user, Credential $credential): bool { - // + return $user->can('update_credential'); } /** @@ -46,22 +49,62 @@ public function update(User $user, Credential $credential): bool */ public function delete(User $user, Credential $credential): bool { - // + return $user->can('delete_credential'); } /** - * Determine whether the user can restore the model. + * Determine whether the user can bulk delete. */ - public function restore(User $user, Credential $credential): bool + public function deleteAny(User $user): bool { - // + return $user->can('delete_any_credential'); } /** - * Determine whether the user can permanently delete the model. + * Determine whether the user can permanently delete. */ public function forceDelete(User $user, Credential $credential): bool { - // + return $user->can('force_delete_credential'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_credential'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Credential $credential): bool + { + return $user->can('restore_credential'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_credential'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Credential $credential): bool + { + return $user->can('replicate_credential'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_credential'); } } diff --git a/app/Policies/NavigationPolicy.php b/app/Policies/NavigationPolicy.php new file mode 100644 index 0000000..b8a4510 --- /dev/null +++ b/app/Policies/NavigationPolicy.php @@ -0,0 +1,110 @@ +can('view_any_navigation'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Navigation $navigation): bool + { + return $user->can('view_navigation'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create_navigation'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Navigation $navigation): bool + { + return $user->can('update_navigation'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Navigation $navigation): bool + { + return $user->can('delete_navigation'); + } + + /** + * Determine whether the user can bulk delete. + */ + public function deleteAny(User $user): bool + { + return $user->can('delete_any_navigation'); + } + + /** + * Determine whether the user can permanently delete. + */ + public function forceDelete(User $user, Navigation $navigation): bool + { + return $user->can('force_delete_navigation'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_navigation'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Navigation $navigation): bool + { + return $user->can('restore_navigation'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_navigation'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Navigation $navigation): bool + { + return $user->can('replicate_navigation'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_navigation'); + } +} diff --git a/app/Policies/PagePolicy.php b/app/Policies/PagePolicy.php index 2bb5944..b4db3e4 100644 --- a/app/Policies/PagePolicy.php +++ b/app/Policies/PagePolicy.php @@ -6,15 +6,18 @@ use App\Models\Page; use App\Models\User; +use Illuminate\Auth\Access\HandlesAuthorization; class PagePolicy { + use HandlesAuthorization; + /** * Determine whether the user can view any models. */ public function viewAny(User $user): bool { - // + return $user->can('view_any_page'); } /** @@ -22,7 +25,7 @@ public function viewAny(User $user): bool */ public function view(User $user, Page $page): bool { - // + return $user->can('view_page'); } /** @@ -30,7 +33,7 @@ public function view(User $user, Page $page): bool */ public function create(User $user): bool { - // + return $user->can('create_page'); } /** @@ -38,7 +41,7 @@ public function create(User $user): bool */ public function update(User $user, Page $page): bool { - // + return $user->can('update_page'); } /** @@ -46,22 +49,62 @@ public function update(User $user, Page $page): bool */ public function delete(User $user, Page $page): bool { - // + return $user->can('delete_page'); } /** - * Determine whether the user can restore the model. + * Determine whether the user can bulk delete. */ - public function restore(User $user, Page $page): bool + public function deleteAny(User $user): bool { - // + return $user->can('delete_any_page'); } /** - * Determine whether the user can permanently delete the model. + * Determine whether the user can permanently delete. */ public function forceDelete(User $user, Page $page): bool { - // + return $user->can('force_delete_page'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_page'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Page $page): bool + { + return $user->can('restore_page'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_page'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Page $page): bool + { + return $user->can('replicate_page'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_page'); } } diff --git a/app/Policies/PersonPolicy.php b/app/Policies/PersonPolicy.php index d4b3239..38bf72e 100644 --- a/app/Policies/PersonPolicy.php +++ b/app/Policies/PersonPolicy.php @@ -6,15 +6,18 @@ use App\Models\Person; use App\Models\User; +use Illuminate\Auth\Access\HandlesAuthorization; class PersonPolicy { + use HandlesAuthorization; + /** * Determine whether the user can view any models. */ public function viewAny(User $user): bool { - // + return $user->can('view_any_person'); } /** @@ -22,7 +25,7 @@ public function viewAny(User $user): bool */ public function view(User $user, Person $person): bool { - // + return $user->can('view_person'); } /** @@ -30,7 +33,7 @@ public function view(User $user, Person $person): bool */ public function create(User $user): bool { - // + return $user->can('create_person'); } /** @@ -38,7 +41,7 @@ public function create(User $user): bool */ public function update(User $user, Person $person): bool { - // + return $user->can('update_person'); } /** @@ -46,22 +49,62 @@ public function update(User $user, Person $person): bool */ public function delete(User $user, Person $person): bool { - // + return $user->can('delete_person'); } /** - * Determine whether the user can restore the model. + * Determine whether the user can bulk delete. */ - public function restore(User $user, Person $person): bool + public function deleteAny(User $user): bool { - // + return $user->can('delete_any_person'); } /** - * Determine whether the user can permanently delete the model. + * Determine whether the user can permanently delete. */ public function forceDelete(User $user, Person $person): bool { - // + return $user->can('force_delete_person'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_person'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Person $person): bool + { + return $user->can('restore_person'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_person'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Person $person): bool + { + return $user->can('replicate_person'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_person'); } } diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php index 1a0c788..018cb4e 100644 --- a/app/Policies/ProjectPolicy.php +++ b/app/Policies/ProjectPolicy.php @@ -6,15 +6,18 @@ use App\Models\Project; use App\Models\User; +use Illuminate\Auth\Access\HandlesAuthorization; class ProjectPolicy { + use HandlesAuthorization; + /** * Determine whether the user can view any models. */ public function viewAny(User $user): bool { - // + return $user->can('view_any_project'); } /** @@ -22,7 +25,7 @@ public function viewAny(User $user): bool */ public function view(User $user, Project $project): bool { - // + return $user->can('view_project', $project); } /** @@ -30,7 +33,7 @@ public function view(User $user, Project $project): bool */ public function create(User $user): bool { - // + return $user->can('create_project'); } /** @@ -38,7 +41,7 @@ public function create(User $user): bool */ public function update(User $user, Project $project): bool { - // + return $user->can('update_project', $project); } /** @@ -46,22 +49,62 @@ public function update(User $user, Project $project): bool */ public function delete(User $user, Project $project): bool { - // + return $user->can('delete_project', $project); } /** - * Determine whether the user can restore the model. + * Determine whether the user can bulk delete. */ - public function restore(User $user, Project $project): bool + public function deleteAny(User $user): bool { - // + return $user->can('delete_any_project'); } /** - * Determine whether the user can permanently delete the model. + * Determine whether the user can permanently delete. */ public function forceDelete(User $user, Project $project): bool { - // + return $user->can('force_delete_project', $project); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_project'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Project $project): bool + { + return $user->can('restore_project', $project); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_project'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Project $project): bool + { + return $user->can('replicate_project', $project); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_project'); } } diff --git a/app/Policies/RolePolicy.php b/app/Policies/RolePolicy.php new file mode 100644 index 0000000..3c68e66 --- /dev/null +++ b/app/Policies/RolePolicy.php @@ -0,0 +1,110 @@ +can('view_any_shield::role'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Role $role): bool + { + return $user->can('view_shield::role'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create_shield::role'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Role $role): bool + { + return $user->can('update_shield::role'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Role $role): bool + { + return $user->can('delete_shield::role'); + } + + /** + * Determine whether the user can bulk delete. + */ + public function deleteAny(User $user): bool + { + return $user->can('delete_any_shield::role'); + } + + /** + * Determine whether the user can permanently delete. + */ + public function forceDelete(User $user, Role $role): bool + { + return $user->can('{{ ForceDelete }}'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('{{ ForceDeleteAny }}'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user, Role $role): bool + { + return $user->can('{{ Restore }}'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('{{ RestoreAny }}'); + } + + /** + * Determine whether the user can replicate. + */ + public function replicate(User $user, Role $role): bool + { + return $user->can('{{ Replicate }}'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('{{ Reorder }}'); + } +} diff --git a/app/Policies/ScriptPolicy.php b/app/Policies/ScriptPolicy.php new file mode 100644 index 0000000..1e5b12e --- /dev/null +++ b/app/Policies/ScriptPolicy.php @@ -0,0 +1,67 @@ +can('view_any_user'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user): bool + { + return $user->can('view_user'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->can('create_user'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user): bool + { + return $user->can('update_user'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user): bool + { + return $user->can('delete_user'); + } + + /** + * Determine whether the user can bulk delete. + */ + public function deleteAny(User $user): bool + { + return $user->can('delete_any_user'); + } + + /** + * Determine whether the user can permanently delete. + */ + public function forceDelete(User $user): bool + { + return $user->can('force_delete_user'); + } + + /** + * Determine whether the user can permanently bulk delete. + */ + public function forceDeleteAny(User $user): bool + { + return $user->can('force_delete_any_user'); + } + + /** + * Determine whether the user can restore. + */ + public function restore(User $user): bool + { + return $user->can('restore_user'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function restoreAny(User $user): bool + { + return $user->can('restore_any_user'); + } + + /** + * Determine whether the user can bulk restore. + */ + public function replicate(User $user): bool + { + return $user->can('replicate_user'); + } + + /** + * Determine whether the user can reorder. + */ + public function reorder(User $user): bool + { + return $user->can('reorder_user'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 8325058..6d66cc1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,9 @@ namespace App\Providers; +use App\Contracts\Services\PlaidServiceContract; +use App\Operations\Operator; +use App\Services\Finance\PlaidService; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -13,7 +16,8 @@ class AppServiceProvider extends ServiceProvider */ public function register(): void { - // + $this->app->bind(PlaidServiceContract::class, PlaidService::class); + $this->app->alias(Operator::class, 'operator'); } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 9b7e475..78dc9d4 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -5,7 +5,13 @@ namespace App\Providers; use App\Events; +use App\Events\Domains\DnsRecordVerified; +use App\Events\Domains\DomainCreated; +use App\Events\Domains\NameServerRecordVerified; +use App\Events\Pages\PageCreated; +use App\Events\Pages\PageUpdated; use App\Listeners; +use App\Listeners\DebugEventListener; use App\Listeners\Pages\GenerateNewRoutesFileFromDatabase; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; @@ -19,24 +25,28 @@ class EventServiceProvider extends ServiceProvider * @var array> */ protected $listen = [ + NameServerRecordVerified::class => [ + DebugEventListener::class, // code: this is an autogenerated line + ], + DnsRecordVerified::class => [ + ], Registered::class => [ SendEmailVerificationNotification::class, ], - Events\Pages\PageCreated::class => [ + PageCreated::class => [ + DebugEventListener::class, // code: this is an autogenerated line GenerateNewRoutesFileFromDatabase::class, ], - Events\Pages\PageUpdated::class => [ + PageUpdated::class => [ + DebugEventListener::class, // code: this is an autogenerated line GenerateNewRoutesFileFromDatabase::class, ], - Events\DataRefreshRequested::class => [], - Events\Domains\DomainCreated::class => [ + DomainCreated::class => [ + DebugEventListener::class, // code: this is an autogenerated line Listeners\Domains\UpdateDomainToUseCloudflareDns::class, ], - Events\Domains\DnsRecordVerified::class => [ - - ], - Events\Domains\NameServerRecordVerified::class => [ - + Events\Models\User\UserCreated::class => [ + Listeners\Finance\CreateDefaultAutomatedTags::class, ], ]; diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php new file mode 100644 index 0000000..95e7ee7 --- /dev/null +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -0,0 +1,64 @@ +default() + ->id('admin') + ->path('admin') + ->login() + ->colors([ + 'primary' => Color::Amber, + ]) + ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources') + ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages') + ->pages([ + Pages\Dashboard::class, + ]) + ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') + ->widgets([ + Widgets\AccountWidget::class, + Widgets\FilamentInfoWidget::class, + ]) + ->middleware([ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + AuthenticateSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + DisableBladeIconComponents::class, + DispatchServingFilamentEvent::class, + ]) + ->plugins([ + \BezhanSalleh\FilamentShield\FilamentShieldPlugin::make(), + + ]) + ->authMiddleware([ + Authenticate::class, + ]); + } +} diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php new file mode 100644 index 0000000..f7b8827 --- /dev/null +++ b/app/Providers/HorizonServiceProvider.php @@ -0,0 +1,38 @@ +email, [ + 'spork@kregel.email', + ]); + }); + } +} diff --git a/app/Providers/OperationServiceProvider.php b/app/Providers/OperationServiceProvider.php new file mode 100644 index 0000000..b64ac5a --- /dev/null +++ b/app/Providers/OperationServiceProvider.php @@ -0,0 +1,45 @@ +publishes([ + __DIR__.'/../config/operations.php' => config_path('operations.php'), + ]); + + if ($this->app->runningInConsole()) { + $this->commands([ + QueueCommand::class, + MakeOperationCommand::class, + MakeOperationMigrationCommand::class, + ]); + } + } + + public function register(): void + { + if ($this->app->environment() === 'testing') { + $this->loadMigrationsFrom(base_path('/tests/database')); + } + + $this->app->alias(Operator::class, 'operator'); + + $this->app->when(MigrationCreator::class) + ->needs('$customStubPath') + ->give(function ($app) { + return $app->basePath('stubs'); + }); + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index ea8f67a..e08f7ae 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -4,12 +4,16 @@ namespace App\Providers; +use App\Services\Code; use Illuminate\Cache\RateLimiting\Limit; +use Illuminate\Database\Eloquent\Model; use Illuminate\Filesystem\Filesystem; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Str; use Symfony\Component\Finder\SplFileInfo; class RouteServiceProvider extends ServiceProvider @@ -28,12 +32,18 @@ class RouteServiceProvider extends ServiceProvider */ public function boot(): void { + Route::macro('domains', function (array $domains, $callback) { + foreach ($domains as $domain) { + Route::domain($domain)->group($callback)->name($domain); + } + }); + RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); Route::bind('abstract_model', function ($tableName) { - return collect(app(Filesystem::class)->allFiles(app_path('Models')))->map(function (SplFileInfo $file) { - $modelClass = ucfirst(str_replace('/', '\\', str_replace('.php', '', substr(str_replace(base_path(), '', $file), 1)))); + return collect(app(Filesystem::class)->allFiles(app_path('Models')))->filter(fn (SplFileInfo $file) => ! str_contains(strtolower($file->getRealPath()), 'trait'))->map(function (SplFileInfo $file) { + $modelClass = ucfirst(str_replace('/', '\\', str_replace('.php', '', substr(str_replace(base_path(), '', $file->getRealPath()), 1)))); return new $modelClass; })->filter(function ($model) use ($tableName) { @@ -48,23 +58,48 @@ public function boot(): void return $model::find($value); }); - $this->routes(function () { - Route::middleware('api') - ->prefix('api') - ->group(base_path('routes/api.php')); + Route::bind('link', function ($value) { + $model = Arr::first(array_values(array_filter( + Code::instancesOf(Model::class) + ->getClasses(), + function ($class) use ($value) { + try { + return (new $class)->getTable() === Str::slug($value, '_'); + } catch (\Throwable $e) { + return false; + } + }))); + + abort_unless(isset($model), 404); + + return $model; + }); + + $this->routes(function () { if (file_exists(base_path('routes/generate-pages.php'))) { include_once base_path('routes/generate-pages.php'); } + Route::prefix('api') + ->domain(config('app.env') == 'production' ? 'spork.zone' : 'spork.localhost') + ->middleware(config('jetstream.middleware', ['web'])) + ->group(base_path('routes/crud.php')); + Route::middleware('web') - ->group(base_path('routes/web.php')); + ->domain(config('app.env') == 'production' ? 'petoskey.today' : 'petoskey.localhost') + ->group(base_path('routes/pages/petoskey.php')); - if (config('app.env') === 'local') { - Route::prefix('api') - ->middleware(config('jetstream.middleware', ['web'])) - ->group(base_path('routes/crud.php')); - } + Route::middleware('web') + ->domain(config('app.env') == 'production' ? 'spork.zone' : 'spork.localhost') + ->group(base_path('routes/pages/spork.php')); + + Route::middleware('api') + ->domain(config('app.env') == 'production' ? 'deploy.kregel.host' : 'deploy.localhost') + ->group(base_path('routes/pages/deploy.php')); + + Route::domain(env('LINK_SHORTENING_DOMAIN')) + ->group(base_path('routes/pages/link-shortening.php')); }); } } diff --git a/app/Services/Code.php b/app/Services/Code.php new file mode 100644 index 0000000..93ed95f --- /dev/null +++ b/app/Services/Code.php @@ -0,0 +1,612 @@ +phpFiles[$file] = PhpFile::fromCode(file_get_contents($file)); + } + } + + public static function with(string $driver): static|LaravelProgrammingStyle + { + return match ($driver) { + 'laravel' => new CodeForLaravel([]), + LaravelProgrammingStyle::class => new LaravelProgrammingStyle([]), + }; + } + + public static function composerMappedClasses(): array + { + /** @var ClassLoader $loader */ + $loader = require base_path('vendor').'/autoload.php'; + + return $loader->getClassMap(); + } + + public static function for(string $desiredParentClass): static + { + $classmap = require base_path('vendor/composer/autoload_classmap.php'); + + $classes = array_key_exists($desiredParentClass, $classmap) ? $classmap[$desiredParentClass] : $desiredParentClass; + + return new static( + $desiredParentClass, + $classes + ); + } + + public function getPrimaryClassType(): ?ClassType + { + /** @var PhpFile $first */ + $first = Arr::first($this->phpFiles); + + /** @var ClassType $classInstance */ + return Arr::first($first->getClasses()); + } + + protected function isArray(Property|Constant $property) + { + $contents = explode("\n", (string) $property->getValue()); + + return $contents[0] === '[' && $contents[count($contents) - 1] === ']'; + } + + public function addValueToProperty(string $property, string $name, mixed $value) + { + $this->modifyProperty($property, function (Property $property) use ($name, $value) { + if (is_callable($value)) { + $property->setValue(call_user_func($value, $property)); + + return; + } + $contents = explode("\n", (string) $property->getValue()); + $content = array_values(array_filter($contents)); + + if ($contents[0] === '[' && $contents[count($contents) - 1] === ']') { + // technically an unsafe eval, but you're using code to write code.. + // Soo uhhh... This may be dubious, but also it could just work well? + $literal = eval('return '.$property->getValue().';'); + // This really isn't ideal. but we are ensuring the contents are likely an array if (is_array($literal)) { + // Since it's an array, we should look for the $name, and then insert that right below it + // Or if it evaluates to an KV array, we can set the value instead. + if (is_array($literal[$name])) { + // This would be an array of arrays, see `EventServiceProvider` + $content = array_values(array_filter($contents)); + + foreach ($content as $lineNumber => $line) { + if (str_contains($line, $name)) { + $lineWithEvent = $lineNumber; + } + } + + if ($lineWithEvent === null) { + $content = array_merge(array_slice($content, 0, 1), [ + // new line with our listener, + " /*(n*/\\$name::class => [", + " /*(n*/\\$value::class.'@handle', // code: this is an autogenerated line", + ' ],', + ], array_slice($content, 1, count($content))); + } else { + $content = array_merge(array_slice($content, 0, $lineWithEvent + 1), [ + // new line with our listener, + " /*(n*/\\$value::class . '@handle', // code: this is an autogenerated line", + ], array_slice($content, $lineWithEvent + 1, count($content))); + } + } else { + // This could be an array of strings, objects, or basically anything that isn't an array. + $content = array_values(array_filter($contents)); + foreach ($content as $lineNumber => $line) { + if (str_contains($line, $name)) { + $lineWithEvent = $lineNumber; + } + } + + // if ($lineWithEvent === null) { + // $content = array_merge(array_slice($content, 0, 1), [ + // // new line with our listener, + // " /*(n*/\\$name::class => [", + // " /*(n*/\\$value::class.'@handle', // code: this is an autogenerated line", + // " ]," + // ], array_slice($content, 1, count($content))); + // } else { + // $content = array_merge(array_slice($content, 0, $lineWithEvent+1), [ + // // new line with our listener, + // " /*(n*/\\$value::class . '@handle', // code: this is an autogenerated line" + // ], array_slice($content, $lineWithEvent+1, count($content))); + // } + // I + } + } + + $property->setValue((new Literal(implode("\n", $content)))); + }); + + return $this; + } + + public static function instancesOf(string $desiredParentClass): static + { + return cache()->rememberForever('instanceOfCache.'.$desiredParentClass, function () use ($desiredParentClass) { + // Classes known to composer in array form + $traits = []; + $classes = []; + $interfaces = []; + + foreach (static::composerMappedClasses() as $className => $filePath) { + $filePath = realpath($filePath); + + if ($filePath === false) { + continue; + } + + if (stripos($className, 'reflection') !== false) { + // The class has reflection in the name, generally speaking, I'd like to avoid those... + continue; + } + if (stripos($className, 'abstract') !== false) { + // The class has reflection in the name, generally speaking, I'd like to avoid those... + continue; + } + + $vendorParts = explode('/vendor/', $filePath); + + if (str_contains($filePath, 'vendor/')) { + $possibleVendor = explode('/', $vendorParts[1], 2)[0]; + + if (! empty(config('spork.code.settings.blacklist')) && in_array($possibleVendor, config('spork.code.settings.blacklist'))) { + continue; + } + + if (! empty(config('spork.code.settings.whitelist')) && ! in_array($possibleVendor, config('spork.code.settings.whitelist'))) { + continue; + } + } + + try { + if (interface_exists($className)) { + $interfaces[] = $className; + } elseif (class_exists($className)) { + $classes[] = $className; + } elseif (trait_exists($className)) { + $traits[] = $className; + } + } catch (\Throwable|\Error|\ErrorException|ErrorException|\ReflectionException|\Whoops\Exception\ErrorException|\Symfony\Component\ErrorHandler\Error\FatalError|FatalError $e) { + // Missing classes based on my experience so far. + } + } + + $possibleInstances = match (true) { + interface_exists($desiredParentClass) => array_values(array_filter(array_merge($interfaces, $classes), fn ($declaredClass) => isset(class_implements($declaredClass)[$desiredParentClass]))), + class_exists($desiredParentClass) => array_values(array_filter($classes, fn ($declaredClass) => is_subclass_of($declaredClass, $desiredParentClass))), + trait_exists($desiredParentClass) => array_values(array_filter(array_merge($traits, $classes), fn ($declaredClass) => in_array($desiredParentClass, trait_uses_recursive($declaredClass)))), + }; + + return new static($possibleInstances); + }); + } + + public function import(string|array $fqns): static + { + $imports = is_array($fqns) ? $fqns : func_get_args(); + foreach ($this->phpFiles as $phpFile) { + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + foreach ($imports as $import) { + $possibleImport = class_basename($import); + $uses = $namespace->getUses(); + // contains use ClassName; + // it could be aliased + if (in_array($import, $uses)) { + // already imported; + continue; + } + + if (isset($uses[$possibleImport])) { + // Not imported, but the import already exists, so we need to alias it. + // idk how I want to handle this yet, so I'ma just leave this for future me... + + continue; + } + + $traitBaseName = class_basename($import); + + if ($namespace->getName().'\\'.$traitBaseName === $import) { + // Our class is in the same namespace as the class we're importing + // So we don't actually need to do anything + continue; + } + $namespace->addUse($import); + } + } + } + + return $this; + } + + public function use(string|array $fqns): static + { + $imports = is_array($fqns) ? $fqns : func_get_args(); + + foreach ($imports as $import) { + $this->import($import); + } + foreach ($this->phpFiles as $phpFile) { + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + /** @var ClassType $class */ + foreach ($namespace->getClasses() as $class) { + $existingTraits = $class->getTraits(); + foreach ($imports as $import) { + if (isset($existingTraits[$import])) { + // The trait is already used by the class. + continue; + } + + $class->addTrait($import); + } + } + } + } + + return $this; + } + + protected const TYPE_PROPERTY = 'property'; + + protected const TYPE_ATTRIBUTES = 'attribute'; + + protected const TYPE_TRAIT = 'trait'; + + protected const TYPE_CONSTANT = 'constant'; + + protected const TYPE_EXTEND = 'extend'; + + protected const TYPE_IMPLEMENT = 'implement'; + + protected const TYPE_METHOD = 'method'; + + protected function modify( + string $type, + string $name, + \Closure $valueResolver + ): static { + /** @var PhpFile $file */ + foreach ($this->phpFiles as $file) { + /** @var PhpNamespace $namespaceObject */ + foreach ($file->getNamespaces() as $namespace => $namespaceObject) { + // Add code at the namespace level like use statements, declare(strict_types=1); + /** @var \Nette\PhpGenerator\ClassType $class */ + foreach ($file->getClasses() as $class) { + switch ($type) { + case static::TYPE_PROPERTY: + /** @var \Nette\PhpGenerator\Property $property */ + foreach ($class->getProperties() as $propertyName => $property) { + if ($propertyName === $name) { + call_user_func($valueResolver, $property); + } + } + break; + case static::TYPE_ATTRIBUTES: + foreach ($class->getAttributes() as $attributeName => $attribute) { + if ($attributeName === $name) { + call_user_func($valueResolver, $attribute); + } + } + break; + case static::TYPE_TRAIT: + foreach ($class->getTraits() as $traitName => $trait) { + if ($traitName === $name) { + call_user_func($valueResolver, $trait); + } + } + break; + case static::TYPE_CONSTANT: + foreach ($class->getConstants() as $constantName => $constant) { + if ($constantName === $name) { + call_user_func($valueResolver, $constant); + } + } + break; + case static::TYPE_EXTEND: + foreach ($class->getExtends() as $extendClass => $extends) { + if ($extendClass === $name) { + call_user_func($valueResolver, $extends); + } + } + break; + case static::TYPE_IMPLEMENT: + foreach ($class->getImplements() as $implementationClass => $implementation) { + if ($implementationClass === $name) { + call_user_func($valueResolver, $implementation); + } + } + break; + case static::TYPE_METHOD: + foreach ($class->getMethods() as $methodName => $method) { + if ($methodName === $name) { + call_user_func($valueResolver, $method); + } + } + break; + default: + throw new \InvalidArgumentException('Unknown argument type, please see me after class for more details'); + } + } + } + } + + return $this; + } + + public function modifyProperty(string $name, \Closure $closure) + { + $this->modify(static::TYPE_PROPERTY, $name, $closure); + + return $this; + } + + public function addProperty(string $name, mixed $value = []) + { + /** @var PhpFile $file */ + foreach ($this->phpFiles as $file) { + /** @var PhpNamespace $namespaceObject */ + foreach ($file->getNamespaces() as $namespace => $namespaceObject) { + // Add code at the namespace level like use statements, declare(strict_types=1); + /** @var \Nette\PhpGenerator\ClassType $class */ + foreach ($file->getClasses() as $class) { + $class->addProperty($name, $value); + } + } + } + + return $this; + } + + public function extends(string|array $fqns): static + { + $imports = is_array($fqns) ? $fqns : func_get_args(); + + if (count($imports) > 1) { + throw new \DomainException('Failed to extend class, classes may only be extended once in this language.'); + } + + foreach ($imports as $import) { + $this->import($import); + } + foreach ($this->phpFiles as $phpFile) { + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + /** @var ClassType $class */ + foreach ($namespace->getClasses() as $class) { + $existingExtends = $class->getExtends(); + + foreach ($imports as $import) { + if (isset($existingExtends[$import])) { + // We already extend the class. + continue; + } + + $class->setExtends($import); + } + } + } + } + + return $this; + } + + public function getInterfaces() + { + $existingInterfaces = []; + foreach ($this->phpFiles as $phpFile) { + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + /** @var ClassType $class */ + foreach ($namespace->getClasses() as $class) { + $existingInterfaces = array_merge($existingInterfaces, $class->getImplements()); + } + } + } + + return $existingInterfaces; + } + + public function getProperty(string $property): array + { + return array_map(function (PhpFile $phpFile) use ($property) { + return array_map(fn (PhpNamespace $namespace) => array_map(fn (ClassType $type) => $type->getProperty($property), $namespace->getClasses()), $phpFile->getNamespaces()); + }, $this->phpFiles); + } + + public function getClasses(): array + { + return array_reduce($this->phpFiles, function (array $allClasses, PhpFile $phpFile) { + return array_reduce( + $phpFile->getNamespaces(), + fn (array $result, PhpNamespace $namespace) => array_merge( + $result, + array_values(array_map(fn (ClassType|InterfaceType $type) => $namespace->getName().'\\'.$type->getName(), $namespace->getClasses())), + ), + $allClasses + ); + }, []); + } + + public function implements(string|array $fqns): static + { + $imports = is_array($fqns) ? $fqns : func_get_args(); + + foreach ($imports as $import) { + $this->import($import); + } + foreach ($this->phpFiles as $phpFile) { + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + /** @var ClassType $class */ + foreach ($namespace->getClasses() as $class) { + $existingInterfaces = $class->getImplements(); + + foreach ($imports as $import) { + if (isset($existingInterfaces[$import])) { + // We already implement the interface. + continue; + } + + $class->addImplement($import); + } + } + } + } + + return $this; + } + + protected function resolveFileOrClassName(string $fileOrClassName) + { + if (str_contains($fileOrClassName, '.')) { + return $fileOrClassName; + } + + return static::composerMappedClasses()[$fileOrClassName]; + } + + // Default to non-destructive action. + public function toFile($howToHandleFile = self::RETURN_CONTENTS) + { + foreach ($this->phpFiles as $className => $file) { + $contents = str_replace("\t", ' ', $this->validateFile($file)); + switch ($howToHandleFile) { + case static::REPLACE_FILE: + if (! file_exists($filePath = $this->resolveFileOrClassName($className))) { + // Our class name isn't found int composer. + // Perhaps we need to run composer:dump + continue 2; + } + file_put_contents($filePath, $contents); + break; + case static::RETURN_CONTENTS: + default: + return $contents; + } + } + + return $this; + } + + protected function validateFile(PhpFile $file) + { + $printer = new Printer; + $printer->indentation = ' '; + $printer->linesBetweenMethods = 1; + + $processedFile = (string) $printer->printFile($file); + + $lines = explode("\n", $processedFile); + $location = array_keys(array_filter($lines, fn ($line) => str_starts_with($line, 'namespace')))[0] ?? 0; + $preLocation = array_slice($lines, 0, $location + 1); + $postLocation = array_slice($lines, $location + 1, count($lines)); + + $filesystem = new Filesystem(); + $filesystem->makeDirectory(storage_path('tmp'), 0755, true, true); + // How can we validate the file, like make sure there aren't any parsing errors or obvious exceptions. + $path = storage_path('tmp/'.Str::random(16)); + + $filesystem->put($path, $contentToVerify = implode("\n", array_merge($preLocation, [ + sprintf('require "%s/vendor/autoload.php";', base_path()), + ], $postLocation))); + try { + // Generally this isn't safe, but we need to check for syntax issues but we also want to add + // a require line for the autoloader, + try { + exec('php -f '.escapeshellarg($path).' 2>&1', $output, $errorCode); + $output = implode("\n", $output); + } catch (SyntaxErrorException $e) { + return; + } + if ($errorCode !== 0) { + if (str_starts_with($output, 'PHP Fatal error:')) { + $line = explode('on line ', $output, 2)[1] ?? ''; + + throw new FatalError($output, $errorCode, [ + 'file' => $path, + 'line' => explode("\n", $line, 2)[0], + ]); + } + + throw new SyntaxErrorException('There were syntax errors found in the generated file, this shouldn\'t happen. '.$output); + } + + return $processedFile; + } finally { + $filesystem->delete($path); + } + } + + public function propertyIncludesValue($field, \Closure $closure) + { + call_user_func($closure, $field); + + return false; + } + + public function saveAs(string $fileName) + { + $file = basename($fileName); + $directory = str_replace($file, '', $fileName); + + (new Filesystem)->makeDirectory(base_path($directory), 0755, true, true); + + file_put_contents(base_path($fileName), $this->toFile()); + } + + public function constructorProperty(string $name) + { + return array_map(function (PhpFile $file) use ($name) { + return array_map(function (ClassType $class) use ($name) { + return (string) $class->getMethod('__construct')?->getParameter($name)?->getDefaultValue(); + }, $file->getClasses()); + }, $this->phpFiles); + } +} diff --git a/app/Services/Condition/AbstractLogicalOperator.php b/app/Services/Condition/AbstractLogicalOperator.php new file mode 100644 index 0000000..6fd5932 --- /dev/null +++ b/app/Services/Condition/AbstractLogicalOperator.php @@ -0,0 +1,20 @@ +compute($value1, $value2); + } + + public function butTheOpposite(mixed $firstValue, mixed $secondValue): bool + { + return $this->inverse($firstValue, $secondValue); + } +} diff --git a/app/Services/Condition/ActionFilter.php b/app/Services/Condition/ActionFilter.php new file mode 100644 index 0000000..94ea318 --- /dev/null +++ b/app/Services/Condition/ActionFilter.php @@ -0,0 +1,88 @@ +actionString = $actionString; + $this->parseAction(); + } + + public function getMethod() + { + return $this->method; + } + + public function getArguments(): array + { + return $this->arguments; + } + + public function hasArguments(): bool + { + return $this->hasArguments; + } + + public function execute(QueryBuilder $query) + { + if ($this->hasArguments()) { + return $query->{$this->getMethod()}(...$this->getArguments()); + } + + return $query->{$this->getMethod()}(); + } + + protected function parseAction() + { + $parts = explode(':', $this->actionString); + + $this->method = $this->validateAction(Arr::first($parts)); + + if (count($parts) === 1) { + $this->hasArguments = false; + + return; + } + + $this->arguments = array_filter(explode(',', end($parts)), function ($bit) { + return ! empty($bit); + }); + + $this->hasArguments = count($this->arguments) > 0; + } + + protected function validateAction(string $action): string + { + if (! in_array($action, static::WHITELISTED_ACTIONS)) { + return static::DEFAULT_ACTION; + } + + return $action; + } +} diff --git a/app/Services/Condition/ContainsValueOperator.php b/app/Services/Condition/ContainsValueOperator.php new file mode 100644 index 0000000..20a1d89 --- /dev/null +++ b/app/Services/Condition/ContainsValueOperator.php @@ -0,0 +1,21 @@ +$needle); + } + + return str_contains(strtolower((string) $haystack), strtolower($needle)); + } +} diff --git a/app/Services/Condition/ContainsValueStrictOperator.php b/app/Services/Condition/ContainsValueStrictOperator.php new file mode 100644 index 0000000..559485b --- /dev/null +++ b/app/Services/Condition/ContainsValueStrictOperator.php @@ -0,0 +1,21 @@ +$needle); + } + + return str_contains((string) $haystack, $needle); + } +} diff --git a/app/Services/Condition/DoesntContainValueOperator.php b/app/Services/Condition/DoesntContainValueOperator.php new file mode 100644 index 0000000..8de8fbe --- /dev/null +++ b/app/Services/Condition/DoesntContainValueOperator.php @@ -0,0 +1,13 @@ +butTheOpposite($needle, $haystack); + } +} diff --git a/app/Services/Condition/DoesntEqualValueOperator.php b/app/Services/Condition/DoesntEqualValueOperator.php new file mode 100644 index 0000000..4580634 --- /dev/null +++ b/app/Services/Condition/DoesntEqualValueOperator.php @@ -0,0 +1,13 @@ +butTheOpposite($needle, $haystack); + } +} diff --git a/app/Services/Condition/EndsWithOperator.php b/app/Services/Condition/EndsWithOperator.php new file mode 100644 index 0000000..a251f9c --- /dev/null +++ b/app/Services/Condition/EndsWithOperator.php @@ -0,0 +1,15 @@ +whereIn($property, $value); + } +} diff --git a/app/Services/Condition/GreaterThanOperator.php b/app/Services/Condition/GreaterThanOperator.php new file mode 100644 index 0000000..d8cebb1 --- /dev/null +++ b/app/Services/Condition/GreaterThanOperator.php @@ -0,0 +1,38 @@ +isAfter(Carbon::parse($secondValue)); + } + + if (is_string($firstValue) && is_string($secondValue)) { + // This is meant to be a numeric or date operator, checking the greatness of a string is beyond the scope of this lib. + return strlen($firstValue) > strlen($secondValue); + } + + if (! is_numeric($firstValue)) { + // At the time of writing, I'm not sure what could end up here other than maybe objects/arrays? + $firstValue = strlen($firstValue); + } + + if (! is_numeric($secondValue)) { + $secondValue = strlen($secondValue); + } + + return $firstValue > $secondValue; + } +} diff --git a/app/Services/Condition/GreaterThanOrEqualToOperator.php b/app/Services/Condition/GreaterThanOrEqualToOperator.php new file mode 100644 index 0000000..0487e61 --- /dev/null +++ b/app/Services/Condition/GreaterThanOrEqualToOperator.php @@ -0,0 +1,13 @@ +compute($firstValue, $secondValue) || $firstValue === $secondValue; + } +} diff --git a/app/Services/Condition/HasRoleOperator.php b/app/Services/Condition/HasRoleOperator.php new file mode 100644 index 0000000..9616ac5 --- /dev/null +++ b/app/Services/Condition/HasRoleOperator.php @@ -0,0 +1,15 @@ +hasRole($role); + } +} diff --git a/app/Services/Condition/LessThanOperator.php b/app/Services/Condition/LessThanOperator.php new file mode 100644 index 0000000..a58cd2f --- /dev/null +++ b/app/Services/Condition/LessThanOperator.php @@ -0,0 +1,38 @@ +isBefore(Carbon::parse($secondValue)); + } + + if (is_string($firstValue) && is_string($secondValue)) { + // This is meant to be a numeric or date operator, checking the greatness of a string is beyond the scope of this lib. + return strlen($firstValue) < strlen($secondValue); + } + + if (! is_numeric($firstValue)) { + // At the time of writing, I'm not sure what could end up here other than maybe objects/arrays? + $firstValue = strlen($firstValue); + } + + if (! is_numeric($secondValue)) { + $secondValue = strlen($secondValue); + } + + return $firstValue < $secondValue; + } +} diff --git a/app/Services/Condition/LessThanOrEqualToOperator.php b/app/Services/Condition/LessThanOrEqualToOperator.php new file mode 100644 index 0000000..ef2b575 --- /dev/null +++ b/app/Services/Condition/LessThanOrEqualToOperator.php @@ -0,0 +1,13 @@ +compute($firstValue, $secondValue) || $firstValue === $secondValue; + } +} diff --git a/app/Services/Condition/StartsWithOperator.php b/app/Services/Condition/StartsWithOperator.php new file mode 100644 index 0000000..893bd72 --- /dev/null +++ b/app/Services/Condition/StartsWithOperator.php @@ -0,0 +1,15 @@ + DoesntEqualValueOperator::class, + Condition::COMPARATOR_EQUALS => EqualsValueOperator::class, + + // *strings* or arrays, + Condition::COMPARATOR_IN => ContainsValueOperator::class, + Condition::COMPARATOR_LIKE => ContainsValueOperator::class, + Condition::COMPARATOR_LIKE_STRICT => ContainsValueStrictOperator::class, + Condition::COMPARATOR_NOT_LIKE => DoesntContainValueOperator::class, + + // Numbers + Condition::COMPARATOR_GREATER_THAN => GreaterThanOperator::class, + Condition::COMPARATOR_GREATER_THAN_EQUAL => GreaterThanOrEqualToOperator::class, + Condition::COMPARATOR_LESS_THAN => LessThanOperator::class, + Condition::COMPARATOR_LESS_THAN_EQUAL => LessThanOrEqualToOperator::class, + + // Strings + Condition::COMPARATOR_STARTS_WITH => StartsWithOperator::class, + Condition::COMPARATOR_ENDS_WITH => EndsWithOperator::class, + + 'HAS_ROLE' => HasRoleOperator::class, + ]; + + public function navigation() + { + // So we want to filter out any nav items + $navItems = Navigation::query() + ->with('conditions') + ->where('authentication_required', auth()->check()) + ->whereNull('parent_id') + ->orderBy('order') + ->get() + ->map(function (Navigation $item) { + $item->current = $item->href === request()->getRequestUri() || ($item->children->isNotEmpty() && $item->children->filter(fn ($item) => $item->href === request()->getRequestUri())->count() > 0); + + return $item; + }); + + return $navItems->filter(function (Navigation $item) { + return $this->process($item); + }); + } + + public function process(Conditionable $item) + { + if ($item->conditions->count() === 0) { + return true; + } + + return $item->conditions->filter(function (Condition $condition) { + $comparator = static::AVAILABLE_CONDITIONS[$condition->comparator]; + /** @var ContainsValueOperator $instance */ + $instance = new $comparator; + + return $instance->compute($this->processParameter($condition->parameter), $condition->value); + })->count() === $item->conditions->count(); + } + + protected function processParameter(string $parameter) + { + if (str_contains($parameter, ':')) { + [$primaryKey, $field] = explode(':', $parameter); + + $valueExtractionFunction = $this->matchCustomPrimaryKeyFunctions($primaryKey, $field); + + return $valueExtractionFunction($field); + } + + if ($parameter === 'user') { + return auth()->user(); + } + + dd('this is likely a parameter, not a function', $parameter); + } + + protected function matchCustomPrimaryKeyFunctions(string $key, ?string $parameter) + { + return match ($key) { + 'config' => fn ($field) => config($field), + default => dd($key, $parameter), + }; + } +} diff --git a/app/Services/Development/DescribeTableService.php b/app/Services/Development/DescribeTableService.php new file mode 100644 index 0000000..c0f568a --- /dev/null +++ b/app/Services/Development/DescribeTableService.php @@ -0,0 +1,162 @@ + array_values(array_map(fn ($query) => $query->Field, $everything)); + $description = cache()->remember( + 'description.'.get_class($model), + now()->addHour(), + fn () => DB::select('describe '.(new $model)->getTable()) + ); + $indexes = cache()->remember( + 'indexes.'.get_class($model), + now()->addHour(), + fn () => DB::select('show indexes from '.(new $model)->getTable()) + ); + $fields = $mapField($description); + $sorts = array_filter($description, function ($query) { + if (str_contains($query->Type, 'int') && $query->Null == 'NO') { + return true; + } + + if (Str::contains($query->Type, [ + 'timestamp', + 'date', + ]) && $query->Null == 'NO') { + return true; + } + + if (Str::contains($query->Field, [ + 'name', + 'created_at', + 'deleted_at', + 'updated_at', + ])) { + return true; + } + + return false; + }); + + $returnTypes = array_reduce(get_class_methods($model), function ($allClassMethods, $method) use ($model) { + $ref = new \ReflectionMethod($model, $method); + + $type = $ref->getReturnType(); + if (empty($type)) { + return $allClassMethods; + } + + return array_merge($allClassMethods, + [ + $method => $type, + ]); + }, []); + + $methodsThatReturnAClass = array_filter($returnTypes, fn (\ReflectionNamedType $type) => class_exists($type->getName())); + $relations = array_filter($methodsThatReturnAClass, function ($type) { + $c = new \ReflectionClass($type->getName()); + + if (! empty($parentClass = $c->getParentClass())) { + if (! empty($parentParentClass = $parentClass->getParentClass())) { + if ($parentParentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { + return true; + } + } + if ($parentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { + return true; + } + } + + return false; + }); + + // $actions = Code::instancesOf(ActionInterface::class)->getClasses(); + // + // dd(array_map(fn ($q) => new $q, $actions)); + + return [ + 'actions' => array_map(fn ($class) => (array) (new $class), $model->actions ?? []), + 'query_actions' => ActionFilter::WHITELISTED_ACTIONS, + 'fillable' => empty($model->getFillable()) ? ['name'] : $model->getFillable(), + 'fields' => $fields, + 'filters' => array_map(fn ($query) => $query->Column_name, $indexes), + 'includes' => array_keys($relations), + 'sorts' => $mapField($sorts), + 'required' => $mapField(array_filter($description, fn ($query) => $query->Null === 'NO' && $query->Extra !== 'auto_increment')), + ]; + } + + public function describeTable(string $table): array + { + $mapField = fn ($everything) => array_values(array_map(fn ($query) => $query->Field, $everything)); + $description = cache()->remember('description.'.$table, now()->addHour(), fn () => DB::select('describe '.$table)); + $indexes = cache()->remember('indexes.'.$table, now()->addHour(), fn () => DB::select('show indexes from '.$table)); + $fields = $mapField($description); + $sorts = array_filter($description, function ($query) { + if (str_contains($query->Type, 'int') && $query->Null == 'NO') { + return true; + } + + if (Str::contains($query->Type, [ + 'timestamp', + 'date', + ]) && $query->Null == 'NO') { + return true; + } + + if (Str::contains($query->Field, [ + 'name', + 'created_at', + 'deleted_at', + 'updated_at', + ])) { + return true; + } + + return false; + }); + + $returnTypes = []; + + $methodsThatReturnAClass = array_filter($returnTypes, fn (\ReflectionNamedType $type) => class_exists($type->getName())); + $relations = array_filter($methodsThatReturnAClass, function ($type) { + $c = new \ReflectionClass($type->getName()); + + if (! empty($parentClass = $c->getParentClass())) { + if (! empty($parentParentClass = $parentClass->getParentClass())) { + if ($parentParentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { + return true; + } + } + if ($parentClass->getName() === \Illuminate\Database\Eloquent\Relations\Relation::class) { + return true; + } + } + + return false; + }); + + return [ + 'actions' => ActionFilter::WHITELISTED_ACTIONS, + 'fillable' => ['name'], + 'fields' => $fields, + 'filters' => array_map(fn ($query) => $query->Column_name, $indexes), + 'includes' => array_keys($relations), + 'sorts' => $mapField($sorts), + 'required' => $mapField(array_filter($description, fn ($query) => $query->Null === 'NO' && $query->Extra !== 'auto_increment')), + ]; + } +} diff --git a/app/Services/Development/ForgeDevelopmentService.php b/app/Services/Development/ForgeDevelopmentService.php index 3de9f60..c0f375a 100644 --- a/app/Services/Development/ForgeDevelopmentService.php +++ b/app/Services/Development/ForgeDevelopmentService.php @@ -5,9 +5,15 @@ namespace App\Services\Development; use App\Models\Credential; +use App\Models\Domain; +use App\Models\Project; +use App\Models\Server; use Illuminate\Support\Collection; use Laravel\Forge\Exceptions\ValidationException; use Laravel\Forge\Forge; +use Laravel\Forge\Resources\Certificate; +use Laravel\Forge\Resources\Job; +use Laravel\Forge\Resources\Site; class ForgeDevelopmentService { @@ -21,20 +27,20 @@ public function __construct( public function findAllServers() { - $servers = $this->client->get('https://forge.laravel.com/api/v1/servers'); - - return array_map(fn ($serverConfig) => [ - 'id' => $serverConfig['id'], - 'name' => $serverConfig['name'], - 'size' => $serverConfig['size'], - 'ip_address' => $serverConfig['ip_address'], - 'private_ip_address' => $serverConfig['private_ip_address'], - 'ssh_port' => $serverConfig['ssh_port'], - 'network' => $serverConfig['network'], - 'php_version' => $serverConfig['php_version'], - 'type' => $serverConfig['type'], - 'created_at' => $serverConfig['created_at'], - ], $servers['servers']); + $servers = $this->client->servers(); + + return array_map(fn (\Laravel\Forge\Resources\Server $serverConfig) => [ + 'id' => $serverConfig->id, + 'name' => $serverConfig->name, + 'size' => $serverConfig->size, + 'ip_address' => $serverConfig->ipAddress, + 'private_ip_address' => $serverConfig->privateIpAddress, + 'ssh_port' => $serverConfig->sshPort, + 'network' => $serverConfig->network, + 'php_version' => $serverConfig->phpVersion, + 'type' => $serverConfig->type, + 'created_at' => $serverConfig->createdAt, + ], $servers); } public function getDomains($serverId) @@ -44,29 +50,30 @@ public function getDomains($serverId) dd($servers); } - public function createDomainIfNotExists(\App\Models\Domain $domain, Collection $domains, \App\Models\Server $server) + public function createDomainIfNotExists(Domain $domain, Collection $domains, Server $server) { - $sites = $this->client->get("https://forge.laravel.com/api/v1/servers/$server->server_id/sites"); + $sites = $this->client->sites($server->server_id); - foreach ($sites['sites'] as $site) { - if ($domain->name === $site['name']) { + foreach ($sites as $site) { + if ($domain->name === $site->name) { // The domain already exists on the server. return $site; } } try { - - $site = $this->client->post("https://forge.laravel.com/api/v1/servers/$server->server_id/sites", [ + /** @var Site $site */ + $site = $this->client->createSite($server->server_id, [ 'domain' => $domain->name, 'project_type' => 'php', 'aliases' => $domains->map(fn ($domain) => $domain->name)->toArray(), 'directory' => 'public', 'isolated' => false, - ]); + ], true); } catch (ValidationException $e) { dd($e->errors()); } + /** * { * "id": 2, @@ -97,33 +104,37 @@ public function createDomainIfNotExists(\App\Models\Domain $domain, Collection $ * "tags": [] * } */ - return $site['site']; + return (array) $site; } - public function setupSslCertificate(\App\Models\Domain $domain, Collection $domains, \App\Models\Server $server, array $site) + public function setupSslCertificate(Domain $domain, Collection $domains, Server $server, array $site) { - $certificates = $this->client->get("https://forge.laravel.com/api/v1/servers/{$server->server_id}/sites/".$site['id'].'/certificates'); + $certificates = $this->client->certificates($server->server_id, $site['id']); - foreach ($certificates['certificates'] as $certificate) { - if ($certificate['active']) { + /** @var Certificate $certificate */ + foreach ($certificates as $certificate) { + if ($certificate->active) { return $certificate; } } + $allDomains = $domains->map(fn ($d) => $d->name)->concat([$domain->name]); $data = [ - 'domains' => $domains->map(fn ($d) => $d->name)->concat([$domain->name])->toArray(), + 'domains' => $allDomains->concat($allDomains->map(fn ($name) => '*.'.$name))->toArray(), 'type' => 'cloudflare', 'dns_provider' => [ + 'type' => 'cloudflare', // TODO: Come back to this and use a better credential form. 'cloudflare_api_token' => Credential::where('service', 'cloudflare')->first()->access_token, ], ]; try { - $certificate = $this->client->post("https://forge.laravel.com/api/v1/servers/{$server->server_id}/sites/".$site['id'].'/certificates/letsencrypt', $data); + $certificate = $this->client->obtainLetsEncryptCertificate($server->server_id, $site['id'], $data, true); } catch (ValidationException $e) { dd($e->errors(), $data); } + /** * We've got * { @@ -137,4 +148,169 @@ public function setupSslCertificate(\App\Models\Domain $domain, Collection $doma */ return $certificate['certificate']; } + + public function createDaemonIfNotExists(Domain $domain, Server $server, array $daemon) + { + + $servers = $this->client->get("https://forge.laravel.com/api/v1/servers/{$server->server_id}/daemons"); + + foreach ($servers['daemons'] as $existingDaemon) { + if ($existingDaemon['command'] === $daemon['command']) { + return $existingDaemon; + } + } + + try { + $daemon = $this->client->createDaemon($server->server_id, $daemon, true); + } catch (ValidationException $e) { + dd($e->errors()); + } + + return $daemon['daemon']; + } + + public function createCronIfNotExists(Domain $domain, Server $server, array $cron) + { + $servers = $this->client->jobs($server->server_id); + + /** @var Job $existingCron */ + foreach ($servers['cron'] as $existingCron) { + if ($existingCron->command === $cron['command']) { + return $existingCron; + } + } + + try { + $this->client->createJob($server->server_id, $cron, true); + } catch (ValidationException $e) { + dd($e->errors()); + } + + return $cron['cron']; + } + + // create a redirect in laravel forge if there isn't already an existing redirect + public function createRedirectIfNotExists(Domain $domain, Server $server, array $redirect) + { + $servers = $this->client->get("https://forge.laravel.com/api/v1/servers/{$server->server_id}/redirects"); + + foreach ($servers['redirects'] as $existingRedirect) { + if ($existingRedirect['from'] === $redirect['from']) { + return $existingRedirect; + } + } + + try { + $redirect = $this->client->createRedirectRule($server->server_id, $domain->domain_id, $redirect, true); + } catch (ValidationException $e) { + dd($e->errors()); + } + + return $redirect['redirect']; + } + + public function createDeploymentScriptIfNotExists(Domain $domain, Server $server, array $script) + { + $servers = $this->client->get("https://forge.laravel.com/api/v1/servers/{$server->server_id}/sites/{$domain->forge_site_id}/deployment/script"); + + if ($servers['script'] === $script['script']) { + return $servers['script']; + } + + try { + $script = $this->client->put("https://forge.laravel.com/api/v1/servers/{$server->server_id}/sites/{$domain->forge_site_id}/deployment/script", $script); + } catch (ValidationException $e) { + dd($e->errors()); + } + + return $script['script']; + } + + protected function getWeight(Server $server) + { + $tags = $server->tags->map(fn ($tag) => $tag->name); + if ($tags->contains('app')) { + return 1; + } + if ($tags->contains('web')) { + return 5; + } + + return 10; + } + + protected function getBackup( + Server $server, + $appServers, + $webServers, + $jobServers + ) { + $tags = $server->tags->map(fn ($tag) => $tag->name); + if ($tags->contains('jobs')) { + // Job servers should only ever serve HTTP requests if the app servers are down. + return true; + } + + if ($appServers->count() > 0 && $webServers->count() > 0) { + // If there are app servers and web servers, then we can assume that the app servers will be able to serve + // requests if the web servers are down. + return $tags->contains('web'); + } + + return false; + } + + public function updateLoadBalancer(Domain $domain, Project $project, array $site) + { + $servers = $project->servers()->with('tags')->get(); + + $loadBalancingServer = $project->servers()->with('tags')->whereHas('tags', fn ($q) => $q->where('name->en', 'loadbalancer'))->first(); + $appServers = $project->servers()->with('tags')->whereHas('tags', fn ($q) => $q->where('name->en', 'app'))->get(); + $webServers = $project->servers()->with('tags')->whereHas('tags', fn ($q) => $q->where('name->en', 'web'))->get(); + $jobServers = $project->servers()->with('tags')->whereHas('tags', fn ($q) => $q->where('name->en', 'jobs'))->get(); + + $mapFunction = fn (Server $server) => [ + 'id' => $server->server_id, + 'weight' => $this->getWeight($server), + 'backup' => $this->getBackup( + server: $server, + appServers: $appServers, + webServers: $webServers, + jobServers: $jobServers, + ), + 'down' => 0, + 'port' => 80, + ]; + + try { + $this->client->put("https://forge.laravel.com/api/v1/servers/{$loadBalancingServer->server_id}/sites/{$site['id']}/balancing", [ + 'json' => [ + 'servers' => $appServers->map($mapFunction) + ->concat($webServers->map($mapFunction)) + ->concat($jobServers->map($mapFunction)) + ->toArray(), + 'method' => 'least_conn', + ], + ]); + } catch (ValidationException $e) { + dd($e->errors()); + } + } + + public function addSSHKeyToServer(Server $server, Credential $credential) + { + $serverKeys = $this->client->keys($server->server_id); + + foreach ($serverKeys as $key) { + if ($key->name === 'Reforged Spork - Automation SSH Key') { + return; + } + } + + $this->client->createSSHKey($server->server_id, [ + 'name' => 'Reforged Spork - Automation SSH Key', + 'key' => $credential->settings['pub_key'], + 'username' => 'forge', + ], true); + } } diff --git a/app/Services/Documents/HtmlJsonDataLinkingService.php b/app/Services/Documents/HtmlJsonDataLinkingService.php new file mode 100644 index 0000000..bcfc022 --- /dev/null +++ b/app/Services/Documents/HtmlJsonDataLinkingService.php @@ -0,0 +1,38 @@ +client->get($url); + + $content = $page->getBody()->getContents(); + + $dom = new \DOMDocument(); + $dom->loadHTML($content, LIBXML_NOERROR); + libxml_use_internal_errors(true); + $linkedDataSets = []; + foreach ($dom->getElementsByTagName('script') as $script) { + if ($script->getAttribute('type') == 'application/ld+json') { + $json_txt = preg_replace('@/\*.*?\*/@', '', $script->textContent); + $json_txt = preg_replace("/\r|\n/", ' ', trim($json_txt)); + $json = json_decode($json_txt); + + $linkedDataSets[] = $json; + } + } + + return $linkedDataSets; + } +} diff --git a/app/Services/Domain/CloudflareDomainService.php b/app/Services/Domain/CloudflareDomainService.php index 35976eb..67151e3 100644 --- a/app/Services/Domain/CloudflareDomainService.php +++ b/app/Services/Domain/CloudflareDomainService.php @@ -8,7 +8,6 @@ use App\Contracts\Services\DomainServiceContract; use App\Models\Credential; use App\Models\Domain; -use App\Models\DomainAnalytics; use Carbon\Carbon; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Http; @@ -100,20 +99,22 @@ public function createDomain(string $domain): array if (empty($response->json('result.name_servers'))) { if (str_contains($response->json('errors.0.message'), 'already exists')) { - $domains = array_filter($allDomains = $this->getDomains(1000, 1)->items(), fn ($domain) => $domain['domain'] === $domain); - dd($response->json(), $domains, $domain, $allDomains); + $domains = array_filter($allDomains = $this->getDomains(1000, 1)->items(), fn ($d) => $d['domain'] === $domain); - return []; + foreach ($domains as $domain) { + return $domain['name_servers']; + } + + throw new \Exception('Domain exists but could not be found'); } throw $response->toException(); } - dd($response->json()); return $response->json('result.name_servers'); } - public function getDns(string $domain, ?string $type = null, int $limit = 10, int $page = 1): LengthAwarePaginator + public function getDns(string $domain, string $type = null, int $limit = 10, int $page = 1): LengthAwarePaginator { $response = Http::withHeaders([ 'Authorization' => 'Bearer '.$this->apiKey, @@ -125,6 +126,10 @@ public function getDns(string $domain, ?string $type = null, int $limit = 10, in $data = $response->json('result'); + if (! isset($data)) { + dd($response->json()); + } + return new LengthAwarePaginator( array_map(fn ($dnsRecord) => [ 'id' => $dnsRecord['id'], @@ -142,10 +147,17 @@ public function getDns(string $domain, ?string $type = null, int $limit = 10, in public function createDnsRecord(string $domain, array $dnsRecordArray): void { - Http::withHeaders([ + $response = Http::withHeaders([ 'Authorization' => 'Bearer '.$this->apiKey, 'x-auth-email' => $this->email, + 'content-type' => 'application/json', ])->post(static::CLOUDFLARE_URL."/zones/$domain/dns_records", $dnsRecordArray); + + $id = $response->json('result.id'); + + if (empty($id)) { + throw new \Exception('Could not create DNS record'); + } } public function hasEmailRouting(string $domain): bool @@ -230,7 +242,7 @@ public function getAnalytics(Domain $domain, Carbon $startDate, Carbon $endDate) 'uncachedCount', 'staleCount', ]), - 'since' => $startDate->subDay()->startOfDay(), + 'since' => $startDate->startOfDay(), 'until' => $endDate->endOfDay(), ])); @@ -268,7 +280,7 @@ public function getAnalytics(Domain $domain, Carbon $startDate, Carbon $endDate) 'dimensions' => $dimensions, ] = $rowOfData; - $analytic = DomainAnalytics::firstOrCreate($dimensions, $metrics); + $analytic = $domain->domainAnalytics()->firstOrCreate($dimensions, $metrics); if (! $analytic->wasRecentlyCreated) { $analytic->update($metrics); diff --git a/app/Services/Facades/Operator.php b/app/Services/Facades/Operator.php new file mode 100644 index 0000000..09cbfed --- /dev/null +++ b/app/Services/Facades/Operator.php @@ -0,0 +1,15 @@ +url = sprintf($this->baseUrl, $this->env); + parent::__construct($this->url, []); + } + + public function sandbox(): self + { + $this->url = sprintf($this->baseUrl, 'sandbox'); + $this->new($this->url, []); + + return $this; + } + + public function development(): self + { + $this->url = sprintf($this->baseUrl, 'development'); + $this->new($this->url, []); + + return $this; + } + + public function auth($data): HttpService + { + $this->authBits = $data; + + return $this; + } + + /** + * @param null $data + * @return \Illuminate\Support\Collection + * + * @throws \Exception + */ + protected function request($action, $data = []) + { + return parent::request($action, array_merge((array) $data, $this->authBits)); + } +} diff --git a/app/Services/Finance/PlaidService.php b/app/Services/Finance/PlaidService.php new file mode 100644 index 0000000..c045e5f --- /dev/null +++ b/app/Services/Finance/PlaidService.php @@ -0,0 +1,259 @@ +http = $httpService; + } + + public function getInstitutionsByName(string $bankName): array + { + return $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'public_key' => env('PLAID_PUBLIC_KEY'), + ]) + ->post('/institutions/search', [ + 'query' => $bankName, + 'products' => ['transactions'], + ]) + ->get('institutions'); + } + + public function getInstitutionsById(string $bankId): Collection + { + return new Collection($this->http + ->{config('services.plaid.env')}() + ->auth([ + 'public_key' => config('services.plaid.public_key'), + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'country_codes' => ['us'], + ]) + ->post('/institutions/get_by_id', [ + 'institution_id' => $bankId, + 'options' => [ + 'include_optional_metadata' => true, + ], + ]) + ->get('institution')); + } + + public function getTransactions(string $accessToken, Carbon $startDate, Carbon $endDate): Collection + { + throw_if( + $startDate->gte($endDate), + \InvalidArgumentException::class, + sprintf( + 'Your start date of %s is after your end date of %s, which is illegal... You Can\'t do that you fool', + $startDate->format('Y-m-d'), + $endDate->format('Y-m-d') + ) + ); + + /// We should account for rate limiting here to make sure we don't make too many requests per second. + $items = new Collection; + $page = 1; + do { + $paginator = $this->getPaginator($accessToken, $startDate, $endDate, $page); + + $items = $items->concat($paginator->get('transactions')); + $accounts = $paginator->get('accounts', []); + + $totalAvailable = count($paginator->get('transactions')) + ($page - 1) * 500; + $total = $paginator->get('total_transactions'); + $loop = $total !== $totalAvailable; + $page++; + } while ($loop); + + return Collection::make([ + 'transactions' => $items->toArray(), + 'accounts' => $accounts, + ]); + } + + protected function getPaginator(string $accessToken, Carbon $startDate, Carbon $endDate, int $page = 1) + { + throw_if( + $startDate->gte($endDate), + \InvalidArgumentException::class, + sprintf( + 'Your paginator start date of %s is after your end date of %s, which is illegal... You Can\'t do that you fool', + $startDate->format('Y-m-d'), + $endDate->format('Y-m-d') + ) + ); + /** @var Collection $paginator */ + try { + return $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'access_token' => $accessToken, + ]) + ->post('/transactions/get', [ + 'start_date' => $startDate->format('Y-m-d'), + 'end_date' => $endDate->format('Y-m-d'), + 'options' => [ + 'count' => 500, + 'offset' => ($page - 1) * 500, + ], + ]); + } catch (ClientException $exception) { + if ($exception->getCode() === 400) { + echo $exception->getMessage(); + + return new Collection([ + 'transactions' => [], + 'total_transactions' => 0, + ]); + } + + sleep(30); + + return $this->getPaginator($accessToken, $startDate, $endDate, $page); + } + } + + /** + * Exchange the public token for a new access token + * + * @throws \Exception + */ + public function getAccessToken(string $publicToken): array + { + return $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'public_token' => $publicToken, + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + ]) + ->post('/item/public_token/exchange') + ->toArray(); + } + + /** + * Get the accounts for the token + * + * @throws \Exception + */ + public function getAccounts(string $accessToken): array + { + return $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'access_token' => $accessToken, + ]) + ->post('/accounts/get') + ->toArray(); + } + + public function rotateAccessTokens(Account $account): Account + { + return $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'access_token' => $account->accessToken->token, + ]) + ->post('/item/access_token/invalidate') + ->toArray(); + } + + public function getCategories(): array + { + return $this->http + ->{config('services.plaid.env')}() + ->post('/categories/get', []) + ->get('categories'); + } + + public function getInstitutions(int $count = 500, int $page = 1): LengthAwarePaginatorContract + { + $items = $this->http + ->{config('services.plaid.env')}() + ->auth([ + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'count' => $count, + 'offset' => $count * ($page - 1), + ]) + ->post('/institutions/get', [ + 'options' => [ + 'include_optional_metadata' => true, + ], + ]) + ->toArray(); + + return new LengthAwarePaginator($items['institutions'], $items['total'], $count, $page); + } + + public function createLinkToken(string $userId): array + { + return $this->http + ->{config('services.plaid.env')}() + ->post('/link/token/create', [ + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'client_name' => config('services.plaid.client_name'), + 'user' => [ + 'client_user_id' => $userId, + ], + 'products' => config('services.plaid.products'), + 'country_codes' => config('services.plaid.country_codes'), + 'language' => config('services.plaid.language'), + ]) + ->toArray(); + } + + public function updateLinkToken(string $userId, string $accessToken): array + { + return $this->http + ->{config('services.plaid.env')}() + ->post('/link/token/create', [ + 'user' => [ + 'client_user_id' => $userId, + ], + 'client_name' => config('services.plaid.client_name'), + 'country_codes' => config('services.plaid.country_codes'), + 'language' => config('services.plaid.language'), + 'client_id' => config('services.plaid.client_id'), + 'access_token' => $accessToken, + 'secret' => config('services.plaid.secret_key'), + ]) + ->toArray(); + } + + public function updateWebhook(string $access_token): array + { + return $this->http + ->{config('services.plaid.env')}() + ->post('/item/webhook/update', [ + 'access_token' => $access_token, + 'client_id' => config('services.plaid.client_id'), + 'secret' => config('services.plaid.secret_key'), + 'webhook' => route('webhook'), + ]) + ->toArray(); + } +} diff --git a/app/Services/HttpService.php b/app/Services/HttpService.php new file mode 100644 index 0000000..f7d06b2 --- /dev/null +++ b/app/Services/HttpService.php @@ -0,0 +1,126 @@ +url = $url; + $this->new($url, $data); + } + + public static function __callStatic($method, $arguments) + { + if (empty(self::$instance)) { + self::$instance = new static; + } + + return call_user_func_array([self::$instance, $method], $arguments); + } + + public function __call($method, $arguments) + { + return call_user_func_array([self::$instance, $method], $arguments); + } + + public function toArray() + { + return $this->response->toArray(); + } + + protected function new($path, $data = []) + { + $this->path = $path; + $this->client = new Client(array_merge(['base_uri' => $this->url], $data)); + + return $this; + } + + protected function request($action, $data = []) + { + if (! in_array(strtolower($action), ['get', 'post', 'put', 'delete', 'patch'])) { + throw new \Exception('Your desired action is not supported'); + } + + if (empty($data)) { + $body = [ + 'body' => '{}', + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accepts' => 'application/json', + ], + ]; + } else { + $body = [ + 'json' => $data, + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accepts' => 'application/json', + ], + ]; + } + + try { + $response = $this->client->request($action, $this->path, $body); + + $json = $response->getBody()->getContents(); + } catch (ClientException $exception) { + throw new \Exception($exception->getResponse()->getBody()->getContents()); + } + + return $this->response = collect(json_decode($json)); + } + + public function get($path, $data = null) + { + $this->path = $path; + + return $this->request('get', $data); + } + + public function post($path, $data = null) + { + $this->path = $path; + + return $this->request('post', $data); + } + + public function patch($path, $data = null) + { + $this->path = $path; + + return $this->request('patch', $data); + } + + public function delete(string $path, $data = null) + { + $this->path = $path; + + return $this->request('delete', $data); + } + + public function put(string $path, $data = null) + { + $this->path = $path; + + return $this->request('put', $data); + } +} diff --git a/app/Services/ImapService.php b/app/Services/ImapService.php new file mode 100644 index 0000000..6a578f4 --- /dev/null +++ b/app/Services/ImapService.php @@ -0,0 +1,193 @@ +buildMailboxString(), + env('IMAP_USERNAME'), + env('IMAP_PASSWORD') + ), $this->buildMailboxString(), '*')) + ->tap(fn () => imap_close($inbox)); + } + + public function findAllFromDate(string $mailbox, Carbon $date): Collection + { + return collect(imap_search($inbox = imap_open( + sprintf('%s%s', $this->buildMailboxString(), $mailbox), + env('IMAP_USERNAME'), + env('IMAP_PASSWORD') + ), + sprintf('SINCE "%s"', $date->format('Y-m-d')) + )) + ->map(function ($messageNumber) use ($inbox) { + $headers = Str::of($headerRaw = imap_fetchheader($inbox, $messageNumber)) + ->explode("\r\n") + ->filter() + ->reduce(fn ($lines, $line) => array_merge( + $lines, + [explode(': ', $line, 2)[0] => explode(': ', $line, 2)[1] ?? null] + ), []); + + $rfcHeaders = imap_rfc822_parse_headers($headerRaw); + $body = null; + $overview = Arr::first(imap_fetch_overview($inbox, (string) $messageNumber)); + + try { + Carbon::parse($headers['X-Pm-Date']); + } catch (\Throwable $e) { + dd($headers); + } + + return [ + 'id' => imap_uid($inbox, $messageNumber), + 'to' => $this->extractEmailAndName($headers['To']), + 'addressed-to' => $this->extractEmailAndName($headers['X-Simplelogin-Envelope-To'] ?? $headers['X-Original-To'] ?? null), + 'addressed-from' => $this->extractEmailAndName($rfcHeaders->fromaddress ?? $headers['X-Pm-External-Id'] ?? null), + 'date' => Carbon::parse($headers['X-Pm-Date']), + 'human_date' => Carbon::parse($headers['X-Pm-Date'])->fromNow(), + 'subject' => imap_utf8($headers['Subject']), + 'from' => $this->extractEmailAndName($rfcHeaders->senderaddress ?? $rfcHeaders->fromaddress ?? $headers['From'], $headers), + 'reply-to' => $this->extractEmailAndName($rfcHeaders->reply_toaddress ?? $headers['Reply-To']), + 'spam' => intval($headers['X-Pm-Spamscore'] ?? 0), + 'seen' => (bool) $overview->seen ?? false, + 'deleted' => (bool) $overview->deleted ?? false, + 'answered' => (bool) $overview->answered ?? false, + 'recent' => (bool) $overview->recent ?? false, + 'draft' => (bool) $overview->draft ?? false, + ]; + }) + ->sortByDesc('date') + ->values() + ->tap(fn () => imap_close($inbox)); + } + + public function findMessage(string $messageNumber, bool $peak = true): array + { + $mailbox = new \PhpImap\Mailbox( + sprintf($this->buildMailboxString().'INBOX'), // IMAP server and mailbox folder + env('IMAP_USERNAME'), // Username for the before configured mailbox + env('IMAP_PASSWORD'), // Password for the before configured username + storage_path(), // Directory, where attachments will be saved (optional) + 'UTF-8', // Server encoding (optional) + true, // Trim leading/ending whitespaces of IMAP path (optional) + false // Attachment filename mode (optional; false = random filename; true = original filename) + ); + + $message = $mailbox->getMail((int) $messageNumber, false); + + $headers = Str::of($message->headersRaw) + ->explode("\r\n") + ->filter() + ->reduce(fn ($lines, $line) => array_merge( + $lines, + [explode(': ', $line, 2)[0] => explode(': ', $line, 2)[1] ?? null] + ), []); + + $rfcHeaders = imap_rfc822_parse_headers($message->headersRaw); + + $body = base64_encode(empty($message->textHtml) ? $message->textPlain : $message->textHtml); + + return [ + 'id' => $messageNumber, + 'to' => $this->extractEmailAndName($headers['To']), + 'addressed-to' => $this->extractEmailAndName($headers['X-Simplelogin-Envelope-To'] ?? $headers['X-Original-To'] ?? null), + 'addressed-from' => $this->extractEmailAndName($rfcHeaders->fromaddress ?? $headers['X-Pm-External-Id'] ?? null), + 'date' => Carbon::parse($headers['X-Pm-Date']), + 'human_date' => Carbon::parse($headers['X-Pm-Date'])->fromNow(), + 'subject' => imap_utf8($headers['Subject']), + 'from' => $this->extractEmailAndName($rfcHeaders->senderaddress ?? $rfcHeaders->fromaddress ?? $headers['From'], $headers), + 'reply-to' => $this->extractEmailAndName($rfcHeaders->reply_toaddress ?? $headers['Reply-To']), + 'spam' => $headers['X-Pm-Spamscore'], + 'seen' => $message->isSeen ?? false, + 'deleted' => $message->isDeleted ?? false, + 'answered' => $message->isAnswered ?? false, + 'recent' => $message->isRecent ?? false, + 'draft' => $message->isDraft ?? false, + 'body' => $body, + 'view' => empty($message->textHtml) ? 'render-plain-inbox' : 'render-rich-inbox', + ]; + } + + protected function buildMailboxString() + { + return sprintf('{'.env('IMAP_HOST').':'.env('IMAP_PORT').'/imap/'.env('IMAP_ENCRYPTION', 'notls').'}'); + } + + protected function extractEmailAndName(?string $value, $headers = []) + { + if (empty($value)) { + return $value; + } + + if (! str_contains($value, '<')) { + return array_merge([ + 'email' => $value, + ]); + } + + if (str_contains($value, '"')) { + preg_match_all('/(\".*\")?(\s)?(\<.*\>)/', $value, $matches); + } else { + preg_match_all('/(.*)(\s)(\<.*\>)/', $value, $matches); + } + + return match (count($matches)) { + 3 => [ + 'email' => trim(Arr::first($matches[2])), + 'original' => $value, + ], + 4 => empty(trim((string) Arr::first($matches[1]), "\"'")) ? [ + // Address + 'email' => trim((string) Arr::first($matches[3]), '<>'), + 'original' => $value, + ] : [ + // Name + 'name' => trim(Arr::first($matches[1]), "\"'"), + // Address + 'email' => trim(Arr::first($matches[3]), '<>'), + 'original' => $value, + ], + }; + } + + public function markAsRead(string $messageId) + { + $mailbox = new \PhpImap\Mailbox( + sprintf($this->buildMailboxString().'INBOX'), // IMAP server and mailbox folder + env('IMAP_USERNAME'), // Username for the before configured mailbox + env('IMAP_PASSWORD'), // Password for the before configured username + storage_path(), // Directory, where attachments will be saved (optional) + 'UTF-8', // Server encoding (optional) + true, // Trim leading/ending whitespaces of IMAP path (optional) + false // Attachment filename mode (optional; false = random filename; true = original filename) + ); + + $mailbox->getMail($messageId, true); + } + + public function markAsUnread(string $messageId) + { + $mailbox = new \PhpImap\Mailbox( + sprintf($this->buildMailboxString().'INBOX'), // IMAP server and mailbox folder + env('IMAP_USERNAME'), // Username for the before configured mailbox + env('IMAP_PASSWORD'), // Password for the before configured username + storage_path(), // Directory, where attachments will be saved (optional) + 'UTF-8', // Server encoding (optional) + true, // Trim leading/ending whitespaces of IMAP path (optional) + false // Attachment filename mode (optional; false = random filename; true = original filename) + ); + + $mailbox->markMailAsUnread($messageId); + } +} diff --git a/app/Services/LaravelForgeService.php b/app/Services/LaravelForgeService.php deleted file mode 100644 index 4c12303..0000000 --- a/app/Services/LaravelForgeService.php +++ /dev/null @@ -1,53 +0,0 @@ -client = new Forge($this->credential->access_token, new Client([ - 'base_uri' => 'https://forge.laravel.com/api/v1/', - 'http_errors' => false, - 'headers' => [ - 'Authorization' => 'Bearer '.$this->credential->access_token, - 'Accept' => 'application/json', - 'Content-Type' => 'application/json', - ], - ])); - } - - public function getServers() - { - $servers = $this->client->get('https://forge.laravel.com/api/v1/servers'); - - return array_map(fn ($serverConfig) => [ - 'id' => $serverConfig['id'], - 'name' => $serverConfig['name'], - 'size' => $serverConfig['size'], - 'ip_address' => $serverConfig['ip_address'], - 'private_ip_address' => $serverConfig['private_ip_address'], - 'ssh_port' => $serverConfig['ssh_port'], - 'network' => $serverConfig['network'], - 'php_version' => $serverConfig['php_version'], - 'type' => $serverConfig['type'], - 'created_at' => $serverConfig['created_at'], - ], $servers['servers']); - } - - public function getDomains($serverId) - { - $servers = $this->client->get("https://forge.laravel.com/api/v1/servers/{$serverId}/sites"); - - dd($servers); - } -} diff --git a/app/Services/Matrix/MatrixClient.php b/app/Services/Matrix/MatrixClient.php new file mode 100644 index 0000000..23e19b2 --- /dev/null +++ b/app/Services/Matrix/MatrixClient.php @@ -0,0 +1,117 @@ + 'application/json', + // 'Content-type' => 'application/json', + // ])->get('https://matrix.'.$this->homeserver.'/_matrix/client/r0/login')->json() + $rooms = Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.env('MATRIX_ACCESS_TOKEN'), + + ])->get('https://matrix.'.$this->homeserver.'/_matrix/client/v3/joined_rooms', [ + // 'type' => 'm.login.application_service', + // 'identifier' => [ + // "type" => "m.id.user", + // 'user' => 'austinkregel', + // + // ] + ])->json('joined_rooms'); + + dd(array_reduce($rooms, function ($all, $room) { + $roomAliases = cache()->rememberForever('room-cache'.$room, fn () => Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.env('MATRIX_ACCESS_TOKEN'), + ])->get('https://matrix.'.$this->homeserver.'/_matrix/client/v3/rooms/'.$room.'/aliases')->json('aliases')); + + echo $room."\n"; + + return array_merge( + $all, + [ + $room => $roomAliases, + ], + ); + }, [])); + } + + public function requestCodeForBeeper(string $email): string + { + return cache()->remember(md5(json_encode([ + 'beeper', '|', + $email, + ])), now()->addMinutes(30), function () use ($email) { + $request = Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE', + ])->post('https://api.beeper.com/user/login', [ + ])->json('request'); + + Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE', + ])->post('https://api.beeper.com/user/login/email', [ + 'request' => $request, + 'email' => $email, + ])->body(); + + return $request; + }); + } + + public function loginWithBeeperCode(string $email, string $code): array + { + if (! cache()->has(md5(json_encode([ + 'beeper', '|', + $email, + ])))) { + abort(404); + } + + return Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE', + ])->post('https://api.beeper.com/user/login/response', [ + 'request' => cache()->get(md5(json_encode([ + 'beeper', '|', + $email, + ]))), + 'response' => $code, + ])->json(); + } + + public function loginWithJwt(string $jwt): array + { + $login = Http::withHeaders([ + 'Accept' => 'application/json', + ])->post('https://matrix.'.$this->homeserver.'/_matrix/client/v3/login', [ + 'type' => 'org.matrix.login.jwt', + 'token' => $jwt, + ])->json(); + + return $login; + } + + public function devices(string $jwt): array + { + dd(Http::withHeaders([ + 'Accept' => 'application/json', + 'Authorization' => 'Bearer '.$jwt, + ])->get('https://matrix.'.$this->homeserver.'/_matrix/client/v3/devices')->json()); + } +} diff --git a/app/Services/News/Feeds/AtomFeed.php b/app/Services/News/Feeds/AtomFeed.php index a4b64de..706be70 100644 --- a/app/Services/News/Feeds/AtomFeed.php +++ b/app/Services/News/Feeds/AtomFeed.php @@ -25,12 +25,16 @@ public function getLastModified(): ?string public function getPhoto(): ?string { - return $this->element->icon ?? null; + if (! isset($this->element->icon)) { + return null; + } + + return (string) $this->element->icon; } public function getName(): string { - return $this->element->title; + return (string) $this->element?->title; } public function getData(): array @@ -59,7 +63,8 @@ public function getData(): array $feedItem = new FeedItem(); $feedItem->id = $post['id']; - $feedItem->title = $post['title']; + $feedItem->title = $post['title'] ?? null; + $feedItem->content = $post['content'] ?? null; $feedItem->published_at = $post['published'] ?? $post['updated']; if (isset($post['link']) && is_string($post['link'])) { diff --git a/app/Services/News/Feeds/FeedItem.php b/app/Services/News/Feeds/FeedItem.php index 821a7e3..ac366e1 100644 --- a/app/Services/News/Feeds/FeedItem.php +++ b/app/Services/News/Feeds/FeedItem.php @@ -12,7 +12,7 @@ class FeedItem public ?string $uuid; - public string $title; + public ?string $title; public string $url; @@ -46,7 +46,12 @@ public function getExternalId(): string public function getTitle(): string { - return $this->title; + return $this->title ?? ''; + } + + public function getContent(): ?string + { + return $this->content; } public function getUrl(): string @@ -62,7 +67,7 @@ public function getPublishedAt(): Carbon public function setUrl($post) { if (isset($post->link)) { - $this->url = $post->link; + $this->url = (string) $post->link; return; } diff --git a/app/Services/News/Feeds/RssFeed.php b/app/Services/News/Feeds/RssFeed.php index 2774d8d..714da31 100644 --- a/app/Services/News/Feeds/RssFeed.php +++ b/app/Services/News/Feeds/RssFeed.php @@ -5,6 +5,7 @@ namespace App\Services\News\Feeds; use Illuminate\Support\Arr; +use Illuminate\Support\Str; class RssFeed extends AbstractFeed { @@ -26,7 +27,7 @@ public function getLastModified(): ?string public function getPhoto(): ?string { if (isset($this->element->channel->image) && isset($this->element->channel->image->url)) { - return $this->element->channel->image->url; + return (string) $this->element->channel->image->url; } return null; @@ -34,23 +35,19 @@ public function getPhoto(): ?string public function getName(): string { - return $this->element->channel->title; + return (string) $this->element->channel->title; } public function getData(): array { return array_map(function ($post) { $feedItem = new FeedItem(); - $feedItem->id = $post->guid; + $feedItem->id = (string) ($post->guid ?? Str::uuid()); $feedItem->setTitle($post->title); $feedItem->setPublishedAt($post->pubDate); $feedItem->setUrl($post); - $feedItem->content = $post->description ?? null; - $feedItem->authorName = $post->source ?? null; - - if (empty($feedItem->getTitle())) { - dd($feedItem, $post); - } + $feedItem->content = (string) $post->description ?? null; + $feedItem->authorName = (string) $post->source ?? null; return $feedItem; }, ((array) $this->element->channel)['item']); diff --git a/app/Services/News/NewsService.php b/app/Services/News/NewsService.php index be9f847..0c6e2d6 100644 --- a/app/Services/News/NewsService.php +++ b/app/Services/News/NewsService.php @@ -50,7 +50,7 @@ public function query(string $query): array ]); } - public function headlines(string $query, ?string $category = null): array + public function headlines(string $query, string $category = null): array { return $this->request('top-headlines', array_merge([ 'apiKey' => env('NEWS_API_KEY'), diff --git a/app/Services/Programming/LaravelProgrammingStyle.php b/app/Services/Programming/LaravelProgrammingStyle.php new file mode 100644 index 0000000..9642610 --- /dev/null +++ b/app/Services/Programming/LaravelProgrammingStyle.php @@ -0,0 +1,450 @@ +import([$listener, $eventName]); + $this->modifyProperty('listen', function (Property $property) use ($eventName, $listener) { + /** @var Literal $literal */ + $literal = $property->getValue(); + // plan: Split the literal into chunks, look for the $eventName, + // When $eventName is found, on the following line insert $listener with a trailing comma. + + $lineWithEvent = null; + $content = explode("\n", $literal); + + if (count($content) <= 2) { + throw new \DomainException('This EventServiceProvider needs to have at least 3 lines for the $listen'); + } + + $content = array_values(array_filter($content)); + + foreach ($content as $lineNumber => $line) { + if (str_contains($line, $eventName)) { + $lineWithEvent = $lineNumber; + } + } + + if ($lineWithEvent === null) { + $content = array_merge(array_slice($content, 0, 1), [ + // new line with our listener, + ' '.$this->formatClassWithClass($eventName).' => [', + ' '.$this->formatClassWithClass($listener).', // code: this is an autogenerated line', + ' ],', + ], array_slice($content, 1, count($content))); + } else { + $content = array_merge(array_slice($content, 0, $lineWithEvent + 1), [ + // new line with our listener, + ' '.$this->formatClassWithClass($listener).', // code: this is an autogenerated line', + ], array_slice($content, $lineWithEvent + 1, count($content))); + } + + $property->setValue((new Literal(implode("\n", $content)))); + }); + + return $this; + } + + public function propertyContainsValue(string $propertyName, string $valueToLookFor) + { + /** @var PhpFile $file */ + foreach ($this->phpFiles as $file) { + /** @var PhpNamespace $namespaceObject */ + foreach ($file->getNamespaces() as $namespace => $namespaceObject) { + // Add code at the namespace level like use statements, declare(strict_types=1); + /** @var \Nette\PhpGenerator\ClassType $class */ + foreach ($file->getClasses() as $class) { + /** @var \Nette\PhpGenerator\Property $property */ + foreach ($class->getProperties() as $definedProperty => $property) { + if ($definedProperty === $propertyName) { + /** @var Literal $value */ + $value = $property->getValue(); + + if (is_string($value) && str_contains($value, $valueToLookFor)) { + return true; + } + $value = $value->formatWith(new Dumper); + + if (str_contains($value, $valueToLookFor)) { + return true; + } + } + } + } + } + } + + return false; + } + + public function removeListenerFromEvent(string $eventName, string $listener) + { + // We need to find the event service provider + // Then find the $listen property + // then the index of the event + // lastly add the listener to the array of listeners + // and rebuild the file. + // $this->removeImport([$listener]); + $this->modifyProperty('listen', function (Property $property) use ($eventName, $listener) { + /** @var Literal $literal */ + $literal = $property->getValue(); + // plan: Split the literal into chunks, look for the $eventName,` + // When $eventName is found, on the following line insert $listener with a trailing comma. + + $content = explode("\n", $literal); + + if (count($content) <= 2) { + throw new \DomainException('This EventServiceProvider needs to have at least 3 lines for the $listen'); + } + + $content = array_values(($content)); + + $lineWithEvent = null; + $eventLineEnd = null; + foreach ($content as $lineNumber => $line) { + if ($lineWithEvent === null && str_contains($line, $eventName)) { + $lineWithEvent = $lineNumber; + } + + if ($lineWithEvent !== null && $eventLineEnd === null && str_contains($line, $listener)) { + unset($content[$lineNumber]); + } + + if ($eventLineEnd === null && $lineWithEvent !== null && $lineNumber > $lineWithEvent && str_contains($line, ']')) { + $eventLineEnd = $lineNumber; + } + + } + + $property->setValue(new Literal(implode("\n", $content))); + }); + + return $this; + } + + public function formatClassWithClass(string $class) + { + return '/*(n*/'.$class.'::class'; + } + + public function removeImport($fqns) + { + $imports = is_array($fqns) ? $fqns : func_get_args(); + foreach ($this->phpFiles as $phpFile) { + /** + * @var string $namespaceName + * @var PhpNamespace $namespace + */ + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + foreach ($imports as $importToRemove) { + $uses = $namespace->getUses(); + if (! in_array($importToRemove, $uses)) { + // not already imported; + continue; + } + + $namespace->removeUse($importToRemove); + } + } + } + + return $this; + } + + public function renameClass(string $newClassName, string $newNamespace = null) + { + /** @var PhpFile $phpFile */ + foreach ($this->phpFiles as $filePath => $phpFile) { + if (count($phpFile->getNamespaces()) > 1) { + throw new \Exception('Too many namespaces in class file to rename: '.$filePath); + } + + /** + * @var string $namespaceName + * @var PhpNamespace $namespace + */ + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + if (! empty($newNamespace)) { + + $newNamespaceObject = new PhpNamespace($newNamespace); + foreach ($namespace->getUses() as $importAlias => $importedClass) { + $newNamespaceObject->addUse($importedClass, $importAlias); + } + + foreach ($namespace->getClasses() as $class) { + $newNamespaceObject->add($class); + } + foreach ($namespace->getFunctions() as $function) { + $newNamespaceObject->add($function); + } + + $file = new PhpFile(); + $file->setStrictTypes($phpFile->hasStrictTypes()); + $file->addNamespace($newNamespaceObject); + + $this->phpFiles[$filePath] = $file; + } + + $classes = $namespace->getClasses(); + + if (count($classes) > 1) { + throw new \Exception('Too many classes in class file to rename: '.$filePath); + } + foreach ($classes as $class) { + $class->setName($newClassName); + } + } + } + + return $this; + } + + public function modifyConstructor(string $parameterToEdit, string $typeOfParameter, mixed $defaultValue = null, ?bool $nullable = false): static + { + foreach ($this->phpFiles as $phpFile) { + /** + * @var string $namespaceName + * @var PhpNamespace $namespace + */ + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + $classes = $namespace->getClasses(); + foreach ($classes as $class) { + if (! $class->hasMethod('__construct')) { + // no constructor + $class->addMethod('__construct'); + } + + $constructor = $class->getMethod('__construct'); + + if (! $constructor->hasParameter($parameterToEdit)) { + $constructor->addParameter($parameterToEdit); + } + + $parameter = $constructor->getParameter($parameterToEdit); + + $parameter->setNullable($nullable); + + $this->setValidType($parameter, $typeOfParameter); + + if (empty($defaultValue) && $parameter->isNullable()) { + $parameter->setDefaultValue($defaultValue); + } elseif (! empty($defaultValue)) { + $parameter->setDefaultValue($defaultValue); + } + } + } + + return $this; + } + + return $this; + } + + public function modifyMethod(string $methodName, string $content, mixed $returnType = null, mixed $defaultValue = null, ?bool $nullable = false, ?array $parameters = []): static + { + foreach ($this->phpFiles as $phpFile) { + /** + * @var string $namespaceName + * @var PhpNamespace $namespace + */ + foreach ($phpFile->getNamespaces() as $namespaceName => $namespace) { + $classes = $namespace->getClasses(); + foreach ($classes as $class) { + if (! $class->hasMethod($methodName)) { + // no constructor + $class->addMethod($methodName); + } + + $method = $class->getMethod($methodName); + + $method->setBody($content); + if (! empty($returnType)) { + $this->setValidType($method, $returnType); + } + + if (! empty($parameters)) { + $method->setParameters($parameters); + } + } + } + + return $this; + } + + return $this; + } + + public function removeMethod(string $methodName): static + { + foreach ($this->phpFiles as $phpFile) { + /** + * @var string $namespaceName + * @var PhpNamespace $namespace + */ + foreach ($phpFile->getNamespaces() as $namespace) { + $classes = $namespace->getClasses(); + foreach ($classes as $class) { + if ($class->hasMethod($methodName)) { + // no constructor + $class->removeMethod($methodName); + } + } + } + + return $this; + } + + return $this; + } + + protected function setValidType($parameter, $typeOfParameter) + { + if (in_array($typeOfParameter, ['int', 'integer', 'string', 'float', 'null'])) { + $parameter->setType($typeOfParameter); + } elseif (class_exists($typeOfParameter)) { + $this->import($typeOfParameter); + $parameter->setType($typeOfParameter); + } else { + // Intentionally ignoring classes that return false + } + + return $parameter; + } + + protected function isClassInstanceOfModel(string $listenerParameterType) + { + if (empty($listenerParameterType)) { + return false; + } + + if (! class_exists($listenerParameterType)) { + return false; + } + $models = static::instancesOf(Model::class)->getClasses(); + + if (! in_array($listenerParameterType, $models)) { + return false; + } + + // it's a laravel model + return true; + } + + protected function recurseGetUnionType(\ReflectionNamedType|\ReflectionUnionType $type): array + { + if ($type instanceof \ReflectionNamedType) { + $types = [$type]; + } + if ($type instanceof \ReflectionUnionType) { + $types = $type->getTypes(); + } + + return array_reduce($types, function ($allBits, \ReflectionNamedType|\ReflectionUnionType $type) { + return array_merge($allBits, [ + method_exists($type, 'getTypes') ? $type->getTypes() : $type->getName(), + ]); + }, []); + } + + public static function findLogicalEvents(): array + { + $code = static::instancesOf(LogicalEvent::class); + + $nonExampleEvents = array_filter($code->getClasses(), fn ($class) => ! str_contains($class, 'Example')); + + $allEventsReducedWithContext = array_values(array_reduce($nonExampleEvents, function ($allClasses, $instanceOfLogicalEvent) use ($code) { + $actualListeners = []; + /** @var \Closure $listener */ + foreach (\Illuminate\Support\Facades\Event::getListeners($instanceOfLogicalEvent) as $listener) { + // Laravel puts all listeners inside of a closure. + $listenerInformation = (new \Laravel\SerializableClosure\Support\ReflectionClosure($listener))->getUseVariables(); + if (empty($listenerInformation['listener'])) { + continue; + } + + array_push($actualListeners, $listenerInformation['listener']); + } + + $logicalEventReflection = new \ReflectionClass($instanceOfLogicalEvent); + // Only technically exist for class types and not interfaces + $constructorParametersThatAreModels = $logicalEventReflection->getConstructor()?->getParameters() ?? []; + $traits = $logicalEventReflection->getTraits(); + + $methodsProvidedByTraits = array_reduce( + $traits, + fn ($methods, \ReflectionClass $trait) => array_merge( + $methods, array_map(fn (\ReflectionMethod $m) => $m->getName(), $trait->getMethods()) + ), + [] + ); + + $methodNamesOnClass = array_map(fn (\ReflectionMethod $method) => $method->getName(), $logicalEventReflection->getMethods()); + + $methodsActuallyDefinedOnOurLogicalEvent = array_filter(array_diff($methodNamesOnClass, $methodsProvidedByTraits), fn ($value) => $value !== '__construct'); + + $netteCodeInstance = static::for($instanceOfLogicalEvent)->getPrimaryClassType(); + + return array_merge($allClasses, [ + $instanceOfLogicalEvent => [ + 'listeners' => $actualListeners, + 'constructor' => array_map(function (\ReflectionParameter $param) use ($code) { + return [ + $param->getName() => $code->recurseGetUnionType($param->getType()), + ]; + }, $constructorParametersThatAreModels), + 'event' => $instanceOfLogicalEvent, + 'name' => class_basename($instanceOfLogicalEvent), + 'methods' => array_reduce($methodsActuallyDefinedOnOurLogicalEvent, function ($allMethods, $method) use ($netteCodeInstance) { + try { + $methodInstance = $netteCodeInstance->getMethod($method); + } catch (InvalidArgumentException $e) { + return $allMethods; + } + + return array_merge($allMethods, [ + $method => [ + 'parameters' => array_map(function (PromotedParameter $param) { + return trim($param->getType(), '\\'); + }, $methodInstance->getParameters()), + 'body' => $methodInstance->getBody(), + ], + ]); + }, []), + ], + ]); + }, [])); + + return collect($allEventsReducedWithContext) + ->sortByDesc(fn ($e) => count($e['listeners'])) + ->values() + ->toArray(); + } + + public static function findLogicalListeners(): array + { + return static::instancesOf(LogicalListener::class)->getClasses(); + } +} diff --git a/app/Services/Registrar/NamecheapService.php b/app/Services/Registrar/NamecheapService.php index 31e026a..8b91de7 100644 --- a/app/Services/Registrar/NamecheapService.php +++ b/app/Services/Registrar/NamecheapService.php @@ -4,13 +4,13 @@ namespace App\Services\Registrar; -use App\Contracts\Services\NamecheapDomainServiceContract; +use App\Contracts\Services\NamecheapServiceContract; use App\Models\Credential; use Carbon\Carbon; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Http; -class NamecheapService implements NamecheapDomainServiceContract +class NamecheapService implements NamecheapServiceContract { public const NAMECHEAP_URL = 'https://api.namecheap.com/xml.response'; @@ -79,7 +79,6 @@ public function getDomainNs(string $domain): array if (isset($domainResponse->Errors->Error)) { throw new \Exception($domainResponse->Errors->Error); } - try { return $domainResponse->CommandResponse->DomainDNSGetListResult->Nameserver; } catch (\Throwable $e) { @@ -114,38 +113,38 @@ public function updateDomainNs(string $domain, array $nameservers): array public function fetchPriceOfRenewal(string $domain): string { [$domainPart, $tld] = explode('.', $domain); - if (cache()->has($key = 'tld-pricing-for-namecheap.'.$tld)) { - return cache()->get($key, ''); - } - - $response = Http::get(static::NAMECHEAP_URL.'?'.http_build_query([ - // Auth - 'ApiUser' => $this->credential->settings['api_user'], - 'ApiKey' => $this->credential->access_token, - 'UserName' => $this->credential->settings['username'], - 'ClientIp' => $this->credential->settings['client_ip'], - // command - 'Command' => 'namecheap.users.getPricing', - // request deets - 'ProductType' => 'DOMAIN', - 'ActionName' => 'RENEW', - 'ProductName' => $tld, - ])); - $domainResponse = json_decode(json_encode(simplexml_load_string($xmlDebugResponse = $response->body()))); - - if (isset($domainResponse->Errors->Error)) { - throw new \Exception($domainResponse->Errors->Error); - } - - $prices = $domainResponse?->CommandResponse?->UserGetPricingResult?->ProductType?->ProductCategory?->Product?->Price ?? []; - - if (empty($prices)) { - return ''; - } - - foreach ($prices as $price) { - return cache()->remember($key, now()->addHour(), fn () => $price?->{'@attributes'}?->Price ?? $price->Price ?? dd($price, $prices)); - } + return cache()->remember($key = 'tld-pricing-for-namecheap.'.$tld, now()->addHour(), function () use ($tld) { + $response = Http::get(static::NAMECHEAP_URL.'?'.http_build_query([ + // Auth + 'ApiUser' => $this->credential->settings['api_user'], + 'ApiKey' => $this->credential->access_token, + 'UserName' => $this->credential->settings['username'], + 'ClientIp' => $this->credential->settings['client_ip'], + // command + 'Command' => 'namecheap.users.getPricing', + // request deets + 'ProductType' => 'DOMAIN', + 'ActionName' => 'RENEW', + 'ProductName' => $tld, + ])); + + $domainResponse = json_decode(json_encode(simplexml_load_string($xmlDebugResponse = $response->body()))); + + if (isset($domainResponse->Errors->Error)) { + throw new \Exception($domainResponse->Errors->Error); + } + + $prices = $domainResponse?->CommandResponse?->UserGetPricingResult?->ProductType?->ProductCategory?->Product?->Price ?? []; + + if (empty($prices)) { + return ''; + } + + foreach ($prices as $price) { + return $price?->{'@attributes'}?->Price ?? $price->Price ?? dd($price, $prices); + } + + }); } } diff --git a/app/Services/Server/DigitalOceanService.php b/app/Services/Server/DigitalOceanService.php index 199598d..74a5d45 100644 --- a/app/Services/Server/DigitalOceanService.php +++ b/app/Services/Server/DigitalOceanService.php @@ -135,4 +135,9 @@ public function updateDomainNs(string $domain, array $nameservers): array { // TODO: Implement updateDomainNs() method. } + + public function createDomain(string $domain): array + { + // TODO: Implement createDomain() method. + } } diff --git a/app/Services/SshKeyGeneratorService.php b/app/Services/SshKeyGeneratorService.php new file mode 100644 index 0000000..44b42d2 --- /dev/null +++ b/app/Services/SshKeyGeneratorService.php @@ -0,0 +1,65 @@ + $this->curveName, + 'private_key_type' => OPENSSL_KEYTYPE_EC, + ]); + + if (! $res) { + throw new Exception('Could not generate the key pair.'); + } + + openssl_pkey_export($res, $privKey, $this->passKey); + + $this->encryptedPrivateKey = $privKey; + $pubKeyDetails = openssl_pkey_get_details($res); + unset($privKey); + unset($res); + $this->encryptedPublicKey = $pubKeyDetails['key']; + + if (! file_exists($this->privateKeyFile)) { + file_put_contents($this->privateKeyFile, $this->encryptedPrivateKey); + chmod($this->privateKeyFile, 0600); + } + + if (! file_exists($this->publicKeyFile)) { + // dd(sprintf('echo "%s"', $this->passKey).' && ssh-keygen -y -f '.$this->privateKeyFile.' > '.$this->publicKeyFile); + file_put_contents($this->publicKeyFile, $this->encryptedPublicKey); + chmod($this->publicKeyFile, 0600); + } + } + + public function getPrivateKey(): string + { + return $this->encryptedPrivateKey; + } + + public function getPublicKey(): string + { + return $this->encryptedPublicKey; + } +} diff --git a/app/Services/SshService.php b/app/Services/SshService.php new file mode 100644 index 0000000..e2523bb --- /dev/null +++ b/app/Services/SshService.php @@ -0,0 +1,131 @@ +connection = ssh2_connect($this->host, $this->port); + + if (! $this->connection) { + throw new Exception('Connection failed.'); + } + + // try { + ssh2_auth_pubkey_file($this->connection, $username, $publicKeyFile, $privateKeyFile, $passKey); + // } catch (\Throwable $e) { + // throw new Exception('Public key authentication failed. ' . $this->username . '@' . $this->host . ':' . $this->port); + // } + } + + public function __destruct() + { + if (is_resource($this->connection)) { + ssh2_disconnect($this->connection); + } + } + + public function execute(string $command, string $directory = ''): string + { + if (! empty($directory)) { + $command = 'cd '.escapeshellarg($directory).' && '.$command; + } + + $stream = ssh2_exec($this->connection, $command); + + if (! $stream) { + throw new Exception('Command execution failed.'); + } + + stream_set_blocking($stream, true); + $stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO); + + return stream_get_contents($stream_out); + } + + public function run(Script $script, string $directory = ''): array + { + $localFilePath = storage_path('scripts/'.Str::slug($script->name).'_server.sh'); + + file_put_contents($localFilePath, $script->script); + + // Ensure our .basement folder exists + ssh2_exec($this->connection, 'mkdir /tmp/.spork -f'); + stream_set_blocking($this->connection, true); + + // Create a local copy of our script to make sure it runs like we'd expect. + ssh2_scp_send($this->connection, $localFilePath, $file = '/tmp/.spork/'.Str::random(32).'.sh'); + + unlink($localFilePath); + // Run a command that will probably write to stderr (unless you have a folder named /hom) + $stream_out = ssh2_exec($this->connection, 'bash '.escapeshellcmd($file)); + + $stream_error = ssh2_fetch_stream($this->connection, SSH2_STREAM_STDERR); + ssh2_exec($this->connection, "rm $file -f"); + + return [ + 'stdout' => stream_get_contents($stream_out), + 'stderr' => stream_get_contents($stream_error), + ]; + } + + /** + * @throws Exception + */ + public static function factory(string $host, User $user): Credential + { + $credential = $user->credentials()->where(array_merge([ + 'service' => Credential::TYPE_SSH, + 'type' => Credential::TYPE_SSH, + ]))->first(); + + if (empty($user) && empty($credential)) { + abort(404, 'user does ot exist'); + } + + if (empty($credential)) { + $randomName = Str::random(16); + + (new Filesystem)->makeDirectory(storage_path('app/keys'), 0755, true, true); + + $generatorService = new SshKeyGeneratorService( + privateKeyFile: $privateKeyFile = storage_path('app/keys/'.$randomName.'.key'), + publicKeyFile: $publicKeyFile = storage_path('app/keys/'.$randomName.'.pub'), + passKey: $passKey = ''// Str::random(16), + ); + + return $user->credentials()->create([ + 'service' => Credential::TYPE_SSH, + 'type' => Credential::TYPE_SSH, + 'name' => $host.' ssh', + 'settings' => [ + 'pub_key' => $generatorService->getPublicKey(), + 'pub_key_file' => $publicKeyFile, + 'private_key' => $generatorService->getPrivateKey(), + 'private_key_file' => $privateKeyFile, + 'pass_key' => encrypt($passKey), + ], + ]); + } + + return $credential; + } +} diff --git a/app/Services/Weather/OpenWeatherService.php b/app/Services/Weather/OpenWeatherService.php new file mode 100644 index 0000000..24db067 --- /dev/null +++ b/app/Services/Weather/OpenWeatherService.php @@ -0,0 +1,70 @@ +remember( + 'current-weather-for-petoskey', + now()->addMinutes(30), + fn () => json_decode( + $client + ->get(sprintf('https://api.openweathermap.org/data/2.5/weather?lat=45.3757675&lon=-84.961903&appid=%s&units=imperial', env('OPEN_WEATHER_KEY'))) + ->getBody() + ->getContents() + ) + ); + + $currentlyDayTime = now()->isBetween( + Carbon::parse($weather->sys->sunrise, 'UTC')->setTimezone('America/Detroit'), + Carbon::parse($weather->sys->sunset, 'UTC')->setTimezone('America/Detroit'), + ); + + $currentTime = now()->roundHour()->format('Y-m-d H:i'); + $forecast = new Forecast(); + $forecast->updated_at = Carbon::parse($weather->dt, 'UTC')->setTimezone('America/Detroit')->format('Y-m-d H:i'); + $forecast->condition = $weather->weather[0]->description; + $forecast->condition_url = sprintf('https://openweathermap.org/img/wn/%s.png', $weather->weather[0]->icon); + $forecast->condition_image = match ($weather->weather[0]->description) { + // The remaining weather conditions should be based on whats available at https://openweathermap.org/weather-conditions + 'rain', 'freezing rain', 'heavy rain', 'shower rain', 'moderate rain', 'light rain' => '🌧', + 'snow', 'light snow', 'heavy snow', 'sleet', 'shower sleet', 'light shower sleet', 'rain and snow', 'light rain and snow', 'light shower snow', 'shower snow', 'heavy shower snow' => '🌨️', + 'light intensity drizzle','drizzle','heavy intensity drizzle','light intensity drizzle rain','drizzle rain','heavy intensity drizzle rain','shower rain and drizzle','heavy shower rain and drizzle','shower drizzle' => '🌧️', + 'thunderstorm with light rain','thunderstorm with rain','thunderstorm with heavy rain','light thunderstorm','thunderstorm','heavy thunderstorm','ragged thunderstorm','thunderstorm with light drizzle','thunderstorm with drizzle','thunderstorm with heavy drizzle' => '🌩️', + 'light rain','moderate rain','heavy intensity rain','very heavy rain','extreme rain','light intensity shower rain','shower rain','heavy intensity shower rain','ragged shower rain' => '🌧', + 'mist','smoke','haze','sand, dust whirls','fog','sand','dust','volcanic ash','squalls','tornado' => '🌫💨', + 'clear sky' => $currentlyDayTime ? '☀️' : '🌙', + 'few clouds' => $currentlyDayTime ? '🌤' : '🌙☁️', + 'scattered clouds' => $currentlyDayTime ? '⛅️' : '🌙☁️', + 'broken clouds' => '🌥', + 'overcast clouds' => '☁️', + default => '❓', + }; + + $forecast->temperature = $weather->main->temp; + $forecast->feels_like = $weather->main->feels_like; + $forecast->humidity = $weather->main->humidity; + $forecast->pressure = $weather->main->pressure; + $forecast->wind_speed = $weather->wind->speed; + $forecast->cloud_cover = $weather->clouds->all; + $forecast->chance_of_rain = 0; + $forecast->chance_of_snow = $weather->snow->all ?? 0; + $forecast->sunset = Carbon::parse($weather->sys->sunset, 'UTC')->setTimezone('America/Detroit')->format('Y-m-d H:i'); + $forecast->sunrise = Carbon::parse($weather->sys->sunrise, 'UTC')->setTimezone('America/Detroit')->format('Y-m-d H:i'); + + return [ + $forecast, + ]; + } +} diff --git a/app/Services/Weather/WeatherApiService.php b/app/Services/Weather/WeatherApiService.php new file mode 100644 index 0000000..35c8c4f --- /dev/null +++ b/app/Services/Weather/WeatherApiService.php @@ -0,0 +1,55 @@ +remember( + 'key', + now(), + fn () => json_decode( + $client + ->get(sprintf('http://api.weatherapi.com/v1/forecast.json?key=%s&q=%s&aqi=no&alerts=yes&days=3', env('WEATHER_API_KEY'), $address)) + ->getBody() + ->getContents() + ) + ); + + $currentTime = Carbon::parse($weather->location->localtime, $weather->location->tz_id); + $key = (int) $currentTime->format('H'); + + $forecasts = []; + foreach ($weather->forecast->forecastday as $forecastday) { + $forecast = new Forecast(); + $forecast->address = $address; + + $hourForecast = $forecastday->hour[$key]; + + $forecast->updated_at = Carbon::parse($hourForecast->time, $weather->location->tz_id)->format('Y-m-d H:s:i'); + $forecast->condition = $hourForecast->condition->text; + $forecast->temperature = $hourForecast->temp_f; + $forecast->feels_like = $hourForecast->feelslike_f; + $forecast->humidity = $hourForecast->humidity; + $forecast->pressure = $hourForecast->pressure_mb; + $forecast->wind_speed = $hourForecast->gust_mph; + $forecast->cloud_cover = $hourForecast->cloud; + $forecast->chance_of_rain = $hourForecast->chance_of_rain; + $forecast->chance_of_snow = $hourForecast->chance_of_snow; + + $forecasts[Carbon::parse($hourForecast->time, $weather->location->tz_id)->format('Y-m-d')] = $forecast; + } + + return $forecasts; + } +} diff --git a/bin/sail b/bin/sail new file mode 100755 index 0000000..bb3e832 --- /dev/null +++ b/bin/sail @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -ex + +export WWWUSER=${WWWUSER:-$UID} +export WWWGROUP=${WWWGROUP:-$(id -g)} + +if [ ! -f vendor/bin/sail ]; then + docker run --rm \ + --pull=always \ + -v "$(pwd)":/var/www/html \ + -w /var/www/html \ + austinkregel/base:latest \ + bash -c "composer install" +fi + +vendor/bin/sail "$@" diff --git a/composer.json b/composer.json index ce59f15..2a91bc7 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,24 @@ "name": "laravel/laravel", "type": "project", "description": "The skeleton application for the Laravel framework.", - "keywords": ["laravel", "framework"], + "keywords": [ + "laravel", + "framework" + ], "license": "MIT", "require": { "php": "^8.2", + "ext-bcmath": "*", + "ext-dom": "*", + "ext-imap": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "ext-ssh2": "*", + "beyondcode/laravel-websockets": "^1.14", + "bezhansalleh/filament-shield": "^3.1", + "bugsnag/bugsnag-laravel": "^2.0", + "composer/composer": "^2.6", + "filament/filament": "^3.0-stable", "guzzlehttp/guzzle": "^7.7", "inertiajs/inertia-laravel": "^0.6.8", "laravel/forge-sdk": "^3.13", @@ -14,19 +28,28 @@ "laravel/jetstream": "^3.2", "laravel/sanctum": "^3.2", "laravel/tinker": "^2.8", + "league/flysystem-ftp": "^3.0", + "miguilim/filament-auto-panel": "^1.4", "mustache/mustache": "^2.14", + "nette/php-generator": "*", + "nunomaduro/laravel-console-dusk": "^1.11", + "php-imap/php-imap": "^5.0", + "pusher/pusher-php-server": "^7.2", + "spatie/laravel-activitylog": "^4.7", + "spatie/laravel-feed": "^4.2", + "spatie/laravel-ignition": "^2.2", "spatie/laravel-query-builder": "^5.2", "spatie/laravel-tags": "^4.4", "tightenco/ziggy": "^1.0" }, "require-dev": { "fakerphp/faker": "^1.9.1", - "laravel/pint": "^1.0", + "laravel/dusk": "^7.9", + "laravel/pint": "^1.13", "laravel/sail": "^1.18", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^7.0", - "phpunit/phpunit": "^10.1", - "spatie/laravel-ignition": "^2.0" + "phpunit/phpunit": "^10.1" }, "autoload": { "psr-4": { @@ -43,11 +66,13 @@ "scripts": { "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover --ansi" + "@php artisan package:discover --ansi", + "@php artisan optimize:clear", + "@php artisan filament:upgrade" ], "post-update-cmd": [ "@php artisan vendor:publish --tag=laravel-assets --ansi --force", - "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + "@php artisan optimize:clear" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" @@ -70,6 +95,6 @@ "php-http/discovery": true } }, - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true } diff --git a/composer.lock b/composer.lock index af24ee9..65b7fb1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4c16c94406a09362fcbc71068ab6c323", + "content-hash": "23284d0d19d9010494a5537b7b3252d2", "packages": [ { "name": "bacon/bacon-qr-code", @@ -61,143 +61,206 @@ "time": "2022-12-07T17:46:57+00:00" }, { - "name": "brick/math", - "version": "0.11.0", + "name": "beyondcode/laravel-websockets", + "version": "1.14.1", "source": { "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + "url": "https://github.com/beyondcode/laravel-websockets.git", + "reference": "fee9a81e42a096d2aaca216ce91acf6e25d8c06d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "url": "https://api.github.com/repos/beyondcode/laravel-websockets/zipball/fee9a81e42a096d2aaca216ce91acf6e25d8c06d", + "reference": "fee9a81e42a096d2aaca216ce91acf6e25d8c06d", "shasum": "" }, "require": { - "php": "^8.0" + "cboden/ratchet": "^0.4.1", + "ext-json": "*", + "facade/ignition-contracts": "^1.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "illuminate/broadcasting": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/routing": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "php": "^7.2|^8.0", + "pusher/pusher-php-server": "^3.0|^4.0|^5.0|^6.0|^7.0", + "react/dns": "^1.1", + "react/http": "^1.1", + "symfony/http-kernel": "^4.0|^5.0|^6.0", + "symfony/psr-http-message-bridge": "^1.1|^2.0" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.0", - "vimeo/psalm": "5.0.0" + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "phpunit/phpunit": "^8.0|^9.0|^10.0" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "BeyondCode\\LaravelWebSockets\\WebSocketsServiceProvider" + ], + "aliases": { + "WebSocketRouter": "BeyondCode\\LaravelWebSockets\\Facades\\WebSocketRouter" + } + } + }, "autoload": { "psr-4": { - "Brick\\Math\\": "src/" + "BeyondCode\\LaravelWebSockets\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Arbitrary-precision arithmetic library", + "authors": [ + { + "name": "Marcel Pociot", + "email": "marcel@beyondco.de", + "homepage": "https://beyondcode.de", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "An easy to use WebSocket server", + "homepage": "https://github.com/beyondcode/laravel-websockets", "keywords": [ - "Arbitrary-precision", - "BigInteger", - "BigRational", - "arithmetic", - "bigdecimal", - "bignum", - "brick", - "math" + "beyondcode", + "laravel-websockets" ], "support": { - "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.11.0" + "issues": "https://github.com/beyondcode/laravel-websockets/issues", + "source": "https://github.com/beyondcode/laravel-websockets/tree/1.14.1" }, - "funding": [ - { - "url": "https://github.com/BenMorel", - "type": "github" - } - ], - "time": "2023-01-15T23:15:59+00:00" + "time": "2023-08-30T07:23:12+00:00" }, { - "name": "dasprid/enum", - "version": "1.0.4", + "name": "bezhansalleh/filament-shield", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/DASPRiD/Enum.git", - "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f" + "url": "https://github.com/bezhanSalleh/filament-shield.git", + "reference": "919856a29f76ce73eec417dcfd564c93994df0fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f", - "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f", + "url": "https://api.github.com/repos/bezhanSalleh/filament-shield/zipball/919856a29f76ce73eec417dcfd564c93994df0fd", + "reference": "919856a29f76ce73eec417dcfd564c93994df0fd", "shasum": "" }, "require": { - "php": ">=7.1 <9.0" + "filament/filament": "^3.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9", + "spatie/laravel-permission": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^7 | ^8 | ^9", - "squizlabs/php_codesniffer": "*" + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.0", + "nunomaduro/larastan": "^2.1", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^2.10", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^10.1" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "BezhanSalleh\\FilamentShield\\FilamentShieldServiceProvider" + ], + "aliases": { + "FilamentShield": "BezhanSalleh\\FilamentShield\\Facades\\FilamentShield" + } + } + }, "autoload": { "psr-4": { - "DASPRiD\\Enum\\": "src/" + "BezhanSalleh\\FilamentShield\\": "src", + "BezhanSalleh\\FilamentShield\\Database\\Factories\\": "database/factories" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], "authors": [ { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", + "name": "Bezhan Salleh", + "email": "bezhan_salleh@yahoo.com", "role": "Developer" } ], - "description": "PHP 7.1 enum implementation", + "description": "Filament support for `spatie/laravel-permission`.", + "homepage": "https://github.com/bezhansalleh/filament-shield", "keywords": [ - "enum", - "map" + "acl", + "bezhanSalleh", + "filament", + "filament-shield", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security" ], "support": { - "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.4" + "issues": "https://github.com/bezhanSalleh/filament-shield/issues", + "source": "https://github.com/bezhanSalleh/filament-shield/tree/3.1.1" }, - "time": "2023-03-01T18:44:03+00:00" + "funding": [ + { + "url": "https://github.com/bezhanSalleh", + "type": "github" + } + ], + "time": "2023-12-06T17:28:54+00:00" }, { - "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "name": "blade-ui-kit/blade-heroicons", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "url": "https://github.com/blade-ui-kit/blade-heroicons.git", + "reference": "f756c807b0d04afd2caf7079bac26492da9cc6d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/blade-ui-kit/blade-heroicons/zipball/f756c807b0d04afd2caf7079bac26492da9cc6d4", + "reference": "f756c807b0d04afd2caf7079bac26492da9cc6d4", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "blade-ui-kit/blade-icons": "^1.1", + "illuminate/support": "^9.0|^10.0", + "php": "^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.42", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", - "scrutinizer/ocular": "1.6.0", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.0.0" + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.x-dev" + "laravel": { + "providers": [ + "BladeUI\\Heroicons\\BladeHeroiconsServiceProvider" + ] } }, "autoload": { "psr-4": { - "Dflydev\\DotAccessData\\": "src/" + "BladeUI\\Heroicons\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -206,69 +269,78 @@ ], "authors": [ { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - }, - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com" + "name": "Dries Vints", + "homepage": "https://driesvints.com" } ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "description": "A package to easily make use of Heroicons in your Laravel Blade views.", + "homepage": "https://github.com/blade-ui-kit/blade-heroicons", "keywords": [ - "access", - "data", - "dot", - "notation" + "Heroicons", + "blade", + "laravel" ], "support": { - "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "issues": "https://github.com/blade-ui-kit/blade-heroicons/issues", + "source": "https://github.com/blade-ui-kit/blade-heroicons/tree/2.1.0" }, - "time": "2022-10-27T11:44:00+00:00" + "funding": [ + { + "url": "https://github.com/caneco", + "type": "github" + }, + { + "url": "https://github.com/driesvints", + "type": "github" + } + ], + "time": "2023-01-11T08:38:22+00:00" }, { - "name": "doctrine/inflector", - "version": "2.0.6", + "name": "blade-ui-kit/blade-icons", + "version": "1.5.3", "source": { "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" + "url": "https://github.com/blade-ui-kit/blade-icons.git", + "reference": "b5e6603218e2347ac81cb780bc6f71c8c3b31f5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "url": "https://api.github.com/repos/blade-ui-kit/blade-icons/zipball/b5e6603218e2347ac81cb780bc6f71c8c3b31f5b", + "reference": "b5e6603218e2347ac81cb780bc6f71c8c3b31f5b", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "illuminate/contracts": "^8.0|^9.0|^10.0", + "illuminate/filesystem": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "illuminate/view": "^8.0|^9.0|^10.0", + "php": "^7.4|^8.0", + "symfony/console": "^5.3|^6.0", + "symfony/finder": "^5.3|^6.0" }, "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25" + "mockery/mockery": "^1.3", + "orchestra/testbench": "^6.0|^7.0|^8.0", + "phpunit/phpunit": "^9.0" }, + "bin": [ + "bin/blade-icons-generate" + ], "type": "library", + "extra": { + "laravel": { + "providers": [ + "BladeUI\\Icons\\BladeIconsServiceProvider" + ] + } + }, "autoload": { + "files": [ + "src/helpers.php" + ], "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "BladeUI\\Icons\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -277,168 +349,124 @@ ], "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" + "name": "Dries Vints", + "homepage": "https://driesvints.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", + "description": "A package to easily make use of icons in your Laravel Blade views.", + "homepage": "https://github.com/blade-ui-kit/blade-icons", "keywords": [ - "inflection", - "inflector", - "lowercase", - "manipulation", - "php", - "plural", - "singular", - "strings", - "uppercase", - "words" + "blade", + "icons", + "laravel", + "svg" ], "support": { - "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.6" + "issues": "https://github.com/blade-ui-kit/blade-icons/issues", + "source": "https://github.com/blade-ui-kit/blade-icons" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" + "url": "https://github.com/sponsors/driesvints", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" + "url": "https://www.paypal.com/paypalme/driesvints", + "type": "paypal" } ], - "time": "2022-10-20T09:10:12+00:00" + "time": "2023-10-18T10:50:13+00:00" }, { - "name": "doctrine/lexer", - "version": "3.0.0", + "name": "brick/math", + "version": "0.11.0", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "84a527db05647743d50373e0ec53a152f2cde568" + "url": "https://github.com/brick/math.git", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", - "reference": "84a527db05647743d50373e0ec53a152f2cde568", + "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.0" }, "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.0" + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "5.0.0" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" + "Brick\\Math\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "description": "Arbitrary-precision arithmetic library", "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" ], "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.0" + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.11.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%2Flexer", - "type": "tidelift" + "url": "https://github.com/BenMorel", + "type": "github" } ], - "time": "2022-12-15T16:57:16+00:00" + "time": "2023-01-15T23:15:59+00:00" }, { - "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "name": "bugsnag/bugsnag", + "version": "v3.29.1", "source": { "type": "git", - "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "url": "https://github.com/bugsnag/bugsnag-php.git", + "reference": "7fff8512b237a57323f600975ada6376e2b912c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/bugsnag/bugsnag-php/zipball/7fff8512b237a57323f600975ada6376e2b912c1", + "reference": "7fff8512b237a57323f600975ada6376e2b912c1", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" - }, - "replace": { - "mtdowling/cron-expression": "^1.0" + "composer/ca-bundle": "^1.0", + "guzzlehttp/guzzle": "^5.0|^6.0|^7.0", + "php": ">=5.5" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "guzzlehttp/psr7": "^1.3", + "mtdowling/burgomaster": "dev-master#72151eddf5f0cf101502b94bf5031f9c53501a04", + "php-mock/php-mock-phpunit": "^1.1|^2.1", + "phpunit/phpunit": "^4.8.36|^7.5.15|^9.3.10", + "sebastian/version": ">=1.0.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.20-dev" + } + }, "autoload": { "psr-4": { - "Cron\\": "src/Cron/" + "Bugsnag\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -447,63 +475,61 @@ ], "authors": [ { - "name": "Chris Tankersley", - "email": "chris@ctankersley.com", - "homepage": "https://github.com/dragonmantank" + "name": "James Smith", + "email": "notifiers@bugsnag.com", + "homepage": "https://bugsnag.com" } ], - "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "description": "Official Bugsnag notifier for PHP applications.", + "homepage": "https://github.com/bugsnag/bugsnag-php", "keywords": [ - "cron", - "schedule" + "bugsnag", + "errors", + "exceptions", + "logging", + "tracking" ], "support": { - "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "issues": "https://github.com/bugsnag/bugsnag-php/issues", + "source": "https://github.com/bugsnag/bugsnag-php/tree/v3.29.1" }, - "funding": [ - { - "url": "https://github.com/dragonmantank", - "type": "github" - } - ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-05-10T11:07:22+00:00" }, { - "name": "egulias/email-validator", - "version": "4.0.1", + "name": "bugsnag/bugsnag-laravel", + "version": "v2.26.0", "source": { "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff" + "url": "https://github.com/bugsnag/bugsnag-laravel.git", + "reference": "333a912e38ead3e02724381093778b271168c8a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff", - "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff", + "url": "https://api.github.com/repos/bugsnag/bugsnag-laravel/zipball/333a912e38ead3e02724381093778b271168c8a3", + "reference": "333a912e38ead3e02724381093778b271168c8a3", "shasum": "" }, "require": { - "doctrine/lexer": "^2.0 || ^3.0", - "php": ">=8.1", - "symfony/polyfill-intl-idn": "^1.26" + "bugsnag/bugsnag": "^3.29.0", + "bugsnag/bugsnag-psr-logger": "^1.4|^2.0", + "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "monolog/monolog": "^1.12|^2.0|^3.0", + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^4.30" - }, - "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + "orchestra/testbench": "^3.1|^4.0|^5.0|^6.0|^7.0|^8.0", + "phpunit/phpunit": "^4.8.36|^6.3.1|^7.5.15|^8.3.5|^9.3.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "2.18-dev" } }, "autoload": { "psr-4": { - "Egulias\\EmailValidator\\": "src" + "Bugsnag\\BugsnagLaravel\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -512,62 +538,54 @@ ], "authors": [ { - "name": "Eduardo Gulias Davis" + "name": "James Smith", + "email": "notifiers@bugsnag.com" } ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", + "description": "Official Bugsnag notifier for Laravel applications.", + "homepage": "https://github.com/bugsnag/bugsnag-laravel", "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" + "bugsnag", + "errors", + "exceptions", + "laravel", + "logging", + "tracking" ], "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.1" + "issues": "https://github.com/bugsnag/bugsnag-laravel/issues", + "source": "https://github.com/bugsnag/bugsnag-laravel/tree/v2.26.0" }, - "funding": [ - { - "url": "https://github.com/egulias", - "type": "github" - } - ], - "time": "2023-01-14T14:17:03+00:00" + "time": "2023-02-16T08:48:39+00:00" }, { - "name": "fruitcake/php-cors", - "version": "v1.2.0", + "name": "bugsnag/bugsnag-psr-logger", + "version": "v2.0.0", "source": { "type": "git", - "url": "https://github.com/fruitcake/php-cors.git", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e" + "url": "https://github.com/bugsnag/bugsnag-psr-logger.git", + "reference": "6c1126e28edcfb7c1da4b492eed883011a09416a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/58571acbaa5f9f462c9c77e911700ac66f446d4e", - "reference": "58571acbaa5f9f462c9c77e911700ac66f446d4e", + "url": "https://api.github.com/repos/bugsnag/bugsnag-psr-logger/zipball/6c1126e28edcfb7c1da4b492eed883011a09416a", + "reference": "6c1126e28edcfb7c1da4b492eed883011a09416a", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6" + "bugsnag/bugsnag": "^3.10", + "php": ">=8.0", + "psr/log": "^2.0|^3.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "graham-campbell/testbench-core": "^1.1", + "mockery/mockery": "^1.3.1", + "phpunit/phpunit": "^9.4.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.1-dev" - } - }, "autoload": { "psr-4": { - "Fruitcake\\Cors\\": "src/" + "Bugsnag\\PsrLogger\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -576,62 +594,56 @@ ], "authors": [ { - "name": "Fruitcake", - "homepage": "https://fruitcake.nl" - }, - { - "name": "Barryvdh", - "email": "barryvdh@gmail.com" + "name": "James Smith", + "email": "notifiers@bugsnag.com", + "homepage": "https://bugsnag.com" } ], - "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", - "homepage": "https://github.com/fruitcake/php-cors", + "description": "Official Bugsnag PHP PSR Logger.", + "homepage": "https://github.com/bugsnag/bugsnag-psr", "keywords": [ - "cors", - "laravel", - "symfony" + "bugsnag", + "errors", + "exceptions", + "logging", + "psr", + "tracking" ], "support": { - "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.2.0" + "issues": "https://github.com/bugsnag/bugsnag-psr-logger/issues", + "source": "https://github.com/bugsnag/bugsnag-psr-logger/tree/v2.0.0" }, - "funding": [ - { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], - "time": "2022-02-20T15:07:15+00:00" + "time": "2022-01-12T11:07:19+00:00" }, { - "name": "graham-campbell/result-type", - "version": "v1.1.1", + "name": "carbonphp/carbon-doctrine-types", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", - "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/67a77972b9f398ae7068dabacc39c08aeee170d5", + "reference": "67a77972b9f398ae7068dabacc39c08aeee170d5", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.1" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.7.0 || >=4.0.0" }, "require-dev": { - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "doctrine/dbal": "^3.7.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" }, "type": "library", "autoload": { "psr-4": { - "GrahamCampbell\\ResultType\\": "src/" + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" } }, "notification-url": "https://packagist.org/downloads/", @@ -640,86 +652,68 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" } ], - "description": "An Implementation Of The Result Type", + "description": "Types to use Carbon in Doctrine", "keywords": [ - "Graham Campbell", - "GrahamCampbell", - "Result Type", - "Result-Type", - "result" + "carbon", + "date", + "datetime", + "doctrine", + "time" ], "support": { - "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1" + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.0.0" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", + "url": "https://github.com/kylekatarnls", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", "type": "tidelift" } ], - "time": "2023-02-25T20:23:15+00:00" + "time": "2023-10-01T14:29:01+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "7.7.0", + "name": "cboden/ratchet", + "version": "v0.4.4", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" + "url": "https://github.com/ratchetphp/Ratchet.git", + "reference": "5012dc954541b40c5599d286fd40653f5716a38f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", + "url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/5012dc954541b40c5599d286fd40653f5716a38f", + "reference": "5012dc954541b40c5599d286fd40653f5716a38f", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=5.4.2", + "ratchet/rfc6455": "^0.3.1", + "react/event-loop": ">=0.4", + "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5", + "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0|^6.0", + "symfony/routing": "^2.6|^3.0|^4.0|^5.0|^6.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" + "phpunit/phpunit": "~4.8" }, "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\": "src/" + "Ratchet\\": "src/Ratchet" } }, "notification-url": "https://packagist.org/downloads/", @@ -728,104 +722,65 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" }, { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Matt Bonneau", + "role": "Developer" } ], - "description": "Guzzle is a PHP HTTP client library", + "description": "PHP WebSocket library", + "homepage": "http://socketo.me", "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" + "Ratchet", + "WebSockets", + "server", + "sockets", + "websocket" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/Ratchet/issues", + "source": "https://github.com/ratchetphp/Ratchet/tree/v0.4.4" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2023-05-21T14:04:53+00:00" + "time": "2021-12-14T00:20:41+00:00" }, { - "name": "guzzlehttp/promises", - "version": "2.0.0", + "name": "composer/ca-bundle", + "version": "1.3.7", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" + "url": "https://github.com/composer/ca-bundle.git", + "reference": "76e46335014860eec1aa5a724799a00a2e47cc85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/76e46335014860eec1aa5a724799a00a2e47cc85", + "reference": "76e46335014860eec1aa5a724799a00a2e47cc85", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "phpstan/phpstan": "^0.12.55", + "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "type": "library", "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false + "branch-alias": { + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Promise\\": "src/" + "Composer\\CaBundle\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -834,92 +789,76 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Guzzle promises library", + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", "keywords": [ - "promise" + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" ], "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.0" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.3.7" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "url": "https://packagist.com", + "type": "custom" }, { - "url": "https://github.com/Nyholm", + "url": "https://github.com/composer", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2023-05-21T13:50:22+00:00" + "time": "2023-08-30T09:31:38+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "2.5.0", + "name": "composer/class-map-generator", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6" + "url": "https://github.com/composer/class-map-generator.git", + "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/953cc4ea32e0c31f2185549c7d216d7921f03da9", + "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + "phpstan/phpstan": "^1.6", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/filesystem": "^5.4 || ^6", + "symfony/phpunit-bridge": "^5" }, "type": "library", "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false + "branch-alias": { + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" + "Composer\\ClassMapGenerator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -928,103 +867,102 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "Utilities to scan PHP code and generate class maps.", "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" + "classmap" ], "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.5.0" + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.1.0" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "url": "https://packagist.com", + "type": "custom" }, { - "url": "https://github.com/Nyholm", + "url": "https://github.com/composer", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2023-04-17T16:11:26+00:00" + "time": "2023-06-30T13:58:57+00:00" }, { - "name": "guzzlehttp/uri-template", - "version": "v1.0.1", + "name": "composer/composer", + "version": "2.6.4", "source": { "type": "git", - "url": "https://github.com/guzzle/uri-template.git", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2" + "url": "https://github.com/composer/composer.git", + "reference": "d75d17c16a863438027d1d96401cddcd6aa5bb60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2", + "url": "https://api.github.com/repos/composer/composer/zipball/d75d17c16a863438027d1d96401cddcd6aa5bb60", + "reference": "d75d17c16a863438027d1d96401cddcd6aa5bb60", "shasum": "" }, "require": { + "composer/ca-bundle": "^1.0", + "composer/class-map-generator": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/pcre": "^2.1 || ^3.1", + "composer/semver": "^3.2.5", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^5.2.11", "php": "^7.2.5 || ^8.0", - "symfony/polyfill-php80": "^1.17" + "psr/log": "^1.0 || ^2.0 || ^3.0", + "react/promise": "^2.8 || ^3", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "seld/signal-handler": "^2.0", + "symfony/console": "^5.4.11 || ^6.0.11 || ^7", + "symfony/filesystem": "^5.4 || ^6.0 || ^7", + "symfony/finder": "^5.4 || ^6.0 || ^7", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "symfony/process": "^5.4 || ^6.0 || ^7" }, "require-dev": { - "phpunit/phpunit": "^8.5.19 || ^9.5.8", - "uri-template/tests": "1.0.0" + "phpstan/phpstan": "^1.9.3", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1", + "phpstan/phpstan-symfony": "^1.2.10", + "symfony/phpunit-bridge": "^6.0 || ^7" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" }, + "bin": [ + "bin/composer" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.6-dev" + }, + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] } }, "autoload": { "psr-4": { - "GuzzleHttp\\UriTemplate\\": "src" + "Composer\\": "src/Composer/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1033,93 +971,76 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" }, { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "A polyfill class for uri_template of PHP", + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", "keywords": [ - "guzzlehttp", - "uri-template" + "autoload", + "dependency", + "package" ], "support": { - "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.1" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy", + "source": "https://github.com/composer/composer/tree/2.6.4" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "url": "https://packagist.com", + "type": "custom" }, { - "url": "https://github.com/Nyholm", + "url": "https://github.com/composer", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2021-10-07T12:57:01+00:00" + "time": "2023-09-29T08:54:47+00:00" }, { - "name": "inertiajs/inertia-laravel", - "version": "v0.6.9", + "name": "composer/metadata-minifier", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/inertiajs/inertia-laravel.git", - "reference": "b983c6eb2fe7460df6170060cdd7b47b5ef6832a" + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/b983c6eb2fe7460df6170060cdd7b47b5ef6832a", - "reference": "b983c6eb2fe7460df6170060cdd7b47b5ef6832a", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", "shasum": "" }, "require": { - "ext-json": "*", - "laravel/framework": "^6.0|^7.0|^8.74|^9.0|^10.0", - "php": "^7.2|~8.0.0|~8.1.0|~8.2.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^1.3.3", - "orchestra/testbench": "^4.0|^5.0|^6.4|^7.0|^8.0", - "phpunit/phpunit": "^8.0|^9.5.8", - "roave/security-advisories": "dev-master" - }, - "suggest": { - "ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command." + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { - "laravel": { - "providers": [ - "Inertia\\ServiceProvider" - ] + "branch-alias": { + "dev-main": "1.x-dev" } }, "autoload": { - "files": [ - "./helpers.php" - ], "psr-4": { - "Inertia\\": "src" + "Composer\\MetadataMinifier\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1128,52 +1049,67 @@ ], "authors": [ { - "name": "Jonathan Reinink", - "email": "jonathan@reinink.ca", - "homepage": "https://reinink.ca" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "The Laravel adapter for Inertia.js.", + "description": "Small utility library that handles metadata minification and expansion.", "keywords": [ - "inertia", - "laravel" + "composer", + "compression" ], "support": { - "issues": "https://github.com/inertiajs/inertia-laravel/issues", - "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.6.9" + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" }, "funding": [ { - "url": "https://github.com/reinink", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2023-01-17T01:02:51+00:00" + "time": "2021-04-07T13:37:33+00:00" }, { - "name": "jaybizzle/crawler-detect", - "version": "v1.2.115", + "name": "composer/pcre", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "4531e4a70d55d10cbe7d41ac1ff0d75a5fe2ef1e" + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/4531e4a70d55d10cbe7d41ac1ff0d75a5fe2ef1e", - "reference": "4531e4a70d55d10cbe7d41ac1ff0d75a5fe2ef1e", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4" + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Jaybizzle\\CrawlerDetect\\": "src/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1182,69 +1118,68 @@ ], "authors": [ { - "name": "Mark Beech", - "email": "m@rkbee.ch", - "role": "Developer" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent", - "homepage": "https://github.com/JayBizzle/Crawler-Detect/", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "crawler", - "crawler detect", - "crawler detector", - "crawlerdetect", - "php crawler detect" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", - "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.115" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" }, - "time": "2023-06-05T21:32:18+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" }, { - "name": "jenssegers/agent", - "version": "v2.6.4", + "name": "composer/semver", + "version": "3.4.0", "source": { "type": "git", - "url": "https://github.com/jenssegers/agent.git", - "reference": "daa11c43729510b3700bc34d414664966b03bffe" + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jenssegers/agent/zipball/daa11c43729510b3700bc34d414664966b03bffe", - "reference": "daa11c43729510b3700bc34d414664966b03bffe", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { - "jaybizzle/crawler-detect": "^1.2", - "mobiledetect/mobiledetectlib": "^2.7.6", - "php": ">=5.6" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5.0|^6.0|^7.0" - }, - "suggest": { - "illuminate/support": "Required for laravel service providers" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" - }, - "laravel": { - "providers": [ - "Jenssegers\\Agent\\AgentServiceProvider" - ], - "aliases": { - "Agent": "Jenssegers\\Agent\\Facades\\Agent" - } + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Jenssegers\\Agent\\": "src/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1253,71 +1188,79 @@ ], "authors": [ { - "name": "Jens Segers", - "homepage": "https://jenssegers.com" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "Desktop/mobile user agent parser with support for Laravel, based on Mobiledetect", - "homepage": "https://github.com/jenssegers/agent", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "Agent", - "browser", - "desktop", - "laravel", - "mobile", - "platform", - "user agent", - "useragent" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/jenssegers/agent/issues", - "source": "https://github.com/jenssegers/agent/tree/v2.6.4" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { - "url": "https://github.com/jenssegers", - "type": "github" + "url": "https://packagist.com", + "type": "custom" }, { - "url": "https://tidelift.com/funding/github/packagist/jenssegers/agent", + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2020-06-13T08:05:20+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { - "name": "laravel/forge-sdk", - "version": "v3.13.5", + "name": "composer/spdx-licenses", + "version": "1.5.7", "source": { "type": "git", - "url": "https://github.com/laravel/forge-sdk.git", - "reference": "eb06f7eddf0b88d473f5328f8be2cfebbd4f0fb9" + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "c848241796da2abf65837d51dce1fae55a960149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/forge-sdk/zipball/eb06f7eddf0b88d473f5328f8be2cfebbd4f0fb9", - "reference": "eb06f7eddf0b88d473f5328f8be2cfebbd4f0fb9", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/c848241796da2abf65837d51dce1fae55a960149", + "reference": "c848241796da2abf65837d51dce1fae55a960149", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/guzzle": "^6.3.1|^7.0", - "php": "^7.2|^8.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^1.3.1", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.4|^9.0" + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "Laravel\\Forge\\": "src/" + "Composer\\Spdx\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1326,74 +1269,76 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Mohamed Said", - "email": "mohamed@laravel.com" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" }, { - "name": "Dries Vints", - "email": "dries@laravel.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" }, { - "name": "James Brooks", - "email": "james@laravel.com" + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "The official Laravel Forge PHP SDK.", + "description": "SPDX licenses list and validation library.", "keywords": [ - "forge", - "laravel" + "license", + "spdx", + "validator" ], "support": { - "issues": "https://github.com/laravel/forge-sdk/issues", - "source": "https://github.com/laravel/forge-sdk" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.7" }, - "time": "2023-05-16T06:50:53+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-05-23T07:37:50+00:00" }, { - "name": "laravel/fortify", - "version": "v1.17.3", + "name": "composer/xdebug-handler", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/laravel/fortify.git", - "reference": "e99f7cb135bb6e05e4c49e9224c9c9a33c27cfa0" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/e99f7cb135bb6e05e4c49e9224c9c9a33c27cfa0", - "reference": "e99f7cb135bb6e05e4c49e9224c9c9a33c27cfa0", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", "shasum": "" }, "require": { - "bacon/bacon-qr-code": "^2.0", - "ext-json": "*", - "illuminate/support": "^8.82|^9.0|^10.0", - "php": "^7.3|^8.0", - "pragmarx/google2fa": "^7.0|^8.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Fortify\\FortifyServiceProvider" - ] - } - }, "autoload": { "psr-4": { - "Laravel\\Fortify\\": "src/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1402,197 +1347,61 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Backend controllers and scaffolding for Laravel authentication.", + "description": "Restarts a process without Xdebug.", "keywords": [ - "auth", - "laravel" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/laravel/fortify/issues", - "source": "https://github.com/laravel/fortify" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" }, - "time": "2023-06-02T12:58:20+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" }, { - "name": "laravel/framework", - "version": "v10.13.2", + "name": "danharrin/date-format-converter", + "version": "v0.3.0", "source": { "type": "git", - "url": "https://github.com/laravel/framework.git", - "reference": "fd4619b56b56308e2c2c1840fedd0b8ebb73dfc5" + "url": "https://github.com/danharrin/date-format-converter.git", + "reference": "42b6ddc52059d4ba228a67c15adaaa0c039e75f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/fd4619b56b56308e2c2c1840fedd0b8ebb73dfc5", - "reference": "fd4619b56b56308e2c2c1840fedd0b8ebb73dfc5", + "url": "https://api.github.com/repos/danharrin/date-format-converter/zipball/42b6ddc52059d4ba228a67c15adaaa0c039e75f2", + "reference": "42b6ddc52059d4ba228a67c15adaaa0c039e75f2", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11", - "composer-runtime-api": "^2.2", - "doctrine/inflector": "^2.0.5", - "dragonmantank/cron-expression": "^3.3.2", - "egulias/email-validator": "^3.2.1|^4.0", - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-session": "*", - "ext-tokenizer": "*", - "fruitcake/php-cors": "^1.2", - "guzzlehttp/uri-template": "^1.0", - "laravel/serializable-closure": "^1.3", - "league/commonmark": "^2.2.1", - "league/flysystem": "^3.8.0", - "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.62.1", - "nunomaduro/termwind": "^1.13", - "php": "^8.1", - "psr/container": "^1.1.1|^2.0.1", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", - "ramsey/uuid": "^4.7", - "symfony/console": "^6.2", - "symfony/error-handler": "^6.2", - "symfony/finder": "^6.2", - "symfony/http-foundation": "^6.2", - "symfony/http-kernel": "^6.2", - "symfony/mailer": "^6.2", - "symfony/mime": "^6.2", - "symfony/process": "^6.2", - "symfony/routing": "^6.2", - "symfony/uid": "^6.2", - "symfony/var-dumper": "^6.2", - "tijsverkoyen/css-to-inline-styles": "^2.2.5", - "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^2.0" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "provide": { - "psr/container-implementation": "1.1|2.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0" - }, - "replace": { - "illuminate/auth": "self.version", - "illuminate/broadcasting": "self.version", - "illuminate/bus": "self.version", - "illuminate/cache": "self.version", - "illuminate/collections": "self.version", - "illuminate/conditionable": "self.version", - "illuminate/config": "self.version", - "illuminate/console": "self.version", - "illuminate/container": "self.version", - "illuminate/contracts": "self.version", - "illuminate/cookie": "self.version", - "illuminate/database": "self.version", - "illuminate/encryption": "self.version", - "illuminate/events": "self.version", - "illuminate/filesystem": "self.version", - "illuminate/hashing": "self.version", - "illuminate/http": "self.version", - "illuminate/log": "self.version", - "illuminate/macroable": "self.version", - "illuminate/mail": "self.version", - "illuminate/notifications": "self.version", - "illuminate/pagination": "self.version", - "illuminate/pipeline": "self.version", - "illuminate/process": "self.version", - "illuminate/queue": "self.version", - "illuminate/redis": "self.version", - "illuminate/routing": "self.version", - "illuminate/session": "self.version", - "illuminate/support": "self.version", - "illuminate/testing": "self.version", - "illuminate/translation": "self.version", - "illuminate/validation": "self.version", - "illuminate/view": "self.version" - }, - "require-dev": { - "ably/ably-php": "^1.0", - "aws/aws-sdk-php": "^3.235.5", - "doctrine/dbal": "^3.5.1", - "ext-gmp": "*", - "fakerphp/faker": "^1.21", - "guzzlehttp/guzzle": "^7.5", - "league/flysystem-aws-s3-v3": "^3.0", - "league/flysystem-ftp": "^3.0", - "league/flysystem-path-prefixing": "^3.3", - "league/flysystem-read-only": "^3.3", - "league/flysystem-sftp-v3": "^3.0", - "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.4", - "pda/pheanstalk": "^4.0", - "phpstan/phpdoc-parser": "^1.15", - "phpstan/phpstan": "^1.4.7", - "phpunit/phpunit": "^10.0.7", - "predis/predis": "^2.0.2", - "symfony/cache": "^6.2", - "symfony/http-client": "^6.2.4" - }, - "suggest": { - "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", - "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", - "ext-apcu": "Required to use the APC cache driver.", - "ext-fileinfo": "Required to use the Filesystem class.", - "ext-ftp": "Required to use the Flysystem FTP driver.", - "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", - "ext-memcached": "Required to use the memcache cache driver.", - "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", - "ext-pdo": "Required to use all database features.", - "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", - "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", - "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", - "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", - "league/flysystem-read-only": "Required to use read-only disks (^3.3)", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", - "mockery/mockery": "Required to use mocking (^1.5.1).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", - "predis/predis": "Required to use the predis connector (^2.0.2).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + "php": "^7.2|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "10.x-dev" - } - }, "autoload": { "files": [ - "src/Illuminate/Collections/helpers.php", - "src/Illuminate/Events/functions.php", - "src/Illuminate/Foundation/helpers.php", - "src/Illuminate/Support/helpers.php" + "src/helpers.php", + "src/standards.php" ], "psr-4": { - "Illuminate\\": "src/Illuminate/", - "Illuminate\\Support\\": [ - "src/Illuminate/Macroable/", - "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/" - ] + "DanHarrin\\DateFormatConverter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1601,77 +1410,52 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Dan Harrin", + "email": "dan@danharrin.com" } ], - "description": "The Laravel Framework.", - "homepage": "https://laravel.com", - "keywords": [ - "framework", - "laravel" - ], + "description": "Convert token-based date formats between standards.", + "homepage": "https://github.com/danharrin/date-format-converter", "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" + "issues": "https://github.com/danharrin/date-format-converter/issues", + "source": "https://github.com/danharrin/date-format-converter" }, - "time": "2023-06-05T15:48:15+00:00" - }, - { - "name": "laravel/horizon", - "version": "v5.16.1", + "funding": [ + { + "url": "https://github.com/danharrin", + "type": "github" + } + ], + "time": "2022-09-29T07:48:20+00:00" + }, + { + "name": "danharrin/livewire-rate-limiting", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/laravel/horizon.git", - "reference": "ddd6a49063ba3bdfdf5f8ce885d27a8368ad03c4" + "url": "https://github.com/danharrin/livewire-rate-limiting.git", + "reference": "bc2cc0a0b5b517fdc5bba8671013dd71081f70a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/ddd6a49063ba3bdfdf5f8ce885d27a8368ad03c4", - "reference": "ddd6a49063ba3bdfdf5f8ce885d27a8368ad03c4", + "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/bc2cc0a0b5b517fdc5bba8671013dd71081f70a8", + "reference": "bc2cc0a0b5b517fdc5bba8671013dd71081f70a8", "shasum": "" }, "require": { - "ext-json": "*", - "ext-pcntl": "*", - "ext-posix": "*", - "illuminate/contracts": "^8.17|^9.0|^10.0", - "illuminate/queue": "^8.17|^9.0|^10.0", - "illuminate/support": "^8.17|^9.0|^10.0", - "nesbot/carbon": "^2.17", - "php": "^7.3|^8.0", - "ramsey/uuid": "^4.0", - "symfony/error-handler": "^5.0|^6.0", - "symfony/process": "^5.0|^6.0" + "illuminate/support": "^9.0|^10.0", + "php": "^8.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.0", - "predis/predis": "^1.1|^2.0" - }, - "suggest": { - "ext-redis": "Required to use the Redis PHP driver.", - "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0)." + "livewire/livewire": "^3.0", + "livewire/volt": "^1.3", + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.0|^10.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Horizon\\HorizonServiceProvider" - ], - "aliases": { - "Horizon": "Laravel\\Horizon\\Horizon" - } - } - }, "autoload": { "psr-4": { - "Laravel\\Horizon\\": "src/" + "DanHarrin\\LivewireRateLimiting\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1680,132 +1464,107 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Dan Harrin", + "email": "dan@danharrin.com" } ], - "description": "Dashboard and code-driven configuration for Laravel queues.", - "keywords": [ - "laravel", - "queue" - ], + "description": "Apply rate limiters to Laravel Livewire actions.", + "homepage": "https://github.com/danharrin/livewire-rate-limiting", "support": { - "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.16.1" + "issues": "https://github.com/danharrin/livewire-rate-limiting/issues", + "source": "https://github.com/danharrin/livewire-rate-limiting" }, - "time": "2023-05-29T15:03:42+00:00" + "funding": [ + { + "url": "https://github.com/danharrin", + "type": "github" + } + ], + "time": "2023-10-27T15:01:19+00:00" }, { - "name": "laravel/jetstream", - "version": "v3.2.2", + "name": "dasprid/enum", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/laravel/jetstream.git", - "reference": "6ca907084102b567645b59a7114889b0a0a992cf" + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/jetstream/zipball/6ca907084102b567645b59a7114889b0a0a992cf", - "reference": "6ca907084102b567645b59a7114889b0a0a992cf", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", "shasum": "" }, "require": { - "ext-json": "*", - "illuminate/console": "^10.0", - "illuminate/support": "^10.0", - "jenssegers/agent": "^2.6", - "laravel/fortify": "^1.15", - "php": "^8.1.0" + "php": ">=7.1 <9.0" }, "require-dev": { - "inertiajs/inertia-laravel": "^0.6.5", - "laravel/sanctum": "^3.0", - "livewire/livewire": "^2.12", - "mockery/mockery": "^1.0", - "orchestra/testbench": "^8.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^7 | ^8 | ^9", + "squizlabs/php_codesniffer": "*" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Jetstream\\JetstreamServiceProvider" - ] - } - }, "autoload": { "psr-4": { - "Laravel\\Jetstream\\": "src/" + "DASPRiD\\Enum\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-2-Clause" ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" } ], - "description": "Tailwind scaffolding for the Laravel framework.", + "description": "PHP 7.1 enum implementation", "keywords": [ - "auth", - "laravel", - "tailwind" + "enum", + "map" ], "support": { - "issues": "https://github.com/laravel/jetstream/issues", - "source": "https://github.com/laravel/jetstream" + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" }, - "time": "2023-05-30T14:24:35+00:00" + "time": "2023-08-25T16:18:39+00:00" }, { - "name": "laravel/sanctum", - "version": "v3.2.5", + "name": "dflydev/dot-access-data", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/laravel/sanctum.git", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876" + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "f41715465d65213d644d3141a6a93081be5d3549" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/8ebda85d59d3c414863a7f4d816ef8302faad876", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", + "reference": "f41715465d65213d644d3141a6a93081be5d3549", "shasum": "" }, "require": { - "ext-json": "*", - "illuminate/console": "^9.21|^10.0", - "illuminate/contracts": "^9.21|^10.0", - "illuminate/database": "^9.21|^10.0", - "illuminate/support": "^9.21|^10.0", - "php": "^8.0.2" + "php": "^7.1 || ^8.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.0|^8.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Sanctum\\SanctumServiceProvider" - ] + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Laravel\\Sanctum\\": "src/" + "Dflydev\\DotAccessData\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1814,54 +1573,72 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" } ], - "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", "keywords": [ - "auth", - "laravel", - "sanctum" + "access", + "data", + "dot", + "notation" ], "support": { - "issues": "https://github.com/laravel/sanctum/issues", - "source": "https://github.com/laravel/sanctum" + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" }, - "time": "2023-05-01T19:39:51+00:00" + "time": "2022-10-27T11:44:00+00:00" }, { - "name": "laravel/serializable-closure", - "version": "v1.3.0", + "name": "doctrine/cache", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "nesbot/carbon": "^2.61", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "psr-4": { - "Laravel\\SerializableClosure\\": "src/" + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } }, "notification-url": "https://packagist.org/downloads/", @@ -1870,69 +1647,106 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Nuno Maduro", - "email": "nuno@laravel.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": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ - "closure", - "laravel", - "serializable" + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" ], "support": { - "issues": "https://github.com/laravel/serializable-closure/issues", - "source": "https://github.com/laravel/serializable-closure" + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" }, - "time": "2023-01-30T18:31:20+00:00" + "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%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" }, { - "name": "laravel/tinker", - "version": "v2.8.1", + "name": "doctrine/dbal", + "version": "3.7.2", "source": { "type": "git", - "url": "https://github.com/laravel/tinker.git", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10" + "url": "https://github.com/doctrine/dbal.git", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/0ac3c270590e54910715e9a1a044cc368df282b2", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", - "php": "^7.2.5|^8.0", - "psy/psysh": "^0.10.4|^0.11.1", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0" + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" }, "require-dev": { - "mockery/mockery": "~1.3.3|^1.4.2", - "phpunit/phpunit": "^8.5.8|^9.3.3" + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.42", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "9.6.13", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0)." + "symfony/console": "For helpful console commands such as SQL execution and import of files." }, + "bin": [ + "bin/doctrine-dbal" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Tinker\\TinkerServiceProvider" - ] - } - }, "autoload": { "psr-4": { - "Laravel\\Tinker\\": "src/" + "Doctrine\\DBAL\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1941,257 +1755,321 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "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" } ], - "description": "Powerful REPL for the Laravel framework.", + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", "keywords": [ - "REPL", - "Tinker", - "laravel", - "psysh" + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" ], "support": { - "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.8.1" + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.7.2" }, - "time": "2023-02-15T16:40:09+00:00" + "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%2Fdbal", + "type": "tidelift" + } + ], + "time": "2023-11-19T08:06:58+00:00" }, { - "name": "league/commonmark", - "version": "2.4.0", + "name": "doctrine/deprecations", + "version": "1.1.2", "source": { "type": "git", - "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { - "ext-mbstring": "*", - "league/config": "^1.1.1", - "php": "^7.4 || ^8.0", - "psr/event-dispatcher": "^1.0", - "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/polyfill-php80": "^1.16" + "php": "^7.1 || ^8.0" }, "require-dev": { - "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.0", - "commonmark/commonmark.js": "0.30.0", - "composer/package-versions-deprecated": "^1.8", - "embed/embed": "^4.4", - "erusev/parsedown": "^1.0", - "ext-json": "*", - "github/gfm": "0.29.0", - "michelf/php-markdown": "^1.4 || ^2.0", - "nyholm/psr7": "^1.5", - "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.21", - "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", - "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { - "symfony/yaml": "v2.3+ required if using the Front Matter extension" + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" } }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.28" + }, + "type": "library", "autoload": { "psr-4": { - "League\\CommonMark\\": "src" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Lead Developer" + "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" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" } ], - "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", - "homepage": "https://commonmark.thephpleague.com", + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", "keywords": [ - "commonmark", - "flavored", - "gfm", - "github", - "github-flavored", - "markdown", - "md", - "parser" + "event", + "event dispatcher", + "event manager", + "event system", + "events" ], "support": { - "docs": "https://commonmark.thephpleague.com/", - "forum": "https://github.com/thephpleague/commonmark/discussions", - "issues": "https://github.com/thephpleague/commonmark/issues", - "rss": "https://github.com/thephpleague/commonmark/releases.atom", - "source": "https://github.com/thephpleague/commonmark" + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.0" }, "funding": [ { - "url": "https://www.colinodell.com/sponsor", - "type": "custom" - }, - { - "url": "https://www.paypal.me/colinpodell/10.00", + "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { - "url": "https://github.com/colinodell", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "time": "2022-10-12T20:59:15+00:00" }, { - "name": "league/config", - "version": "v1.2.0", + "name": "doctrine/inflector", + "version": "2.0.8", "source": { "type": "git", - "url": "https://github.com/thephpleague/config.git", - "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + "url": "https://github.com/doctrine/inflector.git", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", - "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", "shasum": "" }, "require": { - "dflydev/dot-access-data": "^3.0.1", - "nette/schema": "^1.2", - "php": "^7.4 || ^8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.8.2", - "phpunit/phpunit": "^9.5.5", - "scrutinizer/ocular": "^1.8.1", - "unleashedtech/php-coding-standard": "^3.1", - "vimeo/psalm": "^4.7.3" + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.2-dev" - } - }, "autoload": { "psr-4": { - "League\\Config\\": "src" + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Lead Developer" + "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": "Define configuration arrays with strict schemas and access values with dot notation", - "homepage": "https://config.thephpleague.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": [ - "array", - "config", - "configuration", - "dot", - "dot-access", - "nested", - "schema" + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" ], "support": { - "docs": "https://config.thephpleague.com/", - "issues": "https://github.com/thephpleague/config/issues", - "rss": "https://github.com/thephpleague/config/releases.atom", - "source": "https://github.com/thephpleague/config" + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.8" }, "funding": [ { - "url": "https://www.colinodell.com/sponsor", + "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { - "url": "https://www.paypal.me/colinpodell/10.00", - "type": "custom" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "url": "https://github.com/colinodell", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" } ], - "time": "2022-12-11T20:36:23+00:00" + "time": "2023-06-16T13:40:37+00:00" }, { - "name": "league/flysystem", - "version": "3.15.1", + "name": "doctrine/lexer", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed" + "url": "https://github.com/doctrine/lexer.git", + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "league/flysystem-local": "^3.0.0", - "league/mime-type-detection": "^1.0.0", - "php": "^8.0.2" - }, - "conflict": { - "aws/aws-sdk-php": "3.209.31 || 3.210.0", - "guzzlehttp/guzzle": "<7.0", - "guzzlehttp/ringphp": "<1.1.1", - "phpseclib/phpseclib": "3.0.15", - "symfony/http-client": "<5.2" + "php": "^8.1" }, "require-dev": { - "async-aws/s3": "^1.5", - "async-aws/simple-s3": "^1.1", - "aws/aws-sdk-php": "^3.220.0", - "composer/semver": "^3.0", - "ext-fileinfo": "*", - "ext-ftp": "*", - "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.5", - "google/cloud-storage": "^1.23", - "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.14", - "phpstan/phpstan": "^0.12.26", - "phpunit/phpunit": "^9.5.11", - "sabre/dav": "^4.3.1" + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { "psr-4": { - "League\\Flysystem\\": "src" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2200,64 +2078,78 @@ ], "authors": [ { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "File storage abstraction for PHP", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "WebDAV", - "aws", - "cloud", - "file", - "files", - "filesystem", - "filesystems", - "ftp", - "s3", - "sftp", - "storage" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.15.1" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.0" }, "funding": [ { - "url": "https://ecologi.com/frankdejonge", + "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { - "url": "https://github.com/frankdejonge", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" } ], - "time": "2023-05-04T09:04:26+00:00" + "time": "2022-12-15T16:57:16+00:00" }, { - "name": "league/flysystem-local", - "version": "3.15.0", + "name": "dragonmantank/cron-expression", + "version": "v3.3.3", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3" + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "league/flysystem": "^3.0.0", - "league/mime-type-detection": "^1.0.0", - "php": "^8.0.2" + "php": "^7.2|^8.0", + "webmozart/assert": "^1.0" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", "autoload": { "psr-4": { - "League\\Flysystem\\Local\\": "" + "Cron\\": "src/Cron/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2266,61 +2158,63 @@ ], "authors": [ { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" } ], - "description": "Local filesystem adapter for Flysystem.", + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", "keywords": [ - "Flysystem", - "file", - "files", - "filesystem", - "local" + "cron", + "schedule" ], "support": { - "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0" + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { - "url": "https://ecologi.com/frankdejonge", - "type": "custom" - }, - { - "url": "https://github.com/frankdejonge", + "url": "https://github.com/dragonmantank", "type": "github" } ], - "time": "2023-05-02T20:02:14+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { - "name": "league/mime-type-detection", - "version": "1.11.0", + "name": "egulias/email-validator", + "version": "4.0.2", "source": { "type": "git", - "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, "autoload": { "psr-4": { - "League\\MimeTypeDetection\\": "src" + "Egulias\\EmailValidator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2329,55 +2223,104 @@ ], "authors": [ { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" + "name": "Eduardo Gulias Davis" } ], - "description": "Mime-type detection for Flysystem", + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], "support": { - "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" }, "funding": [ { - "url": "https://github.com/frankdejonge", + "url": "https://github.com/egulias", "type": "github" - }, + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "type": "tidelift" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "time": "2022-04-17T13:12:02+00:00" + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "mobiledetect/mobiledetectlib", - "version": "2.8.41", + "name": "facade/ignition-contracts", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1" + "url": "https://github.com/facade/ignition-contracts.git", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", - "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", + "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/3c921a1cdba35b68a7f0ccffc6dffc1995b18267", + "reference": "3c921a1cdba35b68a7f0ccffc6dffc1995b18267", "shasum": "" }, "require": { - "php": ">=5.0.0" + "php": "^7.3|^8.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35||~5.7" + "friendsofphp/php-cs-fixer": "^v2.15.8", + "phpunit/phpunit": "^9.3.11", + "vimeo/psalm": "^3.17.1" }, "type": "library", "autoload": { - "psr-0": { - "Detection": "namespaced/" - }, - "classmap": [ - "Mobile_Detect.php" - ] + "psr-4": { + "Facade\\IgnitionContracts\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2385,92 +2328,5221 @@ ], "authors": [ { - "name": "Serban Ghita", - "email": "serbanghita@gmail.com", - "homepage": "http://mobiledetect.net", + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://flareapp.io", "role": "Developer" } ], - "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", - "homepage": "https://github.com/serbanghita/Mobile-Detect", + "description": "Solution contracts for Ignition", + "homepage": "https://github.com/facade/ignition-contracts", "keywords": [ - "detect mobile devices", - "mobile", - "mobile detect", - "mobile detector", - "php mobile detect" + "contracts", + "flare", + "ignition" ], "support": { - "issues": "https://github.com/serbanghita/Mobile-Detect/issues", - "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.41" + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" }, - "time": "2022-11-08T18:31:26+00:00" + "time": "2020-10-16T08:27:54+00:00" }, { - "name": "monolog/monolog", - "version": "3.3.1", + "name": "fig/http-message-util", + "version": "1.1.5", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "9b5daeaffce5b926cac47923798bba91059e60e2" + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/9b5daeaffce5b926cac47923798bba91059e60e2", - "reference": "9b5daeaffce5b926cac47923798bba91059e60e2", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/log": "^2.0 || ^3.0" + "php": "^5.3 || ^7.0 || ^8.0" }, - "provide": { - "psr/log-implementation": "3.0.0" + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" }, - "require-dev": { - "aws/aws-sdk-php": "^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7 || ^8", - "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4.5", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^9.5.26", - "predis/predis": "^1.1 || ^2", - "ruflin/elastica": "^7", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "ext-openssl": "Required to send log messages using SSL", - "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "filament/actions", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/actions.git", + "reference": "1c913429592cbd0f6228e8e4738e5eb3df8cb325" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/actions/zipball/1c913429592cbd0f6228e8e4738e5eb3df8cb325", + "reference": "1c913429592cbd0f6228e8e4738e5eb3df8cb325", + "shasum": "" + }, + "require": { + "filament/forms": "self.version", + "filament/infolists": "self.version", + "filament/notifications": "self.version", + "filament/support": "self.version", + "illuminate/contracts": "^10.0", + "illuminate/database": "^10.0", + "illuminate/support": "^10.0", + "league/csv": "9.11.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Actions\\ActionsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Filament\\Actions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful action modals to any Livewire component.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-10T00:21:16+00:00" + }, + { + "name": "filament/filament", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/panels.git", + "reference": "85988957d2c37b42646082654e9133d838e7d086" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/85988957d2c37b42646082654e9133d838e7d086", + "reference": "85988957d2c37b42646082654e9133d838e7d086", + "shasum": "" + }, + "require": { + "danharrin/livewire-rate-limiting": "^0.3|^1.0", + "filament/actions": "self.version", + "filament/forms": "self.version", + "filament/infolists": "self.version", + "filament/notifications": "self.version", + "filament/support": "self.version", + "filament/tables": "self.version", + "filament/widgets": "self.version", + "illuminate/auth": "^10.0", + "illuminate/console": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/cookie": "^10.0", + "illuminate/database": "^10.0", + "illuminate/http": "^10.0", + "illuminate/routing": "^10.0", + "illuminate/session": "^10.0", + "illuminate/support": "^10.0", + "illuminate/view": "^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\FilamentServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/global_helpers.php", + "src/helpers.php" + ], + "psr-4": { + "Filament\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A collection of full-stack components for accelerated Laravel app development.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-10T00:21:18+00:00" + }, + { + "name": "filament/forms", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/forms.git", + "reference": "7b04f43d152599795581cf21c96a26f6d648e0d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/7b04f43d152599795581cf21c96a26f6d648e0d6", + "reference": "7b04f43d152599795581cf21c96a26f6d648e0d6", + "shasum": "" + }, + "require": { + "danharrin/date-format-converter": "^0.3", + "filament/actions": "self.version", + "filament/support": "self.version", + "illuminate/console": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/database": "^10.0", + "illuminate/filesystem": "^10.0", + "illuminate/support": "^10.0", + "illuminate/validation": "^10.0", + "illuminate/view": "^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Forms\\FormsServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Filament\\Forms\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful forms to any Livewire component.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-10T00:21:13+00:00" + }, + { + "name": "filament/infolists", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/infolists.git", + "reference": "00798e2aa59602e9dc85f56d5d1743d16842125f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/00798e2aa59602e9dc85f56d5d1743d16842125f", + "reference": "00798e2aa59602e9dc85f56d5d1743d16842125f", + "shasum": "" + }, + "require": { + "filament/actions": "self.version", + "filament/support": "self.version", + "illuminate/console": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/database": "^10.0", + "illuminate/filesystem": "^10.0", + "illuminate/support": "^10.0", + "illuminate/view": "^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Infolists\\InfolistsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Filament\\Infolists\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful read-only infolists to any Livewire component.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-07T12:32:29+00:00" + }, + { + "name": "filament/notifications", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/notifications.git", + "reference": "13029b0f257ce9f0f9691b5fe8de933c581d03bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/notifications/zipball/13029b0f257ce9f0f9691b5fe8de933c581d03bc", + "reference": "13029b0f257ce9f0f9691b5fe8de933c581d03bc", + "shasum": "" + }, + "require": { + "filament/actions": "self.version", + "filament/support": "self.version", + "illuminate/contracts": "^10.0", + "illuminate/filesystem": "^10.0", + "illuminate/notifications": "^10.0", + "illuminate/support": "^10.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Notifications\\NotificationsServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Testing/Autoload.php" + ], + "psr-4": { + "Filament\\Notifications\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful notifications to any Livewire app.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-07T12:32:30+00:00" + }, + { + "name": "filament/support", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/support.git", + "reference": "09d33cb7462dabb249d7cc32fbafb6f567ed8cd6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/support/zipball/09d33cb7462dabb249d7cc32fbafb6f567ed8cd6", + "reference": "09d33cb7462dabb249d7cc32fbafb6f567ed8cd6", + "shasum": "" + }, + "require": { + "blade-ui-kit/blade-heroicons": "^2.0", + "doctrine/dbal": "^3.2", + "ext-intl": "*", + "illuminate/contracts": "^10.0", + "illuminate/support": "^10.0", + "illuminate/view": "^10.0", + "livewire/livewire": "^3.2.3", + "php": "^8.1", + "ryangjchandler/blade-capture-directive": "^0.2|^0.3", + "spatie/color": "^1.5", + "spatie/invade": "^1.0|^2.0", + "spatie/laravel-package-tools": "^1.9", + "symfony/html-sanitizer": "^6.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Support\\SupportServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Filament\\Support\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Core helper methods and foundation code for all Filament packages.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-10T00:21:13+00:00" + }, + { + "name": "filament/tables", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/tables.git", + "reference": "f7251ddedbe3b12c402ea4b55d307f94f13f8bca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/f7251ddedbe3b12c402ea4b55d307f94f13f8bca", + "reference": "f7251ddedbe3b12c402ea4b55d307f94f13f8bca", + "shasum": "" + }, + "require": { + "filament/actions": "self.version", + "filament/forms": "self.version", + "filament/support": "self.version", + "illuminate/console": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/database": "^10.0", + "illuminate/filesystem": "^10.0", + "illuminate/support": "^10.0", + "illuminate/view": "^10.0", + "kirschbaum-development/eloquent-power-joins": "^3.0", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Tables\\TablesServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Filament\\Tables\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful tables to any Livewire component.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-10T00:21:14+00:00" + }, + { + "name": "filament/widgets", + "version": "v3.1.18", + "source": { + "type": "git", + "url": "https://github.com/filamentphp/widgets.git", + "reference": "4a1f2e836ede27f9cc32d7ce43172c2d088376f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filamentphp/widgets/zipball/4a1f2e836ede27f9cc32d7ce43172c2d088376f8", + "reference": "4a1f2e836ede27f9cc32d7ce43172c2d088376f8", + "shasum": "" + }, + "require": { + "filament/support": "self.version", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Filament\\Widgets\\WidgetsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Filament\\Widgets\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Easily add beautiful dashboard widgets to any Livewire component.", + "homepage": "https://github.com/filamentphp/filament", + "support": { + "issues": "https://github.com/filamentphp/filament/issues", + "source": "https://github.com/filamentphp/filament" + }, + "time": "2023-12-04T15:57:33+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", + "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "symfony/http-foundation": "^4.4|^5.4|^6|^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2023-10-12T05:21:21+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", + "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2023-11-12T22:16:48+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "ext-curl": "*", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:20:53+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2023-08-03T15:11:55+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-08-27T10:13:57+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", + "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2023-12-03T19:50:20+00:00" + }, + { + "name": "inertiajs/inertia-laravel", + "version": "v0.6.10", + "source": { + "type": "git", + "url": "https://github.com/inertiajs/inertia-laravel.git", + "reference": "609f960c9392e61f8f10418e333599cf1b12efbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/609f960c9392e61f8f10418e333599cf1b12efbe", + "reference": "609f960c9392e61f8f10418e333599cf1b12efbe", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel/framework": "^6.0|^7.0|^8.74|^9.0|^10.0", + "php": "^7.2|~8.0.0|~8.1.0|~8.2.0|~8.3.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^4.0|^5.0|^6.4|^7.0|^8.0", + "phpunit/phpunit": "^8.0|^9.5.8", + "roave/security-advisories": "dev-master" + }, + "suggest": { + "ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Inertia\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./helpers.php" + ], + "psr-4": { + "Inertia\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "homepage": "https://reinink.ca" + } + ], + "description": "The Laravel adapter for Inertia.js.", + "keywords": [ + "inertia", + "laravel" + ], + "support": { + "issues": "https://github.com/inertiajs/inertia-laravel/issues", + "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.6.10" + }, + "funding": [ + { + "url": "https://github.com/reinink", + "type": "github" + } + ], + "time": "2023-09-13T02:24:55+00:00" + }, + { + "name": "jaybizzle/crawler-detect", + "version": "v1.2.116", + "source": { + "type": "git", + "url": "https://github.com/JayBizzle/Crawler-Detect.git", + "reference": "97e9fe30219e60092e107651abb379a38b342921" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/97e9fe30219e60092e107651abb379a38b342921", + "reference": "97e9fe30219e60092e107651abb379a38b342921", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jaybizzle\\CrawlerDetect\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Beech", + "email": "m@rkbee.ch", + "role": "Developer" + } + ], + "description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent", + "homepage": "https://github.com/JayBizzle/Crawler-Detect/", + "keywords": [ + "crawler", + "crawler detect", + "crawler detector", + "crawlerdetect", + "php crawler detect" + ], + "support": { + "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.116" + }, + "time": "2023-07-21T15:49:49+00:00" + }, + { + "name": "jenssegers/agent", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/jenssegers/agent.git", + "reference": "daa11c43729510b3700bc34d414664966b03bffe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jenssegers/agent/zipball/daa11c43729510b3700bc34d414664966b03bffe", + "reference": "daa11c43729510b3700bc34d414664966b03bffe", + "shasum": "" + }, + "require": { + "jaybizzle/crawler-detect": "^1.2", + "mobiledetect/mobiledetectlib": "^2.7.6", + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5.0|^6.0|^7.0" + }, + "suggest": { + "illuminate/support": "Required for laravel service providers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "laravel": { + "providers": [ + "Jenssegers\\Agent\\AgentServiceProvider" + ], + "aliases": { + "Agent": "Jenssegers\\Agent\\Facades\\Agent" + } + } + }, + "autoload": { + "psr-4": { + "Jenssegers\\Agent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jens Segers", + "homepage": "https://jenssegers.com" + } + ], + "description": "Desktop/mobile user agent parser with support for Laravel, based on Mobiledetect", + "homepage": "https://github.com/jenssegers/agent", + "keywords": [ + "Agent", + "browser", + "desktop", + "laravel", + "mobile", + "platform", + "user agent", + "useragent" + ], + "support": { + "issues": "https://github.com/jenssegers/agent/issues", + "source": "https://github.com/jenssegers/agent/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/jenssegers", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/jenssegers/agent", + "type": "tidelift" + } + ], + "time": "2020-06-13T08:05:20+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, + { + "name": "kirschbaum-development/eloquent-power-joins", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/kirschbaum-development/eloquent-power-joins.git", + "reference": "9238fcb53d777265ee9d8d139810e2cadecde079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kirschbaum-development/eloquent-power-joins/zipball/9238fcb53d777265ee9d8d139810e2cadecde079", + "reference": "9238fcb53d777265ee9d8d139810e2cadecde079", + "shasum": "" + }, + "require": { + "illuminate/database": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/legacy-factories": "^1.0@dev", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "phpunit/phpunit": "^8.0|^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Kirschbaum\\PowerJoins\\PowerJoinsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Kirschbaum\\PowerJoins\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luis Dalmolin", + "email": "luis.nh@gmail.com", + "role": "Developer" + } + ], + "description": "The Laravel magic applied to joins.", + "homepage": "https://github.com/kirschbaum-development/eloquent-power-joins", + "keywords": [ + "eloquent", + "join", + "laravel", + "mysql" + ], + "support": { + "issues": "https://github.com/kirschbaum-development/eloquent-power-joins/issues", + "source": "https://github.com/kirschbaum-development/eloquent-power-joins/tree/3.4.0" + }, + "time": "2023-12-07T10:44:41+00:00" + }, + { + "name": "laravel/dusk", + "version": "v7.11.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "076865d1057a4951f796342aa6a8f97a317e7638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/076865d1057a4951f796342aa6a8f97a317e7638", + "reference": "076865d1057a4951f796342aa6a8f97a317e7638", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-zip": "*", + "guzzlehttp/guzzle": "^7.2", + "illuminate/console": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "nesbot/carbon": "^2.0", + "php": "^8.0", + "php-webdriver/webdriver": "^1.9.0", + "symfony/console": "^6.0", + "symfony/finder": "^6.0", + "symfony/process": "^6.0", + "vlucas/phpdotenv": "^5.2" + }, + "require-dev": { + "mockery/mockery": "^1.4.2", + "orchestra/testbench": "^7.0|^8.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.10|^10.0.1", + "psy/psysh": "^0.11.12" + }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/laravel/dusk/issues", + "source": "https://github.com/laravel/dusk/tree/v7.11.1" + }, + "time": "2023-09-26T13:23:43+00:00" + }, + { + "name": "laravel/forge-sdk", + "version": "v3.14.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/forge-sdk.git", + "reference": "af92308b144f060c6059e04c5c1180243fa843bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/forge-sdk/zipball/af92308b144f060c6059e04c5c1180243fa843bd", + "reference": "af92308b144f060c6059e04c5c1180243fa843bd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.3.1|^7.0", + "php": "^7.2|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.4|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Forge\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mohamed Said", + "email": "mohamed@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries@laravel.com" + }, + { + "name": "James Brooks", + "email": "james@laravel.com" + } + ], + "description": "The official Laravel Forge PHP SDK.", + "keywords": [ + "forge", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/forge-sdk/issues", + "source": "https://github.com/laravel/forge-sdk" + }, + "time": "2023-09-25T08:22:20+00:00" + }, + { + "name": "laravel/fortify", + "version": "v1.18.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/fortify.git", + "reference": "5af43d5cc10b70da20ddebdbe62e0dadd69c18e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/fortify/zipball/5af43d5cc10b70da20ddebdbe62e0dadd69c18e3", + "reference": "5af43d5cc10b70da20ddebdbe62e0dadd69c18e3", + "shasum": "" + }, + "require": { + "bacon/bacon-qr-code": "^2.0", + "ext-json": "*", + "illuminate/support": "^8.82|^9.0|^10.0", + "php": "^7.3|^8.0", + "pragmarx/google2fa": "^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^6.0|^7.0|^8.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Fortify\\FortifyServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Fortify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Backend controllers and scaffolding for Laravel authentication.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/fortify/issues", + "source": "https://github.com/laravel/fortify" + }, + "time": "2023-09-12T11:19:24+00:00" + }, + { + "name": "laravel/framework", + "version": "v10.35.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/91ec2d92d2f6007e9084fe06438b99c91845da69", + "reference": "91ec2d92d2f6007e9084fe06438b99c91845da69", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.3.2", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", + "laravel/serializable-closure": "^1.3", + "league/commonmark": "^2.2.1", + "league/flysystem": "^3.8.0", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^2.67", + "nunomaduro/termwind": "^1.13", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^6.2", + "symfony/error-handler": "^6.2", + "symfony/finder": "^6.2", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.2", + "symfony/mailer": "^6.2", + "symfony/mime": "^6.2", + "symfony/process": "^6.2", + "symfony/routing": "^6.2", + "symfony/uid": "^6.2", + "symfony/var-dumper": "^6.2", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.4.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.235.5", + "doctrine/dbal": "^3.5.1", + "ext-gmp": "*", + "fakerphp/faker": "^1.21", + "guzzlehttp/guzzle": "^7.5", + "league/flysystem-aws-s3-v3": "^3.0", + "league/flysystem-ftp": "^3.0", + "league/flysystem-path-prefixing": "^3.3", + "league/flysystem-read-only": "^3.3", + "league/flysystem-sftp-v3": "^3.0", + "mockery/mockery": "^1.5.1", + "nyholm/psr7": "^1.2", + "orchestra/testbench-core": "^8.15.1", + "pda/pheanstalk": "^4.0", + "phpstan/phpstan": "^1.4.7", + "phpunit/phpunit": "^10.0.7", + "predis/predis": "^2.0.2", + "symfony/cache": "^6.2", + "symfony/http-client": "^6.2.4", + "symfony/psr-http-message-bridge": "^2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", + "league/flysystem-read-only": "Required to use read-only disks (^3.3)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "mockery/mockery": "Required to use mocking (^1.5.1).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8|^10.0.7).", + "predis/predis": "Required to use the predis connector (^2.0.2).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-12-05T14:50:33+00:00" + }, + { + "name": "laravel/horizon", + "version": "v5.21.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/horizon.git", + "reference": "ded3632dca7fd66618d5b712b4a0c6c1c77ecb3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/horizon/zipball/ded3632dca7fd66618d5b712b4a0c6c1c77ecb3f", + "reference": "ded3632dca7fd66618d5b712b4a0c6c1c77ecb3f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcntl": "*", + "ext-posix": "*", + "illuminate/contracts": "^8.17|^9.0|^10.0", + "illuminate/queue": "^8.17|^9.0|^10.0", + "illuminate/support": "^8.17|^9.0|^10.0", + "nesbot/carbon": "^2.17", + "php": "^7.3|^8.0", + "ramsey/uuid": "^4.0", + "symfony/error-handler": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^6.0|^7.0|^8.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0", + "predis/predis": "^1.1|^2.0" + }, + "suggest": { + "ext-redis": "Required to use the Redis PHP driver.", + "predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Horizon\\HorizonServiceProvider" + ], + "aliases": { + "Horizon": "Laravel\\Horizon\\Horizon" + } + } + }, + "autoload": { + "psr-4": { + "Laravel\\Horizon\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Dashboard and code-driven configuration for Laravel queues.", + "keywords": [ + "laravel", + "queue" + ], + "support": { + "issues": "https://github.com/laravel/horizon/issues", + "source": "https://github.com/laravel/horizon/tree/v5.21.1" + }, + "time": "2023-09-19T22:42:19+00:00" + }, + { + "name": "laravel/jetstream", + "version": "v3.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/jetstream.git", + "reference": "6660ede858e1a964134732bb41dff9fd08450d0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/jetstream/zipball/6660ede858e1a964134732bb41dff9fd08450d0e", + "reference": "6660ede858e1a964134732bb41dff9fd08450d0e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^10.17", + "illuminate/support": "^10.17", + "jenssegers/agent": "^2.6", + "laravel/fortify": "^1.15", + "php": "^8.1.0" + }, + "require-dev": { + "inertiajs/inertia-laravel": "^0.6.5", + "laravel/sanctum": "^3.0", + "livewire/livewire": "^2.12", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^8.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Jetstream\\JetstreamServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Jetstream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Tailwind scaffolding for the Laravel framework.", + "keywords": [ + "auth", + "laravel", + "tailwind" + ], + "support": { + "issues": "https://github.com/laravel/jetstream/issues", + "source": "https://github.com/laravel/jetstream" + }, + "time": "2023-08-23T13:50:02+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.13", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/e1379d8ead15edd6cc4369c22274345982edc95a", + "reference": "e1379d8ead15edd6cc4369c22274345982edc95a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.13" + }, + "time": "2023-10-27T13:53:59+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "338f633e6487e76b255470d3373fbc29228aa971" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/338f633e6487e76b255470d3373fbc29228aa971", + "reference": "338f633e6487e76b255470d3373fbc29228aa971", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21|^10.0", + "illuminate/contracts": "^9.21|^10.0", + "illuminate/database": "^9.21|^10.0", + "illuminate/support": "^9.21|^10.0", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.28.2|^8.8.3", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2023-09-07T15:46:33+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", + "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2023-11-08T14:08:06+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.8.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/b936d415b252b499e8c3b1f795cd4fc20f57e1f3", + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.10.4|^0.11.1", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.8.2" + }, + "time": "2023-08-15T14:27:00+00:00" + }, + { + "name": "league/commonmark", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2023-08-30T16:55:00+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/csv", + "version": "9.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/csv.git", + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/33149c4bea4949aa4fa3d03fb11ed28682168b39", + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.1.2" + }, + "require-dev": { + "doctrine/collections": "^2.1.3", + "ext-dom": "*", + "ext-xdebug": "*", + "friendsofphp/php-cs-fixer": "^v3.22.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/phpstan": "^1.10.26", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^10.3.1", + "symfony/var-dumper": "^6.3.3" + }, + "suggest": { + "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "League\\Csv\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://github.com/nyamsprod/", + "role": "Developer" + } + ], + "description": "CSV data manipulation made easy in PHP", + "homepage": "https://csv.thephpleague.com", + "keywords": [ + "convert", + "csv", + "export", + "filter", + "import", + "read", + "transform", + "write" + ], + "support": { + "docs": "https://csv.thephpleague.com", + "issues": "https://github.com/thephpleague/csv/issues", + "rss": "https://github.com/thephpleague/csv/releases.atom", + "source": "https://github.com/thephpleague/csv" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2023-09-23T10:09:54+00:00" + }, + { + "name": "league/flysystem", + "version": "3.23.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", + "reference": "d4ad81e2b67396e33dc9d7e54ec74ccf73151dcc", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.220.0", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "microsoft/azure-storage-blob": "^1.1", + "phpseclib/phpseclib": "^3.0.34", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.23.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2023-12-04T10:16:17+00:00" + }, + { + "name": "league/flysystem-ftp", + "version": "3.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-ftp.git", + "reference": "60b6c44194ee94d53eb81971637ef017e123fb20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-ftp/zipball/60b6c44194ee94d53eb81971637ef017e123fb20", + "reference": "60b6c44194ee94d53eb81971637ef017e123fb20", + "shasum": "" + }, + "require": { + "ext-ftp": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Ftp\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "FTP filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "ftp", + "ftpd" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-ftp/tree/3.16.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2023-08-30T10:17:23+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.23.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/5cf046ba5f059460e86a997c504dd781a39a109b", + "reference": "5cf046ba5f059460e86a997c504dd781a39a109b", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-local/issues", + "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2023-12-04T10:14:46+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b6a5854368533df0295c5761a0253656a2e52d9e", + "reference": "b6a5854368533df0295c5761a0253656a2e52d9e", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.14.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2023-10-17T14:13:20+00:00" + }, + { + "name": "league/uri", + "version": "7.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bf414ba956d902f5d98bf9385fcf63954f09dce5", + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.3", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.4.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2023-12-01T06:24:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2023-11-24T15:40:42+00:00" + }, + { + "name": "livewire/livewire", + "version": "v3.2.6", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "ecded08cdc4b36bbb4b26bcc7f7a171ea2e4368c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/ecded08cdc4b36bbb4b26bcc7f7a171ea2e4368c", + "reference": "ecded08cdc4b36bbb4b26bcc7f7a171ea2e4368c", + "shasum": "" + }, + "require": { + "illuminate/database": "^10.0", + "illuminate/support": "^10.0", + "illuminate/validation": "^10.0", + "league/mime-type-detection": "^1.9", + "php": "^8.1", + "symfony/http-kernel": "^6.2" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^10.0", + "laravel/prompts": "^0.1.6", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^8.0", + "orchestra/testbench-dusk": "^8.0", + "phpunit/phpunit": "^9.0", + "psy/psysh": "@stable" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Livewire\\LivewireServiceProvider" + ], + "aliases": { + "Livewire": "Livewire\\Livewire" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v3.2.6" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2023-12-04T21:20:19+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + }, + "time": "2023-05-10T11:58:31+00:00" + }, + { + "name": "miguilim/filament-auto-panel", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/miguilimzero/filament-auto-panel.git", + "reference": "f8ace5ca4e172b1ae40f62fb36a0d387de81adbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/miguilimzero/filament-auto-panel/zipball/f8ace5ca4e172b1ae40f62fb36a0d387de81adbc", + "reference": "f8ace5ca4e172b1ae40f62fb36a0d387de81adbc", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^3.6", + "filament/filament": "^3.0", + "php": "^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Miguilim\\FilamentAutoPanel\\FilamentAutoPanelProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Miguilim\\FilamentAutoPanel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Construct your Filament panel Resources and Relation Managers at execution time like magic.", + "support": { + "issues": "https://github.com/miguilimzero/filament-auto-panel/issues", + "source": "https://github.com/miguilimzero/filament-auto-panel/tree/1.4.0" + }, + "time": "2023-09-26T02:44:29+00:00" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.41", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", + "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.35||~5.7" + }, + "type": "library", + "autoload": { + "psr-0": { + "Detection": "namespaced/" + }, + "classmap": [ + "Mobile_Detect.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "support": { + "issues": "https://github.com/serbanghita/Mobile-Detect/issues", + "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.41" + }, + "time": "2022-11-08T18:31:26+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.1", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2023-10-27T15:32:31+00:00" + }, + { + "name": "mustache/mustache", + "version": "v2.14.2", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/mustache.php.git", + "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e62b7c3849d22ec55f3ec425507bf7968193a6cb", + "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~1.11", + "phpunit/phpunit": "~3.7|~4.0|~5.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mustache": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "A Mustache implementation in PHP.", + "homepage": "https://github.com/bobthecow/mustache.php", + "keywords": [ + "mustache", + "templating" + ], + "support": { + "issues": "https://github.com/bobthecow/mustache.php/issues", + "source": "https://github.com/bobthecow/mustache.php/tree/v2.14.2" + }, + "time": "2022-08-23T13:07:01+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.72.1", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2b3b3db0a2d0556a177392ff1a3bf5608fa09f78", + "reference": "2b3b3db0a2d0556a177392ff1a3bf5608fa09f78", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "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://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/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": "2023-12-08T23:47:49+00:00" + }, + { + "name": "nette/php-generator", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/nette/php-generator.git", + "reference": "8b728c622c49b196513c0f95508f2f66342d1e8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/php-generator/zipball/8b728c622c49b196513c0f95508f2f66342d1e8f", + "reference": "8b728c622c49b196513c0f95508f2f66342d1e8f", + "shasum": "" + }, + "require": { + "nette/utils": "^3.2.9 || ^4.0", + "php": "8.0 - 8.3" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.4", + "nikic/php-parser": "^4.15", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.8" + }, + "suggest": { + "nikic/php-parser": "to use ClassType::from(withBodies: true) & ClassType::fromCode()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.3 features.", + "homepage": "https://nette.org", + "keywords": [ + "code", + "nette", + "php", + "scaffolding" + ], + "support": { + "issues": "https://github.com/nette/php-generator/issues", + "source": "https://github.com/nette/php-generator/tree/v4.1.0" + }, + "time": "2023-09-26T12:28:52+00:00" + }, + { + "name": "nette/schema", + "version": "v1.2.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", + "shasum": "" + }, + "require": { + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", + "php": "7.1 - 8.3" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.2.5" + }, + "time": "2023-10-05T20:37:59+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "shasum": "" + }, + "require": { + "php": ">=8.0 <8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.3" + }, + "time": "2023-10-29T21:02:13+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "nunomaduro/laravel-console-dusk", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/laravel-console-dusk.git", + "reference": "86899b9f3c4cb3fb3aa0717de05715a85c2e3f45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-dusk/zipball/86899b9f3c4cb3fb3aa0717de05715a85c2e3f45", + "reference": "86899b9f3c4cb3fb3aa0717de05715a85c2e3f45", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "laravel/dusk": "^7.0|^8.0", + "nunomaduro/laravel-console-task": "^1.8", + "php": "^8.1" + }, + "suggest": { + "phpunit/phpunit": "Required to perform browser test assertions." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\LaravelConsoleDusk\\LaravelConsoleDuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\LaravelConsoleDusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Laravel Console Dusk allows the usage of Laravel Dusk in Laravel/Laravel Zero artisan commands.", + "keywords": [ + "Laravel Dusk", + "artisan", + "cli", + "command-line", + "console", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/laravel-console-dusk/issues", + "source": "https://github.com/nunomaduro/laravel-console-dusk" + }, + "time": "2023-01-11T15:34:19+00:00" + }, + { + "name": "nunomaduro/laravel-console-task", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/laravel-console-task.git", + "reference": "e49e7be261a7b7329c4538777489b355fb234bde" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/laravel-console-task/zipball/e49e7be261a7b7329c4538777489b355fb234bde", + "reference": "e49e7be261a7b7329c4538777489b355fb234bde", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "php": "^8.1" + }, + "require-dev": { + "pestphp/pest": "^1.22.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\LaravelConsoleTask\\LaravelConsoleTaskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\LaravelConsoleTask\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Laravel Console Task is a output method for your Laravel/Laravel Zero commands.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/laravel-console-task/issues", + "source": "https://github.com/nunomaduro/laravel-console-task" + }, + "time": "2023-01-11T15:16:19+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.15.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" + }, + "require-dev": { + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2023-02-08T01:06:31+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "paragonie/sodium_compat", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/e592a3e06d1fa0d43988c7c7d9948ca836f644b6", + "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6", + "shasum": "" + }, + "require": { + "paragonie/random_compat": ">=1", + "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" + }, + "suggest": { + "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", + "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "support": { + "issues": "https://github.com/paragonie/sodium_compat/issues", + "source": "https://github.com/paragonie/sodium_compat/tree/v1.20.0" + }, + "time": "2023-04-30T00:54:53+00:00" + }, + { + "name": "php-imap/php-imap", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/barbushin/php-imap.git", + "reference": "94107fdd1383285459a7f6c2dd2f39e25a1b8373" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barbushin/php-imap/zipball/94107fdd1383285459a7f6c2dd2f39e25a1b8373", + "reference": "94107fdd1383285459a7f6c2dd2f39e25a1b8373", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-iconv": "*", + "ext-imap": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "maglnet/composer-require-checker": "^2.0|^3.2", + "nikic/php-parser": "^4.3,<4.7|^4.10", + "paragonie/hidden-string": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpunit/phpunit": "^8.5|^9.5", + "povils/phpmnd": "^2.2", + "psalm/plugin-phpunit": "^0.10.0|^0.15.1", + "roave/security-advisories": "dev-master", + "sebastian/phpcpd": "^4.1|^6.0" + }, + "suggest": { + "ext-fileinfo": "To facilitate IncomingMailAttachment::getFileInfo() auto-detection" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpImap\\": "src/PhpImap" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sergey Barbushin", + "email": "barbushin@gmail.com", + "homepage": "http://linkedin.com/in/barbushin" + } + ], + "description": "Manage mailboxes, filter/get/delete emails in PHP (supports IMAP/POP3/NNTP)", + "homepage": "https://github.com/barbushin/php-imap", + "keywords": [ + "imap", + "mail", + "mailbox", + "php", + "pop3", + "receive emails" + ], + "support": { + "issues": "https://github.com/barbushin/php-imap/issues", + "source": "https://github.com/barbushin/php-imap/tree/5.0.1" + }, + "time": "2022-12-05T15:47:03+00:00" + }, + { + "name": "php-webdriver/webdriver", + "version": "1.15.0", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "a1578689290055586f1ee51eaf0ec9d52895bb6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/a1578689290055586f1ee51eaf0ec9d52895bb6d", + "reference": "a1578689290055586f1ee51eaf0ec9d52895bb6d", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^7.3 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^5.0 || ^6.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.20.0", + "ondram/ci-detector": "^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^9.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0 || ^6.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.0" + }, + "time": "2023-08-29T13:52:26+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.2", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", + "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2023-11-12T21:59:55+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.18", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + }, + "time": "2022-06-13T21:57:56+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.11.21", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "bcb22101107f3bf770523b65630c9d547f60c540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/bcb22101107f3bf770523b65630c9d547f60c540", + "reference": "bcb22101107f3bf770523b65630c9d547f60c540", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^4.0 || ^3.1", + "php": "^8.0 || ^7.0.8", + "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.11.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.11.21" + }, + "time": "2023-09-17T21:15:54+00:00" + }, + { + "name": "pusher/pusher-php-server", + "version": "7.2.3", + "source": { + "type": "git", + "url": "https://github.com/pusher/pusher-http-php.git", + "reference": "416e68dd5f640175ad5982131c42a7a666d1d8e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/416e68dd5f640175ad5982131c42a7a666d1d8e9", + "reference": "416e68dd5f640175ad5982131c42a7a666d1d8e9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.2", + "paragonie/sodium_compat": "^1.6", + "php": "^7.3|^8.0", + "psr/log": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "overtrue/phplint": "^2.3", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Pusher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Library for interacting with the Pusher REST API", + "keywords": [ + "events", + "messaging", + "php-pusher-server", + "publish", + "push", + "pusher", + "real time", + "real-time", + "realtime", + "rest", + "trigger" + ], + "support": { + "issues": "https://github.com/pusher/pusher-http-php/issues", + "source": "https://github.com/pusher/pusher-http-php/tree/7.2.3" + }, + "time": "2023-05-17T16:00:06+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.28.3", + "fakerphp/faker": "^1.21", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^1.0", + "mockery/mockery": "^1.5", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcsstandards/phpcsutils": "^1.0.0-rc1", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18.4", + "ramsey/coding-standard": "^2.0.3", + "ramsey/conventional-commits": "^1.3", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2022-12-31T21:50:55+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.7.5", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", + "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11", + "ext-json": "*", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.10", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "doctrine/annotations": "^1.8", + "ergebnis/composer-normalize": "^2.15", + "mockery/mockery": "^1.3", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.2", + "php-mock/php-mock-mockery": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^1.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^8.5 || ^9", + "ramsey/composer-repl": "^1.4", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.9" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.7.5" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", + "type": "tidelift" + } + ], + "time": "2023-11-08T05:53:05+00:00" + }, + { + "name": "ratchet/rfc6455", + "version": "v0.3.1", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "7c964514e93456a52a99a20fcfa0de242a43ccdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/7c964514e93456a52a99a20fcfa0de242a43ccdb", + "reference": "7c964514e93456a52a99a20fcfa0de242a43ccdb", + "shasum": "" + }, + "require": { + "guzzlehttp/psr7": "^2 || ^1.7", + "php": ">=5.4.2" + }, + "require-dev": { + "phpunit/phpunit": "^5.7", + "react/socket": "^1.3" }, + "type": "library", "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Ratchet\\RFC6455\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2479,59 +7551,54 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", "keywords": [ - "log", - "logging", - "psr-3" + "WebSockets", + "rfc6455", + "websocket" ], "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.3.1" + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/RFC6455/issues", + "source": "https://github.com/ratchetphp/RFC6455/tree/v0.3.1" }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2023-02-06T13:46:10+00:00" + "time": "2021-12-09T23:20:49+00:00" }, { - "name": "mustache/mustache", - "version": "v2.14.2", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e62b7c3849d22ec55f3ec425507bf7968193a6cb", - "reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "php": ">=5.2.4" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "phpunit/phpunit": "~3.7|~4.0|~5.0" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { - "psr-0": { - "Mustache": "src/" + "psr-4": { + "React\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2540,80 +7607,74 @@ ], "authors": [ { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A Mustache implementation in PHP.", - "homepage": "https://github.com/bobthecow/mustache.php", + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ - "mustache", - "templating" + "cache", + "caching", + "promise", + "reactphp" ], "support": { - "issues": "https://github.com/bobthecow/mustache.php/issues", - "source": "https://github.com/bobthecow/mustache.php/tree/v2.14.2" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, - "time": "2022-08-23T13:07:01+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "nesbot/carbon", - "version": "2.67.0", + "name": "react/dns", + "version": "v1.11.0", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8" + "url": "https://github.com/reactphp/dns.git", + "reference": "3be0fc8f1eb37d6875cd6f0c6c7d0be81435de9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/c1001b3bc75039b07f38a79db5237c4c529e04c8", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8", + "url": "https://api.github.com/repos/reactphp/dns/zipball/3be0fc8f1eb37d6875cd6f0c6c7d0be81435de9f", + "reference": "3be0fc8f1eb37d6875cd6f0c6c7d0be81435de9f", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.1.8 || ^8.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.7 || ^1.2.1" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4", - "doctrine/orm": "^2.7", - "friendsofphp/php-cs-fixer": "^3.0", - "kylekatarnls/multi-tester": "^2.0", - "ondrejmirtes/better-reflection": "*", - "phpmd/phpmd": "^2.9", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.99 || ^1.7.14", - "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", - "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", - "squizlabs/php_codesniffer": "^3.4" + "phpunit/phpunit": "^9.5 || ^4.8.35", + "react/async": "^4 || ^3 || ^2", + "react/promise-timer": "^1.9" }, - "bin": [ - "bin/carbon" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-3.x": "3.x-dev", - "dev-master": "2.x-dev" - }, - "laravel": { - "providers": [ - "Carbon\\Laravel\\ServiceProvider" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, "autoload": { "psr-4": { - "Carbon\\": "src/Carbon/" + "React\\Dns\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2622,293 +7683,314 @@ ], "authors": [ { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "https://markido.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "kylekatarnls", - "homepage": "https://github.com/kylekatarnls" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "date", - "datetime", - "time" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.11.0" }, "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" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-05-25T22:09:47+00:00" + "time": "2023-06-02T12:45:26+00:00" }, { - "name": "nette/schema", - "version": "v1.2.3", + "name": "react/event-loop", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "6e7e587714fff7a83dcc7025aee42ab3b265ae05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6e7e587714fff7a83dcc7025aee42ab3b265ae05", + "reference": "6e7e587714fff7a83dcc7025aee42ab3b265ae05", "shasum": "" }, "require": { - "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "php": ">=5.3.0" }, "require-dev": { - "nette/tester": "^2.3 || ^2.4", - "phpstan/phpstan-nette": "^1.0", - "tracy/tracy": "^2.7" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\EventLoop\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0-only", - "GPL-3.0-only" + "MIT" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "📐 Nette Schema: validating data structures against a given Schema.", - "homepage": "https://nette.org", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "config", - "nette" + "asynchronous", + "event-loop" ], "support": { - "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.4.0" }, - "time": "2022-10-13T01:24:26+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-05-05T10:11:24+00:00" }, { - "name": "nette/utils", - "version": "v4.0.0", + "name": "react/http", + "version": "v1.9.0", "source": { "type": "git", - "url": "https://github.com/nette/utils.git", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + "url": "https://github.com/reactphp/http.git", + "reference": "bb3154dbaf2dfe3f0467f956a05f614a69d5f1d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "url": "https://api.github.com/repos/reactphp/http/zipball/bb3154dbaf2dfe3f0467f956a05f614a69d5f1d0", + "reference": "bb3154dbaf2dfe3f0467f956a05f614a69d5f1d0", "shasum": "" }, "require": { - "php": ">=8.0 <8.3" - }, - "conflict": { - "nette/finder": "<3", - "nette/schema": "<1.2.2" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "fig/http-message-util": "^1.1", + "php": ">=5.3.0", + "psr/http-message": "^1.0", + "react/event-loop": "^1.2", + "react/promise": "^3 || ^2.3 || ^1.2.1", + "react/socket": "^1.12", + "react/stream": "^1.2", + "ringcentral/psr7": "^1.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "^2.4", - "phpstan/phpstan": "^1.0", - "tracy/tracy": "^2.9" - }, - "suggest": { - "ext-gd": "to use Image", - "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", - "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", - "ext-json": "to use Nette\\Utils\\Json", - "ext-mbstring": "to use Strings::lower() etc...", - "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "clue/http-proxy-react": "^1.8", + "clue/reactphp-ssh-proxy": "^1.4", + "clue/socks-react": "^1.4", + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/async": "^4 || ^3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.9" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Http\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause", - "GPL-2.0-only", - "GPL-3.0-only" + "MIT" ], "authors": [ { - "name": "David Grudl", - "homepage": "https://davidgrudl.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Nette Community", - "homepage": "https://nette.org/contributors" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", - "homepage": "https://nette.org", + "description": "Event-driven, streaming HTTP client and server implementation for ReactPHP", "keywords": [ - "array", - "core", - "datetime", - "images", - "json", - "nette", - "paginator", - "password", - "slugify", - "string", - "unicode", - "utf-8", - "utility", - "validation" + "async", + "client", + "event-driven", + "http", + "http client", + "http server", + "https", + "psr-7", + "reactphp", + "server", + "streaming" ], "support": { - "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.0" + "issues": "https://github.com/reactphp/http/issues", + "source": "https://github.com/reactphp/http/tree/v1.9.0" }, - "time": "2023-02-02T10:41:53+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-04-26T10:29:24+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.15.5", + "name": "react/promise", + "version": "v3.0.0", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" + "url": "https://github.com/reactphp/promise.git", + "reference": "c86753c76fd3be465d93b308f18d189f01a22be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", - "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "url": "https://api.github.com/repos/reactphp/promise/zipball/c86753c76fd3be465d93b308f18d189f01a22be4", + "reference": "c86753c76fd3be465d93b308f18d189f01a22be4", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.1.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpstan/phpstan": "1.10.20 || 1.4.10", + "phpunit/phpunit": "^9.5 || ^7.5" }, - "bin": [ - "bin/php-parse" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "PhpParser\\": "lib/PhpParser" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A PHP parser written in PHP", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "parser", - "php" + "promise", + "promises" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.0.0" }, - "time": "2023-05-19T20:20:00+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-07-11T16:12:49+00:00" }, { - "name": "nunomaduro/termwind", - "version": "v1.15.1", + "name": "react/socket", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + "url": "https://github.com/reactphp/socket.git", + "reference": "21591111d3ea62e31f2254280ca0656bc2b1bda6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "url": "https://api.github.com/repos/reactphp/socket/zipball/21591111d3ea62e31f2254280ca0656bc2b1bda6", + "reference": "21591111d3ea62e31f2254280ca0656bc2b1bda6", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": "^8.0", - "symfony/console": "^5.3.0|^6.0.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.11", + "react/event-loop": "^1.2", + "react/promise": "^3 || ^2.6 || ^1.2.1", + "react/stream": "^1.2" }, "require-dev": { - "ergebnis/phpstan-rules": "^1.0.", - "illuminate/console": "^8.0|^9.0", - "illuminate/support": "^8.0|^9.0", - "laravel/pint": "^1.0.0", - "pestphp/pest": "^1.21.0", - "pestphp/pest-plugin-mock": "^1.0", - "phpstan/phpstan": "^1.4.6", - "phpstan/phpstan-strict-rules": "^1.1.0", - "symfony/var-dumper": "^5.2.7|^6.0.0", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/async": "^4 || ^3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.10" }, "type": "library", - "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - } - }, "autoload": { - "files": [ - "src/Functions.php" - ], "psr-4": { - "Termwind\\": "src/" + "React\\Socket\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2917,64 +7999,73 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Its like Tailwind CSS, but for the console.", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" + "Connection", + "Socket", + "async", + "reactphp", + "stream" ], "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.14.0" }, "funding": [ { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://github.com/xiCO2k", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-02-08T01:06:31+00:00" + "time": "2023-08-25T13:48:09+00:00" }, { - "name": "paragonie/constant_time_encoding", - "version": "v2.6.3", + "name": "react/stream", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "58c3f47f650c94ec05a151692652a868995d2938" + "url": "https://github.com/reactphp/stream.git", + "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", - "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "url": "https://api.github.com/repos/reactphp/stream/zipball/6fbc9672905c7d5a885f2da2fc696f65840f4a66", + "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66", "shasum": "" }, "require": { - "php": "^7|^8" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^6|^7|^8|^9", - "vimeo/psalm": "^1|^2|^3|^4" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { "psr-4": { - "ParagonIE\\ConstantTime\\": "src/" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2983,141 +8074,156 @@ ], "authors": [ { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com", - "role": "Maintainer" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Steve 'Sc00bz' Thomas", - "email": "steve@tobtu.com", - "homepage": "https://www.tobtu.com", - "role": "Original Developer" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", "keywords": [ - "base16", - "base32", - "base32_decode", - "base32_encode", - "base64", - "base64_decode", - "base64_encode", - "bin2hex", - "encoding", - "hex", - "hex2bin", - "rfc4648" + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" ], "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/constant_time_encoding/issues", - "source": "https://github.com/paragonie/constant_time_encoding" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.3.0" }, - "time": "2022-06-14T06:56:20+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-06-16T10:52:11+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.9.1", + "name": "ringcentral/psr7", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + "url": "https://github.com/ringcentral/psr7.git", + "reference": "360faaec4b563958b673fb52bbe94e37f14bc686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/360faaec4b563958b673fb52bbe94e37f14bc686", + "reference": "360faaec4b563958b673fb52bbe94e37f14bc686", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + "phpunit/phpunit": "~4.0" }, "type": "library", "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": true - }, "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.0-dev" } }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "PhpOption\\": "src/PhpOption/" + "RingCentral\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh" - }, - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "description": "Option Type for PHP", + "description": "PSR-7 message implementation", "keywords": [ - "language", - "option", - "php", - "type" + "http", + "message", + "stream", + "uri" ], "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" + "source": "https://github.com/ringcentral/psr7/tree/master" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2023-02-25T19:38:58+00:00" + "time": "2018-05-29T20:21:04+00:00" }, { - "name": "pragmarx/google2fa", - "version": "v8.0.1", + "name": "ryangjchandler/blade-capture-directive", + "version": "v0.3.0", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + "url": "https://github.com/ryangjchandler/blade-capture-directive.git", + "reference": "62fd2ecb50b938a46025093bcb64fcaddd531f89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "url": "https://api.github.com/repos/ryangjchandler/blade-capture-directive/zipball/62fd2ecb50b938a46025093bcb64fcaddd531f89", + "reference": "62fd2ecb50b938a46025093bcb64fcaddd531f89", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0", - "php": "^7.1|^8.0" + "illuminate/contracts": "^9.0|^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.9.2" }, "require-dev": { - "phpstan/phpstan": "^0.12.18", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + "nunomaduro/collision": "^6.0|^7.0", + "nunomaduro/larastan": "^2.0", + "orchestra/testbench": "^7.22|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "RyanChandler\\BladeCaptureDirective\\BladeCaptureDirectiveServiceProvider" + ], + "aliases": { + "BladeCaptureDirective": "RyanChandler\\BladeCaptureDirective\\Facades\\BladeCaptureDirective" + } + } + }, "autoload": { "psr-4": { - "PragmaRX\\Google2FA\\": "src/" + "RyanChandler\\BladeCaptureDirective\\": "src", + "RyanChandler\\BladeCaptureDirective\\Database\\Factories\\": "database/factories" } }, "notification-url": "https://packagist.org/downloads/", @@ -3126,50 +8232,58 @@ ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" + "name": "Ryan Chandler", + "email": "support@ryangjchandler.co.uk", + "role": "Developer" } ], - "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "description": "Create inline partials in your Blade templates with ease.", + "homepage": "https://github.com/ryangjchandler/blade-capture-directive", "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa" + "blade-capture-directive", + "laravel", + "ryangjchandler" ], "support": { - "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + "issues": "https://github.com/ryangjchandler/blade-capture-directive/issues", + "source": "https://github.com/ryangjchandler/blade-capture-directive/tree/v0.3.0" }, - "time": "2022-06-13T21:57:56+00:00" + "funding": [ + { + "url": "https://github.com/ryangjchandler", + "type": "github" + } + ], + "time": "2023-02-14T16:54:54+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "seld/jsonlint", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": "^5.3 || ^7.0 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3178,51 +8292,60 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "JSON Linter", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } ], - "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" + "time": "2023-05-11T13:16:46+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "seld/phar-utils", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Seld\\PharUtils\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3231,49 +8354,54 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" } ], - "description": "Standard interfaces for event handling.", + "description": "PHAR file format utilities, for when PHP phars you up", "keywords": [ - "events", - "psr", - "psr-14" + "phar" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2022-08-31T10:31:18+00:00" }, { - "name": "psr/http-client", - "version": "1.0.2", + "name": "seld/signal-handler", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" + "php": ">=7.2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "2.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Http\\Client\\": "src/" + "Seld\\Signal\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3282,50 +8410,52 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", "keywords": [ - "http", - "http-client", - "psr", - "psr-18" + "posix", + "sigint", + "signal", + "sigterm", + "unix" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2" }, - "time": "2023-04-10T20:12:12+00:00" + "time": "2023-09-03T09:24:00+00:00" }, { - "name": "psr/http-factory", - "version": "1.0.2", + "name": "spatie/backtrace", + "version": "1.5.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "url": "https://github.com/spatie/backtrace.git", + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/483f76a82964a0431aa836b6ed0edde0c248e3ab", + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" + "php": "^7.3|^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "ext-json": "*", + "phpunit/phpunit": "^9.3", + "spatie/phpunit-snapshot-assertions": "^4.2", + "symfony/var-dumper": "^5.1" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Spatie\\Backtrace\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3334,52 +8464,58 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Freek Van de Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "A better backtrace", + "homepage": "https://github.com/spatie/backtrace", "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" + "Backtrace", + "spatie" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + "source": "https://github.com/spatie/backtrace/tree/1.5.3" }, - "time": "2023-04-10T20:10:41+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/spatie", + "type": "github" + }, + { + "url": "https://spatie.be/open-source/support-us", + "type": "other" + } + ], + "time": "2023-06-28T12:59:17+00:00" }, { - "name": "psr/http-message", - "version": "2.0", + "name": "spatie/color", + "version": "1.5.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "url": "https://github.com/spatie/color.git", + "reference": "49739265900cabce4640cd26c3266fd8d2cca390" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/spatie/color/zipball/49739265900cabce4640cd26c3266fd8d2cca390", + "reference": "49739265900cabce4640cd26c3266fd8d2cca390", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.3|^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^6.5||^9.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Spatie\\Color\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3388,51 +8524,68 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "A little library to handle color conversions", + "homepage": "https://github.com/spatie/color", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "color", + "conversion", + "rgb", + "spatie" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "issues": "https://github.com/spatie/color/issues", + "source": "https://github.com/spatie/color/tree/1.5.3" }, - "time": "2023-04-04T09:54:51+00:00" + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-12-18T12:58:32+00:00" }, { - "name": "psr/log", - "version": "3.0.0", + "name": "spatie/eloquent-sortable", + "version": "4.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "url": "https://github.com/spatie/eloquent-sortable.git", + "reference": "74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/spatie/eloquent-sortable/zipball/74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a", + "reference": "74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a", "shasum": "" }, "require": { - "php": ">=8.0.0" + "illuminate/database": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "nesbot/carbon": "^2.63", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.9" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev" + "laravel": { + "providers": [ + "Spatie\\EloquentSortable\\EloquentSortableServiceProvider" + ] } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Spatie\\EloquentSortable\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3441,122 +8594,154 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Freek Van der Herten", + "email": "freek@spatie.be" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Sortable behaviour for eloquent models", + "homepage": "https://github.com/spatie/eloquent-sortable", "keywords": [ - "log", - "psr", - "psr-3" + "behaviour", + "eloquent", + "laravel", + "model", + "sort", + "sortable" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "issues": "https://github.com/spatie/eloquent-sortable/issues", + "source": "https://github.com/spatie/eloquent-sortable/tree/4.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-23T08:34:14+00:00" }, { - "name": "psr/simple-cache", - "version": "3.0.0", + "name": "spatie/flare-client-php", + "version": "1.4.2", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + "url": "https://github.com/spatie/flare-client-php.git", + "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5f2c6a7a0d2c1d90c12559dc7828fd942911a544", + "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544", "shasum": "" }, "require": { - "php": ">=8.0.0" + "illuminate/pipeline": "^8.0|^9.0|^10.0", + "nesbot/carbon": "^2.62.1", + "php": "^8.0", + "spatie/backtrace": "^1.5.2", + "symfony/http-foundation": "^5.0|^6.0", + "symfony/mime": "^5.2|^6.0", + "symfony/process": "^5.2|^6.0", + "symfony/var-dumper": "^5.2|^6.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.3.0", + "pestphp/pest": "^1.20", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "spatie/phpunit-snapshot-assertions": "^4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-main": "1.3.x-dev" } }, "autoload": { + "files": [ + "src/helpers.php" + ], "psr-4": { - "Psr\\SimpleCache\\": "src/" + "Spatie\\FlareClient\\": "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", + "description": "Send PHP errors to Flare", + "homepage": "https://github.com/spatie/flare-client-php", "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" + "exception", + "flare", + "reporting", + "spatie" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + "issues": "https://github.com/spatie/flare-client-php/issues", + "source": "https://github.com/spatie/flare-client-php/tree/1.4.2" }, - "time": "2021-10-29T13:26:27+00:00" + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-07-28T08:07:24+00:00" }, { - "name": "psy/psysh", - "version": "v0.11.18", + "name": "spatie/ignition", + "version": "1.11.2", "source": { "type": "git", - "url": "https://github.com/bobthecow/psysh.git", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec" + "url": "https://github.com/spatie/ignition.git", + "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", + "url": "https://api.github.com/repos/spatie/ignition/zipball/48b23411ca4bfbc75c75dfc638b6b36159c375aa", + "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa", "shasum": "" }, "require": { "ext-json": "*", - "ext-tokenizer": "*", - "nikic/php-parser": "^4.0 || ^3.1", - "php": "^8.0 || ^7.0.8", - "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" - }, - "conflict": { - "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + "ext-mbstring": "*", + "php": "^8.0", + "spatie/backtrace": "^1.5.3", + "spatie/flare-client-php": "^1.4.0", + "symfony/console": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "illuminate/cache": "^9.52", + "mockery/mockery": "^1.4", + "pestphp/pest": "^1.20", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "psr/simple-cache-implementation": "*", + "symfony/cache": "^6.0", + "symfony/process": "^5.4|^6.0", + "vlucas/phpdotenv": "^5.5" }, "suggest": { - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + "openai-php/client": "Require get solutions from OpenAI", + "simple-cache-implementation": "To cache solutions from OpenAI" }, - "bin": [ - "bin/psysh" - ], "type": "library", "extra": { "branch-alias": { - "dev-main": "0.11.x-dev" + "dev-main": "1.5.x-dev" } }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Psy\\": "src/" + "Spatie\\Ignition\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3565,51 +8750,63 @@ ], "authors": [ { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" } ], - "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "description": "A beautiful error page for PHP applications.", + "homepage": "https://flareapp.io/ignition", "keywords": [ - "REPL", - "console", - "interactive", - "shell" + "error", + "flare", + "laravel", + "page" ], "support": { - "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.18" + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/ignition/issues", + "source": "https://github.com/spatie/ignition" }, - "time": "2023-05-23T02:31:11+00:00" + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-09-19T15:29:52+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "spatie/invade", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/spatie/invade.git", + "reference": "7b20a25486de69198e402da20dc924d8bcc8024a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/spatie/invade/zipball/7b20a25486de69198e402da20dc924d8bcc8024a", + "reference": "7b20a25486de69198e402da20dc924d8bcc8024a", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^8.0" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "pestphp/pest": "^1.20", + "phpstan/phpstan": "^1.4", + "spatie/ray": "^1.28" }, "type": "library", "autoload": { "files": [ - "src/getallheaders.php" - ] + "src/functions.php" + ], + "psr-4": { + "Spatie\\Invade\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3617,68 +8814,68 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" } ], - "description": "A polyfill for getallheaders.", + "description": "A PHP function to work with private properties and methods", + "homepage": "https://github.com/spatie/invade", + "keywords": [ + "invade", + "spatie" + ], "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "source": "https://github.com/spatie/invade/tree/2.0.0" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-07-19T18:55:36+00:00" }, { - "name": "ramsey/collection", - "version": "2.0.0", + "name": "spatie/laravel-activitylog", + "version": "4.7.3", "source": { "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "url": "https://github.com/spatie/laravel-activitylog.git", + "reference": "ec65a478a909b8df1b4f0c3c45de2592ca7639e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/ec65a478a909b8df1b4f0c3c45de2592ca7639e5", + "reference": "ec65a478a909b8df1b4f0c3c45de2592ca7639e5", "shasum": "" }, "require": { - "php": "^8.1" + "illuminate/config": "^8.0 || ^9.0 || ^10.0", + "illuminate/database": "^8.69 || ^9.27 || ^10.0", + "illuminate/support": "^8.0 || ^9.0 || ^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.6.3" }, "require-dev": { - "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", - "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", - "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "ext-json": "*", + "orchestra/testbench": "^6.23 || ^7.0 || ^8.0", + "pestphp/pest": "^1.20" }, "type": "library", "extra": { - "captainhook": { - "force-install": true - }, - "ramsey/conventional-commits": { - "configFile": "conventional-commits.json" + "laravel": { + "providers": [ + "Spatie\\Activitylog\\ActivitylogServiceProvider" + ] } }, "autoload": { + "files": [ + "src/helpers.php" + ], "psr-4": { - "Ramsey\\Collection\\": "src/" + "Spatie\\Activitylog\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3687,164 +8884,198 @@ ], "authors": [ { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Tom Witkowski", + "email": "dev.gummibeer@gmail.com", + "homepage": "https://gummibeer.de", + "role": "Developer" } ], - "description": "A PHP library for representing and manipulating collections.", + "description": "A very simple activity logger to monitor the users of your website or application", + "homepage": "https://github.com/spatie/activitylog", "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" + "activity", + "laravel", + "log", + "spatie", + "user" ], "support": { - "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "issues": "https://github.com/spatie/laravel-activitylog/issues", + "source": "https://github.com/spatie/laravel-activitylog/tree/4.7.3" }, "funding": [ { - "url": "https://github.com/ramsey", - "type": "github" + "url": "https://spatie.be/open-source/support-us", + "type": "custom" }, { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" + "url": "https://github.com/spatie", + "type": "github" } ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2023-01-25T17:04:51+00:00" }, { - "name": "ramsey/uuid", - "version": "4.7.4", + "name": "spatie/laravel-feed", + "version": "4.3.0", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "60a4c63ab724854332900504274f6150ff26d286" + "url": "https://github.com/spatie/laravel-feed.git", + "reference": "1cf06a43b4ee0fdeb919983a76de68467ccdb844" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286", - "reference": "60a4c63ab724854332900504274f6150ff26d286", + "url": "https://api.github.com/repos/spatie/laravel-feed/zipball/1cf06a43b4ee0fdeb919983a76de68467ccdb844", + "reference": "1cf06a43b4ee0fdeb919983a76de68467ccdb844", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11", - "ext-json": "*", + "illuminate/contracts": "^10.0", + "illuminate/http": "^10.0", + "illuminate/support": "^10.0", "php": "^8.0", - "ramsey/collection": "^1.2 || ^2.0" - }, - "replace": { - "rhumsaa/uuid": "self.version" + "spatie/laravel-package-tools": "^1.15" }, "require-dev": { - "captainhook/captainhook": "^5.10", - "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", - "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "orchestra/testbench": "^8.0", + "pestphp/pest": "^2.0", + "spatie/pest-plugin-snapshots": "^2.0", + "spatie/test-time": "^1.2" }, "type": "library", "extra": { - "captainhook": { - "force-install": true + "laravel": { + "providers": [ + "Spatie\\Feed\\FeedServiceProvider" + ] } }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Ramsey\\Uuid\\": "src/" + "Spatie\\Feed\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "authors": [ + { + "name": "Jolita Grazyte", + "email": "jolita@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Patrick Organ", + "homepage": "https://github.com/patinthehat", + "role": "Developer" + } + ], + "description": "Generate rss feeds", + "homepage": "https://github.com/spatie/laravel-feed", "keywords": [ - "guid", - "identifier", - "uuid" + "laravel", + "laravel-feed", + "rss", + "spatie" ], "support": { - "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.4" + "source": "https://github.com/spatie/laravel-feed/tree/4.3.0" }, "funding": [ { - "url": "https://github.com/ramsey", - "type": "github" + "url": "https://spatie.be/open-source/support-us", + "type": "custom" }, { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" + "url": "https://github.com/spatie", + "type": "github" } ], - "time": "2023-04-15T23:01:58+00:00" + "time": "2023-08-07T14:46:53+00:00" }, { - "name": "spatie/eloquent-sortable", - "version": "4.0.2", + "name": "spatie/laravel-ignition", + "version": "2.3.0", "source": { "type": "git", - "url": "https://github.com/spatie/eloquent-sortable.git", - "reference": "74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a" + "url": "https://github.com/spatie/laravel-ignition.git", + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/eloquent-sortable/zipball/74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a", - "reference": "74994d10a17d15d2cdb319d6b2ad7cb6fa067c0a", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", "shasum": "" }, "require": { - "illuminate/database": "^9.0|^10.0", - "illuminate/support": "^9.0|^10.0", - "nesbot/carbon": "^2.63", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/support": "^10.0", "php": "^8.1", - "spatie/laravel-package-tools": "^1.9" + "spatie/flare-client-php": "^1.3.5", + "spatie/ignition": "^1.9", + "symfony/console": "^6.2.3", + "symfony/var-dumper": "^6.2.3" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0", - "phpunit/phpunit": "^9.5" + "livewire/livewire": "^2.11", + "mockery/mockery": "^1.5.1", + "openai-php/client": "^0.3.4", + "orchestra/testbench": "^8.0", + "pestphp/pest": "^1.22.3", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.1.1", + "phpstan/phpstan-phpunit": "^1.3.3", + "vlucas/phpdotenv": "^5.5" + }, + "suggest": { + "openai-php/client": "Require get solutions from OpenAI", + "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" }, "type": "library", "extra": { "laravel": { "providers": [ - "Spatie\\EloquentSortable\\EloquentSortableServiceProvider" - ] + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ], + "aliases": { + "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" + } } }, "autoload": { + "files": [ + "src/helpers.php" + ], "psr-4": { - "Spatie\\EloquentSortable\\": "src/" + "Spatie\\LaravelIgnition\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3853,48 +9084,45 @@ ], "authors": [ { - "name": "Freek Van der Herten", - "email": "freek@spatie.be" + "name": "Spatie", + "email": "info@spatie.be", + "role": "Developer" } ], - "description": "Sortable behaviour for eloquent models", - "homepage": "https://github.com/spatie/eloquent-sortable", + "description": "A beautiful error page for Laravel applications.", + "homepage": "https://flareapp.io/ignition", "keywords": [ - "behaviour", - "eloquent", + "error", + "flare", "laravel", - "model", - "sort", - "sortable" + "page" ], "support": { - "issues": "https://github.com/spatie/eloquent-sortable/issues", - "source": "https://github.com/spatie/eloquent-sortable/tree/4.0.2" + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/spatie/laravel-ignition/issues", + "source": "https://github.com/spatie/laravel-ignition" }, "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, { "url": "https://github.com/spatie", "type": "github" } ], - "time": "2023-01-23T08:34:14+00:00" + "time": "2023-08-23T06:24:34+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.15.0", + "version": "1.16.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "efab1844b8826443135201c4443690f032c3d533" + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/efab1844b8826443135201c4443690f032c3d533", - "reference": "efab1844b8826443135201c4443690f032c3d533", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/cc7c991555a37f9fa6b814aa03af73f88026a83d", + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d", "shasum": "" }, "require": { @@ -3933,7 +9161,89 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.15.0" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-08-23T09:04:39+00:00" + }, + { + "name": "spatie/laravel-permission", + "version": "6.2.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-permission.git", + "reference": "299dd2c9bce700ea641021c1aea0dfece25e541d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/299dd2c9bce700ea641021c1aea0dfece25e541d", + "reference": "299dd2c9bce700ea641021c1aea0dfece25e541d", + "shasum": "" + }, + "require": { + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/passport": "^11.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0", + "phpunit/phpunit": "^9.4|^10.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.x-dev", + "dev-master": "6.x-dev" + }, + "laravel": { + "providers": [ + "Spatie\\Permission\\PermissionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Permission\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Permission handling for Laravel 8.0 and up", + "homepage": "https://github.com/spatie/laravel-permission", + "keywords": [ + "acl", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-permission/issues", + "source": "https://github.com/spatie/laravel-permission/tree/6.2.0" }, "funding": [ { @@ -3941,20 +9251,20 @@ "type": "github" } ], - "time": "2023-04-27T08:09:01+00:00" + "time": "2023-12-09T01:40:07+00:00" }, { "name": "spatie/laravel-query-builder", - "version": "5.2.0", + "version": "5.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-query-builder.git", - "reference": "7b3911f61dcaa27804b80f30b73a468a9539e850" + "reference": "dc7742cc44a37b7d1e00d80794b0ff46ea052cd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/7b3911f61dcaa27804b80f30b73a468a9539e850", - "reference": "7b3911f61dcaa27804b80f30b73a468a9539e850", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/dc7742cc44a37b7d1e00d80794b0ff46ea052cd8", + "reference": "dc7742cc44a37b7d1e00d80794b0ff46ea052cd8", "shasum": "" }, "require": { @@ -3969,6 +9279,8 @@ "mockery/mockery": "^1.4", "orchestra/testbench": "^7.0|^8.0", "pestphp/pest": "^1.20", + "phpunit/phpunit": "^9.6", + "spatie/invade": "^2.0", "spatie/laravel-ray": "^1.28" }, "type": "library", @@ -4013,20 +9325,20 @@ "type": "custom" } ], - "time": "2023-02-24T15:48:30+00:00" + "time": "2023-09-12T08:55:46+00:00" }, { "name": "spatie/laravel-tags", - "version": "4.4.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-tags.git", - "reference": "7cee368fc273f32b140ab7b8b81cd66026f89255" + "reference": "fcc6c532fa0ce0d1eefe886fd44dcedcca3173aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-tags/zipball/7cee368fc273f32b140ab7b8b81cd66026f89255", - "reference": "7cee368fc273f32b140ab7b8b81cd66026f89255", + "url": "https://api.github.com/repos/spatie/laravel-tags/zipball/fcc6c532fa0ce0d1eefe886fd44dcedcca3173aa", + "reference": "fcc6c532fa0ce0d1eefe886fd44dcedcca3173aa", "shasum": "" }, "require": { @@ -4075,7 +9387,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-tags/issues", - "source": "https://github.com/spatie/laravel-tags/tree/4.4.0" + "source": "https://github.com/spatie/laravel-tags/tree/4.5.1" }, "funding": [ { @@ -4083,20 +9395,20 @@ "type": "github" } ], - "time": "2023-05-16T15:24:45+00:00" + "time": "2023-07-31T08:43:55+00:00" }, { "name": "spatie/laravel-translatable", - "version": "6.5.1", + "version": "6.5.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-translatable.git", - "reference": "b8f102492f816608d832cfe9e420da6a856312cf" + "reference": "1906a3f1492c4b4b99d9f150b67cca4b697d85d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/b8f102492f816608d832cfe9e420da6a856312cf", - "reference": "b8f102492f816608d832cfe9e420da6a856312cf", + "url": "https://api.github.com/repos/spatie/laravel-translatable/zipball/1906a3f1492c4b4b99d9f150b67cca4b697d85d7", + "reference": "1906a3f1492c4b4b99d9f150b67cca4b697d85d7", "shasum": "" }, "require": { @@ -4157,7 +9469,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-translatable/issues", - "source": "https://github.com/spatie/laravel-translatable/tree/6.5.1" + "source": "https://github.com/spatie/laravel-translatable/tree/6.5.3" }, "funding": [ { @@ -4165,20 +9477,20 @@ "type": "github" } ], - "time": "2023-05-06T10:22:13+00:00" + "time": "2023-07-19T19:21:38+00:00" }, { "name": "symfony/console", - "version": "v6.3.0", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd", + "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd", "shasum": "" }, "require": { @@ -4186,7 +9498,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -4200,12 +9512,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4239,7 +9555,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.0" + "source": "https://github.com/symfony/console/tree/v6.4.1" }, "funding": [ { @@ -4255,24 +9571,24 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-11-30T10:54:28+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf" + "reference": "bb51d46e53ef8d50d523f0c5faedba056a27943e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/bb51d46e53ef8d50d523f0c5faedba056a27943e", + "reference": "bb51d46e53ef8d50d523f0c5faedba056a27943e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -4304,7 +9620,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.0" + "source": "https://github.com/symfony/css-selector/tree/v7.0.0" }, "funding": [ { @@ -4320,11 +9636,11 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:43:42+00:00" + "time": "2023-10-31T17:59:56+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -4371,7 +9687,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -4391,30 +9707,31 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99d2d814a6351461af350ead4d963bd67451236f" + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99d2d814a6351461af350ead4d963bd67451236f", - "reference": "99d2d814a6351461af350ead4d963bd67451236f", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c873490a1c97b3a0a4838afc36ff36c112d02788", + "reference": "c873490a1c97b3a0a4838afc36ff36c112d02788", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5" + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^5.4|^6.0|^7.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -4445,7 +9762,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.0" + "source": "https://github.com/symfony/error-handler/tree/v6.4.0" }, "funding": [ { @@ -4461,28 +9778,28 @@ "type": "tidelift" } ], - "time": "2023-05-10T12:03:13+00:00" + "time": "2023-10-18T09:43:34+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa" + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e", + "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4", + "symfony/dependency-injection": "<6.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -4491,13 +9808,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0" + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -4525,7 +9842,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0" }, "funding": [ { @@ -4541,11 +9858,11 @@ "type": "tidelift" } ], - "time": "2023-04-21T14:41:17+00:00" + "time": "2023-07-27T16:29:09+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", @@ -4573,8 +9890,143 @@ }, "autoload": { "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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 basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce", + "reference": "11d736e97f116ac375a81f96e662911a34cd50ce", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4582,26 +10034,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to dispatching event", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/finder/tree/v6.4.0" }, "funding": [ { @@ -4617,32 +10061,32 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-10-31T17:30:12+00:00" }, { - "name": "symfony/finder", - "version": "v6.3.0", + "name": "symfony/html-sanitizer", + "version": "v6.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + "url": "https://github.com/symfony/html-sanitizer.git", + "reference": "9cc71f272eb62504872c80845074f236e8e43536" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/9cc71f272eb62504872c80845074f236e8e43536", + "reference": "9cc71f272eb62504872c80845074f236e8e43536", "shasum": "" }, "require": { + "ext-dom": "*", + "league/uri": "^6.5|^7.0", + "masterminds/html5": "^2.7.2", "php": ">=8.1" }, - "require-dev": { - "symfony/filesystem": "^6.0" - }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Finder\\": "" + "Symfony\\Component\\HtmlSanitizer\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -4654,18 +10098,23 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Titouan Galopin", + "email": "galopintitouan@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Finds files and directories via an intuitive fluent interface", + "description": "Provides an object-oriented API to sanitize untrusted HTML input for safe insertion into a document's DOM.", "homepage": "https://symfony.com", + "keywords": [ + "Purifier", + "html", + "sanitizer" + ], "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.0" + "source": "https://github.com/symfony/html-sanitizer/tree/v6.4.0" }, "funding": [ { @@ -4681,20 +10130,20 @@ "type": "tidelift" } ], - "time": "2023-04-02T01:25:41+00:00" + "time": "2023-10-28T23:12:08+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "718a97ed430d34e5c568ea2c44eab708c6efbefb" + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/718a97ed430d34e5c568ea2c44eab708c6efbefb", - "reference": "718a97ed430d34e5c568ea2c44eab708c6efbefb", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/44a6d39a9cc11e154547d882d5aac1e014440771", + "reference": "44a6d39a9cc11e154547d882d5aac1e014440771", "shasum": "" }, "require": { @@ -4704,17 +10153,17 @@ "symfony/polyfill-php83": "^1.27" }, "conflict": { - "symfony/cache": "<6.2" + "symfony/cache": "<6.3" }, "require-dev": { - "doctrine/dbal": "^2.13.1|^3.0", + "doctrine/dbal": "^2.13.1|^3|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^5.4|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4742,7 +10191,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.0" }, "funding": [ { @@ -4758,29 +10207,29 @@ "type": "tidelift" } ], - "time": "2023-05-19T12:46:45+00:00" + "time": "2023-11-20T16:41:16+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.0", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "241973f3dd900620b1ca052fe409144f11aea748" + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/241973f3dd900620b1ca052fe409144f11aea748", - "reference": "241973f3dd900620b1ca052fe409144f11aea748", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2953274c16a229b3933ef73a6898e18388e12e1b", + "reference": "2953274c16a229b3933ef73a6898e18388e12e1b", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.2.7", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -4788,7 +10237,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3", + "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -4798,7 +10247,7 @@ "symfony/translation": "<5.4", "symfony/translation-contracts": "<2.5", "symfony/twig-bridge": "<5.4", - "symfony/validator": "<5.4", + "symfony/validator": "<6.4", "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, @@ -4807,26 +10256,26 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/clock": "^6.2", - "symfony/config": "^6.1", - "symfony/console": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/clock": "^6.2|^7.0", + "symfony/config": "^6.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^5.4|^6.0", - "symfony/property-access": "^5.4.5|^6.0.5", - "symfony/routing": "^5.4|^6.0", - "symfony/serializer": "^6.3", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4.5|^6.0.5|^7.0", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^6.3", - "symfony/var-exporter": "^6.2", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-exporter": "^6.2|^7.0", "twig/twig": "^2.13|^3.0.4" }, "type": "library", @@ -4855,7 +10304,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.0" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.1" }, "funding": [ { @@ -4871,20 +10320,20 @@ "type": "tidelift" } ], - "time": "2023-05-30T19:03:32+00:00" + "time": "2023-12-01T17:02:02+00:00" }, { "name": "symfony/mailer", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435" + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/7b03d9be1dea29bfec0a6c7b603f5072a4c97435", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", + "reference": "ca8dcf8892cdc5b4358ecf2528429bb5e706f7ba", "shasum": "" }, "require": { @@ -4892,8 +10341,8 @@ "php": ">=8.1", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/mime": "^6.2", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/mime": "^6.2|^7.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4904,10 +10353,10 @@ "symfony/twig-bridge": "<6.2.1" }, "require-dev": { - "symfony/console": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/messenger": "^6.2", - "symfony/twig-bridge": "^6.2" + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/messenger": "^6.2|^7.0", + "symfony/twig-bridge": "^6.2|^7.0" }, "type": "library", "autoload": { @@ -4935,7 +10384,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.3.0" + "source": "https://github.com/symfony/mailer/tree/v6.4.0" }, "funding": [ { @@ -4951,24 +10400,25 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-11-12T18:02:22+00:00" }, { "name": "symfony/mime", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad" + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", + "url": "https://api.github.com/repos/symfony/mime/zipball/ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", + "reference": "ca4f58b2ef4baa8f6cecbeca2573f88cd577d205", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -4977,16 +10427,16 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2" + "symfony/serializer": "<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "^6.2" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.3.2|^7.0" }, "type": "library", "autoload": { @@ -5018,7 +10468,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.0" + "source": "https://github.com/symfony/mime/tree/v6.4.0" }, "funding": [ { @@ -5034,20 +10484,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T15:57:00+00:00" + "time": "2023-10-17T11:49:05+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -5062,7 +10512,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5100,7 +10550,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -5116,20 +10566,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -5141,7 +10591,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5181,7 +10631,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -5197,20 +10647,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", "shasum": "" }, "require": { @@ -5224,7 +10674,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5268,7 +10718,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" }, "funding": [ { @@ -5284,20 +10734,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:30:37+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -5309,7 +10759,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5352,7 +10802,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -5368,20 +10818,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -5396,7 +10846,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5435,7 +10885,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -5451,20 +10901,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { @@ -5473,7 +10923,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5511,7 +10961,86 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "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 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" }, "funding": [ { @@ -5527,20 +11056,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -5549,7 +11078,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5594,7 +11123,86 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "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.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" }, "funding": [ { @@ -5610,20 +11218,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57" + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/508c652ba3ccf69f8c97f251534f229791b52a57", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", "shasum": "" }, "require": { @@ -5633,7 +11241,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5646,7 +11254,10 @@ ], "psr-4": { "Symfony\\Polyfill\\Php83\\": "" - } + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5671,7 +11282,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" }, "funding": [ { @@ -5687,20 +11298,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-08-16T06:22:46+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166" + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e", + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e", "shasum": "" }, "require": { @@ -5715,7 +11326,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5753,7 +11364,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0" }, "funding": [ { @@ -5769,20 +11380,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/process", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "url": "https://api.github.com/repos/symfony/process/zipball/191703b1566d97a5425dc969e4350d32b8ef17aa", + "reference": "191703b1566d97a5425dc969e4350d32b8ef17aa", "shasum": "" }, "require": { @@ -5814,7 +11425,96 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.0" + "source": "https://github.com/symfony/process/tree/v6.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-17T21:06:49+00:00" + }, + { + "name": "symfony/psr-http-message-bridge", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/581ca6067eb62640de5ff08ee1ba6850a0ee472e", + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/http-foundation": "^5.4 || ^6.0" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "psr/log": "^1.1 || ^2 || ^3", + "symfony/browser-kit": "^5.4 || ^6.0", + "symfony/config": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/framework-bundle": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/phpunit-bridge": "^6.2" + }, + "suggest": { + "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-main": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "http://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/symfony/psr-http-message-bridge/issues", + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.3.1" }, "funding": [ { @@ -5830,24 +11530,25 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-07-26T11:53:26+00:00" }, { "name": "symfony/routing", - "version": "v6.3.0", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "827f59fdc67eecfc4dfff81f9c93bf4d98f0c89b" + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/827f59fdc67eecfc4dfff81f9c93bf4d98f0c89b", - "reference": "827f59fdc67eecfc4dfff81f9c93bf4d98f0c89b", + "url": "https://api.github.com/repos/symfony/routing/zipball/0c95c164fdba18b12523b75e64199ca3503e6d40", + "reference": "0c95c164fdba18b12523b75e64199ca3503e6d40", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", @@ -5858,11 +11559,11 @@ "require-dev": { "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^6.2", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "symfony/config": "^6.2|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5896,7 +11597,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.0" + "source": "https://github.com/symfony/routing/tree/v6.4.1" }, "funding": [ { @@ -5912,20 +11613,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T15:57:00+00:00" + "time": "2023-12-01T14:54:37+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", - "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { @@ -5978,7 +11679,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -5994,24 +11695,24 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/string", - "version": "v6.3.0", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -6021,11 +11722,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -6064,7 +11765,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.0" + "source": "https://github.com/symfony/string/tree/v7.0.0" }, "funding": [ { @@ -6080,24 +11781,25 @@ "type": "tidelift" } ], - "time": "2023-03-21T21:06:29+00:00" + "time": "2023-11-29T08:40:23+00:00" }, { "name": "symfony/translation", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f" + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/f72b2cba8f79dd9d536f534f76874b58ad37876f", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f", + "url": "https://api.github.com/repos/symfony/translation/zipball/b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", + "reference": "b1035dbc2a344b21f8fa8ac451c7ecec4ea45f37", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -6117,17 +11819,17 @@ "require-dev": { "nikic/php-parser": "^4.13", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0" + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6158,7 +11860,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.3.0" + "source": "https://github.com/symfony/translation/tree/v6.4.0" }, "funding": [ { @@ -6174,20 +11876,20 @@ "type": "tidelift" } ], - "time": "2023-05-19T12:46:45+00:00" + "time": "2023-11-29T08:14:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86" + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/02c24deb352fb0d79db5486c0c79905a85e37e86", - "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", "shasum": "" }, "require": { @@ -6236,7 +11938,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" }, "funding": [ { @@ -6252,20 +11954,20 @@ "type": "tidelift" } ], - "time": "2023-05-30T17:17:10+00:00" + "time": "2023-07-25T15:08:44+00:00" }, { "name": "symfony/uid", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384" + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384", - "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384", + "url": "https://api.github.com/repos/symfony/uid/zipball/8092dd1b1a41372110d06374f99ee62f7f0b9a92", + "reference": "8092dd1b1a41372110d06374f99ee62f7f0b9a92", "shasum": "" }, "require": { @@ -6273,7 +11975,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^5.4|^6.0" + "symfony/console": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6310,7 +12012,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.3.0" + "source": "https://github.com/symfony/uid/tree/v6.4.0" }, "funding": [ { @@ -6326,24 +12028,25 @@ "type": "tidelift" } ], - "time": "2023-04-08T07:25:02+00:00" + "time": "2023-10-31T08:18:17+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.0", + "version": "v6.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "6acdcd5c122074ee9f7b051e4fb177025c277a0e" + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6acdcd5c122074ee9f7b051e4fb177025c277a0e", - "reference": "6acdcd5c122074ee9f7b051e4fb177025c277a0e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c40f7d17e91d8b407582ed51a2bbf83c52c367f6", + "reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -6351,9 +12054,11 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, "bin": [ @@ -6392,7 +12097,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.0" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.0" }, "funding": [ { @@ -6408,20 +12113,20 @@ "type": "tidelift" } ], - "time": "2023-05-25T13:09:35+00:00" + "time": "2023-11-09T08:28:32+00:00" }, { "name": "tightenco/ziggy", - "version": "v1.6.0", + "version": "v1.6.2", "source": { "type": "git", "url": "https://github.com/tighten/ziggy.git", - "reference": "3beb080be60b1eadad043f3773a160df13fa215f" + "reference": "41eb6384a9f9ae85cf54d6dc8f98dab282b07890" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tighten/ziggy/zipball/3beb080be60b1eadad043f3773a160df13fa215f", - "reference": "3beb080be60b1eadad043f3773a160df13fa215f", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/41eb6384a9f9ae85cf54d6dc8f98dab282b07890", + "reference": "41eb6384a9f9ae85cf54d6dc8f98dab282b07890", "shasum": "" }, "require": { @@ -6473,29 +12178,29 @@ ], "support": { "issues": "https://github.com/tighten/ziggy/issues", - "source": "https://github.com/tighten/ziggy/tree/v1.6.0" + "source": "https://github.com/tighten/ziggy/tree/v1.6.2" }, - "time": "2023-05-12T20:08:56+00:00" + "time": "2023-08-18T20:28:21+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "2.2.6", + "version": "v2.2.7", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c" + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/c42125b83a4fa63b187fdf29f9c93cb7733da30c", - "reference": "c42125b83a4fa63b187fdf29f9c93cb7733da30c", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", + "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" @@ -6526,37 +12231,37 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.6" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" }, - "time": "2023-01-03T09:29:04+00:00" + "time": "2023-12-08T13:03:43+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", - "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.0.2", - "php": "^7.1.3 || ^8.0", - "phpoption/phpoption": "^1.8", - "symfony/polyfill-ctype": "^1.23", - "symfony/polyfill-mbstring": "^1.23.1", - "symfony/polyfill-php80": "^1.23.1" + "graham-campbell/result-type": "^1.1.2", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.2", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "suggest": { "ext-filter": "Required to use the boolean validator." @@ -6568,7 +12273,7 @@ "forward-command": true }, "branch-alias": { - "dev-master": "5.5-dev" + "dev-master": "5.6-dev" } }, "autoload": { @@ -6600,7 +12305,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" }, "funding": [ { @@ -6612,7 +12317,7 @@ "type": "tidelift" } ], - "time": "2022-10-16T01:01:54+00:00" + "time": "2023-11-12T22:43:29+00:00" }, { "name": "voku/portable-ascii", @@ -6750,16 +12455,16 @@ "packages-dev": [ { "name": "fakerphp/faker", - "version": "v1.22.0", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2" + "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2", - "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", + "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01", "shasum": "" }, "require": { @@ -6812,22 +12517,22 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.23.0" }, - "time": "2023-05-14T12:31:37+00:00" + "time": "2023-06-12T08:44:38+00:00" }, { "name": "filp/whoops", - "version": "2.15.2", + "version": "2.15.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73" + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", + "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", "shasum": "" }, "require": { @@ -6877,7 +12582,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.2" + "source": "https://github.com/filp/whoops/tree/2.15.3" }, "funding": [ { @@ -6885,7 +12590,7 @@ "type": "github" } ], - "time": "2023-04-12T12:00:00+00:00" + "time": "2023-07-13T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -6940,16 +12645,16 @@ }, { "name": "laravel/pint", - "version": "v1.10.1", + "version": "v1.13.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "d69f914aa347a448628b672ba90adf0b4ea0ce4a" + "reference": "93b2d0d49719bc6e444ba21cd4dbbccec935413d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/d69f914aa347a448628b672ba90adf0b4ea0ce4a", - "reference": "d69f914aa347a448628b672ba90adf0b4ea0ce4a", + "url": "https://api.github.com/repos/laravel/pint/zipball/93b2d0d49719bc6e444ba21cd4dbbccec935413d", + "reference": "93b2d0d49719bc6e444ba21cd4dbbccec935413d", "shasum": "" }, "require": { @@ -6960,13 +12665,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.17.0", - "illuminate/view": "^10.5.1", - "laravel-zero/framework": "^10.0.2", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.5.1", + "friendsofphp/php-cs-fixer": "^3.34.1", + "illuminate/view": "^10.23.1", + "laravel-zero/framework": "^10.1.2", + "mockery/mockery": "^1.6.6", + "nunomaduro/larastan": "^2.6.4", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.4.0" + "pestphp/pest": "^2.18.2" }, "bin": [ "builds/pint" @@ -7002,20 +12707,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-06-03T15:01:17+00:00" + "time": "2023-10-10T15:39:09+00:00" }, { "name": "laravel/sail", - "version": "v1.22.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "923e1e112b6a8598664dbb0ee79dd3137f1c9d56" + "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/923e1e112b6a8598664dbb0ee79dd3137f1c9d56", - "reference": "923e1e112b6a8598664dbb0ee79dd3137f1c9d56", + "url": "https://api.github.com/repos/laravel/sail/zipball/e81a7bd7ac1a745ccb25572830fecf74a89bb48a", + "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a", "shasum": "" }, "require": { @@ -7067,41 +12772,35 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2023-05-04T14:52:56+00:00" + "time": "2023-09-11T17:37:09+00:00" }, { "name": "mockery/mockery", - "version": "1.6.2", + "version": "1.6.7", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191" + "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/13a7fa2642c76c58fa2806ef7f565344c817a191", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191", + "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.4 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3", - "psalm/plugin-phpunit": "^0.18", - "vimeo/psalm": "^5.9" + "phpunit/phpunit": "^8.5 || ^9.6.10", + "symplify/easy-coding-standard": "^12.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.6.x-dev" - } - }, "autoload": { "files": [ "library/helpers.php", @@ -7119,12 +12818,20 @@ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -7142,10 +12849,13 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.6.2" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2023-06-07T09:07:52+00:00" + "time": "2023-12-10T02:24:34+00:00" }, { "name": "myclabs/deep-copy", @@ -7208,40 +12918,37 @@ }, { "name": "nunomaduro/collision", - "version": "v7.5.2", + "version": "v7.9.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "76b3cabda0aabda455fc3b9db6c3615f5a87c7ff" + "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/76b3cabda0aabda455fc3b9db6c3615f5a87c7ff", - "reference": "76b3cabda0aabda455fc3b9db6c3615f5a87c7ff", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/296d0cf9fe462837ac0da8a568b56fc026b132da", + "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da", "shasum": "" }, "require": { - "filp/whoops": "^2.15.2", + "filp/whoops": "^2.15.3", "nunomaduro/termwind": "^1.15.1", "php": "^8.1.0", - "symfony/console": "^6.2.8" + "symfony/console": "^6.3.4" }, - "conflict": { - "phpunit/phpunit": "<10.1.2" - }, - "require-dev": { - "brianium/paratest": "^7.1.3", - "laravel/framework": "^10.8.0", - "laravel/pint": "^1.9.0", - "laravel/sail": "^1.21.4", - "laravel/sanctum": "^3.2.1", - "laravel/tinker": "^2.8.1", - "nunomaduro/larastan": "^2.6.0", - "orchestra/testbench-core": "^8.5.0", - "pestphp/pest": "^2.5.2", - "phpunit/phpunit": "^10.1.1", + "require-dev": { + "brianium/paratest": "^7.2.7", + "laravel/framework": "^10.23.1", + "laravel/pint": "^1.13.1", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.11.0", + "pestphp/pest": "^2.19.1", + "phpunit/phpunit": "^10.3.5", "sebastian/environment": "^6.0.1", - "spatie/laravel-ignition": "^2.1.0" + "spatie/laravel-ignition": "^2.3.0" }, "type": "library", "extra": { @@ -7300,7 +13007,7 @@ "type": "patreon" } ], - "time": "2023-04-22T22:12:40+00:00" + "time": "2023-09-19T10:45:09+00:00" }, { "name": "phar-io/manifest", @@ -7415,16 +13122,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "56f33548fe522c8d82da7ff3824b42829d324364" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364", + "reference": "56f33548fe522c8d82da7ff3824b42829d324364", "shasum": "" }, "require": { @@ -7481,7 +13188,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6" }, "funding": [ { @@ -7489,20 +13196,20 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-09-19T04:59:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.0.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "5647d65443818959172645e7ed999217360654b6" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", - "reference": "5647d65443818959172645e7ed999217360654b6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { @@ -7542,7 +13249,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.0.2" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -7550,7 +13257,7 @@ "type": "github" } ], - "time": "2023-05-07T09:13:23+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", @@ -7617,16 +13324,16 @@ }, { "name": "phpunit/php-text-template", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { @@ -7664,7 +13371,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -7672,7 +13380,7 @@ "type": "github" } ], - "time": "2023-02-03T06:56:46+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", @@ -7735,16 +13443,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.1", + "version": "10.3.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "599b33294350e8f51163119d5670512f98b0490d" + "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/599b33294350e8f51163119d5670512f98b0490d", - "reference": "599b33294350e8f51163119d5670512f98b0490d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503", + "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503", "shasum": "" }, "require": { @@ -7758,7 +13466,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.1", + "phpunit/php-code-coverage": "^10.1.5", "phpunit/php-file-iterator": "^4.0", "phpunit/php-invoker": "^4.0", "phpunit/php-text-template": "^3.0", @@ -7768,8 +13476,8 @@ "sebastian/comparator": "^5.0", "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -7784,7 +13492,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.2-dev" + "dev-main": "10.3-dev" } }, "autoload": { @@ -7816,7 +13524,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5" }, "funding": [ { @@ -7832,7 +13540,7 @@ "type": "tidelift" } ], - "time": "2023-06-05T05:15:51+00:00" + "time": "2023-09-19T05:42:37+00:00" }, { "name": "sebastian/cli-parser", @@ -8003,16 +13711,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -8023,7 +13731,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -8067,7 +13775,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -8075,20 +13784,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:16+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -8101,7 +13810,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -8124,7 +13833,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -8132,7 +13842,7 @@ "type": "github" } ], - "time": "2023-02-03T06:59:47+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -8267,16 +13977,16 @@ }, { "name": "sebastian/exporter", - "version": "5.0.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -8290,7 +14000,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -8332,7 +14042,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -8340,20 +14051,20 @@ "type": "github" } ], - "time": "2023-02-03T07:06:49+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "aab257c712de87b90194febd52e4d184551c2d44" + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", - "reference": "aab257c712de87b90194febd52e4d184551c2d44", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", "shasum": "" }, "require": { @@ -8393,7 +14104,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" }, "funding": [ { @@ -8401,20 +14113,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:38+00:00" + "time": "2023-07-19T07:19:23+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "shasum": "" }, "require": { @@ -8450,7 +14162,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" }, "funding": [ { @@ -8458,7 +14171,7 @@ "type": "github" } ], - "time": "2023-02-03T07:08:02+00:00" + "time": "2023-08-31T09:25:50+00:00" }, { "name": "sebastian/object-enumerator", @@ -8518,554 +14231,249 @@ "time": "2023-02-03T07:08:32+00:00" }, { - "name": "sebastian/object-reflector", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:06:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:05:40+00:00" - }, - { - "name": "sebastian/type", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:10:45+00:00" - }, - { - "name": "sebastian/version", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-07T11:34:05+00:00" - }, - { - "name": "spatie/backtrace", - "version": "1.4.0", + "name": "sebastian/object-reflector", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/spatie/backtrace.git", - "reference": "ec4dd16476b802dbdc6b4467f84032837e316b8c" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/ec4dd16476b802dbdc6b4467f84032837e316b8c", - "reference": "ec4dd16476b802dbdc6b4467f84032837e316b8c", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": ">=8.1" }, "require-dev": { - "ext-json": "*", - "phpunit/phpunit": "^9.3", - "spatie/phpunit-snapshot-assertions": "^4.2", - "symfony/var-dumper": "^5.1" + "phpunit/phpunit": "^10.0" }, "type": "library", - "autoload": { - "psr-4": { - "Spatie\\Backtrace\\": "src" + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Freek Van de Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "A better backtrace", - "homepage": "https://github.com/spatie/backtrace", - "keywords": [ - "Backtrace", - "spatie" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/spatie/backtrace/tree/1.4.0" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { - "url": "https://github.com/sponsors/spatie", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://spatie.be/open-source/support-us", - "type": "other" } ], - "time": "2023-03-04T08:57:24+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { - "name": "spatie/flare-client-php", - "version": "1.3.6", + "name": "sebastian/recursion-context", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/spatie/flare-client-php.git", - "reference": "530ac81255af79f114344286e4275f8869c671e2" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/530ac81255af79f114344286e4275f8869c671e2", - "reference": "530ac81255af79f114344286e4275f8869c671e2", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "illuminate/pipeline": "^8.0|^9.0|^10.0", - "php": "^8.0", - "spatie/backtrace": "^1.2", - "symfony/http-foundation": "^5.0|^6.0", - "symfony/mime": "^5.2|^6.0", - "symfony/process": "^5.2|^6.0", - "symfony/var-dumper": "^5.2|^6.0" + "php": ">=8.1" }, "require-dev": { - "dms/phpunit-arraysubset-asserts": "^0.3.0", - "pestphp/pest": "^1.20", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "spatie/phpunit-snapshot-assertions": "^4.0" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1.x-dev" + "dev-main": "5.0-dev" } }, "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Spatie\\FlareClient\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "Send PHP errors to Flare", - "homepage": "https://github.com/spatie/flare-client-php", - "keywords": [ - "exception", - "flare", - "reporting", - "spatie" + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.3.6" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { - "url": "https://github.com/spatie", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2023-04-12T07:57:12+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "spatie/ignition", - "version": "1.8.1", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/spatie/ignition.git", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/d8eb8ea1ed27f48a694405cff363746ffd37f13e", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^8.0", - "spatie/backtrace": "^1.4", - "spatie/flare-client-php": "^1.1", - "symfony/console": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "php": ">=8.1" }, "require-dev": { - "illuminate/cache": "^9.52", - "mockery/mockery": "^1.4", - "pestphp/pest": "^1.20", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "psr/simple-cache-implementation": "*", - "symfony/cache": "^6.2", - "symfony/process": "^5.4|^6.0", - "vlucas/phpdotenv": "^5.5" - }, - "suggest": { - "openai-php/client": "Require get solutions from OpenAI", - "simple-cache-implementation": "To cache solutions from OpenAI" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.5.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "Spatie\\Ignition\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Spatie", - "email": "info@spatie.be", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A beautiful error page for PHP applications.", - "homepage": "https://flareapp.io/ignition", - "keywords": [ - "error", - "flare", - "laravel", - "page" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", - "forum": "https://twitter.com/flareappio", - "issues": "https://github.com/spatie/ignition/issues", - "source": "https://github.com/spatie/ignition" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { - "url": "https://github.com/spatie", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2023-06-06T14:14:58+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { - "name": "spatie/laravel-ignition", - "version": "2.1.3", + "name": "sebastian/version", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/35711943d4725aa80f8033e4f1cb3a6775530b25", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "illuminate/support": "^10.0", - "php": "^8.1", - "spatie/flare-client-php": "^1.3.5", - "spatie/ignition": "^1.5.0", - "symfony/console": "^6.2.3", - "symfony/var-dumper": "^6.2.3" - }, - "require-dev": { - "livewire/livewire": "^2.11", - "mockery/mockery": "^1.5.1", - "openai-php/client": "^0.3.4", - "orchestra/testbench": "^8.0", - "pestphp/pest": "^1.22.3", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan-deprecation-rules": "^1.1.1", - "phpstan/phpstan-phpunit": "^1.3.3", - "vlucas/phpdotenv": "^5.5" - }, - "suggest": { - "openai-php/client": "Require get solutions from OpenAI", - "psr/simple-cache-implementation": "Needed to cache solutions from OpenAI" + "php": ">=8.1" }, "type": "library", "extra": { - "laravel": { - "providers": [ - "Spatie\\LaravelIgnition\\IgnitionServiceProvider" - ], - "aliases": { - "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" - } + "branch-alias": { + "dev-main": "4.0-dev" } }, "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Spatie\\LaravelIgnition\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Spatie", - "email": "info@spatie.be", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A beautiful error page for Laravel applications.", - "homepage": "https://flareapp.io/ignition", - "keywords": [ - "error", - "flare", - "laravel", - "page" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", - "forum": "https://twitter.com/flareappio", - "issues": "https://github.com/spatie/laravel-ignition/issues", - "source": "https://github.com/spatie/laravel-ignition" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { - "url": "https://github.com/spatie", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2023-05-25T11:30:27+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "symfony/yaml", - "version": "v6.3.0", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a9a8337aa641ef2aa39c3e028f9107ec391e5927" + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a9a8337aa641ef2aa39c3e028f9107ec391e5927", - "reference": "a9a8337aa641ef2aa39c3e028f9107ec391e5927", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e23292e8c07c85b971b44c1c4b87af52133e2add", + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -9103,7 +14511,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.3.0" + "source": "https://github.com/symfony/yaml/tree/v6.3.3" }, "funding": [ { @@ -9119,7 +14527,7 @@ "type": "tidelift" } ], - "time": "2023-04-28T13:28:14+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "theseer/tokenizer", @@ -9173,13 +14581,19 @@ } ], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.2", + "ext-bcmath": "*", + "ext-dom": "*", + "ext-imap": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "ext-ssh2": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/activitylog.php b/config/activitylog.php new file mode 100644 index 0000000..0eda096 --- /dev/null +++ b/config/activitylog.php @@ -0,0 +1,54 @@ + env('ACTIVITY_LOGGER_ENABLED', true), + + /* + * When the clean-command is executed, all recording activities older than + * the number of days specified here will be deleted. + */ + 'delete_records_older_than_days' => 365, + + /* + * If no log name is passed to the activity() helper + * we use this default log name. + */ + 'default_log_name' => 'default', + + /* + * You can specify an auth driver here that gets user models. + * If this is null we'll use the current Laravel auth driver. + */ + 'default_auth_driver' => null, + + /* + * If set to true, the subject returns soft deleted models. + */ + 'subject_returns_soft_deleted_models' => false, + + /* + * This model will be used to log activity. + * It should implement the Spatie\Activitylog\Contracts\Activity interface + * and extend Illuminate\Database\Eloquent\Model. + */ + 'activity_model' => \Spatie\Activitylog\Models\Activity::class, + + /* + * This is the name of the table that will be created by the migration and + * used by the Activity model shipped with this package. + */ + 'table_name' => 'activity_log', + + /* + * This is the database connection that will be used by the migration and + * the Activity model shipped with this package. In case it's not set + * Laravel's database.default will be used instead. + */ + 'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'), +]; diff --git a/config/app.php b/config/app.php index 1799d10..63bb19c 100644 --- a/config/app.php +++ b/config/app.php @@ -161,7 +161,7 @@ /* * Package Service Providers... */ - + Bugsnag\BugsnagLaravel\BugsnagServiceProvider::class, /* * Application Service Providers... */ @@ -169,9 +169,12 @@ App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, + App\Providers\HorizonServiceProvider::class, + App\Providers\Filament\AdminPanelProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\FortifyServiceProvider::class, App\Providers\JetstreamServiceProvider::class, + App\Providers\OperationServiceProvider::class, ])->toArray(), /* @@ -187,6 +190,7 @@ 'aliases' => Facade::defaultAliases()->merge([ // 'Example' => App\Facades\Example::class, + 'Operator' => App\Operations\Operator::class, ])->toArray(), ]; diff --git a/config/auth.php b/config/auth.php index 3fdb672..c6c76da 100644 --- a/config/auth.php +++ b/config/auth.php @@ -114,4 +114,5 @@ 'password_timeout' => 10800, + 'admin_emails' => explode(',', env('SPORK_ADMIN_EMAILS', '')), ]; diff --git a/config/bugsnag.php b/config/bugsnag.php new file mode 100644 index 0000000..d9f4c46 --- /dev/null +++ b/config/bugsnag.php @@ -0,0 +1,366 @@ + env('BUGSNAG_API_KEY', ''), + + /* + |-------------------------------------------------------------------------- + | App Type + |-------------------------------------------------------------------------- + | + | Set the type of application executing the current code. + | + */ + + 'app_type' => env('BUGSNAG_APP_TYPE'), + + /* + |-------------------------------------------------------------------------- + | App Version + |-------------------------------------------------------------------------- + | + | Set the version of application executing the current code. + | + */ + + 'app_version' => env('BUGSNAG_APP_VERSION'), + + /* + |-------------------------------------------------------------------------- + | Batch Sending + |-------------------------------------------------------------------------- + | + | Set to true to send the errors through to Bugsnag when the PHP process + | shuts down, in order to prevent your app waiting on HTTP requests. + | + | Setting this to false will send an HTTP request straight away for each + | error. + | + */ + + 'batch_sending' => env('BUGSNAG_BATCH_SENDING'), + + /* + |-------------------------------------------------------------------------- + | Endpoint + |-------------------------------------------------------------------------- + | + | Set what server the Bugsnag notifier should send errors to. By default + | this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise + | this should be the URL to your Bugsnag instance. + | + */ + + 'endpoint' => env('BUGSNAG_ENDPOINT'), + + /* + |-------------------------------------------------------------------------- + | Filters + |-------------------------------------------------------------------------- + | + | Use this if you want to ensure you don't send sensitive data such as + | passwords, and credit card numbers to our servers. Any keys which + | contain these strings will be filtered. + | + | This option has been deprecated in favour of 'redacted_keys' + | + */ + + 'filters' => empty(env('BUGSNAG_FILTERS')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_FILTERS'))), + + /* + |-------------------------------------------------------------------------- + | Hostname + |-------------------------------------------------------------------------- + | + | You can set the hostname of your server to something specific for you to + | identify it by if needed. + | + */ + + 'hostname' => env('BUGSNAG_HOSTNAME'), + + /* + |-------------------------------------------------------------------------- + | Proxy + |-------------------------------------------------------------------------- + | + | This is where you can set the proxy settings you'd like us to use when + | communicating with Bugsnag when reporting errors. + | + */ + + 'proxy' => array_filter([ + 'http' => env('HTTP_PROXY'), + 'https' => env('HTTPS_PROXY'), + 'no' => empty(env('NO_PROXY')) ? null : explode(',', str_replace(' ', '', env('NO_PROXY'))), + ]), + + /* + |-------------------------------------------------------------------------- + | Project Root + |-------------------------------------------------------------------------- + | + | Bugsnag marks stacktrace lines as in-project if they come from files + | inside your “project root”. You can set this here. + | + | If this is not set, we will automatically try to detect it. + | + */ + + 'project_root' => env('BUGSNAG_PROJECT_ROOT'), + + /* + |-------------------------------------------------------------------------- + | Project Root Regex + |-------------------------------------------------------------------------- + | + | Bugsnag marks stacktrace lines as in-project if they come from files + | inside your “project root”. You can set this here. + | + | This option allows you to set it as a regular expression and will take + | precedence over "project_root" if both are defined. + | + */ + + 'project_root_regex' => env('BUGSNAG_PROJECT_ROOT_REGEX'), + + /* + |-------------------------------------------------------------------------- + | Strip Path + |-------------------------------------------------------------------------- + | + | The strip path is a path to be trimmed from the start of any filepaths in + | your stacktraces. + | + | If this is not set, we will automatically try to detect it. + | + */ + + 'strip_path' => env('BUGSNAG_STRIP_PATH'), + + /* + |-------------------------------------------------------------------------- + | Strip Path Regex + |-------------------------------------------------------------------------- + | + | The strip path is a path to be trimmed from the start of any filepaths in + | your stacktraces. + | + | This option allows you to set it as a regular expression and will take + | precedence over "strip_path" if both are defined. + | + */ + + 'strip_path_regex' => env('BUGSNAG_STRIP_PATH_REGEX'), + + /* + |-------------------------------------------------------------------------- + | Query + |-------------------------------------------------------------------------- + | + | Enable this if you'd like us to automatically record all queries executed + | as breadcrumbs. + | + */ + + 'query' => env('BUGSNAG_QUERY', true), + + /* + |-------------------------------------------------------------------------- + | Bindings + |-------------------------------------------------------------------------- + | + | Enable this if you'd like us to include the query bindings in our query + | breadcrumbs. + | + */ + + 'bindings' => env('BUGSNAG_QUERY_BINDINGS', false), + + /* + |-------------------------------------------------------------------------- + | Release Stage + |-------------------------------------------------------------------------- + | + | Set the release stage to use when sending notifications to Bugsnag. + | + | Leaving this unset will default to using the application environment. + | + */ + + 'release_stage' => env('BUGSNAG_RELEASE_STAGE'), + + /* + |-------------------------------------------------------------------------- + | Notify Release Stages + |-------------------------------------------------------------------------- + | + | Set which release stages should send notifications to Bugsnag. + | + */ + + 'notify_release_stages' => empty(env('BUGSNAG_NOTIFY_RELEASE_STAGES')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_NOTIFY_RELEASE_STAGES'))), + + /* + |-------------------------------------------------------------------------- + | Send Code + |-------------------------------------------------------------------------- + | + | Bugsnag automatically sends a small snippet of the code that crashed to + | help you diagnose even faster from within your dashboard. If you don’t + | want to send this snippet, then set this to false. + | + */ + + 'send_code' => env('BUGSNAG_SEND_CODE', true), + + /* + |-------------------------------------------------------------------------- + | Callbacks + |-------------------------------------------------------------------------- + | + | Enable this if you'd like us to enable our default set of notification + | callbacks. These add things like the cookie information and session + | details to the error to be sent to Bugsnag. + | + | If you'd like to add your own callbacks, you can call the + | Bugsnag::registerCallback method from the boot method of your app + | service provider. + | + */ + + 'callbacks' => env('BUGSNAG_CALLBACKS', true), + + /* + |-------------------------------------------------------------------------- + | User + |-------------------------------------------------------------------------- + | + | Enable this if you'd like us to set the current user logged in via + | Laravel's authentication system. + | + | If you'd like to add your own user resolver, you can do this by using + | callbacks via Bugsnag::registerCallback. + | + */ + + 'user' => env('BUGSNAG_USER', true), + + /* + |-------------------------------------------------------------------------- + | Logger Notify Level + |-------------------------------------------------------------------------- + | + | This sets the level at which a logged message will trigger a notification + | to Bugsnag. By default this level will be 'notice'. + | + | Must be one of the Psr\Log\LogLevel levels from the Psr specification. + | + */ + + 'logger_notify_level' => env('BUGSNAG_LOGGER_LEVEL'), + + /* + |-------------------------------------------------------------------------- + | Auto Capture Sessions + |-------------------------------------------------------------------------- + | + | Enable this to start tracking sessions and deliver them to Bugsnag. + | + */ + + 'auto_capture_sessions' => env('BUGSNAG_CAPTURE_SESSIONS', false), + + /* + |-------------------------------------------------------------------------- + | Sessions Endpoint + |-------------------------------------------------------------------------- + | + | Sets a url to send tracked sessions to. + | + */ + + 'session_endpoint' => env('BUGSNAG_SESSION_ENDPOINT'), + + /* + |-------------------------------------------------------------------------- + | Builds Endpoint + |-------------------------------------------------------------------------- + | + | Sets a url to send build reports to. + | + */ + + 'build_endpoint' => env('BUGSNAG_BUILD_ENDPOINT'), + + /* + |-------------------------------------------------------------------------- + | Discard Classes + |-------------------------------------------------------------------------- + | + | An array of classes that should not be sent to Bugsnag. + | + | This can contain both fully qualified class names and regular expressions. + | + */ + + 'discard_classes' => empty(env('BUGSNAG_DISCARD_CLASSES')) ? null : explode(',', env('BUGSNAG_DISCARD_CLASSES')), + + /* + |-------------------------------------------------------------------------- + | Redacted Keys + |-------------------------------------------------------------------------- + | + | An array of metadata keys that should be redacted. + | + */ + + 'redacted_keys' => empty(env('BUGSNAG_REDACTED_KEYS')) ? null : explode(',', env('BUGSNAG_REDACTED_KEYS')), + + /* + |-------------------------------------------------------------------------- + | Feature flags + |-------------------------------------------------------------------------- + | + | An array of feature flags to add to all reports. + | + | Each element in the array must have a "name" key and can optionally have a + | "variant" key, for example: + | + | [ + | ['name' => 'example without a variant'], + | ['name' => 'example with a variant', 'variant' => 'example of a variant'], + | ] + | + */ + + 'feature_flags' => [], + + /* + |-------------------------------------------------------------------------- + | Max breadcrumbs + |-------------------------------------------------------------------------- + | + | The maximum number of breadcrumbs to send with a report. + | + | This should be an integer between 0-100 (inclusive). + */ + + 'max_breadcrumbs' => null, +]; diff --git a/config/database.php b/config/database.php index f75f0d9..c4173e6 100644 --- a/config/database.php +++ b/config/database.php @@ -40,7 +40,7 @@ 'sqlite' => [ 'driver' => 'sqlite', 'url' => env('DATABASE_URL'), - 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'database' => env('DB_DATABASE'), 'prefix' => '', 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), ], diff --git a/config/feed.php b/config/feed.php new file mode 100644 index 0000000..30043bb --- /dev/null +++ b/config/feed.php @@ -0,0 +1,57 @@ + [ + 'main' => [ + /* + * Here you can specify which class and method will return + * the items that should appear in the feed. For example: + * [App\Model::class, 'getAllFeedItems'] + * + * You can also pass an argument to that method. Note that their key must be the name of the parameter: + * [App\Model::class, 'getAllFeedItems', 'parameterName' => 'argument'] + */ + 'items' => '', + + /* + * The feed will be available on this url. + */ + 'url' => '', + + 'title' => 'My feed', + 'description' => 'The description of the feed.', + 'language' => 'en-US', + + /* + * The image to display for the feed. For Atom feeds, this is displayed as + * a banner/logo; for RSS and JSON feeds, it's displayed as an icon. + * An empty value omits the image attribute from the feed. + */ + 'image' => '', + + /* + * The format of the feed. Acceptable values are 'rss', 'atom', or 'json'. + */ + 'format' => 'atom', + + /* + * The view that will render the feed. + */ + 'view' => 'feed::atom', + + /* + * The mime type to be used in the tag. Set to an empty string to automatically + * determine the correct value. + */ + 'type' => '', + + /* + * The content type for the feed response. Set to an empty string to automatically + * determine the correct value. + */ + 'contentType' => '', + ], + ], +]; diff --git a/config/filament-shield.php b/config/filament-shield.php new file mode 100644 index 0000000..c1536c1 --- /dev/null +++ b/config/filament-shield.php @@ -0,0 +1,90 @@ + [ + 'should_register_navigation' => true, + 'slug' => 'shield/roles', + 'navigation_sort' => -1, + 'navigation_badge' => true, + 'navigation_group' => true, + 'is_globally_searchable' => false, + 'show_model_path' => true, + 'is_scoped_to_tenant' => true, + ], + + 'auth_provider_model' => [ + 'fqcn' => 'App\\Models\\User', + ], + + 'super_admin' => [ + 'enabled' => true, + 'name' => 'super_admin', + 'define_via_gate' => false, + 'intercept_gate' => 'before', // after + ], + + 'panel_user' => [ + 'enabled' => true, + 'name' => 'panel_user', + ], + + 'permission_prefixes' => [ + 'resource' => [ + 'view', + 'view_any', + 'create', + 'update', + 'restore', + 'restore_any', + 'replicate', + 'reorder', + 'delete', + 'delete_any', + 'force_delete', + 'force_delete_any', + ], + + 'page' => 'page', + 'widget' => 'widget', + ], + + 'entities' => [ + 'pages' => true, + 'widgets' => true, + 'resources' => true, + 'custom_permissions' => false, + ], + + 'generator' => [ + 'option' => 'policies_and_permissions', + ], + + 'exclude' => [ + 'enabled' => true, + + 'pages' => [ + 'Dashboard', + ], + + 'widgets' => [ + 'AccountWidget', 'FilamentInfoWidget', + ], + + 'resources' => [ + + ], + ], + + 'discovery' => [ + 'discover_all_resources' => true, + 'discover_all_widgets' => false, + 'discover_all_pages' => false, + ], + + 'register_role_policy' => [ + 'enabled' => true, + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php index d307268..6650ee6 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -37,6 +37,12 @@ 'root' => storage_path('app'), 'throw' => false, ], + 'ftp' => [ + 'driver' => 'ftp', + 'host' => env('FTP_HOST'), + 'username' => env('FTP_USERNAME'), + 'password' => env('FTP_PASSWORD'), + ], 'public' => [ 'driver' => 'local', diff --git a/config/fortify.php b/config/fortify.php index 157cd82..a2fcaab 100644 --- a/config/fortify.php +++ b/config/fortify.php @@ -134,7 +134,7 @@ */ 'features' => [ - Features::registration(), + // Features::registration(), Features::resetPasswords(), Features::emailVerification(), Features::updateProfileInformation(), diff --git a/config/horizon.php b/config/horizon.php new file mode 100644 index 0000000..aba628c --- /dev/null +++ b/config/horizon.php @@ -0,0 +1,231 @@ + env('HORIZON_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | Horizon Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Horizon will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => env('HORIZON_PATH', 'horizon'), + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Connection + |-------------------------------------------------------------------------- + | + | This is the name of the Redis connection where Horizon will store the + | meta information required for it to function. It includes the list + | of supervisors, failed jobs, job metrics, and other information. + | + */ + + 'use' => 'default', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Prefix + |-------------------------------------------------------------------------- + | + | This prefix will be used when storing all Horizon data in Redis. You + | may modify the prefix when you are running multiple installations + | of Horizon on the same server so that they don't have problems. + | + */ + + 'prefix' => env( + 'HORIZON_PREFIX', + Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:' + ), + + /* + |-------------------------------------------------------------------------- + | Horizon Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will get attached onto each Horizon route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + + 'middleware' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Queue Wait Time Thresholds + |-------------------------------------------------------------------------- + | + | This option allows you to configure when the LongWaitDetected event + | will be fired. Every connection / queue combination may have its + | own, unique threshold (in seconds) before this event is fired. + | + */ + + 'waits' => [ + 'redis:default' => 60, + ], + + /* + |-------------------------------------------------------------------------- + | Job Trimming Times + |-------------------------------------------------------------------------- + | + | Here you can configure for how long (in minutes) you desire Horizon to + | persist the recent and failed jobs. Typically, recent jobs are kept + | for one hour while all failed jobs are stored for an entire week. + | + */ + + 'trim' => [ + 'recent' => 60, + 'pending' => 60, + 'completed' => 60, + 'recent_failed' => 10080, + 'failed' => 10080, + 'monitored' => 10080, + ], + + /* + |-------------------------------------------------------------------------- + | Silenced Jobs + |-------------------------------------------------------------------------- + | + | Silencing a job will instruct Horizon to not place the job in the list + | of completed jobs within the Horizon dashboard. This setting may be + | used to fully remove any noisy jobs from the completed jobs list. + | + */ + + 'silenced' => [ + // App\Jobs\ExampleJob::class, + ], + + /* + |-------------------------------------------------------------------------- + | Metrics + |-------------------------------------------------------------------------- + | + | Here you can configure how many snapshots should be kept to display in + | the metrics graph. This will get used in combination with Horizon's + | `horizon:snapshot` schedule to define how long to retain metrics. + | + */ + + 'metrics' => [ + 'trim_snapshots' => [ + 'job' => 24, + 'queue' => 24, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Fast Termination + |-------------------------------------------------------------------------- + | + | When this option is enabled, Horizon's "terminate" command will not + | wait on all of the workers to terminate unless the --wait option + | is provided. Fast termination can shorten deployment delay by + | allowing a new instance of Horizon to start while the last + | instance will continue to terminate each of its workers. + | + */ + + 'fast_termination' => false, + + /* + |-------------------------------------------------------------------------- + | Memory Limit (MB) + |-------------------------------------------------------------------------- + | + | This value describes the maximum amount of memory the Horizon master + | supervisor may consume before it is terminated and restarted. For + | configuring these limits on your workers, see the next section. + | + */ + + 'memory_limit' => 64, + + /* + |-------------------------------------------------------------------------- + | Queue Worker Configuration + |-------------------------------------------------------------------------- + | + | Here you may define the queue worker settings used by your application + | in all environments. These supervisors and settings handle all your + | queued jobs and will be provisioned by Horizon during deployment. + | + */ + + 'defaults' => [ + 'supervisor-1' => [ + 'connection' => 'redis', + 'queue' => ['default'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'maxProcesses' => 1, + 'maxTime' => 0, + 'maxJobs' => 0, + 'memory' => 128, + 'tries' => 1, + 'timeout' => 6000, + 'nice' => 0, + ], + 'supervisor-2' => [ + 'connection' => 'redis', + 'queue' => ['secondary'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'maxProcesses' => 1, + 'maxTime' => 0, + 'maxJobs' => 0, + 'memory' => 128, + 'tries' => 1, + 'timeout' => 6000, + 'nice' => 0, + ], + ], + + 'environments' => [ + 'production' => [ + 'supervisor-1' => [ + 'maxProcesses' => 10, + 'balanceMaxShift' => 1, + 'balanceCooldown' => 3, + ], + ], + + 'local' => [ + 'supervisor-1' => [ + 'maxProcesses' => 5, + ], + 'supervisor-2' => [ + 'maxProcesses' => 5, + ], + ], + ], +]; diff --git a/config/laravel-console-dusk.php b/config/laravel-console-dusk.php new file mode 100644 index 0000000..d08144c --- /dev/null +++ b/config/laravel-console-dusk.php @@ -0,0 +1,29 @@ + [ + 'screenshots' => storage_path('laravel-console-dusk/screenshots'), + 'log' => storage_path('laravel-console-dusk/log'), + ], + + /* + | -------------------------------------------------------------------------- + | Headless Mode + | -------------------------------------------------------------------------- + | + | When false it will show a Chrome window while running. Within production + | it will be forced to run in headless mode. + */ + 'headless' => true, + +]; diff --git a/config/logging.php b/config/logging.php index cd3851a..3c31726 100644 --- a/config/logging.php +++ b/config/logging.php @@ -56,7 +56,7 @@ 'channels' => [ 'stack' => [ 'driver' => 'stack', - 'channels' => ['single'], + 'channels' => ['single', 'flare', 'bugsnag'], 'ignore_exceptions' => false, ], @@ -128,6 +128,14 @@ 'emergency' => [ 'path' => storage_path('logs/laravel.log'), ], + 'flare' => [ + 'driver' => 'flare', + ], + + // Create a bugsnag logging channel: + 'bugsnag' => [ + 'driver' => 'bugsnag', + ], ], ]; diff --git a/config/mail.php b/config/mail.php index eb9256a..222cd1d 100644 --- a/config/mail.php +++ b/config/mail.php @@ -124,4 +124,15 @@ ], ], + 'boxes' => [ + + 'imap' => [ + 'transport' => 'imap', + 'host' => env('IMAP_HOST', 'smtp.mailgun.org'), + 'port' => env('IMAP_PORT', 587), + 'encryption' => env('IMAP_ENCRYPTION', 'notls'), + 'username' => env('IMAP_USERNAME'), + 'password' => env('IMAP_PASSWORD'), + ], + ], ]; diff --git a/config/operations.php b/config/operations.php new file mode 100644 index 0000000..46625f6 --- /dev/null +++ b/config/operations.php @@ -0,0 +1,12 @@ + [ + ], +]; diff --git a/config/pulse.php b/config/pulse.php new file mode 100644 index 0000000..528085c --- /dev/null +++ b/config/pulse.php @@ -0,0 +1,229 @@ + env('PULSE_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | Pulse Path + |-------------------------------------------------------------------------- + | + | This is the path which the Pulse dashboard will be accessible from. Feel + | free to change this path to anything you'd like. Note that this won't + | affect the path of the internal API that is never exposed to users. + | + */ + + 'path' => env('PULSE_PATH', 'pulse'), + + /* + |-------------------------------------------------------------------------- + | Pulse Master Switch + |-------------------------------------------------------------------------- + | + | This configuration option may be used to completely disable all Pulse + | data recorders regardless of their individual configurations. This + | provides a single option to quickly disable all Pulse recording. + | + */ + + 'enabled' => env('PULSE_ENABLED', true), + + /* + |-------------------------------------------------------------------------- + | Pulse Storage Driver + |-------------------------------------------------------------------------- + | + | This configuration option determines which storage driver will be used + | while storing entries from Pulse's recorders. In addition, you also + | may provide any options to configure the selected storage driver. + | + */ + + 'storage' => [ + 'driver' => env('PULSE_STORAGE_DRIVER', 'database'), + + 'database' => [ + 'connection' => env('PULSE_DB_CONNECTION', null), + 'chunk' => 1000, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Pulse Ingest Driver + |-------------------------------------------------------------------------- + | + | This configuration options determines the ingest driver that will be used + | to capture entries from Pulse's recorders. Ingest drivers are great to + | free up your request workers quickly by offloading the data storage. + | + */ + + 'ingest' => [ + 'driver' => env('PULSE_INGEST_DRIVER', 'storage'), + + 'trim_lottery' => [1, 1_000], + + 'redis' => [ + 'connection' => env('PULSE_REDIS_CONNECTION'), + 'chunk' => 1000, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Pulse Cache Driver + |-------------------------------------------------------------------------- + | + | This configuration option determines the cache driver that will be used + | for various tasks, including caching dashboard results, establishing + | locks for events that should only occur on one server and signals. + | + */ + + 'cache' => env('PULSE_CACHE_DRIVER'), + + /* + |-------------------------------------------------------------------------- + | Pulse Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will be assigned to every Pulse route, giving you the + | chance to add your own middleware to this list or change any of the + | existing middleware. Of course, reasonable defaults are provided. + | + */ + + 'middleware' => [ + 'web', + Authorize::class, + ], + + /* + |-------------------------------------------------------------------------- + | Pulse Recorders + |-------------------------------------------------------------------------- + | + | The following array lists the "recorders" that will be registered with + | Pulse, along with their configuration. Recorders gather application + | event data from requests and tasks to pass to your ingest driver. + | + */ + + 'recorders' => [ + Recorders\CacheInteractions::class => [ + 'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true), + 'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1), + 'ignore' => [ + '/^laravel:pulse:/', // Internal Pulse keys... + '/^illuminate:/', // Internal Laravel keys... + '/^telescope:/', // Internal Telescope keys... + '/^nova/', // Internal Nova keys... + '/^.+@.+\|(?:(?:\d+\.\d+\.\d+\.\d+)|[0-9a-fA-F:]+)(?::timer)?$/', // Breeze / Jetstream authentication rate limiting... + '/^[a-zA-Z0-9]{40}$/', // Session IDs... + ], + 'groups' => [ + '/^job-exceptions:.*/' => 'job-exceptions:*', + // '/:\d+/' => ':*', + ], + ], + + Recorders\Exceptions::class => [ + 'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true), + 'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1), + 'location' => env('PULSE_EXCEPTIONS_LOCATION', true), + 'ignore' => [ + // '/^Package\\\\Exceptions\\\\/', + ], + ], + + Recorders\Queues::class => [ + 'enabled' => env('PULSE_QUEUES_ENABLED', true), + 'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1), + 'ignore' => [ + // '/^Package\\\\Jobs\\\\/', + ], + ], + + Recorders\Servers::class => [ + 'server_name' => env('PULSE_SERVER_NAME', gethostname()), + 'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')), + ], + + Recorders\SlowJobs::class => [ + 'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true), + 'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1), + 'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000), + 'ignore' => [ + // '/^Package\\\\Jobs\\\\/', + ], + ], + + Recorders\SlowOutgoingRequests::class => [ + 'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true), + 'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1), + 'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000), + 'ignore' => [ + // '#^http://127\.0\.0\.1:13714#', // Inertia SSR... + ], + 'groups' => [ + // '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*', + // '#^https?://([^/]*).*$#' => '\1', + // '#/\d+#' => '/*', + ], + ], + + Recorders\SlowQueries::class => [ + 'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true), + 'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1), + 'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000), + 'location' => env('PULSE_SLOW_QUERIES_LOCATION', true), + 'ignore' => [ + '/(["`])pulse_[\w]+?\1/', // Pulse tables... + ], + ], + + Recorders\SlowRequests::class => [ + 'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true), + 'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1), + 'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000), + 'ignore' => [ + '#^/pulse$#', // Pulse dashboard... + ], + ], + + Recorders\UserJobs::class => [ + 'enabled' => env('PULSE_USER_JOBS_ENABLED', true), + 'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1), + 'ignore' => [ + // '/^Package\\\\Jobs\\\\/', + ], + ], + + Recorders\UserRequests::class => [ + 'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true), + 'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1), + 'ignore' => [ + '#^/pulse$#', // Pulse dashboard... + ], + ], + ], +]; diff --git a/config/services.php b/config/services.php index 104826f..dfefa4b 100644 --- a/config/services.php +++ b/config/services.php @@ -33,4 +33,14 @@ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], + 'plaid' => [ + 'env' => env('PLAID_ENV', 'sandbox'), + 'secret_key' => env('PLAID_SANDBOX_SECRET', ''), + 'client_id' => env('PLAID_CLIENT_ID', ''), + 'client_name' => env('APP_NAME'), + 'language' => env('PLAID_LANGUAGE', 'en'), + 'country_codes' => explode(',', env('PLAID_COUNTRY_CODES', 'US')), + 'products' => ['transactions'], + ], + ]; diff --git a/config/spork.php b/config/spork.php new file mode 100644 index 0000000..10313bf --- /dev/null +++ b/config/spork.php @@ -0,0 +1,18 @@ + '', + 'filesystem' => [ + 'default' => env('SPORK_DEFAULT_FILESYSTEM'), + ], + 'code' => [ + 'enabled' => true, + 'settings' => [ + // These vendors dont always match 100% with the versions or available interfaces, likely due to missing dev dependencies. + 'blacklist' => ['nesbot', 'doctrine', 'google', 'psy', 'cboden', 'symfony', 'phpunit', 'mockery', 'zendframework'], + 'whitelist' => [], + ], + ], +]; diff --git a/config/tags.php b/config/tags.php index 67950a7..2b52f0d 100644 --- a/config/tags.php +++ b/config/tags.php @@ -13,7 +13,7 @@ /* * The fully qualified class name of the tag model. */ - 'tag_model' => Spatie\Tags\Tag::class, + 'tag_model' => App\Models\Tag::class, /* * The name of the table associated with the taggable morph relation. @@ -21,5 +21,10 @@ 'taggable' => [ 'table_name' => 'taggables', 'morph_name' => 'taggable', + + /* + * The fully qualified class name of the pivot model. + */ + 'class_name' => Illuminate\Database\Eloquent\Relations\MorphPivot::class, ], ]; diff --git a/config/websockets.php b/config/websockets.php new file mode 100644 index 0000000..359d69d --- /dev/null +++ b/config/websockets.php @@ -0,0 +1,143 @@ + [ + 'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001), + ], + + /* + * This package comes with multi tenancy out of the box. Here you can + * configure the different apps that can use the webSockets server. + * + * Optionally you specify capacity so you can limit the maximum + * concurrent connections for a specific app. + * + * Optionally you can disable client events so clients cannot send + * messages to each other via the webSockets. + */ + 'apps' => [ + [ + 'id' => env('PUSHER_APP_ID'), + 'name' => env('APP_NAME'), + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'path' => env('PUSHER_APP_PATH'), + 'capacity' => null, + 'enable_client_messages' => false, + 'enable_statistics' => true, + ], + ], + + /* + * This class is responsible for finding the apps. The default provider + * will use the apps defined in this config file. + * + * You can create a custom provider by implementing the + * `AppProvider` interface. + */ + 'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class, + + /* + * This array contains the hosts of which you want to allow incoming requests. + * Leave this empty if you want to accept requests from all hosts. + */ + 'allowed_origins' => [ + // + ], + + /* + * The maximum request size in kilobytes that is allowed for an incoming WebSocket request. + */ + 'max_request_size_in_kb' => 250, + + /* + * This path will be used to register the necessary routes for the package. + */ + 'path' => 'laravel-websockets', + + /* + * Dashboard Routes Middleware + * + * These middleware will be assigned to every dashboard route, giving you + * the chance to add your own middleware to this list or change any of + * the existing middleware. Or, you can simply stick with this list. + */ + 'middleware' => [ + 'web', + Authorize::class, + ], + + 'statistics' => [ + /* + * This model will be used to store the statistics of the WebSocketsServer. + * The only requirement is that the model should extend + * `WebSocketsStatisticsEntry` provided by this package. + */ + 'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class, + + /** + * The Statistics Logger will, by default, handle the incoming statistics, store them + * and then release them into the database on each interval defined below. + */ + 'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class, + + /* + * Here you can specify the interval in seconds at which statistics should be logged. + */ + 'interval_in_seconds' => 60, + + /* + * When the clean-command is executed, all recorded statistics older than + * the number of days specified here will be deleted. + */ + 'delete_statistics_older_than_days' => 60, + + /* + * Use an DNS resolver to make the requests to the statistics logger + * default is to resolve everything to 127.0.0.1. + */ + 'perform_dns_lookup' => false, + ], + + /* + * Define the optional SSL context for your WebSocket connections. + * You can see all available options at: http://php.net/manual/en/context.ssl.php + */ + 'ssl' => [ + /* + * Path to local certificate file on filesystem. It must be a PEM encoded file which + * contains your certificate and private key. It can optionally contain the + * certificate chain of issuers. The private key also may be contained + * in a separate file specified by local_pk. + */ + 'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null), + + /* + * Path to local private key file on filesystem in case of separate files for + * certificate (local_cert) and private key. + */ + 'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null), + + /* + * Passphrase for your local_cert file. + */ + 'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null), + ], + + /* + * Channel Manager + * This class handles how channel persistence is handled. + * By default, persistence is stored in an array by the running webserver. + * The only requirement is that the class should implement + * `ChannelManager` interface provided by this package. + */ + 'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class, +]; diff --git a/database/factories/ArticleFactory.php b/database/factories/ArticleFactory.php new file mode 100644 index 0000000..8f7fc47 --- /dev/null +++ b/database/factories/ArticleFactory.php @@ -0,0 +1,25 @@ + + */ +class ArticleFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/ConditionFactory.php b/database/factories/ConditionFactory.php new file mode 100644 index 0000000..9c6410d --- /dev/null +++ b/database/factories/ConditionFactory.php @@ -0,0 +1,25 @@ + + */ +class ConditionFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/CredentialFactory.php b/database/factories/CredentialFactory.php index 0bf3916..4486bec 100644 --- a/database/factories/CredentialFactory.php +++ b/database/factories/CredentialFactory.php @@ -4,6 +4,8 @@ namespace Database\Factories; +use App\Models\Credential; +use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -19,7 +21,15 @@ class CredentialFactory extends Factory public function definition(): array { return [ - // + 'name' => $this->faker->name(), + 'user_id' => User::factory(), + 'api_key' => $this->faker->text(), + 'secret_key' => $this->faker->text(), + 'access_token' => $this->faker->text(), + 'refresh_token' => $this->faker->text(), + 'service' => Credential::TYPE_SSH, + 'type' => Credential::TYPE_SSH, + 'settings' => [], ]; } } diff --git a/database/factories/ExternalRssFeedFactory.php b/database/factories/ExternalRssFeedFactory.php new file mode 100644 index 0000000..26a2b28 --- /dev/null +++ b/database/factories/ExternalRssFeedFactory.php @@ -0,0 +1,25 @@ + + */ +class ExternalRssFeedFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/Finance/AccountFactory.php b/database/factories/Finance/AccountFactory.php new file mode 100644 index 0000000..a14eaf7 --- /dev/null +++ b/database/factories/Finance/AccountFactory.php @@ -0,0 +1,33 @@ + + */ +class AccountFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'credential_id' => Credential::factory(), + 'name' => $this->faker->name, + 'mask' => $this->faker->randomNumber(4), + 'type' => 'checking', + 'account_id' => Str::random(32), + 'balance' => $this->faker->randomNumber(2), + 'available' => $this->faker->randomNumber(2), + ]; + } +} diff --git a/database/factories/Finance/BudgetFactory.php b/database/factories/Finance/BudgetFactory.php new file mode 100644 index 0000000..c0dfb00 --- /dev/null +++ b/database/factories/Finance/BudgetFactory.php @@ -0,0 +1,25 @@ + + */ +class BudgetFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/Finance/TransactionFactory.php b/database/factories/Finance/TransactionFactory.php new file mode 100644 index 0000000..79e3460 --- /dev/null +++ b/database/factories/Finance/TransactionFactory.php @@ -0,0 +1,36 @@ + + */ +class TransactionFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name, + 'amount' => $this->faker->numberBetween(2, 100), + 'account_id' => fn () => Account::factory()->create()->account_id, + 'date' => $this->faker->date(), + 'pending' => $this->faker->boolean, + 'category_id' => $this->faker->numberBetween(2, 1000), + 'transaction_id' => Str::random(32), + 'transaction_type' => 'depository', + 'pending_transaction_id' => null, + 'data' => '[]', + ]; + } +} diff --git a/database/factories/MessageFactory.php b/database/factories/MessageFactory.php new file mode 100644 index 0000000..00a4921 --- /dev/null +++ b/database/factories/MessageFactory.php @@ -0,0 +1,25 @@ + + */ +class MessageFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/NavigationFactory.php b/database/factories/NavigationFactory.php new file mode 100644 index 0000000..2182a67 --- /dev/null +++ b/database/factories/NavigationFactory.php @@ -0,0 +1,25 @@ + + */ +class NavigationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/OperationFactory.php b/database/factories/OperationFactory.php new file mode 100644 index 0000000..89aa62b --- /dev/null +++ b/database/factories/OperationFactory.php @@ -0,0 +1,25 @@ + + */ +class OperationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/Operations/ServerActionFactory.php b/database/factories/Operations/ServerActionFactory.php new file mode 100644 index 0000000..7fb6406 --- /dev/null +++ b/database/factories/Operations/ServerActionFactory.php @@ -0,0 +1,43 @@ + + */ +class ServerActionFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'script_id' => Script::factory(), + 'user_id' => User::factory(), + 'server_id' => Server::factory(), + 'credential_id' => Credential::factory(), + 'output' => $this->faker->text(), + 'error' => $this->faker->text(), + 'should_run_at' => now(), + 'started_run_at' => now(), + 'finished_run_at' => now(), + ]; + } + + public function newModel(array $attributes = []) + { + return new ServerAction($attributes); + } +} diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php index 2f00ca8..a4fcc4f 100644 --- a/database/factories/ProjectFactory.php +++ b/database/factories/ProjectFactory.php @@ -4,6 +4,7 @@ namespace Database\Factories; +use App\Models\Team; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -19,7 +20,8 @@ class ProjectFactory extends Factory public function definition(): array { return [ - // + 'name' => $this->faker->name(), + 'team_id' => Team::factory(), ]; } } diff --git a/database/factories/ServerFactory.php b/database/factories/ServerFactory.php index 728c88a..67d057a 100644 --- a/database/factories/ServerFactory.php +++ b/database/factories/ServerFactory.php @@ -4,6 +4,7 @@ namespace Database\Factories; +use App\Models\Credential; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -19,7 +20,22 @@ class ServerFactory extends Factory public function definition(): array { return [ - // + 'server_id' => $this->faker->randomNumber(), + 'credential_id' => Credential::factory(), + 'name' => $this->faker->name(), + 'vcpu' => $this->faker->randomNumber(), + 'memory' => $this->faker->randomNumber(), + 'disk' => $this->faker->randomNumber(), + 'cost_per_hour' => $this->faker->randomNumber(), + 'ip_address' => '127.0.0.1', + 'ip_address_v6' => '', + 'internal_ip_address' => '127.0.0.1', + 'internal_ip_address_v6' => '', + 'internal_url' => 'localhost', + 'last_ping_at' => null, + 'booted_at' => null, + 'turned_off_at' => null, + 'os' => 'Ubuntu', ]; } } diff --git a/database/factories/ShortCodeFactory.php b/database/factories/ShortCodeFactory.php new file mode 100644 index 0000000..7e273e3 --- /dev/null +++ b/database/factories/ShortCodeFactory.php @@ -0,0 +1,25 @@ + + */ +class ShortCodeFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/Spork/ScriptFactory.php b/database/factories/Spork/ScriptFactory.php new file mode 100644 index 0000000..624ad2c --- /dev/null +++ b/database/factories/Spork/ScriptFactory.php @@ -0,0 +1,29 @@ + + */ +class ScriptFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name(), + 'language' => $this->faker->text(), + 'script' => $this->faker->text(), + 'user_id' => User::factory(), + ]; + } +} diff --git a/database/factories/ThreadFactory.php b/database/factories/ThreadFactory.php new file mode 100644 index 0000000..997e7e0 --- /dev/null +++ b/database/factories/ThreadFactory.php @@ -0,0 +1,25 @@ + + */ +class ThreadFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php b/database/migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php new file mode 100644 index 0000000..9875ebe --- /dev/null +++ b/database/migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php @@ -0,0 +1,37 @@ +increments('id'); + $table->string('app_id'); + $table->integer('peak_connection_count'); + $table->integer('websocket_message_count'); + $table->integer('api_message_count'); + $table->nullableTimestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('websockets_statistics_entries'); + } +} diff --git a/database/migrations/2023_06_08_024338_create_people_table.php b/database/migrations/2023_06_08_024338_create_people_table.php index 170e8bb..ca2b84a 100644 --- a/database/migrations/2023_06_08_024338_create_people_table.php +++ b/database/migrations/2023_06_08_024338_create_people_table.php @@ -17,9 +17,9 @@ public function up(): void $table->id(); $table->string('name'); - $table->string('primary_number'); - $table->string('primary_address'); - $table->string('primary_email'); + $table->string('primary_number')->nullable(); + $table->string('primary_address')->nullable(); + $table->string('primary_email')->nullable(); $table->string('pronouns')->nullable(); $table->date('birthdate')->nullable(); diff --git a/database/migrations/2023_06_08_024343_create_projects_table.php b/database/migrations/2023_06_08_024343_create_projects_table.php index 94c9242..c795949 100644 --- a/database/migrations/2023_06_08_024343_create_projects_table.php +++ b/database/migrations/2023_06_08_024343_create_projects_table.php @@ -38,5 +38,6 @@ public function up(): void public function down(): void { Schema::dropIfExists('projects'); + Schema::dropIfExists('project_resources'); } }; diff --git a/database/migrations/2023_06_09_162056_create_pages_table.php b/database/migrations/2023_06_09_162056_create_pages_table.php index b3557aa..128e87f 100644 --- a/database/migrations/2023_06_09_162056_create_pages_table.php +++ b/database/migrations/2023_06_09_162056_create_pages_table.php @@ -19,6 +19,7 @@ public function up(): void $table->string('slug')->nullable(); $table->string('route'); $table->json('middleware')->nullable(); + $table->json('settings')->nullable(); // 1 sentence $table->string('subtitle')->nullable(); $table->text('excerpt')->nullable(); diff --git a/database/migrations/2023_06_17_024737_create_tag_tables.php b/database/migrations/2023_06_17_024737_create_tag_tables.php index a5b3aa3..0edf8ad 100644 --- a/database/migrations/2023_06_17_024737_create_tag_tables.php +++ b/database/migrations/2023_06_17_024737_create_tag_tables.php @@ -17,6 +17,7 @@ public function up(): void $table->json('slug'); $table->string('type')->nullable(); $table->integer('order_column')->nullable(); + $table->boolean('must_all_conditions_pass')->default(true); $table->timestamps(); }); diff --git a/database/migrations/2023_07_02_044034_create_articles_table.php b/database/migrations/2023_07_02_044034_create_articles_table.php new file mode 100644 index 0000000..5a174b1 --- /dev/null +++ b/database/migrations/2023_07_02_044034_create_articles_table.php @@ -0,0 +1,40 @@ +id(); + $table->uuid('uuid')->unique()->nullable(); + $table->string('external_guid')->unique()->nullable(); + $table->morphs('author'); + $table->datetime('last_modified')->nullable(); + $table->string('etag')->nullable(); + + $table->string('headline')->nullable(); + $table->text('content')->nullable(); + + $table->string('attachment')->nullable(); + $table->string('url', 2048); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('articles'); + } +}; diff --git a/database/migrations/2023_07_02_044703_create_external_rss_feeds_table.php b/database/migrations/2023_07_02_044703_create_external_rss_feeds_table.php new file mode 100644 index 0000000..86b3db7 --- /dev/null +++ b/database/migrations/2023_07_02_044703_create_external_rss_feeds_table.php @@ -0,0 +1,33 @@ +id(); + $table->uuid('uuid')->unique(); + $table->string('url')->unique(); + $table->string('name'); + $table->text('profile_photo_path')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('external_rss_feeds'); + } +}; diff --git a/database/migrations/2023_07_02_051842_create_job_batches_table.php b/database/migrations/2023_07_02_051842_create_job_batches_table.php new file mode 100644 index 0000000..4d95053 --- /dev/null +++ b/database/migrations/2023_07_02_051842_create_job_batches_table.php @@ -0,0 +1,37 @@ +string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('job_batches'); + } +}; diff --git a/database/migrations/2023_08_06_050945_create_activity_log_table.php b/database/migrations/2023_08_06_050945_create_activity_log_table.php new file mode 100644 index 0000000..2ef2b86 --- /dev/null +++ b/database/migrations/2023_08_06_050945_create_activity_log_table.php @@ -0,0 +1,29 @@ +create(config('activitylog.table_name'), function (Blueprint $table) { + $table->bigIncrements('id'); + $table->string('log_name')->nullable(); + $table->text('description'); + $table->nullableMorphs('subject', 'subject'); + $table->nullableMorphs('causer', 'causer'); + $table->json('properties')->nullable(); + $table->timestamps(); + $table->index('log_name'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name')); + } +} diff --git a/database/migrations/2023_08_06_050946_add_event_column_to_activity_log_table.php b/database/migrations/2023_08_06_050946_add_event_column_to_activity_log_table.php new file mode 100644 index 0000000..742e420 --- /dev/null +++ b/database/migrations/2023_08_06_050946_add_event_column_to_activity_log_table.php @@ -0,0 +1,24 @@ +table(config('activitylog.table_name'), function (Blueprint $table) { + $table->string('event')->nullable()->after('subject_type'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { + $table->dropColumn('event'); + }); + } +} diff --git a/database/migrations/2023_08_06_050947_add_batch_uuid_column_to_activity_log_table.php b/database/migrations/2023_08_06_050947_add_batch_uuid_column_to_activity_log_table.php new file mode 100644 index 0000000..ad42e5b --- /dev/null +++ b/database/migrations/2023_08_06_050947_add_batch_uuid_column_to_activity_log_table.php @@ -0,0 +1,24 @@ +table(config('activitylog.table_name'), function (Blueprint $table) { + $table->uuid('batch_uuid')->nullable()->after('properties'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { + $table->dropColumn('batch_uuid'); + }); + } +} diff --git a/database/migrations/2023_09_06_181801_add_columns_to_people_table.php b/database/migrations/2023_09_06_181801_add_columns_to_people_table.php new file mode 100644 index 0000000..fac463b --- /dev/null +++ b/database/migrations/2023_09_06_181801_add_columns_to_people_table.php @@ -0,0 +1,41 @@ +json('names')->nullable()->after('emails'); + // ssn, sos id, ein, usernames, etc + $table->json('identifiers')->nullable()->after('emails'); + // precinct_name, BOE, senate districts, rep districts, ward, etc + $table->json('locality')->nullable()->after('emails'); + + $table->json('jobs')->nullable()->after('emails'); + $table->json('education')->nullable()->after('emails'); + $table->string('estimated_income')->nullable()->after('emails'); + $table->string('estimated_home_value')->nullable()->after('emails'); + $table->string('photo_url', 2048)->nullable()->after('estimated_home_value'); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('people', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2023_09_12_011306_create_server_actions_table.php b/database/migrations/2023_09_12_011306_create_server_actions_table.php new file mode 100644 index 0000000..7066ecc --- /dev/null +++ b/database/migrations/2023_09_12_011306_create_server_actions_table.php @@ -0,0 +1,46 @@ +bigIncrements('id'); + + $table->foreignIdFor(\App\Models\Server::class); + $table->foreignIdFor(\App\Models\Spork\Script::class); + $table->foreignIdFor(\App\Models\Credential::class); + $table->foreignIdFor(\App\Models\User::class); + + $table->longText('output')->nullable(); + $table->longText('error')->nullable(); + + $table->timestamp('should_run_at'); + $table->timestamp('started_run_at')->nullable(); + $table->timestamp('finished_run_at')->nullable(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('server_actions'); + } +}; diff --git a/database/migrations/2023_09_12_011314_create_scripts_table.php b/database/migrations/2023_09_12_011314_create_scripts_table.php new file mode 100644 index 0000000..d72c6f6 --- /dev/null +++ b/database/migrations/2023_09_12_011314_create_scripts_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name'); + $table->string('language'); + $table->text('script'); + // Scripts without a user_id will be considered "global" scripts. + $table->foreignIdFor(\App\Models\User::class); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('scripts'); + } +}; diff --git a/database/migrations/2023_10_01_004656_create_budgets_table.php b/database/migrations/2023_10_01_004656_create_budgets_table.php new file mode 100644 index 0000000..aaf0769 --- /dev/null +++ b/database/migrations/2023_10_01_004656_create_budgets_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignIdFor(\App\Models\User::class); + $table->string('name'); + $table->double('amount'); + $table->string('frequency'); // Year, month, week, + $table->string('interval'); + $table->dateTime('started_at'); + $table->integer('count')->nullable(); + $table->dateTime('breached_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('budgets'); + } +}; diff --git a/database/migrations/2023_10_01_004659_create_accounts_table.php b/database/migrations/2023_10_01_004659_create_accounts_table.php new file mode 100644 index 0000000..cd63ba6 --- /dev/null +++ b/database/migrations/2023_10_01_004659_create_accounts_table.php @@ -0,0 +1,37 @@ +id(); + $table->foreignIdFor(\App\Models\Credential::class); + $table->string('name'); + $table->string('mask')->nullable(); + // Checking, savings, deposit + $table->string('type'); + $table->string('account_id')->index(); + $table->double('balance'); + $table->double('available'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('accounts'); + } +}; diff --git a/database/migrations/2023_10_01_004707_create_transactions_table.php b/database/migrations/2023_10_01_004707_create_transactions_table.php new file mode 100644 index 0000000..58a8bf5 --- /dev/null +++ b/database/migrations/2023_10_01_004707_create_transactions_table.php @@ -0,0 +1,39 @@ +id(); + $table->string('name')->nullable()->index(); + $table->double('amount', 13, 2)->nullable(); + $table->string('account_id')->nullable()->index(); + $table->date('date')->nullable(); + $table->boolean('pending')->default(false); + $table->integer('category_id')->unsigned()->nullable(); + $table->string('transaction_id')->nullable()->index(); + $table->string('transaction_type')->nullable(); + $table->string('pending_transaction_id')->nullable(); + $table->json('data')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('transactions'); + } +}; diff --git a/database/migrations/2023_10_01_004742_create_conditions_table.php b/database/migrations/2023_10_01_004742_create_conditions_table.php new file mode 100644 index 0000000..e1eae64 --- /dev/null +++ b/database/migrations/2023_10_01_004742_create_conditions_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('parameter')->nullable(); + $table->string('comparator')->nullable(); + $table->string('value')->nullable(); + + $table->morphs('conditionable'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('conditions'); + } +}; diff --git a/database/migrations/2023_10_07_023041_create_messages_table.php b/database/migrations/2023_10_07_023041_create_messages_table.php new file mode 100644 index 0000000..028469f --- /dev/null +++ b/database/migrations/2023_10_07_023041_create_messages_table.php @@ -0,0 +1,49 @@ +id(); + $table->foreignIdFor(\App\Models\Person::class, 'from_person'); + + $table->foreignIdFor(\App\Models\Thread::class)->nullable(); + + $table->string('type'); + $table->string('event_id')->unique(); + + $table->timestamp('originated_at'); + + // Maybe it's an image + $table->string('thumbnail_url', 2048)->nullable(); + + $table->boolean('is_decrypted'); + + $table->text('message')->nullable(); + $table->text('html_message')->nullable(); + + // Encryption information to decrypt later? + // Email settings? What if every email had a thread by the sender, and all messages were subsiqent emails? + $table->json('settings')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('messages'); + } +}; diff --git a/database/migrations/2023_10_07_023054_create_threads_table.php b/database/migrations/2023_10_07_023054_create_threads_table.php new file mode 100644 index 0000000..23333ee --- /dev/null +++ b/database/migrations/2023_10_07_023054_create_threads_table.php @@ -0,0 +1,47 @@ +id(); + $table->string('thread_id')->unique(); + + $table->string('name')->nullable(); + $table->string('description')->nullable(); + $table->string('rules')->nullable(); + $table->string('topic')->nullable(); + $table->json('settings')->nullable(); + $table->timestamp('origin_server_ts'); + $table->timestamps(); + }); + + Schema::create('thread_participants', function (Blueprint $table) { + $table->id(); + $table->foreignIdFor(\App\Models\Person::class); + $table->foreignIdFor(\App\Models\Thread::class); + $table->dateTime('joined_at'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('threads'); + Schema::dropIfExists('thread_participants'); + } +}; diff --git a/database/migrations/2023_10_15_205749_create_navigations_table.php b/database/migrations/2023_10_15_205749_create_navigations_table.php new file mode 100644 index 0000000..8f78c78 --- /dev/null +++ b/database/migrations/2023_10_15_205749_create_navigations_table.php @@ -0,0 +1,148 @@ +id(); + $table->foreignIdFor(\App\Models\Domain::class)->nullable(); + $table->foreignId('parent_id')->nullable(); + $table->string('name'); + $table->string('icon')->nullable(); + $table->string('href', 2048); + $table->integer('order')->default(0); + $table->boolean('authentication_required'); + $table->json('settings')->nullable(); + $table->string('title')->nullable(); + $table->string('description')->nullable(); + $table->string('pretty_url', 2048)->nullable(); + $table->string('ugly_url', 2048)->nullable(); + $table->timestamps(); + }); + + \App\Models\Navigation::create([ + 'name' => 'Dashboard', + 'icon' => 'HomeIcon', + 'href' => '/-', + 'order' => 0, + 'authentication_required' => true, + ]); + \App\Models\Navigation::create([ + 'name' => 'Projects', + 'icon' => 'ClipboardIcon', + 'href' => '/-/projects', + 'order' => 5, + 'authentication_required' => true, + ]); + + \App\Models\Navigation::create([ + 'name' => 'Banking', + 'icon' => 'WalletIcon', + 'href' => '/-/', + 'order' => 6, + 'authentication_required' => true, + ]); + \App\Models\Navigation::create([ + 'name' => 'Tags', + 'icon' => 'TagIcon', + 'href' => '/-/', + 'order' => 8, + 'authentication_required' => true, + ]); + /** @var \App\Models\Navigation $crudNav */ + $crudNav = \App\Models\Navigation::create([ + 'name' => 'CRUD', + 'icon' => 'CircleStackIcon', + 'href' => '/-/manage', + 'order' => 6, + 'authentication_required' => true, + ]); + + $models = \App\Services\Code::instancesOf(\App\Models\Crud::class); + + $crudClasses = array_values(array_filter( + $models->getClasses(), + fn ($class) => in_array(\App\Models\Crud::class, class_implements($class)) + )); + + foreach ($crudClasses as $index => $class) { + \App\Models\Navigation::create([ + 'name' => \Illuminate\Support\Str::headline((new $class)->getTable()), + 'href' => '/-/manage/'.\Illuminate\Support\Str::slug( + \Illuminate\Support\Str::headline((new $class)->getTable()) + ), + 'icon' => \Illuminate\Support\Str::studly(\Illuminate\Support\Str::headline(class_basename($class))).'Icon', + 'order' => $index, + 'authentication_required' => true, + 'parent_id' => $crudNav->id, + 'settings' => [ + 'title' => \Illuminate\Support\Str::headline(class_basename($class)), + 'singular' => \Illuminate\Support\Str::studly(class_basename($class)), + 'api_url' => route(((new $class)->getTable()).'.store'), + ], + ]); + } + + \App\Models\Navigation::create([ + 'name' => 'Email', + 'icon' => 'EnvelopeOpenIcon', + 'href' => '/-/inbox', + 'order' => 9, + 'authentication_required' => true, + ]); + \App\Models\Navigation::create([ + 'name' => 'Settings', + 'icon' => 'Cog6ToothIcon', + 'href' => '/-/settings', + 'order' => 10, + 'authentication_required' => true, + ]); + + $logic = \App\Models\Navigation::create([ + 'name' => 'Logic', + 'icon' => 'VariableIcon', + 'href' => '/-/logic', + 'order' => 7, + 'authentication_required' => true, + ]); + + $logic->conditions()->create([ + 'parameter' => 'config:app.env', + 'comparator' => 'IN', + 'value' => 'dev,local', + ]); + + \App\Models\Navigation::create([ + 'name' => 'Banking', + 'icon' => 'WalletIcon', + 'href' => '/-/banking', + 'order' => 6, + 'authentication_required' => true, + ]); + + \App\Models\Navigation::create([ + 'name' => 'Login', + 'href' => '/login', + 'order' => 0, + 'authentication_required' => false, + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('navigations'); + } +}; diff --git a/database/migrations/2023_11_12_000501_create_short_codes_table.php b/database/migrations/2023_11_12_000501_create_short_codes_table.php new file mode 100644 index 0000000..84b85b2 --- /dev/null +++ b/database/migrations/2023_11_12_000501_create_short_codes_table.php @@ -0,0 +1,36 @@ +id()->startingValue(58101822); + $table->foreignIdFor(\App\Models\User::class); + $table->string('short_code', 255)->unique()->index(); + $table->string('long_url', 4096); + $table->boolean('is_enabled'); + // like http status + $table->integer('status'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('short_codes'); + } +}; diff --git a/database/migrations/2023_12_10_074940_create_permission_tables.php b/database/migrations/2023_12_10_074940_create_permission_tables.php new file mode 100644 index 0000000..b647e7d --- /dev/null +++ b/database/migrations/2023_12_10_074940_create_permission_tables.php @@ -0,0 +1,140 @@ +bigIncrements('id'); // permission id + $table->string('name'); // For MySQL 8.0 use string('name', 125); + $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125); + $table->timestamps(); + + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MySQL 8.0 use string('name', 125); + $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125); + $table->timestamps(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + if (empty($tableNames)) { + throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + } + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; diff --git a/database/migrations/2023_12_17_195827_create_notifications_table.php b/database/migrations/2023_12_17_195827_create_notifications_table.php new file mode 100644 index 0000000..1f148f6 --- /dev/null +++ b/database/migrations/2023_12_17_195827_create_notifications_table.php @@ -0,0 +1,33 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/database/seeders/AccountSeeder.php b/database/seeders/AccountSeeder.php new file mode 100644 index 0000000..d3db4dc --- /dev/null +++ b/database/seeders/AccountSeeder.php @@ -0,0 +1,18 @@ + /etc/timezone - -RUN apt-get update \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu focal main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.0-cli php8.0-dev \ - php8.0-pgsql php8.0-sqlite3 php8.0-gd php8.0-imagick \ - php8.0-curl php8.0-memcached \ - php8.0-imap php8.0-mysql php8.0-mbstring \ - php8.0-xml php8.0-zip php8.0-bcmath php8.0-soap \ - php8.0-intl php8.0-readline php8.0-pcov \ - php8.0-msgpack php8.0-igbinary php8.0-ldap \ - php8.0-redis php8.0-swoole php8.0-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN update-alternatives --set php /usr/bin/php8.0 - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail - -COPY start-container /usr/local/bin/start-container -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf -COPY php.ini /etc/php/8.0/cli/conf.d/99-sail.ini -RUN chmod +x /usr/local/bin/start-container - -EXPOSE 8000 - -ENTRYPOINT ["start-container"] diff --git a/docker/8.0/supervisord.conf b/docker/8.0/supervisord.conf deleted file mode 100644 index 9d28479..0000000 --- a/docker/8.0/supervisord.conf +++ /dev/null @@ -1,14 +0,0 @@ -[supervisord] -nodaemon=true -user=root -logfile=/var/log/supervisor/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:php] -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 -user=sail -environment=LARAVEL_SAIL="1" -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 diff --git a/docker/8.1/Dockerfile b/docker/8.1/Dockerfile deleted file mode 100644 index 2875071..0000000 --- a/docker/8.1/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -FROM ubuntu:22.04 - -LABEL maintainer="Taylor Otwell" - -ARG WWWGROUP -ARG NODE_VERSION=18 -ARG POSTGRES_VERSION=15 - -WORKDIR /var/www/html - -ENV DEBIAN_FRONTEND noninteractive -ENV TZ=UTC - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.1-cli php8.1-dev \ - php8.1-pgsql php8.1-sqlite3 php8.1-gd php8.1-imagick \ - php8.1-curl \ - php8.1-imap php8.1-mysql php8.1-mbstring \ - php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \ - php8.1-intl php8.1-readline \ - php8.1-ldap \ - php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \ - php8.1-memcached php8.1-pcov php8.1-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail - -COPY start-container /usr/local/bin/start-container -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf -COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini -RUN chmod +x /usr/local/bin/start-container - -EXPOSE 8000 - -ENTRYPOINT ["start-container"] diff --git a/docker/8.1/php.ini b/docker/8.1/php.ini deleted file mode 100644 index 39dcbca..0000000 --- a/docker/8.1/php.ini +++ /dev/null @@ -1,7 +0,0 @@ -[PHP] -post_max_size = 100M -upload_max_filesize = 100M -variables_order = EGPCS - -[opcache] -opcache.enable_cli=1 diff --git a/docker/8.1/start-container b/docker/8.1/start-container deleted file mode 100644 index b864399..0000000 --- a/docker/8.1/start-container +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -if [ ! -z "$WWWUSER" ]; then - usermod -u $WWWUSER sail -fi - -if [ ! -d /.composer ]; then - mkdir /.composer -fi - -chmod -R ugo+rw /.composer - -if [ $# -gt 0 ]; then - exec gosu $WWWUSER "$@" -else - exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf -fi diff --git a/docker/8.1/supervisord.conf b/docker/8.1/supervisord.conf deleted file mode 100644 index 9d28479..0000000 --- a/docker/8.1/supervisord.conf +++ /dev/null @@ -1,14 +0,0 @@ -[supervisord] -nodaemon=true -user=root -logfile=/var/log/supervisor/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:php] -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 -user=sail -environment=LARAVEL_SAIL="1" -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 diff --git a/docker/8.2/Dockerfile b/docker/8.2/Dockerfile index a6fd1f0..d62bd75 100644 --- a/docker/8.2/Dockerfile +++ b/docker/8.2/Dockerfile @@ -1,52 +1,4 @@ -FROM ubuntu:22.04 - -LABEL maintainer="Taylor Otwell" - -ARG WWWGROUP -ARG NODE_VERSION=18 -ARG POSTGRES_VERSION=15 - -WORKDIR /var/www/html - -ENV DEBIAN_FRONTEND noninteractive -ENV TZ=UTC - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.2-cli php8.2-dev \ - php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ - php8.2-curl \ - php8.2-imap php8.2-mysql php8.2-mbstring \ - php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ - php8.2-intl php8.2-readline \ - php8.2-ldap \ - php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ - php8.2-memcached php8.2-pcov php8.2-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail +FROM austinkregel/base:latest COPY start-container /usr/local/bin/start-container COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile new file mode 100644 index 0000000..968b5cc --- /dev/null +++ b/docker/base/Dockerfile @@ -0,0 +1,49 @@ +FROM ubuntu:22.04 + +# Thanks to the Laravel team for Laravel Sail, which this is all based on. +LABEL maintainer="Austin Kregel" +ARG WWWGROUP +ARG NODE_VERSION=18 +ARG POSTGRES_VERSION=15 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.2-cli php8.2-dev \ + php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ + php8.2-curl \ + php8.2-imap php8.2-mysql php8.2-mbstring \ + php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ + php8.2-intl php8.2-readline \ + php8.2-ldap php8.2-ssh2 \ + php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ + php8.2-memcached php8.2-pcov php8.2-xdebug php8.2-ssh2 \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn openssh-server \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail diff --git a/docker/base/build.sh b/docker/base/build.sh new file mode 100755 index 0000000..958e9da --- /dev/null +++ b/docker/base/build.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -x +set -e + +PHP_VERSION=8.2 + +TAG=$PHP_VERSION-$(git rev-parse HEAD | head -c 7) + +#docker login -u $DOCKER_USER -p $DOCKER_PASS + +export WWWUSER=${WWWUSER:-$UID} +export WWWGROUP=${WWWGROUP:-$(id -g)} + +docker build --build-arg WWWUSER=$WWWUSER --build-arg WWWGROUP=$WWWGROUP -t austinkregel/base:${TAG} . + +docker tag austinkregel/base:${TAG} austinkregel/base:latest + +docker push austinkregel/base:${TAG} +docker push austinkregel/base:latest diff --git a/docker/crontab/Dockerfile b/docker/crontab/Dockerfile index 9fb93c6..a1f14f0 100644 --- a/docker/crontab/Dockerfile +++ b/docker/crontab/Dockerfile @@ -1,52 +1,4 @@ -FROM ubuntu:22.04 - -LABEL maintainer="Taylor Otwell" - -ARG WWWGROUP -ARG NODE_VERSION=18 -ARG POSTGRES_VERSION=15 - -WORKDIR /var/www/html - -ENV DEBIAN_FRONTEND noninteractive -ENV TZ=UTC - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.2-cli php8.2-dev \ - php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ - php8.2-curl \ - php8.2-imap php8.2-mysql php8.2-mbstring \ - php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ - php8.2-intl php8.2-readline \ - php8.2-ldap \ - php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ - php8.2-memcached php8.2-pcov php8.2-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail +FROM austinkregel/base:latest COPY php.ini /etc/php/8.2/cli/conf.d/99-sail.ini @@ -63,4 +15,4 @@ RUN cat /etc/cron.d/basecron | crontab - # Create the log file to be able to run tail RUN touch /var/log/cron.log # Run the command on container startup -CMD cron && tail -f /var/log/cron.log +CMD cron && echo "[----] Starting crontab." && tail -f /var/log/cron.log diff --git a/docker/horizon/Dockerfile b/docker/horizon/Dockerfile index f479a0c..e3a60c4 100644 --- a/docker/horizon/Dockerfile +++ b/docker/horizon/Dockerfile @@ -1,52 +1,4 @@ -FROM ubuntu:22.04 - -LABEL maintainer="Taylor Otwell" - -ARG WWWGROUP -ARG NODE_VERSION=18 -ARG POSTGRES_VERSION=15 - -WORKDIR /var/www/html - -ENV DEBIAN_FRONTEND noninteractive -ENV TZ=UTC - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN apt-get update \ - && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin \ - && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ - && apt-get update \ - && apt-get install -y php8.2-cli php8.2-dev \ - php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ - php8.2-curl \ - php8.2-imap php8.2-mysql php8.2-mbstring \ - php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ - php8.2-intl php8.2-readline \ - php8.2-ldap \ - php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ - php8.2-memcached php8.2-pcov php8.2-xdebug \ - && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ - && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ - && apt-get install -y nodejs \ - && npm install -g npm \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ - && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ - && apt-get update \ - && apt-get install -y yarn \ - && apt-get install -y mysql-client \ - && apt-get install -y postgresql-client-$POSTGRES_VERSION \ - && apt-get -y autoremove \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 - -RUN groupadd --force -g $WWWGROUP sail -RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail +FROM austinkregel/base:latest COPY start-horizon /usr/local/bin/start-horizon COPY horizon-supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/docker/matrix-bot/.gitignore b/docker/matrix-bot/.gitignore new file mode 100644 index 0000000..5ff1af2 --- /dev/null +++ b/docker/matrix-bot/.gitignore @@ -0,0 +1,4 @@ +!.gitignore +bot.json +db/ +node_modules/ diff --git a/docker/matrix-bot/Dockerfile b/docker/matrix-bot/Dockerfile new file mode 100644 index 0000000..73d5c56 --- /dev/null +++ b/docker/matrix-bot/Dockerfile @@ -0,0 +1,7 @@ +FROM node:18-bullseye + +RUN apt update && apt upgrade -y + +RUN apt install -y git libolm-dev libjs-olm + +CMD cd /var/www/html && npm i && npm upgrade @matrix-org/olm && node /var/www/html/docker/matrix-bot/index.js diff --git a/docker/matrix-bot/index.js b/docker/matrix-bot/index.js new file mode 100644 index 0000000..ee3a6d0 --- /dev/null +++ b/docker/matrix-bot/index.js @@ -0,0 +1,696 @@ +import { + MatrixClient, + MatrixAuth, + AutojoinRoomsMixin, + AutojoinUpgradedRoomsMixin, + SimpleFsStorageProvider, + RustSdkCryptoStorageProvider, + RustSdkAppserviceCryptoStorageProvider, + Appservice, + SimpleRetryJoinStrategy, + EncryptionAlgorithm, +} from "matrix-bot-sdk"; +import { StoreType } from "@matrix-org/matrix-sdk-crypto-nodejs"; +import dotenv from 'dotenv'; +import Sequelize, {DataTypes, QueryTypes} from "sequelize"; +import fs from 'fs'; +import ora from 'ora'; +import crypto from "crypto"; + +dotenv.config(); + +const storageProvider = new SimpleFsStorageProvider("/var/www/html/docker/matrix-bot/bot.json"); // or any other IStorageProvider +const cryptoProvider = new RustSdkCryptoStorageProvider("/var/www/html/docker/matrix-bot/db", StoreType.Sled); +// In order to sync room keys, we need to ensure we can trust the devices we're getting them from. +// We can request the list of devices, and verify their keys. +const sequelize = new Sequelize( + process.env.DB_DATABASE, + process.env.DB_USERNAME, + process.env.DB_PASSWORD, + { + host: process.env.DB_HOST, + dialect: process.env.DB_CONNECTION, + logging: false, + } +); + + +// Since we're talking directly with our database, Laravel isn't getting any of the events +// Nothing in our UI will update, and no php code will trigger. +// We need a way to hook + +async function findOrCreateMessageEvent(Threads, Messages, People, thread, message, is_decrypted = false) { + if (message.type !== 'm.room.message') { + console.log('Told this was a message, but reporeted as ', message.type) + return null; + } + console.log('Looking for thread', thread); + + let threadInQuestion = await Threads.findOne({ + where: {thread_id: thread} + }); + + if (!threadInQuestion) { + console.error('Thread doesn\'t exist yet', thread); + + process.exit(1); + } + + const messageToCreate = await Messages.findOne({ + where: {event_id: message.event_id,} + }); + + + if (!messageToCreate) { + const thread_id = threadInQuestion.id + + const sender = await findPersonByIdentifier(People, message); + console.log('no messages found for event', message.event_id, 'creating message', { + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted + }) + + await Messages.create({ + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + ...(message?.content?.settings ? message?.content?.settings : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted, + + }); + + const updatedTime = {origin_server_ts: new Date()}; + + await Threads.update(updatedTime, { + where: {thread_id: thread} + }); + } +} + +async function findPersonByIdentifier(People, message) { + const identifier = message.sender; + + let peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }) + + if (!peoples || peoples.length === 0) { + if (message.type !== 'm.room.member') { + console.log('m.room.member', message); + return null; + } + + await People.create({ + name: message.content.displayname, + identifiers: JSON.stringify([identifier]) + }) + + const data = await client.downloadContent(message.content.avatar_url); + + peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }); + } + + + return peoples[0] ?? null; +} + +const setupSequlize = async () => { + console.log('[-] Sequilize authenticating...'); + await sequelize.authenticate(); + console.log('[-] Sequilize authenticated...'); + + const Threads = sequelize.define('threads', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + }, + description: { + type: DataTypes.STRING, + }, + rules: { + type: DataTypes.STRING, + }, + topic: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.STRING, + }, + origin_server_ts: { + type: DataTypes.DATE, + }, + settings: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const Messages = sequelize.define('messages', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + from_person: { + type: DataTypes.BIGINT, + }, + type: { + type: DataTypes.STRING, + }, + event_id: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.BIGINT, + }, + thumbnail_url: { + type: DataTypes.STRING(2048), + }, + originated_at: { + type: DataTypes.DATE, + }, + message: { + type: DataTypes.TEXT + }, + html_message: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const People = sequelize.define('people', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING(255), + allowNull: false, + }, + primary_number: DataTypes.STRING(255), + primary_address: DataTypes.STRING(255), + primary_email: DataTypes.STRING(255), + pronouns: DataTypes.STRING(255), + birthdate: DataTypes.DATE, + phone_numbers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for phone_numbers'); + } + }, + }, + }, + addresses: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for addresses'); + } + }, + }, + }, + emails: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for emails'); + } + }, + }, + }, + estimated_home_value: DataTypes.STRING(255), + estimated_income: DataTypes.STRING(255), + education: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for education'); + } + }, + }, + }, + jobs: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for jobs'); + } + }, + }, + }, + locality: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for locality'); + } + }, + }, + }, + identifiers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for identifiers'); + } + }, + }, + }, + names: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for names'); + } + }, + }, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, // Set to true if you want Sequelize to manage timestamps + tableName: 'people', + updatedAt: 'updated_at', + createdAt: 'created_at', + collate: 'utf8mb4_unicode_ci', + }); + + const Participant = sequelize.define('thread_participants', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + + person_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + thread_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + + joined_at: { + type: DataTypes.DATE, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, + tableName: 'thread_participants', + updatedAt: 'updated_at', + createdAt: 'created_at', + + }) + console.log('[-] Sequilize models defined...'); + + return { + Threads, + Messages, + People, + Participant, + } +} + +async function main() { + try { + const client = new MatrixClient( + process.env.MATRIX_HOST, + process.env.MATRIX_ACCESS_TOKEN, + storageProvider, + cryptoProvider + ); + + const { user_id, device_id } = await client.getWhoAmI(); + console.log('[-] #############################'); + console.log('[-] Authenticated as', user_id, device_id); + console.log('[-] #############################'); + + if (!await storageProvider.isUserRegistered(user_id)) { + storageProvider.addRegisteredUser(user_id); + } + + const { + Threads, + Messages, + People, + Participant, + } = await setupSequlize(); + + // const client = await auth.passwordLogin(userId, password, "Programmatic Access"); + console.log('[-] Autojoin rooms mixin...'); + AutojoinRoomsMixin.setupOnClient(client); + AutojoinUpgradedRoomsMixin.setupOnClient(client); + console.log('[-] Autojoin rooms mixin established..'); + + client.on('room.join', async (event) => { + await client.crypto.onRoomJoin(event.room_id); + }) + + client.on('room.encrypted_event', async (roomId, event) => { + // handle `m.room.encrypted` event that was received from the server + console.log('Room Ecnrypted eevent', roomId, event) + await findOrCreateMessageEvent(Threads, Messages, People, roomId, { + type: 'm.room.message', + event_id: event.event_id, + origin_server_ts: 0, + room_id: event.room_id, + content: { + ...event.content, + settings: event.content + }, + sender: event.sender, + }, false); + }) + + client.on("room.message", async (roomId, messageEvent) => { + // await client.upload + console.log(roomId, 'received a new message from', messageEvent?.sender) + await findOrCreateMessageEvent(Threads, Messages, People, roomId, messageEvent, true); + }); + + console.log('[-] Preparing crypto lib'); + const cryptoSpinner = ora('Syncing joined rooms, and preparing encryption').start(); + /** @var IStorageProvider userStorage **/ + const userStorage = await client.storageProvider.storageForUser(user_id) + + const dmTarget = '@kregel:communication.ventures'; + const rooms = await client.getJoinedRooms() + await client.crypto.prepare(rooms); + cryptoSpinner.succeed('Crypto library setup, encryption ready.'); + + let encryptedRoomId = null; + for (const roomId in rooms) { + if (await client.crypto.isRoomEncrypted(roomId)) { + encryptedRoomId = roomId; + } + } + + if (!encryptedRoomId) { + encryptedRoomId = await client.createRoom({ + invite: [dmTarget], + is_direct: true, + visibility: "private", + preset: "trusted_private_chat", + initial_state: [ + { + type: "m.room.encryption", + state_key: "", + content: {algorithm: EncryptionAlgorithm.MegolmV1AesSha2} + }, + {type: "m.room.guest_access", state_key: "", content: {guest_access: "can_join"}}, + ], + }); + } + + + console.log('[-] ready', client.crypto.isReady); + console.log('[-]', await client.checkOneTimeKeyCounts()); + + const devices = await client.getUserDevices([user_id]); + + console.log('devices', devices) + + const { one_time_keys } = await client.claimOneTimeKeys({ + [user_id]: {[device_id]: "signed_curve25519"} + }, 10000); + + console.log({ + one_time_keys + }) + + if (!one_time_keys[user_id]) { + console.error('One time keys are not supported for this device'); + return; + } + + // console.log('[-] Finished setting up crypto lib'); + // // await client.begin(); + // console.log('[-] Started'); + // const resp = await client.uploadDeviceOneTimeKeys(one_time_keys[user_id][device_id]) + // + // console.log('Thing has started', device_id, user_id, resp); + // const spinner = ora('Loading message history from all the threads').start(); + // + // for (let index in rooms) { + // const roomId = rooms[index]; + // const state = await client.getRoomState(roomId); + // + // for (let stateIndex in state) { + // const event = state[stateIndex]; + // let test = null; + // switch (event.type) { + // case 'm.room.create': + // test = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // + // if (test) { + // continue; + // } + // await Threads.create({ + // name: roomId, + // thread_id: roomId, + // origin_server_ts: new Date(event.origin_server_ts), + // }); + // break; + // case 'm.room.name': + // test = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // + // if (test) { + // continue; + // } + // await Threads.update({name: event.content.name}, { + // where: {thread_id: roomId} + // }); + // break; + // case 'm.room.member': + // let matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // if (matchingPeople.length === 0) { + // await People.create({ + // name: event.content?.displayname ?? event.sender, + // identifiers: JSON.stringify([event.sender]) + // }) + // } + // matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // for (let index in matchingPeople) { + // const person = matchingPeople[index]; + // + // let thread = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // let participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // + // if (participant.length === 0) { + // await Participant.create({ + // person_id: person.id, + // thread_id: thread.id, + // joined_at: new Date(event.origin_server_ts), + // }) + // participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // } + // + // if (event.content?.avatar_url) { + // let contentType = 'jpg'; + // const data = await client.downloadContent(event.content?.avatar_url); + // + // if (!data.data) { + // continue; + // } + // + // const id = person.id + // contentType = data.contentType.split('/')[1]; + // + // if (!fs.existsSync('/var/www/html/storage/app/' + id + '.' + contentType)) { + // const path = '/var/www/html/storage/app/' + id + '.' + contentType; + // fs.writeFileSync(path, data.data) + // + // await People.update({ + // photo_url: path + // }, { + // where: {id} + // }); + // } + // } + // } + // break; + // case 'm.room.encryption': + // const hash = crypto.createHash('sha512'); + // + // const key = hash.update(roomId, 'utf-8'); + // + // const thread = key.digest('hex'); + // let threadInQuestion = await Threads.findOne({ + // where: {thread_id: roomId} + // }); + // + // const originalSettings = JSON.parse(threadInQuestion?.settings ?? '{}'); + // + // await Threads.update({ + // settings: JSON.stringify({ + // ...originalSettings, + // ...event.content, + // 'hash': thread + // }) + // }, { + // where: {thread_id: roomId} + // }); + // + // console.log('encrypted room found', await client.crypto.isRoomEncrypted(roomId)); + // + // break; + // // this.db.set(`rooms.${key}`, ).write(); + // case 'm.room.topic': + // await Threads.update({topic: event.content.topic}, { + // where: {thread_id: roomId} + // }); + // break; + // // Meta settings that dont' matter for this app + // case 'm.room.canonical_alias': + // case 'm.room.history_visibility': + // case 'm.room.guest_access': + // case 'm.room.power_levels': + // case 'm.room.avatar': + // case 'm.room.join_rules': + // case 'm.bridge': + // case 'uk.half-shot.bridge': + // case 'com.beeper.chatwoot.conversation_id': + // break; + // case 'com.beeper.backfill_status': + // + // case 'com.beeper.rooms.note_to_self': + // case 'com.beeper.support_chat': + // break; + // case "m.space.parent": + // case 'm.space.child': + // case 'com.beeper.feed': + // case 'org.matrix.msc2716.marker': + // // a twitter marker? + // + // default: + // console.log(event.type, JSON.stringify(event, null, 4)); + // }; + // } + // + // + // const isEncrypted = await cryptoProvider.isRoomEncrypted(roomId); + // const roomState = await cryptoProvider.getRoom(roomId); + // if (isEncrypted && !roomState) { + // const encryption = await client.getRoomStateEvent(roomId, 'm.room.encryption') + // const history_visibility = await client.getRoomStateEvent(roomId, 'm.room.history_visibility') + // const guest_access = await client.getRoomStateEvent(roomId, 'm.room.guest_access') + // + // await client.cryptoStore.storeRoom(roomId, { + // ...encryption, + // ...history_visibility, + // ...guest_access, + // }); + // } + // + // spinner.stopAndPersist({ + // text: "✔️ " + index + '/' + rooms.length + ' History sync: ' + roomId, + // }); + // } + + // spinner.succeed('Starting the matrix clint') + await client.start() + + } catch (error) { + console.error("Error:", error.message ?? error?.body ?? 'nothing defined I guess'); + } +} + +process.on('SIGINT', function () { + sequelize.close(); + + process.exit(); +}) + +main(); diff --git a/docker/matrix-bot/matrix-bot-sdk-broken.js b/docker/matrix-bot/matrix-bot-sdk-broken.js new file mode 100644 index 0000000..2aa09db --- /dev/null +++ b/docker/matrix-bot/matrix-bot-sdk-broken.js @@ -0,0 +1,693 @@ +import { + MatrixClient, + MatrixAuth, + AutojoinRoomsMixin, + AutojoinUpgradedRoomsMixin, + SimpleFsStorageProvider, + RustSdkCryptoStorageProvider, + RustSdkAppserviceCryptoStorageProvider, + Appservice, + SimpleRetryJoinStrategy, +} from "matrix-bot-sdk"; +import dotenv from 'dotenv'; +import Sequelize, {DataTypes, QueryTypes} from "sequelize"; +import fs from 'fs'; +import crypto from 'crypto'; +import ora from 'ora'; + +dotenv.config(); +import Pusher from "pusher"; + +const storageProvider = new SimpleFsStorageProvider("/var/www/html/docker/matrix-bot/bot.json"); // or any other IStorageProvider +const cryptoProvider = new RustSdkCryptoStorageProvider("/var/www/html/docker/matrix-bot/db"); +// const auth = new MatrixAuth(homeserverUrl); +// const client = await auth.passwordLogin(process.env.MATRIX_USERNAME, process.env.MATRIX_PASSWORD); +const client = new MatrixClient( + process.env.MATRIX_HOST, + process.env.MATRIX_ACCESS_TOKEN, + storageProvider, + cryptoProvider +); + +// In order to sync room keys, we need to ensure we can trust the devices we're getting them from. +// We can request the list of devices, and verify their keys. +// + +const sequelize = new Sequelize( + process.env.DB_DATABASE, + process.env.DB_USERNAME, + process.env.DB_PASSWORD, + { + host: process.env.DB_HOST, + dialect: process.env.DB_CONNECTION, + logging: false, + } +); + + +// Since we're talking directly with our database, Laravel isn't getting any of the events +// Nothing in our UI will update, and no php code will trigger. +// We need a way to hook +const pusher = new Pusher({ + appId: process.env.PUSHER_APP_ID, + key: process.env.PUSHER_APP_KEY, + secret: process.env.PUSHER_APP_SECRET, + cluster: process.env.PUSHER_APP_CLUSTER, + wsHost: process.env.PUSHER_HOST ? process.env.PUSHER_HOST : `ws-${process.env.PUSHER_APP_CLUSTER}.pusher.com`, + wsPort: process.env.PUSHER_PORT ?? 80, + wssPort: process.env.PUSHER_PORT ?? 443, + useTLS: true +}); + +async function findOrCreateMessageEvent(Threads, Messages, People, thread, message, is_decrypted = false) { + if (message.type !== 'm.room.message') { + console.log('Told this was a message, but reporeted as ', message.type) + return null; + } + console.log('Looking for thread', thread); + + let threadInQuestion = await Threads.findOne({ + where: {thread_id: thread} + }); + + if (!threadInQuestion) { + console.error('Thread doesn\'t exist yet', thread); + + process.exit(1); + } + + const messageToCreate = await Messages.findOne({ + where: {event_id: message.event_id,} + }); + + + if (!messageToCreate) { + const thread_id = threadInQuestion.id + + const sender = await findPersonByIdentifier(People, message); + console.log('no messages found for event', message.event_id, 'creating message', { + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted + }) + + await Messages.create({ + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + ...(message?.content?.settings ? message?.content?.settings : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted, + + }); + + const updatedTime = {origin_server_ts: new Date()}; + + pusher.trigger('user.1', 'eloquent.updated: App\\Models\\Thread', { + ...threadInQuestion, + ...updatedTime, + }) + await Threads.update(updatedTime, { + where: {thread_id: thread} + }); + } +} + +async function findPersonByIdentifier(People, message) { + const identifier = message.sender; + + let peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }) + + if (!peoples || peoples.length === 0) { + if (message.type !== 'm.room.member') { + console.log('m.room.member', message); + return null; + } + + await People.create({ + name: message.content.displayname, + identifiers: JSON.stringify([identifier]) + }) + + const data = await client.downloadContent(message.content.avatar_url); + + console.log(data); + process.exit(); + + peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }); + } + + + return peoples[0] ?? null; +} + +const setupSequlize = async () => { + console.log('[-] Sequilize authenticating...'); + await sequelize.authenticate(); + console.log('[-] Sequilize authenticated...'); + + const Threads = sequelize.define('threads', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + }, + description: { + type: DataTypes.STRING, + }, + rules: { + type: DataTypes.STRING, + }, + topic: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.STRING, + }, + origin_server_ts: { + type: DataTypes.DATE, + }, + settings: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const Messages = sequelize.define('messages', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + from_person: { + type: DataTypes.BIGINT, + }, + type: { + type: DataTypes.STRING, + }, + event_id: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.BIGINT, + }, + thumbnail_url: { + type: DataTypes.STRING(2048), + }, + originated_at: { + type: DataTypes.DATE, + }, + message: { + type: DataTypes.TEXT + }, + html_message: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const People = sequelize.define('people', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING(255), + allowNull: false, + }, + primary_number: DataTypes.STRING(255), + primary_address: DataTypes.STRING(255), + primary_email: DataTypes.STRING(255), + pronouns: DataTypes.STRING(255), + birthdate: DataTypes.DATE, + phone_numbers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for phone_numbers'); + } + }, + }, + }, + addresses: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for addresses'); + } + }, + }, + }, + emails: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for emails'); + } + }, + }, + }, + estimated_home_value: DataTypes.STRING(255), + estimated_income: DataTypes.STRING(255), + education: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for education'); + } + }, + }, + }, + jobs: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for jobs'); + } + }, + }, + }, + locality: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for locality'); + } + }, + }, + }, + identifiers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for identifiers'); + } + }, + }, + }, + names: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for names'); + } + }, + }, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, // Set to true if you want Sequelize to manage timestamps + tableName: 'people', + updatedAt: 'updated_at', + createdAt: 'created_at', + collate: 'utf8mb4_unicode_ci', + }); + + const Participant = sequelize.define('thread_participants', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + + person_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + thread_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + + joined_at: { + type: DataTypes.DATE, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, + tableName: 'thread_participants', + updatedAt: 'updated_at', + createdAt: 'created_at', + + }) + console.log('[-] Sequilize models defined...'); + + return { + Threads, + Messages, + People, + Participant, + } +} + +async function main() { + try { + const {user_id, device_id} = await client.getWhoAmI(); + console.log('[-] #############################'); + console.log('[-] Authenticated as', user_id, device_id); + console.log('[-] #############################'); + + if (!await storageProvider.isUserRegistered(user_id)) { + storageProvider.addRegisteredUser(user_id); + } + + const { + Threads, + Messages, + People, + Participant, + } = await setupSequlize(); + + // const client = await auth.passwordLogin(userId, password, "Programmatic Access"); + console.log('[-] Autojoin rooms mixin...'); + AutojoinRoomsMixin.setupOnClient(client); + AutojoinUpgradedRoomsMixin.setupOnClient(client); + console.log('[-] Autojoin rooms mixin established..'); + + client.on('room.join', async (event) => { + await client.crypto.onRoomJoin(event.room_id); + }) + + client.on('room.encrypted_event', async (roomId, event) => { + // handle `m.room.encrypted` event that was received from the server + console.log('Room Ecnrypted eevent', roomId, event) + await findOrCreateMessageEvent(Threads, Messages, People, roomId, { + type: 'm.room.message', + event_id: event.event_id, + origin_server_ts: 0, + room_id: event.room_id, + content: { + ...event.content, + settings: event.content + }, + sender: event.sender, + }, false); + }) + + client.on("room.message", async (roomId, messageEvent) => { + // await client.upload + console.log(roomId, 'received a new message from', messageEvent?.sender) + await findOrCreateMessageEvent(Threads, Messages, People, roomId, messageEvent, true); + }); + + console.log('[-] Preparing crypto lib'); + const cryptoSpinner = ora('Syncing joined rooms, and preparing encryption').start(); + /** @var IStorageProvider userStorage **/ + const userStorage = await client.storageProvider.storageForUser(user_id) + + const rooms = await client.getJoinedRooms() + + await client.crypto.prepare(rooms); + cryptoSpinner.succeed('Crypto library setup, encryption ready.'); + + console.log('[-] ready', client.crypto.isReady); + + console.log('[-]', await client.checkOneTimeKeyCounts()); + + + const devices = await client.getUserDevices([user_id]); + + const {one_time_keys} = await client.claimOneTimeKeys({ + [user_id]: {[device_id]: "signed_curve25519"} + }, 10000); + + if (!one_time_keys[user_id]) { + console.error('One time keys are not supported for this device'); + return; + } + + console.log('[-] Finished setting up crypto lib'); + // await client.begin(); + console.log('[-] Started'); + const resp = await client.uploadDeviceOneTimeKeys(one_time_keys[user_id][device_id]) + + console.log('Thing has started', device_id, user_id, resp); + const spinner = ora('Loading message history from all the threads').start(); + + for (let index in rooms) { + const roomId = rooms[index]; + + // spinner.spinner = 'dots'; + // spinner.text = index+'/'+rooms.length+' history syncing: ' + roomId + // spinner.start() + + const state = await client.getRoomState(roomId); + + for (let stateIndex in state) { + const event = state[stateIndex]; + let test = null; + switch (event.type) { + case 'm.room.create': + test = await Threads.findOne({ + where: {thread_id: event.room_id} + }); + + if (test) { + continue; + } + await Threads.create({ + name: roomId, + thread_id: roomId, + origin_server_ts: new Date(event.origin_server_ts), + }); + break; + case 'm.room.name': + test = await Threads.findOne({ + where: {thread_id: event.room_id} + }); + + if (test) { + continue; + } + await Threads.update({name: event.content.name}, { + where: {thread_id: roomId} + }); + break; + case 'm.room.member': + let matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + replacements: [JSON.stringify(event.sender)], + type: QueryTypes.SELECT, + model: People, + }) + + if (matchingPeople.length === 0) { + await People.create({ + name: event.content?.displayname ?? event.sender, + identifiers: JSON.stringify([event.sender]) + }) + } + matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + replacements: [JSON.stringify(event.sender)], + type: QueryTypes.SELECT, + model: People, + }) + + for (let index in matchingPeople) { + const person = matchingPeople[index]; + + let thread = await Threads.findOne({ + where: {thread_id: event.room_id} + }); + let participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + type: QueryTypes.SELECT, + }); + + if (participant.length === 0) { + await Participant.create({ + person_id: person.id, + thread_id: thread.id, + joined_at: new Date(event.origin_server_ts), + }) + participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + type: QueryTypes.SELECT, + }); + } + + if (event.content?.avatar_url) { + let contentType = 'jpg'; + const data = await client.downloadContent(event.content?.avatar_url); + + if (!data.data) { + continue; + } + + const id = person.id + contentType = data.contentType.split('/')[1]; + + if (!fs.existsSync('/var/www/html/storage/app/' + id + '.' + contentType)) { + const path = '/var/www/html/storage/app/' + id + '.' + contentType; + fs.writeFileSync(path, data.data) + + await People.update({ + photo_url: path + }, { + where: {id} + }); + } + } + } + break; + case 'm.room.encryption': + const hash = crypto.createHash('sha512'); + + const key = hash.update(roomId, 'utf-8'); + + const thread = key.digest('hex'); + let threadInQuestion = await Threads.findOne({ + where: {thread_id: roomId} + }); + + const originalSettings = JSON.parse(threadInQuestion?.settings ?? '{}'); + + await Threads.update({ + settings: JSON.stringify({ + ...originalSettings, + ...event.content, + 'hash': thread + }) + }, { + where: {thread_id: roomId} + }); + + console.log('encrypted room found', await client.crypto.isRoomEncrypted(roomId)); + + break; + // this.db.set(`rooms.${key}`, ).write(); + case 'm.room.topic': + await Threads.update({topic: event.content.topic}, { + where: {thread_id: roomId} + }); + break; + // Meta settings that dont' matter for this app + case 'm.room.canonical_alias': + case 'm.room.history_visibility': + case 'm.room.guest_access': + case 'm.room.power_levels': + case 'm.room.avatar': + case 'm.room.join_rules': + case 'm.bridge': + case 'uk.half-shot.bridge': + case 'com.beeper.chatwoot.conversation_id': + break; + case 'com.beeper.backfill_status': + + case 'com.beeper.rooms.note_to_self': + case 'com.beeper.support_chat': + break; + case "m.space.parent": + case 'm.space.child': + case 'com.beeper.feed': + case 'org.matrix.msc2716.marker': + // a twitter marker? + + default: + console.log(event.type, JSON.stringify(event, null, 4)); + } + ; + } + + + const isEncrypted = await client.crypto.isRoomEncrypted(roomId); + const roomState = await cryptoProvider.getRoom(roomId); + if (isEncrypted && !roomState) { + const encryption = await client.getRoomStateEvent(roomId, 'm.room.encryption') + const history_visibility = await client.getRoomStateEvent(roomId, 'm.room.history_visibility') + const guest_access = await client.getRoomStateEvent(roomId, 'm.room.guest_access') + + await client.cryptoStore.storeRoom(roomId, { + ...encryption, + ...history_visibility, + ...guest_access, + }); + } + + spinner.stopAndPersist({ + text: "✔️ " + index + '/' + rooms.length + ' History sync: ' + roomId, + }); + } + + spinner.succeed('Starting the matrix clint') + await client.start() + + } catch (error) { + console.error("Error:", error.message, error); + } +} + +process.on('SIGINT', function () { + sequelize.close(); + + process.exit(); +}) + +main(); diff --git a/docker/matrix-bot/matrix-js-sdkindex.js b/docker/matrix-bot/matrix-js-sdkindex.js new file mode 100644 index 0000000..3ea418e --- /dev/null +++ b/docker/matrix-bot/matrix-js-sdkindex.js @@ -0,0 +1,690 @@ +import dotenv from 'dotenv'; +import Sequelize, {DataTypes, QueryTypes} from "sequelize"; +import fs from 'fs'; +import crypto from 'crypto'; +import ora from 'ora'; +import sdk from 'matrix-js-sdk'; +import olm from "@matrix-org/olm"; +import { LocalStorage } from 'node-localstorage'; +import {MemoryCryptoStore} from 'matrix-js-sdk/lib/crypto/store/memory-crypto-store.js' +import path from 'path' +dotenv.config(); +import Pusher from "pusher"; +// In order to sync room keys, we need to ensure we can trust the devices we're getting them from. +// We can request the list of devices, and verify their keys. +global.Olm = olm; + +const sequelize = new Sequelize( + process.env.DB_DATABASE, + process.env.DB_USERNAME, + process.env.DB_PASSWORD, + { + host: process.env.DB_HOST, + dialect: process.env.DB_CONNECTION, + logging: false, + } +); +//bot:b0bac09511e3c51130285daaa + +async function findOrCreateMessageEvent(Threads, Messages, People, thread, message, is_decrypted = false) { + if (message.type !== 'm.room.message') { + console.log('Told this was a message, but reporeted as ', message.type) + return null; + } + console.log('Looking for thread', thread); + + let threadInQuestion = await Threads.findOne({ + where: {thread_id: thread} + }); + + if (!threadInQuestion) { + console.error('Thread doesn\'t exist yet', thread); + + process.exit(1); + } + + const messageToCreate = await Messages.findOne({ + where: {event_id: message.event_id,} + }); + + + if (!messageToCreate) { + const thread_id = threadInQuestion.id + + const sender = await findPersonByIdentifier(People, message); + console.log('no messages found for event', message.event_id, 'creating message', { + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted + }) + + await Messages.create({ + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + ...(message?.content?.settings ? message?.content?.settings : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + is_decrypted, + + }); + + const updatedTime = {origin_server_ts: new Date()}; + + pusher.trigger('user.1', 'eloquent.updated: App\\Models\\Thread', { + ...threadInQuestion, + ...updatedTime, + }) + await Threads.update(updatedTime, { + where: {thread_id: thread} + }); + } +} + +async function findPersonByIdentifier(People, message) { + const identifier = message.sender; + + let peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }) + + if (!peoples || peoples.length === 0) { + if (message.type !== 'm.room.member') { + console.log('m.room.member', message); + return null; + } + + await People.create({ + name: message.content.displayname, + identifiers: JSON.stringify([identifier]) + }) + + const data = await client.downloadContent(message.content.avatar_url); + + console.log(data); + process.exit(); + + peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }); + } + + + return peoples[0] ?? null; +} + +const setupSequlize = async () => { + console.log('[-] Sequilize authenticating...'); + await sequelize.authenticate(); + console.log('[-] Sequilize authenticated...'); + + const Threads = sequelize.define('threads', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + }, + description: { + type: DataTypes.STRING, + }, + rules: { + type: DataTypes.STRING, + }, + topic: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.STRING, + }, + origin_server_ts: { + type: DataTypes.DATE, + }, + settings: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const Messages = sequelize.define('messages', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + from_person: { + type: DataTypes.BIGINT, + }, + type: { + type: DataTypes.STRING, + }, + event_id: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.BIGINT, + }, + thumbnail_url: { + type: DataTypes.STRING(2048), + }, + originated_at: { + type: DataTypes.DATE, + }, + message: { + type: DataTypes.TEXT + }, + html_message: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const People = sequelize.define('people', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING(255), + allowNull: false, + }, + primary_number: DataTypes.STRING(255), + primary_address: DataTypes.STRING(255), + primary_email: DataTypes.STRING(255), + pronouns: DataTypes.STRING(255), + birthdate: DataTypes.DATE, + phone_numbers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for phone_numbers'); + } + }, + }, + }, + addresses: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for addresses'); + } + }, + }, + }, + emails: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for emails'); + } + }, + }, + }, + estimated_home_value: DataTypes.STRING(255), + estimated_income: DataTypes.STRING(255), + education: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for education'); + } + }, + }, + }, + jobs: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for jobs'); + } + }, + }, + }, + locality: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for locality'); + } + }, + }, + }, + identifiers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for identifiers'); + } + }, + }, + }, + names: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for names'); + } + }, + }, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, // Set to true if you want Sequelize to manage timestamps + tableName: 'people', + updatedAt: 'updated_at', + createdAt: 'created_at', + collate: 'utf8mb4_unicode_ci', + }); + + const Participant = sequelize.define('thread_participants', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + + person_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + thread_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + + joined_at: { + type: DataTypes.DATE, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, + tableName: 'thread_participants', + updatedAt: 'updated_at', + createdAt: 'created_at', + + }) + console.log('[-] Sequilize models defined...'); + + return { + Threads, + Messages, + People, + Participant, + } +} + +async function main() { + // try { + // const {user_id, device_id} = await client.getWhoAmI(); + // console.log('[-] #############################'); + // console.log('[-] Authenticated as', user_id, device_id); + // console.log('[-] #############################'); + // + // if (!await storageProvider.isUserRegistered(user_id)) { + // storageProvider.addRegisteredUser(user_id); + // } + // + // const { + // Threads, + // Messages, + // People, + // Participant, + // } = await setupSequlize(); + await Olm.init(); + + const client = sdk.createClient({ + baseUrl: process.env.MATRIX_HOST, + accessToken: process.env.MATRIX_ACCESS_TOKEN, + cryptoStore: new MemoryCryptoStore(), + + userId: '@austinkregel:communication.ventures', + deviceId: process.env.MATRIX_DEVICE, + }); + + try { + console.log('client',await client.whoami()) + } catch (e) { + console.log('found client error'); + console.error(e) + + process.exit(0) + } + + console.log('Initializing crypto client') + + await client.initCrypto(); + console.log('Initialized crypto client') + console.log('Starting client') + const rooms = client.getRooms() + + console.log(rooms); + await client.startClient({ initialSyncLimit: 1 }) + console.log('Started client') + // + // // const client = await auth.passwordLogin(userId, password, "Programmatic Access"); + // console.log('[-] Autojoin rooms mixin...'); + // AutojoinRoomsMixin.setupOnClient(client); + // AutojoinUpgradedRoomsMixin.setupOnClient(client); + // console.log('[-] Autojoin rooms mixin established..'); + // + // client.on('room.join', async (event) => { + // await client.crypto.onRoomJoin(event.room_id); + // }) + // + // client.on('room.encrypted_event', async (roomId, event) => { + // // handle `m.room.encrypted` event that was received from the server + // console.log('Room encrypted event', roomId, event) + // await findOrCreateMessageEvent(Threads, Messages, People, roomId, { + // type: 'm.room.message', + // event_id: event.event_id, + // origin_server_ts: 0, + // room_id: event.room_id, + // content: { + // ...event.content, + // settings: event.content + // }, + // sender: event.sender, + // }, false); + // }) + // + // client.on("room.message", async (roomId, messageEvent) => { + // // await client.upload + // console.log(roomId, 'received a new message from', messageEvent?.sender) + // await findOrCreateMessageEvent(Threads, Messages, People, roomId, messageEvent, true); + // }); + // + // console.log('[-] Preparing crypto lib'); + // const cryptoSpinner = ora('Syncing joined rooms, and preparing encryption').start(); + // /** @var IStorageProvider userStorage **/ + // const userStorage = await client.storageProvider.storageForUser(user_id) + // + // const rooms = await client.getJoinedRooms() + // + // await client.crypto.prepare(rooms); + // cryptoSpinner.succeed('Crypto library setup, encryption ready.'); + // + // console.log('[-] ready', client.crypto.isReady); + // + // console.log('[-]', await client.checkOneTimeKeyCounts()); + // + // + // const devices = await client.getUserDevices([user_id]); + // + // const {one_time_keys} = await client.claimOneTimeKeys({ + // [user_id]: {[device_id]: "signed_curve25519"} + // }, 10000); + // + // if (!one_time_keys[user_id]) { + // console.error('One time keys are not supported for this device'); + // return; + // } + // + // console.log('[-] Finished setting up crypto lib'); + // // await client.begin(); + // console.log('[-] Started'); + // const resp = await client.uploadDeviceOneTimeKeys(one_time_keys[user_id][device_id]) + // + // console.log('Thing has started', device_id, user_id, resp); + // const spinner = ora('Loading message history from all the threads').start(); + // + // for (let index in rooms) { + // const roomId = rooms[index]; + // + // // spinner.spinner = 'dots'; + // // spinner.text = index+'/'+rooms.length+' history syncing: ' + roomId + // // spinner.start() + // + // const state = await client.getRoomState(roomId); + // + // for (let stateIndex in state) { + // const event = state[stateIndex]; + // let test = null; + // switch (event.type) { + // case 'm.room.create': + // test = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // + // if (test) { + // continue; + // } + // await Threads.create({ + // name: roomId, + // thread_id: roomId, + // origin_server_ts: new Date(event.origin_server_ts), + // }); + // break; + // case 'm.room.name': + // test = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // + // if (test) { + // continue; + // } + // await Threads.update({name: event.content.name}, { + // where: {thread_id: roomId} + // }); + // break; + // case 'm.room.member': + // let matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // if (matchingPeople.length === 0) { + // await People.create({ + // name: event.content?.displayname ?? event.sender, + // identifiers: JSON.stringify([event.sender]) + // }) + // } + // matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // for (let index in matchingPeople) { + // const person = matchingPeople[index]; + // + // let thread = await Threads.findOne({ + // where: {thread_id: event.room_id} + // }); + // let participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // + // if (participant.length === 0) { + // await Participant.create({ + // person_id: person.id, + // thread_id: thread.id, + // joined_at: new Date(event.origin_server_ts), + // }) + // participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // } + // + // if (event.content?.avatar_url) { + // let contentType = 'jpg'; + // const data = await client.downloadContent(event.content?.avatar_url); + // + // if (!data.data) { + // continue; + // } + // + // const id = person.id + // contentType = data.contentType.split('/')[1]; + // + // if (!fs.existsSync('/var/www/html/storage/app/' + id + '.' + contentType)) { + // const path = '/var/www/html/storage/app/' + id + '.' + contentType; + // fs.writeFileSync(path, data.data) + // + // await People.update({ + // photo_url: path + // }, { + // where: {id} + // }); + // } + // } + // } + // break; + // case 'm.room.encryption': + // const hash = crypto.createHash('sha512'); + // + // const key = hash.update(roomId, 'utf-8'); + // + // const thread = key.digest('hex'); + // let threadInQuestion = await Threads.findOne({ + // where: {thread_id: roomId} + // }); + // + // const originalSettings = JSON.parse(threadInQuestion?.settings ?? '{}'); + // + // await Threads.update({ + // settings: JSON.stringify({ + // ...originalSettings, + // ...event.content, + // 'hash': thread + // }) + // }, { + // where: {thread_id: roomId} + // }); + // + // console.log('encrypted room found', await client.crypto.isRoomEncrypted(roomId)); + // + // break; + // // this.db.set(`rooms.${key}`, ).write(); + // case 'm.room.topic': + // await Threads.update({topic: event.content.topic}, { + // where: {thread_id: roomId} + // }); + // break; + // // Meta settings that dont' matter for this app + // case 'm.room.canonical_alias': + // case 'm.room.history_visibility': + // case 'm.room.guest_access': + // case 'm.room.power_levels': + // case 'm.room.avatar': + // case 'm.room.join_rules': + // case 'm.bridge': + // case 'uk.half-shot.bridge': + // case 'com.beeper.chatwoot.conversation_id': + // break; + // case 'com.beeper.backfill_status': + // + // case 'com.beeper.rooms.note_to_self': + // case 'com.beeper.support_chat': + // break; + // case "m.space.parent": + // case 'm.space.child': + // case 'com.beeper.feed': + // case 'org.matrix.msc2716.marker': + // // a twitter marker? + // + // default: + // console.log(event.type, JSON.stringify(event, null, 4)); + // } + // ; + // } + // + // + // const isEncrypted = await client.crypto.isRoomEncrypted(roomId); + // const roomState = await cryptoProvider.getRoom(roomId); + // if (isEncrypted && !roomState) { + // const encryption = await client.getRoomStateEvent(roomId, 'm.room.encryption') + // const history_visibility = await client.getRoomStateEvent(roomId, 'm.room.history_visibility') + // const guest_access = await client.getRoomStateEvent(roomId, 'm.room.guest_access') + // + // await client.cryptoStore.storeRoom(roomId, { + // ...encryption, + // ...history_visibility, + // ...guest_access, + // }); + // } + // + // spinner.stopAndPersist({ + // text: "✔️ " + index + '/' + rooms.length + ' History sync: ' + roomId, + // }); + // } + // + // spinner.succeed('Starting the matrix clint') + // await client.start() + // + // } catch (error) { + // console.error("Error:", error.message, error); + // } +} + +process.on('SIGINT', function () { + sequelize.close(); + + process.exit(); +}) + +main(); diff --git a/docker/matrix-bot/personal-client.js b/docker/matrix-bot/personal-client.js new file mode 100644 index 0000000..2adf544 --- /dev/null +++ b/docker/matrix-bot/personal-client.js @@ -0,0 +1,669 @@ +import { + MatrixClient, + MatrixAuth, + AutojoinRoomsMixin, + AutojoinUpgradedRoomsMixin, + SimpleFsStorageProvider, + RustSdkCryptoStorageProvider, + RustSdkAppserviceCryptoStorageProvider, + Appservice, + SimpleRetryJoinStrategy, +} from "matrix-bot-sdk"; +import dotenv from 'dotenv'; +import Sequelize, { DataTypes, QueryTypes } from "sequelize"; +import fs from 'fs'; +import * as sha512 from "hash.js/lib/hash/sha/512.js"; +import crypto from 'crypto'; +const homeserverUrl = "https://matrix.beeper.com"; +import ora from 'ora'; +dotenv.config(); +import Pusher from "pusher"; + +const storageProvider = new SimpleFsStorageProvider("/var/www/html/docker/matrix-bot/bot.json"); // or any other IStorageProvider +const cryptoProvider = new RustSdkCryptoStorageProvider("/var/www/html/docker/matrix-bot/db"); +const cryptoProvider2 = new RustSdkAppserviceCryptoStorageProvider("/var/www/html/docker/matrix-bot/db"); +// const auth = new MatrixAuth(homeserverUrl); +// const client = await auth.passwordLogin(process.env.MATRIX_USERNAME, process.env.MATRIX_PASSWORD); +const client = new MatrixClient( + homeserverUrl, + process.env.MATRIX_ACCESS_TOKEN, + storageProvider, + cryptoProvider +); + +// In order to sync room keys, we need to ensure we can trust the devices we're getting them from. +// We can request the list of devices, and verify their keys. +// + + +const sequelize = new Sequelize( + process.env.DB_DATABASE, + process.env.DB_USERNAME, + process.env.DB_PASSWORD, + { + host: process.env.DB_HOST, + dialect: process.env.DB_CONNECTION, + logging: false, + } +); + + +// Since we're talking directly with our database, Laravel isn't getting any of the events +// Nothing in our UI will update, and no php code will trigger. +// We need a way to hook +const pusher = new Pusher({ + appId: process.env.PUSHER_APP_ID, + key: process.env.PUSHER_APP_KEY, + secret: process.env.PUSHER_APP_SECRET, + cluster: process.env.PUSHER_APP_CLUSTER, + wsHost: process.env.PUSHER_HOST ? process.env.PUSHER_HOST : `ws-${process.env.PUSHER_APP_CLUSTER}.pusher.com`, + wsPort: process.env.PUSHER_PORT ?? 80, + wssPort: process.env.PUSHER_PORT ?? 443, + useTLS: true +}); + +async function findOrCreateMessageEvent(Threads, Messages, People, thread, message) +{ + if (message.type !== 'm.room.message') { + return null; + } + + let threadInQuestion = await Threads.findOne({ + where: { thread_id: thread } + }); + + if (!threadInQuestion) { + console.error('Thread doesn\'t exist yet', thread); + + process.exit(1); + } + + const messageToCreate = await Messages.findOne({ + where: { event_id: message.event_id, } + }); + + + if (!messageToCreate) { + const thread_id = threadInQuestion.id + + const sender = await findPersonByIdentifier(People, message); + console.log('no messages found for event', message.event_id, 'creating message',{ + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + } ) + + await Messages.create({ + ...(message?.content?.info?.thumbnail_url !== undefined ? { + thumbnail_url: message.content.info.thumbnail_url, + } : {}), + from_person: sender?.id, + thread_id, + type: message.type, + originated_at: new Date(message.origin_server_ts), + message: message?.content?.body, + event_id: message.event_id, + html_message: message?.content?.format_body, + }); + + const updatedTime = { origin_server_ts: new Date() }; + + pusher.trigger('user.1', 'eloquent.updated: App\\Models\\Thread', { + ...threadInQuestion, + ...updatedTime, + }) + await Threads.update(updatedTime, { + where: { thread_id: thread } + }); + } +} + +async function findPersonByIdentifier(People, message) +{ + const identifier = message.sender; + + let peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }) + + if (!peoples || peoples.length === 0) { + if (message.type !== 'm.room.member') { + console.log('m.room.member', message); + return null; + } + + await People.create({ + name: message.content.displayname, + identifiers: JSON.stringify([identifier]) + }) + + const data = await client.downloadContent(message.content.avatar_url); + + console.log(data); + process.exit(); + + peoples = await sequelize.query('select * from people where JSON_CONTAINS(identifiers, :identifier, \'$\')', { + type: QueryTypes.SELECT, + replacements: { + identifier: JSON.stringify(identifier) + }, + }); + } + + + return peoples[0] ?? null; +} + +const setupSequlize = async () => { + console.log('[-] Sequilize authenticating...'); + await sequelize.authenticate(); + console.log('[-] Sequilize authenticated...'); + + const Threads = sequelize.define('threads', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + name: { + type: DataTypes.STRING, + }, + description: { + type: DataTypes.STRING, + }, + rules: { + type: DataTypes.STRING, + }, + topic: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.STRING, + }, + origin_server_ts: { + type: DataTypes.DATE, + }, + settings: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const Messages = sequelize.define('messages', { + id: { + type: DataTypes.BIGINT, + autoIncrement: true, + primaryKey: true, + }, + from_person: { + type: DataTypes.BIGINT, + }, + type: { + type: DataTypes.STRING, + }, + event_id: { + type: DataTypes.STRING, + }, + thread_id: { + type: DataTypes.BIGINT, + }, + thumbnail_url: { + type: DataTypes.STRING(2048), + }, + originated_at: { + type: DataTypes.DATE, + }, + message: { + type: DataTypes.TEXT + }, + html_message: { + type: DataTypes.TEXT, + }, + created_at: { + type: DataTypes.DATE, + }, + updated_at: { + type: DataTypes.DATE, + } + }, { + updatedAt: 'updated_at', + createdAt: 'created_at', + }) + + const People = sequelize.define('people', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING(255), + allowNull: false, + }, + primary_number: DataTypes.STRING(255), + primary_address: DataTypes.STRING(255), + primary_email: DataTypes.STRING(255), + pronouns: DataTypes.STRING(255), + birthdate: DataTypes.DATE, + phone_numbers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for phone_numbers'); + } + }, + }, + }, + addresses: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for addresses'); + } + }, + }, + }, + emails: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for emails'); + } + }, + }, + }, + estimated_home_value: DataTypes.STRING(255), + estimated_income: DataTypes.STRING(255), + education: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for education'); + } + }, + }, + }, + jobs: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for jobs'); + } + }, + }, + }, + locality: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for locality'); + } + }, + }, + }, + identifiers: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for identifiers'); + } + }, + }, + }, + names: { + type: DataTypes.TEXT, + validate: { + isJson: function (value) { + try { + JSON.parse(value); + } catch (error) { + throw new Error('Invalid JSON format for names'); + } + }, + }, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, // Set to true if you want Sequelize to manage timestamps + tableName: 'people', + updatedAt: 'updated_at', + createdAt: 'created_at', + collate: 'utf8mb4_unicode_ci', + }); + + const Participant = sequelize.define('thread_participants', { + id: { + type: DataTypes.BIGINT.UNSIGNED, + primaryKey: true, + autoIncrement: true, + }, + + person_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + thread_id: { + type: DataTypes.BIGINT.UNSIGNED, + }, + + joined_at: { + type: DataTypes.DATE, + }, + created_at: DataTypes.DATE, + updated_at: DataTypes.DATE, + }, { + timestamps: true, + tableName: 'thread_participants', + updatedAt: 'updated_at', + createdAt: 'created_at', + + }) + console.log('[-] Sequilize models defined...'); + + return { + Threads, + Messages, + People, + Participant, + } +} + +async function main() { + try { + const { user_id, device_id } = await client.getWhoAmI(); + console.log('[-] #############################'); + console.log('[-] Authenticated as', user_id, device_id); + console.log('[-] #############################'); + + if (!await storageProvider.isUserRegistered(user_id)) { + storageProvider.addRegisteredUser(user_id); + } + + const { + Threads, + Messages, + People, + Participant, + } = await setupSequlize(); + + // const client = await auth.passwordLogin(userId, password, "Programmatic Access"); + console.log('[-] Autojoin rooms mixin...'); + AutojoinRoomsMixin.setupOnClient(client); + AutojoinUpgradedRoomsMixin.setupOnClient(client); + console.log('[-] Autojoin rooms mixin established..'); + + client.on('room.join', async (event) => { + await client.crypto.onRoomJoin(event.room_id); + }) + + client.on('room.encrypted_event', (roomId, event) => { + // handle `m.room.encrypted` event that was received from the server + console.log('Room Ecnrypted eevent', roomId, event) + }) + + + client.on("room.message", async (roomId, messageEvent) => { + // await client.upload + console.log(roomId, 'received a new message from', messageEvent?.sender) + await findOrCreateMessageEvent(Threads, Messages, People, roomId, messageEvent); + }); + + console.log('[-] Preparing crypto lib'); + const cryptoSpinner = ora('Syncing joined rooms, and preparing encryption').start(); + /** @var IStorageProvider userStorage **/ + const userStorage = await client.storageProvider.storageForUser(user_id) + + const rooms = await client.getJoinedRooms() + + await client.crypto.prepare(rooms); + cryptoSpinner.succeed('Crypto library setup, encryption ready.'); + + console.log('[-] ready', client.crypto.isReady); + + console.log('[-]',await client.checkOneTimeKeyCounts()); + + const { one_time_keys } = await client.claimOneTimeKeys({ + [user_id]: {[device_id]: "signed_curve25519"} + }, 10000); + + if (!one_time_keys[user_id]) { + console.error('One time keys are not supported for this device'); + return; + } + + console.log('[-]', one_time_keys); + + console.log('[-] Finished setting up crypto lib'); + // await client.begin(); + console.log('[-] Started'); + const resp = await client.uploadDeviceOneTimeKeys(one_time_keys[user_id][device_id]) + + console.log('Thing has started', device_id, user_id, resp); + const spinner = ora('Loading message history from all the threads').start(); + + for (let index in rooms) { + const roomId = rooms[index]; + + // spinner.spinner = 'dots'; + // spinner.text = index+'/'+rooms.length+' history syncing: ' + roomId + // spinner.start() + + const isEncrypted = await client.crypto.isRoomEncrypted(roomId); + if (isEncrypted) { + await client.crypto.onRoomJoin(roomId); + } + // const state = await client.getRoomState(roomId); + + // for (let stateIndex in state) { + // const event = state[stateIndex]; + // let test = null; + // switch (event.type) { + // case 'm.room.create': + // test = await Threads.findOne({ + // where: { thread_id: event.room_id } + // }); + // + // if (test) { + // continue; + // } + // await Threads.create({ + // name: roomId, + // thread_id: roomId, + // origin_server_ts: new Date(event.origin_server_ts), + // }); + // break; + // case 'm.room.name': + // test = await Threads.findOne({ + // where: { thread_id: event.room_id } + // }); + // + // if (test) { + // continue; + // } + // await Threads.update({ name: event.content.name }, { + // where: { thread_id: roomId } + // }); + // break; + // case 'm.room.member': + // let matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // if (matchingPeople.length === 0) { + // await People.create({ + // name: event.content?.displayname ?? event.sender, + // identifiers: JSON.stringify([event.sender]) + // }) + // } + // matchingPeople = await sequelize.query('select * from people where json_contains(identifiers, ?, \'$\')', { + // replacements: [JSON.stringify(event.sender)], + // type: QueryTypes.SELECT, + // model: People, + // }) + // + // for (let index in matchingPeople) { + // const person = matchingPeople[index]; + // + // let thread = await Threads.findOne({ + // where: { thread_id: event.room_id } + // }); + // let participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // + // if (participant.length === 0) { + // await Participant.create({ + // person_id: person.id, + // thread_id: thread.id, + // joined_at: new Date(event.origin_server_ts), + // }) + // participant = await sequelize.query('select * from thread_participants where person_id = ? and thread_id = ?', { + // replacements: [JSON.stringify(person.id), JSON.stringify(thread.id)], + // type: QueryTypes.SELECT, + // }); + // } + // + // if (event.content?.avatar_url) { + // let contentType = 'jpg'; + // const data = await client.downloadContent(event.content?.avatar_url); + // + // if (!data.data) { + // continue; + // } + // + // const id = person.id + // contentType = data.contentType.split('/')[1]; + // + // if (!fs.existsSync('/var/www/html/storage/app/' + id + '.' + contentType)) { + // const path = '/var/www/html/storage/app/' + id + '.' + contentType; + // fs.writeFileSync(path, data.data) + // + // await People.update({ + // photo_url: path + // }, { + // where: { id } + // }); + // } + // } + // } + // break; + // case 'm.room.encryption': + // const hash = crypto.createHash('sha512'); + // + // const key = hash.update(roomId, 'utf-8'); + // + // const thread = key.digest('hex'); + // let threadInQuestion = await Threads.findOne({ + // where: { thread_id: roomId } + // }); + // + // const originalSettings = JSON.parse(threadInQuestion?.settings ?? '{}'); + // + // await Threads.update({ + // settings: JSON.stringify({ + // ...originalSettings, + // ...event.content, + // 'hash': thread + // }) + // }, { + // where: { thread_id: roomId } + // }); + // + // console.log('encrypted room found', await client.crypto.isRoomEncrypted(roomId)); + // + // break; + // // this.db.set(`rooms.${key}`, ).write(); + // case 'm.room.topic': + // await Threads.update({ topic: event.content.topic }, { + // where: { thread_id: roomId } + // }); + // break; + // // Meta settings that dont' matter for this app + // case 'm.room.canonical_alias': + // case 'm.room.history_visibility': + // case 'm.room.guest_access': + // case 'm.room.power_levels': + // case 'm.room.avatar': + // case 'm.room.join_rules': + // case 'm.bridge': + // case 'uk.half-shot.bridge': + // case 'com.beeper.chatwoot.conversation_id': + // break; + // case 'com.beeper.backfill_status': + // + // case 'com.beeper.rooms.note_to_self': + // case 'com.beeper.support_chat': + // break; + // case "m.space.parent": + // case 'm.space.child': + // case 'com.beeper.feed': + // case 'org.matrix.msc2716.marker': + // // a twitter marker? + // + // default: + // console.log(event.type, JSON.stringify(event, null, 4)); + // }; + // } + + spinner.stopAndPersist({ + text: "✔️ " + index+'/'+rooms.length+' History sync: ' + roomId, + }); + } + + spinner.succeed('Starting the matrix clint') + await client.start() + + } catch (error) { + console.error("Error:", error.message, error); + } +} + +process.on('SIGINT', function () { + sequelize.close(); + + process.exit(); +}) + +main(); diff --git a/docker/matrix-bot/reset-password.tsx b/docker/matrix-bot/reset-password.tsx new file mode 100644 index 0000000..fce0425 --- /dev/null +++ b/docker/matrix-bot/reset-password.tsx @@ -0,0 +1,211 @@ +#!/usr/bin/env node_modules/.bin/tsx + +import { default as originalFetch } from "node-fetch"; +import yargs from "yargs"; +import prompts from "prompts"; + +const DEBUG = ["1", "true"].includes(process.env.DEBUG) ?? false; + +async function debugFetch(url: string, options: any) { + console.debug("[fetch]", url, options); + + const response = await originalFetch(url, options); + + console.debug("[fetch] Response status:", response.status); + console.debug("[fetch] Response headers:", response.headers.raw()); + console.debug("[fetch] Response body:", await response.clone().text()); + + return response; +} + +const fetch = DEBUG ? debugFetch : originalFetch; + +async function loginWithEmail(_email?: string) { + const email = + _email ?? + ( + await prompts({ + type: "text", + name: "email", + message: "Enter your email:", + }) + ).email; + + if (!email) throw new Error("Email is required"); + + const loginResponse = await fetch("https://api.beeper.com/user/login", { + method: "POST", + headers: { + Authorization: "Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE", + }, + }); + const { request } = await loginResponse.json(); + + if (!request) + throw new Error("JSON response object missing required 'request' key"); + + await fetch("https://api.beeper.com/user/login/email", { + method: "POST", + headers: { + Authorization: "Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE", + "Content-Type": "application/json", + }, + body: JSON.stringify({ request, email }), + }); + + const code = ( + await prompts({ + type: "text", + name: "code", + message: "Enter the challenge code sent to your email:", + }) + ).code; + + if (!code) throw new Error("Code is required"); + + const loginChallengeResponse = await fetch( + "https://api.beeper.com/user/login/response", + { + method: "POST", + headers: { + Authorization: "Bearer BEEPER-PRIVATE-API-PLEASE-DONT-USE", + "Content-Type": "application/json", + }, + body: JSON.stringify({ request, response: code }), + } + ); + const { token } = await loginChallengeResponse.json(); + + console.log("Your JWT Token: ", token); +} + +async function loginWithToken(_token?: string) { + const token = + _token ?? + ( + await prompts({ + type: "text", + name: "token", + message: "Enter your JWT token:", + }) + ).token; + + if (!token) throw new Error("Token is required"); + + const jwtLoginResponse = await fetch( + "https://matrix.beeper.com/_matrix/client/v3/login", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ type: "org.matrix.login.jwt", token: token }), + } + ); + + const jwtLoginResponseData = await jwtLoginResponse.json(); + const { + user_id: userId, + access_token: accessToken, + device_id: deviceId, + } = jwtLoginResponseData; + + console.log( + "JWT Login Response:", + JSON.stringify(jwtLoginResponseData, null, 2) + ); + + console.log("User ID:", userId); + console.log("Access Token:", accessToken); + console.log("Device ID:", deviceId); +} + +async function resetPassword( + _accessToken?: string, + _jwtToken?: string, + _newPassword?: string +) { + const accessToken = + _accessToken ?? + ( + await prompts({ + type: "text", + name: "access_token", + message: "Enter your Access Token:", + }) + ).access_token; + + if (!accessToken) throw new Error("Access token is required"); + + const jwtToken = + _jwtToken ?? + ( + await prompts({ + type: "text", + name: "jwt_token", + message: "Enter your JWT Token:", + }) + ).jwt_token; + + if (!jwtToken) throw new Error("JWT token is required"); + + const newPassword = + _newPassword ?? + ( + await prompts({ + type: "password", + name: "new_password", + message: "Enter your new password:", + }) + ).new_password; + + if (!newPassword) throw new Error("New password is required"); + + const userInteractiveAuthenticationFlowsResponse = await fetch( + "https://matrix.beeper.com/_matrix/client/v3/account/password", + { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({}), + } + ); + + const { session, flows } = + await userInteractiveAuthenticationFlowsResponse.json(); + + const hasJwtFlow = flows.find((f) => + f.stages.includes("org.matrix.login.jwt") + ); + + if (!hasJwtFlow) + throw new Error("Matrix server doesn't seem to support JWT flow"); + + await fetch("https://matrix.beeper.com/_matrix/client/v3/account/password", { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + auth: { + type: "org.matrix.login.jwt", + token: jwtToken, + session: session, + }, + new_password: newPassword, + logout_devices: false, + }), + }); + + console.log("Password reset successfully"); +} + +// loginWithEmail('beeper@kregel.email') +const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXN0aW5rcmVnZWwiLCJpYXQiOjE2OTYzNjEzMDYsImV4cCI6MTY5NjM2MjIwNn0.Q70QD1jc3Jz5CZLmoQ1wkKuECb6g2Fcwp9e5UpD6Q-I'; + +// loginWithToken(jwt); + +resetPassword('syt_YXVzdGlua3JlZ2Vs_FiMVgVyaZlHWAtCvTmgo_0lpziI',jwt, 'oKcYigyptEFGcqGLn9JtKkurQ9ymrCJD'); diff --git a/docker/postgres/initialize.sql b/docker/postgres/initialize.sql new file mode 100644 index 0000000..4d398b3 --- /dev/null +++ b/docker/postgres/initialize.sql @@ -0,0 +1,8 @@ +CREATE DATABASE synapse; +GRANT ALL PRIVILEGES ON DATABASE synapse TO postgres; + +CREATE DATABASE synapse_discord; +GRANT ALL PRIVILEGES ON DATABASE synapse_discord TO postgres; + +CREATE DATABASE synapse_facebook; +GRANT ALL PRIVILEGES ON DATABASE synapse_facebook TO postgres; diff --git a/docker/websocket/Dockerfile b/docker/websocket/Dockerfile new file mode 100644 index 0000000..8c14212 --- /dev/null +++ b/docker/websocket/Dockerfile @@ -0,0 +1,10 @@ +FROM austinkregel/base:latest + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/docker/8.0/php.ini b/docker/websocket/php.ini similarity index 70% rename from docker/8.0/php.ini rename to docker/websocket/php.ini index 39dcbca..66d04d5 100644 --- a/docker/8.0/php.ini +++ b/docker/websocket/php.ini @@ -2,6 +2,3 @@ post_max_size = 100M upload_max_filesize = 100M variables_order = EGPCS - -[opcache] -opcache.enable_cli=1 diff --git a/docker/8.0/start-container b/docker/websocket/start-container similarity index 75% rename from docker/8.0/start-container rename to docker/websocket/start-container index b864399..b99ddd0 100644 --- a/docker/8.0/start-container +++ b/docker/websocket/start-container @@ -13,5 +13,5 @@ chmod -R ugo+rw /.composer if [ $# -gt 0 ]; then exec gosu $WWWUSER "$@" else - exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf fi diff --git a/docker/websocket/supervisord.conf b/docker/websocket/supervisord.conf new file mode 100644 index 0000000..bface11 --- /dev/null +++ b/docker/websocket/supervisord.conf @@ -0,0 +1,12 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:websockets] +command=/usr/bin/php /var/www/html/artisan websockets:serve +numprocs=1 +autostart=true +autorestart=true +user=sail diff --git a/package.json b/package.json index 36d9aa7..e9fe713 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,44 @@ "vue": "^3.2.31" }, "dependencies": { + "@google-cloud/dialogflow": "^6.1.0", + "@headlessui/vue": "^1.7.16", "@heroicons/vue": "^2.0.18", "@kbco/query-builder": "^0.0.4", + "@matrix-org/matrix-sdk-crypto-nodejs": "^0.1.0-beta.11", + "@matrix-org/olm": "3.2.15", + "@meforma/vue-toaster": "^1.3.0", + "@types/matrix-js-sdk": "^11.1.0", + "chart.js": "^4.4.0", + "chia": "^0.0.1", + "cli-spinners": "^2.9.1", + "dayjs": "^1.11.9", + "dotenv": "^16.3.1", + "laravel-echo": "^1.15.3", + "mariadb": "^3.2.1", + "matrix-bot-sdk": "^0.6.6", + "matrix-js-sdk": "^29.1.0", + "mocha": "^10.2.0", + "mysql2": "^3.6.1", + "node-fetch": "^3.3.1", + "node-localstorage": "^3.0.5", + "notiwind": "^2.0.2", + "ora": "^7.0.1", + "prompts": "^2.4.2", + "pusher": "^5.1.3", + "pusher-js": "^8.3.0", + "sequelize": "^6.33.0", + "supertest": "^6.3.3", + "vue-draggable-next": "^2.2.1", "vue-json-pretty": "^2.2.4", "vue-select": "^4.0.0-beta.6", "vuedraggable": "^4.1.0", - "vuex": "^4.1.0" + "vuex": "^4.1.0", + "ws": "^8.14.2", + "xterm": "^5.3.0", + "xterm-addon-fit": "^0.8.0", + "xterm-addon-unicode11": "^0.6.0", + "xterm-addon-web-links": "^0.9.0", + "yargs": "^17.7.2" } } diff --git a/phpunit.xml b/phpunit.xml index ff19ac1..5facad6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,9 @@ ./tests/Feature + + ./tests/Integration + @@ -21,11 +24,12 @@ - - + + + diff --git a/public/vendor/horizon/app.js b/public/vendor/horizon/app.js index cf252d5..7291454 100644 --- a/public/vendor/horizon/app.js +++ b/public/vendor/horizon/app.js @@ -1,2 +1,2 @@ /*! For license information please see app.js.LICENSE.txt */ -(()=>{var t,e={30:(t,e,o)=>{"use strict";var p=Object.freeze({}),b=Array.isArray;function n(t){return null==t}function M(t){return null!=t}function z(t){return!0===t}function c(t){return"string"==typeof t||"number"==typeof t||"symbol"==typeof t||"boolean"==typeof t}function r(t){return"function"==typeof t}function i(t){return null!==t&&"object"==typeof t}var a=Object.prototype.toString;function O(t){return"[object Object]"===a.call(t)}function s(t){return"[object RegExp]"===a.call(t)}function l(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return M(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function A(t){return null==t?"":Array.isArray(t)||O(t)&&t.toString===a?JSON.stringify(t,null,2):String(t)}function u(t){var e=parseFloat(t);return isNaN(e)?t:e}function f(t,e){for(var o=Object.create(null),p=t.split(","),b=0;b-1)return t.splice(p,1)}}var m=Object.prototype.hasOwnProperty;function g(t,e){return m.call(t,e)}function v(t){var e=Object.create(null);return function(o){return e[o]||(e[o]=t(o))}}var R=/-(\w)/g,y=v((function(t){return t.replace(R,(function(t,e){return e?e.toUpperCase():""}))})),B=v((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),L=/\B([A-Z])/g,X=v((function(t){return t.replace(L,"-$1").toLowerCase()}));var _=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function o(o){var p=arguments.length;return p?p>1?t.apply(e,arguments):t.call(e,o):t.call(e)}return o._length=t.length,o};function N(t,e){e=e||0;for(var o=t.length-e,p=new Array(o);o--;)p[o]=t[o+e];return p}function w(t,e){for(var o in e)t[o]=e[o];return t}function x(t){for(var e={},o=0;o0,et=Q&&Q.indexOf("edge/")>0;Q&&Q.indexOf("android");var ot=Q&&/iphone|ipad|ipod|ios/.test(Q);Q&&/chrome\/\d+/.test(Q),Q&&/phantomjs/.test(Q);var pt,bt=Q&&Q.match(/firefox\/(\d+)/),nt={}.watch,Mt=!1;if(K)try{var zt={};Object.defineProperty(zt,"passive",{get:function(){Mt=!0}}),window.addEventListener("test-passive",null,zt)}catch(t){}var ct=function(){return void 0===pt&&(pt=!K&&void 0!==o.g&&(o.g.process&&"server"===o.g.process.env.VUE_ENV)),pt},rt=K&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function it(t){return"function"==typeof t&&/native code/.test(t.toString())}var at,Ot="undefined"!=typeof Symbol&&it(Symbol)&&"undefined"!=typeof Reflect&&it(Reflect.ownKeys);at="undefined"!=typeof Set&&it(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var st=null;function lt(t){void 0===t&&(t=null),t||st&&st._scope.off(),st=t,t&&t._scope.on()}var dt=function(){function t(t,e,o,p,b,n,M,z){this.tag=t,this.data=e,this.children=o,this.text=p,this.elm=b,this.ns=void 0,this.context=n,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=M,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=z,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),At=function(t){void 0===t&&(t="");var e=new dt;return e.text=t,e.isComment=!0,e};function ut(t){return new dt(void 0,void 0,void 0,String(t))}function ft(t){var e=new dt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var qt=0,ht=[],Wt=function(){function t(){this._pending=!1,this.id=qt++,this.subs=[]}return t.prototype.addSub=function(t){this.subs.push(t)},t.prototype.removeSub=function(t){this.subs[this.subs.indexOf(t)]=null,this._pending||(this._pending=!0,ht.push(this))},t.prototype.depend=function(e){t.target&&t.target.addDep(this)},t.prototype.notify=function(t){var e=this.subs.filter((function(t){return t}));for(var o=0,p=e.length;o0&&(Jt((p=Kt(p,"".concat(e||"","_").concat(o)))[0])&&Jt(i)&&(a[r]=ut(i.text+p[0].text),p.shift()),a.push.apply(a,p)):c(p)?Jt(i)?a[r]=ut(i.text+p):""!==p&&a.push(ut(p)):Jt(p)&&Jt(i)?a[r]=ut(i.text+p.text):(z(t._isVList)&&M(p.tag)&&n(p.key)&&M(e)&&(p.key="__vlist".concat(e,"_").concat(o,"__")),a.push(p)));return a}function Qt(t,e,o,p,n,a){return(b(o)||c(o))&&(n=p,p=o,o=void 0),z(a)&&(n=2),function(t,e,o,p,n){if(M(o)&&M(o.__ob__))return At();M(o)&&M(o.is)&&(e=o.is);if(!e)return At();0;b(p)&&r(p[0])&&((o=o||{}).scopedSlots={default:p[0]},p.length=0);2===n?p=Gt(p):1===n&&(p=function(t){for(var e=0;e0,z=e?!!e.$stable:!M,c=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(z&&b&&b!==p&&c===b.$key&&!M&&!b.$hasNormal)return b;for(var r in n={},e)e[r]&&"$"!==r[0]&&(n[r]=qe(t,o,r,e[r]))}else n={};for(var i in o)i in n||(n[i]=he(o,i));return e&&Object.isExtensible(e)&&(e._normalized=n),Y(n,"$stable",z),Y(n,"$key",c),Y(n,"$hasNormal",M),n}function qe(t,e,o,p){var n=function(){var e=st;lt(t);var o=arguments.length?p.apply(null,arguments):p({}),n=(o=o&&"object"==typeof o&&!b(o)?[o]:Gt(o))&&o[0];return lt(e),o&&(!n||1===o.length&&n.isComment&&!ue(n))?void 0:o};return p.proxy&&Object.defineProperty(e,o,{get:n,enumerable:!0,configurable:!0}),n}function he(t,e){return function(){return t[e]}}function We(t){return{get attrs(){if(!t._attrsProxy){var e=t._attrsProxy={};Y(e,"_v_attr_proxy",!0),me(e,t.$attrs,p,t,"$attrs")}return t._attrsProxy},get listeners(){t._listenersProxy||me(t._listenersProxy={},t.$listeners,p,t,"$listeners");return t._listenersProxy},get slots(){return function(t){t._slotsProxy||ve(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(t)},emit:_(t.$emit,t),expose:function(e){e&&Object.keys(e).forEach((function(o){return Ft(t,e,o)}))}}}function me(t,e,o,p,b){var n=!1;for(var M in e)M in t?e[M]!==o[M]&&(n=!0):(n=!0,ge(t,M,p,b));for(var M in t)M in e||(n=!0,delete t[M]);return n}function ge(t,e,o,p){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return o[p][e]}})}function ve(t,e){for(var o in e)t[o]=e[o];for(var o in t)o in e||delete t[o]}var Re,ye=null;function Be(t,e){return(t.__esModule||Ot&&"Module"===t[Symbol.toStringTag])&&(t=t.default),i(t)?e.extend(t):t}function Le(t){if(b(t))for(var e=0;edocument.createEvent("Event").timeStamp&&(Ve=function(){return $e.now()})}var Ye=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function Ge(){var t,e;for(Ue=Ve(),Fe=!0,De.sort(Ye),He=0;HeHe&&De[o].id>t.id;)o--;De.splice(o+1,0,t)}else De.push(t);Ie||(Ie=!0,lo(Ge))}}var Ke="watcher";"".concat(Ke," callback"),"".concat(Ke," getter"),"".concat(Ke," cleanup");var Qe;var Ze=function(){function t(t){void 0===t&&(t=!1),this.detached=t,this.active=!0,this.effects=[],this.cleanups=[],this.parent=Qe,!t&&Qe&&(this.index=(Qe.scopes||(Qe.scopes=[])).push(this)-1)}return t.prototype.run=function(t){if(this.active){var e=Qe;try{return Qe=this,t()}finally{Qe=e}}else 0},t.prototype.on=function(){Qe=this},t.prototype.off=function(){Qe=this.parent},t.prototype.stop=function(t){if(this.active){var e=void 0,o=void 0;for(e=0,o=this.effects.length;e-1)if(n&&!g(b,"default"))M=!1;else if(""===M||M===X(t)){var c=tp(String,b.type);(c<0||z-1:"string"==typeof t?t.split(",").indexOf(e)>-1:!!s(t)&&t.test(e)}function np(t,e){var o=t.cache,p=t.keys,b=t._vnode;for(var n in o){var M=o[n];if(M){var z=M.name;z&&!e(z)&&Mp(o,n,p,b)}}}function Mp(t,e,o,p){var b=t[e];!b||p&&b.tag===p.tag||b.componentInstance.$destroy(),t[e]=null,W(o,e)}!function(t){t.prototype._init=function(t){var e=this;e._uid=No++,e._isVue=!0,e.__v_skip=!0,e._scope=new Ze(!0),e._scope._vm=!0,t&&t._isComponent?function(t,e){var o=t.$options=Object.create(t.constructor.options),p=e._parentVnode;o.parent=e.parent,o._parentVnode=p;var b=p.componentOptions;o.propsData=b.propsData,o._parentListeners=b.listeners,o._renderChildren=b.children,o._componentTag=b.tag,e.render&&(o.render=e.render,o.staticRenderFns=e.staticRenderFns)}(e,t):e.$options=Yo(wo(e.constructor),t||{},e),e._renderProxy=e,e._self=e,function(t){var e=t.$options,o=e.parent;if(o&&!e.abstract){for(;o.$options.abstract&&o.$parent;)o=o.$parent;o.$children.push(t)}t.$parent=o,t.$root=o?o.$root:t,t.$children=[],t.$refs={},t._provided=o?o._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(e),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&we(t,e)}(e),function(t){t._vnode=null,t._staticTrees=null;var e=t.$options,o=t.$vnode=e._parentVnode,b=o&&o.context;t.$slots=de(e._renderChildren,b),t.$scopedSlots=o?fe(t.$parent,o.data.scopedSlots,t.$slots):p,t._c=function(e,o,p,b){return Qt(t,e,o,p,b,!1)},t.$createElement=function(e,o,p,b){return Qt(t,e,o,p,b,!0)};var n=o&&o.data;Et(t,"$attrs",n&&n.attrs||p,null,!0),Et(t,"$listeners",e._parentListeners||p,null,!0)}(e),Ee(e,"beforeCreate",void 0,!1),function(t){var e=_o(t.$options.inject,t);e&&(Tt(!1),Object.keys(e).forEach((function(o){Et(t,o,e[o])})),Tt(!0))}(e),vo(e),function(t){var e=t.$options.provide;if(e){var o=r(e)?e.call(t):e;if(!i(o))return;for(var p=to(t),b=Ot?Reflect.ownKeys(o):Object.keys(o),n=0;n1?N(o):o;for(var p=N(arguments,1),b='event handler for "'.concat(t,'"'),n=0,M=o.length;nparseInt(this.max)&&Mp(e,o[0],o,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Mp(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){np(t,(function(t){return bp(e,t)}))})),this.$watch("exclude",(function(e){np(t,(function(t){return!bp(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=Le(t),o=e&&e.componentOptions;if(o){var p=pp(o),b=this.include,n=this.exclude;if(b&&(!p||!bp(b,p))||n&&p&&bp(n,p))return e;var M=this.cache,z=this.keys,c=null==e.key?o.Ctor.cid+(o.tag?"::".concat(o.tag):""):e.key;M[c]?(e.componentInstance=M[c].componentInstance,W(z,c),z.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}},rp={KeepAlive:cp};!function(t){var e={get:function(){return H}};Object.defineProperty(t,"config",e),t.util={warn:jo,extend:w,mergeOptions:Yo,defineReactive:Et},t.set=Dt,t.delete=Pt,t.nextTick=lo,t.observable=function(t){return kt(t),t},t.options=Object.create(null),I.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,w(t.options.components,rp),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var o=N(arguments,1);return o.unshift(this),r(t.install)?t.install.apply(t,o):r(t)&&t.apply(null,o),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=Yo(this.options,t),this}}(t),op(t),function(t){I.forEach((function(e){t[e]=function(t,o){return o?("component"===e&&O(o)&&(o.name=o.name||t,o=this.options._base.extend(o)),"directive"===e&&r(o)&&(o={bind:o,update:o}),this.options[e+"s"][t]=o,o):this.options[e+"s"][t]}}))}(t)}(ep),Object.defineProperty(ep.prototype,"$isServer",{get:ct}),Object.defineProperty(ep.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(ep,"FunctionalRenderContext",{value:xo}),ep.version="2.7.13";var ip=f("style,class"),ap=f("input,textarea,option,select,progress"),Op=function(t,e,o){return"value"===o&&ap(t)&&"button"!==e||"selected"===o&&"option"===t||"checked"===o&&"input"===t||"muted"===o&&"video"===t},sp=f("contenteditable,draggable,spellcheck"),lp=f("events,caret,typing,plaintext-only"),dp=f("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),Ap="http://www.w3.org/1999/xlink",up=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},fp=function(t){return up(t)?t.slice(6,t.length):""},qp=function(t){return null==t||!1===t};function hp(t){for(var e=t.data,o=t,p=t;M(p.componentInstance);)(p=p.componentInstance._vnode)&&p.data&&(e=Wp(p.data,e));for(;M(o=o.parent);)o&&o.data&&(e=Wp(e,o.data));return function(t,e){if(M(t)||M(e))return mp(t,gp(e));return""}(e.staticClass,e.class)}function Wp(t,e){return{staticClass:mp(t.staticClass,e.staticClass),class:M(t.class)?[t.class,e.class]:e.class}}function mp(t,e){return t?e?t+" "+e:t:e||""}function gp(t){return Array.isArray(t)?function(t){for(var e,o="",p=0,b=t.length;p-1?Gp(t,e,o):dp(e)?qp(o)?t.removeAttribute(e):(o="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,o)):sp(e)?t.setAttribute(e,function(t,e){return qp(e)||"false"===e?"false":"contenteditable"===t&&lp(e)?e:"true"}(e,o)):up(e)?qp(o)?t.removeAttributeNS(Ap,fp(e)):t.setAttributeNS(Ap,e,o):Gp(t,e,o)}function Gp(t,e,o){if(qp(o))t.removeAttribute(e);else{if(Z&&!tt&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==o&&!t.__ieph){var p=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",p)};t.addEventListener("input",p),t.__ieph=!0}t.setAttribute(e,o)}}var Jp={create:$p,update:$p};function Kp(t,e){var o=e.elm,p=e.data,b=t.data;if(!(n(p.staticClass)&&n(p.class)&&(n(b)||n(b.staticClass)&&n(b.class)))){var z=hp(e),c=o._transitionClasses;M(c)&&(z=mp(z,gp(c))),z!==o._prevClass&&(o.setAttribute("class",z),o._prevClass=z)}}var Qp,Zp,tb,eb,ob,pb,bb={create:Kp,update:Kp},nb=/[\w).+\-_$\]]/;function Mb(t){var e,o,p,b,n,M=!1,z=!1,c=!1,r=!1,i=0,a=0,O=0,s=0;for(p=0;p=0&&" "===(d=t.charAt(l));l--);d&&nb.test(d)||(r=!0)}}else void 0===b?(s=p+1,b=t.slice(0,p).trim()):A();function A(){(n||(n=[])).push(t.slice(s,p).trim()),s=p+1}if(void 0===b?b=t.slice(0,p).trim():0!==s&&A(),n)for(p=0;p-1?{exp:t.slice(0,eb),key:'"'+t.slice(eb+1)+'"'}:{exp:t,key:null};Zp=t,eb=ob=pb=0;for(;!gb();)vb(tb=mb())?yb(tb):91===tb&&Rb(tb);return{exp:t.slice(0,ob),key:t.slice(ob+1,pb)}}(t);return null===o.key?"".concat(t,"=").concat(e):"$set(".concat(o.exp,", ").concat(o.key,", ").concat(e,")")}function mb(){return Zp.charCodeAt(++eb)}function gb(){return eb>=Qp}function vb(t){return 34===t||39===t}function Rb(t){var e=1;for(ob=eb;!gb();)if(vb(t=mb()))yb(t);else if(91===t&&e++,93===t&&e--,0===e){pb=eb;break}}function yb(t){for(var e=t;!gb()&&(t=mb())!==e;);}var Bb,Lb="__r";function Xb(t,e,o){var p=Bb;return function b(){var n=e.apply(null,arguments);null!==n&&wb(t,b,o,p)}}var _b=Mo&&!(bt&&Number(bt[1])<=53);function Nb(t,e,o,p){if(_b){var b=Ue,n=e;e=n._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=b||t.timeStamp<=0||t.target.ownerDocument!==document)return n.apply(this,arguments)}}Bb.addEventListener(t,e,Mt?{capture:o,passive:p}:o)}function wb(t,e,o,p){(p||Bb).removeEventListener(t,e._wrapper||e,o)}function xb(t,e){if(!n(t.data.on)||!n(e.data.on)){var o=e.data.on||{},p=t.data.on||{};Bb=e.elm||t.elm,function(t){if(M(t.__r)){var e=Z?"change":"input";t[e]=[].concat(t.__r,t[e]||[]),delete t.__r}M(t.__c)&&(t.change=[].concat(t.__c,t.change||[]),delete t.__c)}(o),Vt(o,p,Nb,wb,Xb,e.context),Bb=void 0}}var Tb,Cb={create:xb,update:xb,destroy:function(t){return xb(t,Sp)}};function Sb(t,e){if(!n(t.data.domProps)||!n(e.data.domProps)){var o,p,b=e.elm,c=t.data.domProps||{},r=e.data.domProps||{};for(o in(M(r.__ob__)||z(r._v_attr_proxy))&&(r=e.data.domProps=w({},r)),c)o in r||(b[o]="");for(o in r){if(p=r[o],"textContent"===o||"innerHTML"===o){if(e.children&&(e.children.length=0),p===c[o])continue;1===b.childNodes.length&&b.removeChild(b.childNodes[0])}if("value"===o&&"PROGRESS"!==b.tagName){b._value=p;var i=n(p)?"":String(p);kb(b,i)&&(b.value=i)}else if("innerHTML"===o&&yp(b.tagName)&&n(b.innerHTML)){(Tb=Tb||document.createElement("div")).innerHTML="".concat(p,"");for(var a=Tb.firstChild;b.firstChild;)b.removeChild(b.firstChild);for(;a.firstChild;)b.appendChild(a.firstChild)}else if(p!==c[o])try{b[o]=p}catch(t){}}}}function kb(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var o=!0;try{o=document.activeElement!==t}catch(t){}return o&&t.value!==e}(t,e)||function(t,e){var o=t.value,p=t._vModifiers;if(M(p)){if(p.number)return u(o)!==u(e);if(p.trim)return o.trim()!==e.trim()}return o!==e}(t,e))}var Eb={create:Sb,update:Sb},Db=v((function(t){var e={},o=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var p=t.split(o);p.length>1&&(e[p[0].trim()]=p[1].trim())}})),e}));function Pb(t){var e=jb(t.style);return t.staticStyle?w(t.staticStyle,e):e}function jb(t){return Array.isArray(t)?x(t):"string"==typeof t?Db(t):t}var Ib,Fb=/^--/,Hb=/\s*!important$/,Ub=function(t,e,o){if(Fb.test(e))t.style.setProperty(e,o);else if(Hb.test(o))t.style.setProperty(X(e),o.replace(Hb,""),"important");else{var p=$b(e);if(Array.isArray(o))for(var b=0,n=o.length;b-1?e.split(Jb).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var o=" ".concat(t.getAttribute("class")||""," ");o.indexOf(" "+e+" ")<0&&t.setAttribute("class",(o+e).trim())}}function Qb(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(Jb).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var o=" ".concat(t.getAttribute("class")||""," "),p=" "+e+" ";o.indexOf(p)>=0;)o=o.replace(p," ");(o=o.trim())?t.setAttribute("class",o):t.removeAttribute("class")}}function Zb(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&w(e,tn(t.name||"v")),w(e,t),e}return"string"==typeof t?tn(t):void 0}}var tn=v((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),en=K&&!tt,on="transition",pn="animation",bn="transition",nn="transitionend",Mn="animation",zn="animationend";en&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(bn="WebkitTransition",nn="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Mn="WebkitAnimation",zn="webkitAnimationEnd"));var cn=K?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function rn(t){cn((function(){cn(t)}))}function an(t,e){var o=t._transitionClasses||(t._transitionClasses=[]);o.indexOf(e)<0&&(o.push(e),Kb(t,e))}function On(t,e){t._transitionClasses&&W(t._transitionClasses,e),Qb(t,e)}function sn(t,e,o){var p=dn(t,e),b=p.type,n=p.timeout,M=p.propCount;if(!b)return o();var z=b===on?nn:zn,c=0,r=function(){t.removeEventListener(z,i),o()},i=function(e){e.target===t&&++c>=M&&r()};setTimeout((function(){c0&&(o=on,i=M,a=n.length):e===pn?r>0&&(o=pn,i=r,a=c.length):a=(o=(i=Math.max(M,r))>0?M>r?on:pn:null)?o===on?n.length:c.length:0,{type:o,timeout:i,propCount:a,hasTransform:o===on&&ln.test(p[bn+"Property"])}}function An(t,e){for(;t.length1}function mn(t,e){!0!==e.data.show&&fn(e)}var gn=function(t){var e,o,p={},r=t.modules,i=t.nodeOps;for(e=0;el?h(t,n(o[u+1])?null:o[u+1].elm,o,s,u,p):s>u&&m(e,a,l)}(a,d,u,o,r):M(u)?(M(t.text)&&i.setTextContent(a,""),h(a,null,u,0,u.length-1,o)):M(d)?m(d,0,d.length-1):M(t.text)&&i.setTextContent(a,""):t.text!==e.text&&i.setTextContent(a,e.text),M(l)&&M(s=l.hook)&&M(s=s.postpatch)&&s(t,e)}}}function y(t,e,o){if(z(o)&&M(t.parent))t.parent.data.pendingInsert=e;else for(var p=0;p-1,M.selected!==n&&(M.selected=n);else if(k(Ln(M),p))return void(t.selectedIndex!==z&&(t.selectedIndex=z));b||(t.selectedIndex=-1)}}function Bn(t,e){return e.every((function(e){return!k(e,t)}))}function Ln(t){return"_value"in t?t._value:t.value}function Xn(t){t.target.composing=!0}function _n(t){t.target.composing&&(t.target.composing=!1,Nn(t.target,"input"))}function Nn(t,e){var o=document.createEvent("HTMLEvents");o.initEvent(e,!0,!0),t.dispatchEvent(o)}function wn(t){return!t.componentInstance||t.data&&t.data.transition?t:wn(t.componentInstance._vnode)}var xn={bind:function(t,e,o){var p=e.value,b=(o=wn(o)).data&&o.data.transition,n=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;p&&b?(o.data.show=!0,fn(o,(function(){t.style.display=n}))):t.style.display=p?n:"none"},update:function(t,e,o){var p=e.value;!p!=!e.oldValue&&((o=wn(o)).data&&o.data.transition?(o.data.show=!0,p?fn(o,(function(){t.style.display=t.__vOriginalDisplay})):qn(o,(function(){t.style.display="none"}))):t.style.display=p?t.__vOriginalDisplay:"none")},unbind:function(t,e,o,p,b){b||(t.style.display=t.__vOriginalDisplay)}},Tn={model:vn,show:xn},Cn={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Sn(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Sn(Le(e.children)):t}function kn(t){var e={},o=t.$options;for(var p in o.propsData)e[p]=t[p];var b=o._parentListeners;for(var p in b)e[y(p)]=b[p];return e}function En(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var Dn=function(t){return t.tag||ue(t)},Pn=function(t){return"show"===t.name},jn={name:"transition",props:Cn,abstract:!0,render:function(t){var e=this,o=this.$slots.default;if(o&&(o=o.filter(Dn)).length){0;var p=this.mode;0;var b=o[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return b;var n=Sn(b);if(!n)return b;if(this._leaving)return En(t,b);var M="__transition-".concat(this._uid,"-");n.key=null==n.key?n.isComment?M+"comment":M+n.tag:c(n.key)?0===String(n.key).indexOf(M)?n.key:M+n.key:n.key;var z=(n.data||(n.data={})).transition=kn(this),r=this._vnode,i=Sn(r);if(n.data.directives&&n.data.directives.some(Pn)&&(n.data.show=!0),i&&i.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(n,i)&&!ue(i)&&(!i.componentInstance||!i.componentInstance._vnode.isComment)){var a=i.data.transition=w({},z);if("out-in"===p)return this._leaving=!0,$t(a,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),En(t,b);if("in-out"===p){if(ue(n))return r;var O,s=function(){O()};$t(z,"afterEnter",s),$t(z,"enterCancelled",s),$t(a,"delayLeave",(function(t){O=t}))}}return b}}},In=w({tag:String,moveClass:String},Cn);delete In.mode;var Fn={props:In,beforeMount:function(){var t=this,e=this._update;this._update=function(o,p){var b=Te(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,b(),e.call(t,o,p)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",o=Object.create(null),p=this.prevChildren=this.children,b=this.$slots.default||[],n=this.children=[],M=kn(this),z=0;z-1?Xp[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Xp[t]=/HTMLUnknownElement/.test(e.toString())},w(ep.options.directives,Tn),w(ep.options.components,$n),ep.prototype.__patch__=K?gn:T,ep.prototype.$mount=function(t,e){return function(t,e,o){var p;t.$el=e,t.$options.render||(t.$options.render=At),Ee(t,"beforeMount"),p=function(){t._update(t._render(),o)},new Wo(t,p,T,{before:function(){t._isMounted&&!t._isDestroyed&&Ee(t,"beforeUpdate")}},!0),o=!1;var b=t._preWatchers;if(b)for(var n=0;n\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,nM=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,MM="[a-zA-Z_][\\-\\.0-9_a-zA-Z".concat(U.source,"]*"),zM="((?:".concat(MM,"\\:)?").concat(MM,")"),cM=new RegExp("^<".concat(zM)),rM=/^\s*(\/?)>/,iM=new RegExp("^<\\/".concat(zM,"[^>]*>")),aM=/^]+>/i,OM=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},uM=/&(?:lt|gt|quot|amp|#39);/g,fM=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,qM=f("pre,textarea",!0),hM=function(t,e){return t&&qM(t)&&"\n"===e[0]};function WM(t,e){var o=e?fM:uM;return t.replace(o,(function(t){return AM[t]}))}function mM(t,e){for(var o,p,b=[],n=e.expectHTML,M=e.isUnaryTag||C,z=e.canBeLeftOpenTag||C,c=0,r=function(){if(o=t,p&&lM(p)){var r=0,O=p.toLowerCase(),s=dM[O]||(dM[O]=new RegExp("([\\s\\S]*?)(]*>)","i"));m=t.replace(s,(function(t,o,p){return r=p.length,lM(O)||"noscript"===O||(o=o.replace(//g,"$1").replace(//g,"$1")),hM(O,o)&&(o=o.slice(1)),e.chars&&e.chars(o),""}));c+=t.length-m.length,t=m,a(O,c-r,c)}else{var l=t.indexOf("<");if(0===l){if(OM.test(t)){var d=t.indexOf("--\x3e");if(d>=0)return e.shouldKeepComment&&e.comment&&e.comment(t.substring(4,d),c,c+d+3),i(d+3),"continue"}if(sM.test(t)){var A=t.indexOf("]>");if(A>=0)return i(A+2),"continue"}var u=t.match(aM);if(u)return i(u[0].length),"continue";var f=t.match(iM);if(f){var q=c;return i(f[0].length),a(f[1],q,c),"continue"}var h=function(){var e=t.match(cM);if(e){var o={tagName:e[1],attrs:[],start:c};i(e[0].length);for(var p=void 0,b=void 0;!(p=t.match(rM))&&(b=t.match(nM)||t.match(bM));)b.start=c,i(b[0].length),b.end=c,o.attrs.push(b);if(p)return o.unarySlash=p[1],i(p[0].length),o.end=c,o}}();if(h)return function(t){var o=t.tagName,c=t.unarySlash;n&&("p"===p&&pM(o)&&a(p),z(o)&&p===o&&a(o));for(var r=M(o)||!!c,i=t.attrs.length,O=new Array(i),s=0;s=0){for(m=t.slice(l);!(iM.test(m)||cM.test(m)||OM.test(m)||sM.test(m)||(g=m.indexOf("<",1))<0);)l+=g,m=t.slice(l);W=t.substring(0,l)}l<0&&(W=t),W&&i(W.length),e.chars&&W&&e.chars(W,c-W.length,c)}if(t===o)return e.chars&&e.chars(t),"break"};t;){if("break"===r())break}function i(e){c+=e,t=t.substring(e)}function a(t,o,n){var M,z;if(null==o&&(o=c),null==n&&(n=c),t)for(z=t.toLowerCase(),M=b.length-1;M>=0&&b[M].lowerCasedTag!==z;M--);else M=0;if(M>=0){for(var r=b.length-1;r>=M;r--)e.end&&e.end(b[r].tag,o,n);b.length=M,p=M&&b[M-1].tag}else"br"===z?e.start&&e.start(t,[],!0,o,n):"p"===z&&(e.start&&e.start(t,[],!1,o,n),e.end&&e.end(t,o,n))}a()}var gM,vM,RM,yM,BM,LM,XM,_M,NM=/^@|^v-on:/,wM=/^v-|^@|^:|^#/,xM=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,TM=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,CM=/^\(|\)$/g,SM=/^\[.*\]$/,kM=/:(.*)$/,EM=/^:|^\.|^v-bind:/,DM=/\.[^.\]]+(?=[^\]]*$)/g,PM=/^v-slot(:|$)|^#/,jM=/[\r\n]/,IM=/[ \f\t\r\n]+/g,FM=v(tM),HM="_empty_";function UM(t,e,o){return{type:1,tag:t,attrsList:e,attrsMap:QM(e),rawAttrsMap:{},parent:o,children:[]}}function VM(t,e){gM=e.warn||cb,LM=e.isPreTag||C,XM=e.mustUseProp||C,_M=e.getTagNamespace||C;var o=e.isReservedTag||C;(function(t){return!(!(t.component||t.attrsMap[":is"]||t.attrsMap["v-bind:is"])&&(t.attrsMap.is?o(t.attrsMap.is):o(t.tag)))}),RM=rb(e.modules,"transformNode"),yM=rb(e.modules,"preTransformNode"),BM=rb(e.modules,"postTransformNode"),vM=e.delimiters;var p,b,n=[],M=!1!==e.preserveWhitespace,z=e.whitespace,c=!1,r=!1;function i(t){if(a(t),c||t.processed||(t=$M(t,e)),n.length||t===p||p.if&&(t.elseif||t.else)&&GM(p,{exp:t.elseif,block:t}),b&&!t.forbidden)if(t.elseif||t.else)M=t,z=function(t){for(var e=t.length;e--;){if(1===t[e].type)return t[e];t.pop()}}(b.children),z&&z.if&&GM(z,{exp:M.elseif,block:M});else{if(t.slotScope){var o=t.slotTarget||'"default"';(b.scopedSlots||(b.scopedSlots={}))[o]=t}b.children.push(t),t.parent=b}var M,z;t.children=t.children.filter((function(t){return!t.slotScope})),a(t),t.pre&&(c=!1),LM(t.tag)&&(r=!1);for(var i=0;ic&&(z.push(n=t.slice(c,b)),M.push(JSON.stringify(n)));var r=Mb(p[1].trim());M.push("_s(".concat(r,")")),z.push({"@binding":r}),c=b+p[0].length}return c-1")+("true"===n?":(".concat(e,")"):":_q(".concat(e,",").concat(n,")"))),db(t,"change","var $$a=".concat(e,",")+"$$el=$event.target,"+"$$c=$$el.checked?(".concat(n,"):(").concat(M,");")+"if(Array.isArray($$a)){"+"var $$v=".concat(p?"_n("+b+")":b,",")+"$$i=_i($$a,$$v);"+"if($$el.checked){$$i<0&&(".concat(Wb(e,"$$a.concat([$$v])"),")}")+"else{$$i>-1&&(".concat(Wb(e,"$$a.slice(0,$$i).concat($$a.slice($$i+1))"),")}")+"}else{".concat(Wb(e,"$$c"),"}"),null,!0)}(t,p,b);else if("input"===n&&"radio"===M)!function(t,e,o){var p=o&&o.number,b=Ab(t,"value")||"null";b=p?"_n(".concat(b,")"):b,ib(t,"checked","_q(".concat(e,",").concat(b,")")),db(t,"change",Wb(e,b),null,!0)}(t,p,b);else if("input"===n||"textarea"===n)!function(t,e,o){var p=t.attrsMap.type;0;var b=o||{},n=b.lazy,M=b.number,z=b.trim,c=!n&&"range"!==p,r=n?"change":"range"===p?Lb:"input",i="$event.target.value";z&&(i="$event.target.value.trim()");M&&(i="_n(".concat(i,")"));var a=Wb(e,i);c&&(a="if($event.target.composing)return;".concat(a));ib(t,"value","(".concat(e,")")),db(t,r,a,null,!0),(z||M)&&db(t,"blur","$forceUpdate()")}(t,p,b);else{if(!H.isReservedTag(n))return hb(t,p,b),!1}return!0},text:function(t,e){e.value&&ib(t,"textContent","_s(".concat(e.value,")"),e)},html:function(t,e){e.value&&ib(t,"innerHTML","_s(".concat(e.value,")"),e)}},Mz={expectHTML:!0,modules:oz,directives:nz,isPreTag:function(t){return"pre"===t},isUnaryTag:eM,mustUseProp:Op,canBeLeftOpenTag:oM,isReservedTag:Bp,getTagNamespace:Lp,staticKeys:function(t){return t.reduce((function(t,e){return t.concat(e.staticKeys||[])}),[]).join(",")}(oz)},zz=v((function(t){return f("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(t?","+t:""))}));function cz(t,e){t&&(pz=zz(e.staticKeys||""),bz=e.isReservedTag||C,rz(t),iz(t,!1))}function rz(t){if(t.static=function(t){if(2===t.type)return!1;if(3===t.type)return!0;return!(!t.pre&&(t.hasBindings||t.if||t.for||q(t.tag)||!bz(t.tag)||function(t){for(;t.parent;){if("template"!==(t=t.parent).tag)return!1;if(t.for)return!0}return!1}(t)||!Object.keys(t).every(pz)))}(t),1===t.type){if(!bz(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var e=0,o=t.children.length;e|^function(?:\s+[\w$]+)?\s*\(/,Oz=/\([^)]*?\);*$/,sz=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,lz={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},dz={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Az=function(t){return"if(".concat(t,")return null;")},uz={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Az("$event.target !== $event.currentTarget"),ctrl:Az("!$event.ctrlKey"),shift:Az("!$event.shiftKey"),alt:Az("!$event.altKey"),meta:Az("!$event.metaKey"),left:Az("'button' in $event && $event.button !== 0"),middle:Az("'button' in $event && $event.button !== 1"),right:Az("'button' in $event && $event.button !== 2")};function fz(t,e){var o=e?"nativeOn:":"on:",p="",b="";for(var n in t){var M=qz(t[n]);t[n]&&t[n].dynamic?b+="".concat(n,",").concat(M,","):p+='"'.concat(n,'":').concat(M,",")}return p="{".concat(p.slice(0,-1),"}"),b?o+"_d(".concat(p,",[").concat(b.slice(0,-1),"])"):o+p}function qz(t){if(!t)return"function(){}";if(Array.isArray(t))return"[".concat(t.map((function(t){return qz(t)})).join(","),"]");var e=sz.test(t.value),o=az.test(t.value),p=sz.test(t.value.replace(Oz,""));if(t.modifiers){var b="",n="",M=[],z=function(e){if(uz[e])n+=uz[e],lz[e]&&M.push(e);else if("exact"===e){var o=t.modifiers;n+=Az(["ctrl","shift","alt","meta"].filter((function(t){return!o[t]})).map((function(t){return"$event.".concat(t,"Key")})).join("||"))}else M.push(e)};for(var c in t.modifiers)z(c);M.length&&(b+=function(t){return"if(!$event.type.indexOf('key')&&"+"".concat(t.map(hz).join("&&"),")return null;")}(M)),n&&(b+=n);var r=e?"return ".concat(t.value,".apply(null, arguments)"):o?"return (".concat(t.value,").apply(null, arguments)"):p?"return ".concat(t.value):t.value;return"function($event){".concat(b).concat(r,"}")}return e||o?t.value:"function($event){".concat(p?"return ".concat(t.value):t.value,"}")}function hz(t){var e=parseInt(t,10);if(e)return"$event.keyCode!==".concat(e);var o=lz[t],p=dz[t];return"_k($event.keyCode,"+"".concat(JSON.stringify(t),",")+"".concat(JSON.stringify(o),",")+"$event.key,"+"".concat(JSON.stringify(p))+")"}var Wz={on:function(t,e){t.wrapListeners=function(t){return"_g(".concat(t,",").concat(e.value,")")}},bind:function(t,e){t.wrapData=function(o){return"_b(".concat(o,",'").concat(t.tag,"',").concat(e.value,",").concat(e.modifiers&&e.modifiers.prop?"true":"false").concat(e.modifiers&&e.modifiers.sync?",true":"",")")}},cloak:T},mz=function(t){this.options=t,this.warn=t.warn||cb,this.transforms=rb(t.modules,"transformCode"),this.dataGenFns=rb(t.modules,"genData"),this.directives=w(w({},Wz),t.directives);var e=t.isReservedTag||C;this.maybeComponent=function(t){return!!t.component||!e(t.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function gz(t,e){var o=new mz(e),p=t?"script"===t.tag?"null":vz(t,o):'_c("div")';return{render:"with(this){return ".concat(p,"}"),staticRenderFns:o.staticRenderFns}}function vz(t,e){if(t.parent&&(t.pre=t.pre||t.parent.pre),t.staticRoot&&!t.staticProcessed)return Rz(t,e);if(t.once&&!t.onceProcessed)return yz(t,e);if(t.for&&!t.forProcessed)return Xz(t,e);if(t.if&&!t.ifProcessed)return Bz(t,e);if("template"!==t.tag||t.slotTarget||e.pre){if("slot"===t.tag)return function(t,e){var o=t.slotName||'"default"',p=xz(t,e),b="_t(".concat(o).concat(p?",function(){return ".concat(p,"}"):""),n=t.attrs||t.dynamicAttrs?Sz((t.attrs||[]).concat(t.dynamicAttrs||[]).map((function(t){return{name:y(t.name),value:t.value,dynamic:t.dynamic}}))):null,M=t.attrsMap["v-bind"];!n&&!M||p||(b+=",null");n&&(b+=",".concat(n));M&&(b+="".concat(n?"":",null",",").concat(M));return b+")"}(t,e);var o=void 0;if(t.component)o=function(t,e,o){var p=e.inlineTemplate?null:xz(e,o,!0);return"_c(".concat(t,",").concat(_z(e,o)).concat(p?",".concat(p):"",")")}(t.component,t,e);else{var p=void 0,b=e.maybeComponent(t);(!t.plain||t.pre&&b)&&(p=_z(t,e));var n=void 0,M=e.options.bindings;b&&M&&!1!==M.__isScriptSetup&&(n=function(t,e){var o=y(e),p=B(o),b=function(b){return t[e]===b?e:t[o]===b?o:t[p]===b?p:void 0},n=b("setup-const")||b("setup-reactive-const");if(n)return n;var M=b("setup-let")||b("setup-ref")||b("setup-maybe-ref");if(M)return M}(M,t.tag)),n||(n="'".concat(t.tag,"'"));var z=t.inlineTemplate?null:xz(t,e,!0);o="_c(".concat(n).concat(p?",".concat(p):"").concat(z?",".concat(z):"",")")}for(var c=0;c>>0}(M)):"",")")}(t,t.scopedSlots,e),",")),t.model&&(o+="model:{value:".concat(t.model.value,",callback:").concat(t.model.callback,",expression:").concat(t.model.expression,"},")),t.inlineTemplate){var n=function(t,e){var o=t.children[0];0;if(o&&1===o.type){var p=gz(o,e.options);return"inlineTemplate:{render:function(){".concat(p.render,"},staticRenderFns:[").concat(p.staticRenderFns.map((function(t){return"function(){".concat(t,"}")})).join(","),"]}")}}(t,e);n&&(o+="".concat(n,","))}return o=o.replace(/,$/,"")+"}",t.dynamicAttrs&&(o="_b(".concat(o,',"').concat(t.tag,'",').concat(Sz(t.dynamicAttrs),")")),t.wrapData&&(o=t.wrapData(o)),t.wrapListeners&&(o=t.wrapListeners(o)),o}function Nz(t){return 1===t.type&&("slot"===t.tag||t.children.some(Nz))}function wz(t,e){var o=t.attrsMap["slot-scope"];if(t.if&&!t.ifProcessed&&!o)return Bz(t,e,wz,"null");if(t.for&&!t.forProcessed)return Xz(t,e,wz);var p=t.slotScope===HM?"":String(t.slotScope),b="function(".concat(p,"){")+"return ".concat("template"===t.tag?t.if&&o?"(".concat(t.if,")?").concat(xz(t,e)||"undefined",":undefined"):xz(t,e)||"undefined":vz(t,e),"}"),n=p?"":",proxy:true";return"{key:".concat(t.slotTarget||'"default"',",fn:").concat(b).concat(n,"}")}function xz(t,e,o,p,b){var n=t.children;if(n.length){var M=n[0];if(1===n.length&&M.for&&"template"!==M.tag&&"slot"!==M.tag){var z=o?e.maybeComponent(M)?",1":",0":"";return"".concat((p||vz)(M,e)).concat(z)}var c=o?function(t,e){for(var o=0,p=0;p':'
',jz.innerHTML.indexOf(" ")>0}var Uz=!!K&&Hz(!1),Vz=!!K&&Hz(!0),$z=v((function(t){var e=Np(t);return e&&e.innerHTML})),Yz=ep.prototype.$mount;ep.prototype.$mount=function(t,e){if((t=t&&Np(t))===document.body||t===document.documentElement)return this;var o=this.$options;if(!o.render){var p=o.template;if(p)if("string"==typeof p)"#"===p.charAt(0)&&(p=$z(p));else{if(!p.nodeType)return this;p=p.innerHTML}else t&&(p=function(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}(t));if(p){0;var b=Fz(p,{outputSourceRange:!1,shouldDecodeNewlines:Uz,shouldDecodeNewlinesForHref:Vz,delimiters:o.delimiters,comments:o.comments},this),n=b.render,M=b.staticRenderFns;o.render=n,o.staticRenderFns=M}}return Yz.call(this,t,e)},ep.compile=Fz;var Gz=o(8),Jz=o.n(Gz);function Kz(t){return function(t){if(Array.isArray(t))return Qz(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return Qz(t,e);var o=Object.prototype.toString.call(t).slice(8,-1);"Object"===o&&t.constructor&&(o=t.constructor.name);if("Map"===o||"Set"===o)return Array.from(t);if("Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o))return Qz(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Qz(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,p=new Array(e);o{const e=bc.call(t);return zc[e]||(zc[e]=e.slice(8,-1).toLowerCase())});var zc;const cc=t=>(t=t.toLowerCase(),e=>Mc(e)===t),rc=t=>e=>typeof e===t,{isArray:ic}=Array,ac=rc("undefined");const Oc=cc("ArrayBuffer");const sc=rc("string"),lc=rc("function"),dc=rc("number"),Ac=t=>null!==t&&"object"==typeof t,uc=t=>{if("object"!==Mc(t))return!1;const e=nc(t);return!(null!==e&&e!==Object.prototype&&null!==Object.getPrototypeOf(e)||Symbol.toStringTag in t||Symbol.iterator in t)},fc=cc("Date"),qc=cc("File"),hc=cc("Blob"),Wc=cc("FileList"),mc=cc("URLSearchParams");function gc(t,e,{allOwnKeys:o=!1}={}){if(null==t)return;let p,b;if("object"!=typeof t&&(t=[t]),ic(t))for(p=0,b=t.length;p0;)if(p=o[b],e===p.toLowerCase())return p;return null}const Rc="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,yc=t=>!ac(t)&&t!==Rc;const Bc=(Lc="undefined"!=typeof Uint8Array&&nc(Uint8Array),t=>Lc&&t instanceof Lc);var Lc;const Xc=cc("HTMLFormElement"),_c=(({hasOwnProperty:t})=>(e,o)=>t.call(e,o))(Object.prototype),Nc=cc("RegExp"),wc=(t,e)=>{const o=Object.getOwnPropertyDescriptors(t),p={};gc(o,((o,b)=>{!1!==e(o,b,t)&&(p[b]=o)})),Object.defineProperties(t,p)},xc="abcdefghijklmnopqrstuvwxyz",Tc="0123456789",Cc={DIGIT:Tc,ALPHA:xc,ALPHA_DIGIT:xc+xc.toUpperCase()+Tc};const Sc={isArray:ic,isArrayBuffer:Oc,isBuffer:function(t){return null!==t&&!ac(t)&&null!==t.constructor&&!ac(t.constructor)&&lc(t.constructor.isBuffer)&&t.constructor.isBuffer(t)},isFormData:t=>{const e="[object FormData]";return t&&("function"==typeof FormData&&t instanceof FormData||bc.call(t)===e||lc(t.toString)&&t.toString()===e)},isArrayBufferView:function(t){let e;return e="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer&&Oc(t.buffer),e},isString:sc,isNumber:dc,isBoolean:t=>!0===t||!1===t,isObject:Ac,isPlainObject:uc,isUndefined:ac,isDate:fc,isFile:qc,isBlob:hc,isRegExp:Nc,isFunction:lc,isStream:t=>Ac(t)&&lc(t.pipe),isURLSearchParams:mc,isTypedArray:Bc,isFileList:Wc,forEach:gc,merge:function t(){const{caseless:e}=yc(this)&&this||{},o={},p=(p,b)=>{const n=e&&vc(o,b)||b;uc(o[n])&&uc(p)?o[n]=t(o[n],p):uc(p)?o[n]=t({},p):ic(p)?o[n]=p.slice():o[n]=p};for(let t=0,e=arguments.length;t(gc(e,((e,p)=>{o&&lc(e)?t[p]=pc(e,o):t[p]=e}),{allOwnKeys:p}),t),trim:t=>t.trim?t.trim():t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""),stripBOM:t=>(65279===t.charCodeAt(0)&&(t=t.slice(1)),t),inherits:(t,e,o,p)=>{t.prototype=Object.create(e.prototype,p),t.prototype.constructor=t,Object.defineProperty(t,"super",{value:e.prototype}),o&&Object.assign(t.prototype,o)},toFlatObject:(t,e,o,p)=>{let b,n,M;const z={};if(e=e||{},null==t)return e;do{for(b=Object.getOwnPropertyNames(t),n=b.length;n-- >0;)M=b[n],p&&!p(M,t,e)||z[M]||(e[M]=t[M],z[M]=!0);t=!1!==o&&nc(t)}while(t&&(!o||o(t,e))&&t!==Object.prototype);return e},kindOf:Mc,kindOfTest:cc,endsWith:(t,e,o)=>{t=String(t),(void 0===o||o>t.length)&&(o=t.length),o-=e.length;const p=t.indexOf(e,o);return-1!==p&&p===o},toArray:t=>{if(!t)return null;if(ic(t))return t;let e=t.length;if(!dc(e))return null;const o=new Array(e);for(;e-- >0;)o[e]=t[e];return o},forEachEntry:(t,e)=>{const o=(t&&t[Symbol.iterator]).call(t);let p;for(;(p=o.next())&&!p.done;){const o=p.value;e.call(t,o[0],o[1])}},matchAll:(t,e)=>{let o;const p=[];for(;null!==(o=t.exec(e));)p.push(o);return p},isHTMLForm:Xc,hasOwnProperty:_c,hasOwnProp:_c,reduceDescriptors:wc,freezeMethods:t=>{wc(t,((e,o)=>{if(lc(t)&&-1!==["arguments","caller","callee"].indexOf(o))return!1;const p=t[o];lc(p)&&(e.enumerable=!1,"writable"in e?e.writable=!1:e.set||(e.set=()=>{throw Error("Can not rewrite read-only method '"+o+"'")}))}))},toObjectSet:(t,e)=>{const o={},p=t=>{t.forEach((t=>{o[t]=!0}))};return ic(t)?p(t):p(String(t).split(e)),o},toCamelCase:t=>t.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(t,e,o){return e.toUpperCase()+o})),noop:()=>{},toFiniteNumber:(t,e)=>(t=+t,Number.isFinite(t)?t:e),findKey:vc,global:Rc,isContextDefined:yc,ALPHABET:Cc,generateString:(t=16,e=Cc.ALPHA_DIGIT)=>{let o="";const{length:p}=e;for(;t--;)o+=e[Math.random()*p|0];return o},isSpecCompliantForm:function(t){return!!(t&&lc(t.append)&&"FormData"===t[Symbol.toStringTag]&&t[Symbol.iterator])},toJSONObject:t=>{const e=new Array(10),o=(t,p)=>{if(Ac(t)){if(e.indexOf(t)>=0)return;if(!("toJSON"in t)){e[p]=t;const b=ic(t)?[]:{};return gc(t,((t,e)=>{const n=o(t,p+1);!ac(n)&&(b[e]=n)})),e[p]=void 0,b}}return t};return o(t,0)}};function kc(t,e,o,p,b){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=t,this.name="AxiosError",e&&(this.code=e),o&&(this.config=o),p&&(this.request=p),b&&(this.response=b)}Sc.inherits(kc,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:Sc.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});const Ec=kc.prototype,Dc={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((t=>{Dc[t]={value:t}})),Object.defineProperties(kc,Dc),Object.defineProperty(Ec,"isAxiosError",{value:!0}),kc.from=(t,e,o,p,b,n)=>{const M=Object.create(Ec);return Sc.toFlatObject(t,M,(function(t){return t!==Error.prototype}),(t=>"isAxiosError"!==t)),kc.call(M,t.message,e,o,p,b),M.cause=t,M.name=t.name,n&&Object.assign(M,n),M};const Pc=kc;var jc=o(764).lW;function Ic(t){return Sc.isPlainObject(t)||Sc.isArray(t)}function Fc(t){return Sc.endsWith(t,"[]")?t.slice(0,-2):t}function Hc(t,e,o){return t?t.concat(e).map((function(t,e){return t=Fc(t),!o&&e?"["+t+"]":t})).join(o?".":""):e}const Uc=Sc.toFlatObject(Sc,{},null,(function(t){return/^is[A-Z]/.test(t)}));const Vc=function(t,e,o){if(!Sc.isObject(t))throw new TypeError("target must be an object");e=e||new FormData;const p=(o=Sc.toFlatObject(o,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(t,e){return!Sc.isUndefined(e[t])}))).metaTokens,b=o.visitor||r,n=o.dots,M=o.indexes,z=(o.Blob||"undefined"!=typeof Blob&&Blob)&&Sc.isSpecCompliantForm(e);if(!Sc.isFunction(b))throw new TypeError("visitor must be a function");function c(t){if(null===t)return"";if(Sc.isDate(t))return t.toISOString();if(!z&&Sc.isBlob(t))throw new Pc("Blob is not supported. Use a Buffer instead.");return Sc.isArrayBuffer(t)||Sc.isTypedArray(t)?z&&"function"==typeof Blob?new Blob([t]):jc.from(t):t}function r(t,o,b){let z=t;if(t&&!b&&"object"==typeof t)if(Sc.endsWith(o,"{}"))o=p?o:o.slice(0,-2),t=JSON.stringify(t);else if(Sc.isArray(t)&&function(t){return Sc.isArray(t)&&!t.some(Ic)}(t)||(Sc.isFileList(t)||Sc.endsWith(o,"[]"))&&(z=Sc.toArray(t)))return o=Fc(o),z.forEach((function(t,p){!Sc.isUndefined(t)&&null!==t&&e.append(!0===M?Hc([o],p,n):null===M?o:o+"[]",c(t))})),!1;return!!Ic(t)||(e.append(Hc(b,o,n),c(t)),!1)}const i=[],a=Object.assign(Uc,{defaultVisitor:r,convertValue:c,isVisitable:Ic});if(!Sc.isObject(t))throw new TypeError("data must be an object");return function t(o,p){if(!Sc.isUndefined(o)){if(-1!==i.indexOf(o))throw Error("Circular reference detected in "+p.join("."));i.push(o),Sc.forEach(o,(function(o,n){!0===(!(Sc.isUndefined(o)||null===o)&&b.call(e,o,Sc.isString(n)?n.trim():n,p,a))&&t(o,p?p.concat(n):[n])})),i.pop()}}(t),e};function $c(t){const e={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(t).replace(/[!'()~]|%20|%00/g,(function(t){return e[t]}))}function Yc(t,e){this._pairs=[],t&&Vc(t,this,e)}const Gc=Yc.prototype;Gc.append=function(t,e){this._pairs.push([t,e])},Gc.toString=function(t){const e=t?function(e){return t.call(this,e,$c)}:$c;return this._pairs.map((function(t){return e(t[0])+"="+e(t[1])}),"").join("&")};const Jc=Yc;function Kc(t){return encodeURIComponent(t).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Qc(t,e,o){if(!e)return t;const p=o&&o.encode||Kc,b=o&&o.serialize;let n;if(n=b?b(e,o):Sc.isURLSearchParams(e)?e.toString():new Jc(e,o).toString(p),n){const e=t.indexOf("#");-1!==e&&(t=t.slice(0,e)),t+=(-1===t.indexOf("?")?"?":"&")+n}return t}const Zc=class{constructor(){this.handlers=[]}use(t,e,o){return this.handlers.push({fulfilled:t,rejected:e,synchronous:!!o&&o.synchronous,runWhen:o?o.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){Sc.forEach(this.handlers,(function(e){null!==e&&t(e)}))}},tr={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},er="undefined"!=typeof URLSearchParams?URLSearchParams:Jc,or=FormData,pr=(()=>{let t;return("undefined"==typeof navigator||"ReactNative"!==(t=navigator.product)&&"NativeScript"!==t&&"NS"!==t)&&("undefined"!=typeof window&&"undefined"!=typeof document)})(),br="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,nr={isBrowser:!0,classes:{URLSearchParams:er,FormData:or,Blob},isStandardBrowserEnv:pr,isStandardBrowserWebWorkerEnv:br,protocols:["http","https","file","blob","url","data"]};const Mr=function(t){function e(t,o,p,b){let n=t[b++];const M=Number.isFinite(+n),z=b>=t.length;if(n=!n&&Sc.isArray(p)?p.length:n,z)return Sc.hasOwnProp(p,n)?p[n]=[p[n],o]:p[n]=o,!M;p[n]&&Sc.isObject(p[n])||(p[n]=[]);return e(t,o,p[n],b)&&Sc.isArray(p[n])&&(p[n]=function(t){const e={},o=Object.keys(t);let p;const b=o.length;let n;for(p=0;p{e(function(t){return Sc.matchAll(/\w+|\[(\w*)]/g,t).map((t=>"[]"===t[0]?"":t[1]||t[0]))}(t),p,o,0)})),o}return null},zr={"Content-Type":void 0};const cr={transitional:tr,adapter:["xhr","http"],transformRequest:[function(t,e){const o=e.getContentType()||"",p=o.indexOf("application/json")>-1,b=Sc.isObject(t);b&&Sc.isHTMLForm(t)&&(t=new FormData(t));if(Sc.isFormData(t))return p&&p?JSON.stringify(Mr(t)):t;if(Sc.isArrayBuffer(t)||Sc.isBuffer(t)||Sc.isStream(t)||Sc.isFile(t)||Sc.isBlob(t))return t;if(Sc.isArrayBufferView(t))return t.buffer;if(Sc.isURLSearchParams(t))return e.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let n;if(b){if(o.indexOf("application/x-www-form-urlencoded")>-1)return function(t,e){return Vc(t,new nr.classes.URLSearchParams,Object.assign({visitor:function(t,e,o,p){return nr.isNode&&Sc.isBuffer(t)?(this.append(e,t.toString("base64")),!1):p.defaultVisitor.apply(this,arguments)}},e))}(t,this.formSerializer).toString();if((n=Sc.isFileList(t))||o.indexOf("multipart/form-data")>-1){const e=this.env&&this.env.FormData;return Vc(n?{"files[]":t}:t,e&&new e,this.formSerializer)}}return b||p?(e.setContentType("application/json",!1),function(t,e,o){if(Sc.isString(t))try{return(e||JSON.parse)(t),Sc.trim(t)}catch(t){if("SyntaxError"!==t.name)throw t}return(o||JSON.stringify)(t)}(t)):t}],transformResponse:[function(t){const e=this.transitional||cr.transitional,o=e&&e.forcedJSONParsing,p="json"===this.responseType;if(t&&Sc.isString(t)&&(o&&!this.responseType||p)){const o=!(e&&e.silentJSONParsing)&&p;try{return JSON.parse(t)}catch(t){if(o){if("SyntaxError"===t.name)throw Pc.from(t,Pc.ERR_BAD_RESPONSE,this,null,this.response);throw t}}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:nr.classes.FormData,Blob:nr.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};Sc.forEach(["delete","get","head"],(function(t){cr.headers[t]={}})),Sc.forEach(["post","put","patch"],(function(t){cr.headers[t]=Sc.merge(zr)}));const rr=cr,ir=Sc.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),ar=Symbol("internals");function Or(t){return t&&String(t).trim().toLowerCase()}function sr(t){return!1===t||null==t?t:Sc.isArray(t)?t.map(sr):String(t)}function lr(t,e,o,p){return Sc.isFunction(p)?p.call(this,e,o):Sc.isString(e)?Sc.isString(p)?-1!==e.indexOf(p):Sc.isRegExp(p)?p.test(e):void 0:void 0}class dr{constructor(t){t&&this.set(t)}set(t,e,o){const p=this;function b(t,e,o){const b=Or(e);if(!b)throw new Error("header name must be a non-empty string");const n=Sc.findKey(p,b);(!n||void 0===p[n]||!0===o||void 0===o&&!1!==p[n])&&(p[n||e]=sr(t))}const n=(t,e)=>Sc.forEach(t,((t,o)=>b(t,o,e)));return Sc.isPlainObject(t)||t instanceof this.constructor?n(t,e):Sc.isString(t)&&(t=t.trim())&&!function(t){return/^[-_a-zA-Z]+$/.test(t.trim())}(t)?n((t=>{const e={};let o,p,b;return t&&t.split("\n").forEach((function(t){b=t.indexOf(":"),o=t.substring(0,b).trim().toLowerCase(),p=t.substring(b+1).trim(),!o||e[o]&&ir[o]||("set-cookie"===o?e[o]?e[o].push(p):e[o]=[p]:e[o]=e[o]?e[o]+", "+p:p)})),e})(t),e):null!=t&&b(e,t,o),this}get(t,e){if(t=Or(t)){const o=Sc.findKey(this,t);if(o){const t=this[o];if(!e)return t;if(!0===e)return function(t){const e=Object.create(null),o=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let p;for(;p=o.exec(t);)e[p[1]]=p[2];return e}(t);if(Sc.isFunction(e))return e.call(this,t,o);if(Sc.isRegExp(e))return e.exec(t);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,e){if(t=Or(t)){const o=Sc.findKey(this,t);return!(!o||void 0===this[o]||e&&!lr(0,this[o],o,e))}return!1}delete(t,e){const o=this;let p=!1;function b(t){if(t=Or(t)){const b=Sc.findKey(o,t);!b||e&&!lr(0,o[b],b,e)||(delete o[b],p=!0)}}return Sc.isArray(t)?t.forEach(b):b(t),p}clear(t){const e=Object.keys(this);let o=e.length,p=!1;for(;o--;){const b=e[o];t&&!lr(0,this[b],b,t)||(delete this[b],p=!0)}return p}normalize(t){const e=this,o={};return Sc.forEach(this,((p,b)=>{const n=Sc.findKey(o,b);if(n)return e[n]=sr(p),void delete e[b];const M=t?function(t){return t.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,((t,e,o)=>e.toUpperCase()+o))}(b):String(b).trim();M!==b&&delete e[b],e[M]=sr(p),o[M]=!0})),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const e=Object.create(null);return Sc.forEach(this,((o,p)=>{null!=o&&!1!==o&&(e[p]=t&&Sc.isArray(o)?o.join(", "):o)})),e}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map((([t,e])=>t+": "+e)).join("\n")}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...e){const o=new this(t);return e.forEach((t=>o.set(t))),o}static accessor(t){const e=(this[ar]=this[ar]={accessors:{}}).accessors,o=this.prototype;function p(t){const p=Or(t);e[p]||(!function(t,e){const o=Sc.toCamelCase(" "+e);["get","set","has"].forEach((p=>{Object.defineProperty(t,p+o,{value:function(t,o,b){return this[p].call(this,e,t,o,b)},configurable:!0})}))}(o,t),e[p]=!0)}return Sc.isArray(t)?t.forEach(p):p(t),this}}dr.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]),Sc.freezeMethods(dr.prototype),Sc.freezeMethods(dr);const Ar=dr;function ur(t,e){const o=this||rr,p=e||o,b=Ar.from(p.headers);let n=p.data;return Sc.forEach(t,(function(t){n=t.call(o,n,b.normalize(),e?e.status:void 0)})),b.normalize(),n}function fr(t){return!(!t||!t.__CANCEL__)}function qr(t,e,o){Pc.call(this,null==t?"canceled":t,Pc.ERR_CANCELED,e,o),this.name="CanceledError"}Sc.inherits(qr,Pc,{__CANCEL__:!0});const hr=qr;const Wr=nr.isStandardBrowserEnv?{write:function(t,e,o,p,b,n){const M=[];M.push(t+"="+encodeURIComponent(e)),Sc.isNumber(o)&&M.push("expires="+new Date(o).toGMTString()),Sc.isString(p)&&M.push("path="+p),Sc.isString(b)&&M.push("domain="+b),!0===n&&M.push("secure"),document.cookie=M.join("; ")},read:function(t){const e=document.cookie.match(new RegExp("(^|;\\s*)("+t+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}};function mr(t,e){return t&&!/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)?function(t,e){return e?t.replace(/\/+$/,"")+"/"+e.replace(/^\/+/,""):t}(t,e):e}const gr=nr.isStandardBrowserEnv?function(){const t=/(msie|trident)/i.test(navigator.userAgent),e=document.createElement("a");let o;function p(o){let p=o;return t&&(e.setAttribute("href",p),p=e.href),e.setAttribute("href",p),{href:e.href,protocol:e.protocol?e.protocol.replace(/:$/,""):"",host:e.host,search:e.search?e.search.replace(/^\?/,""):"",hash:e.hash?e.hash.replace(/^#/,""):"",hostname:e.hostname,port:e.port,pathname:"/"===e.pathname.charAt(0)?e.pathname:"/"+e.pathname}}return o=p(window.location.href),function(t){const e=Sc.isString(t)?p(t):t;return e.protocol===o.protocol&&e.host===o.host}}():function(){return!0};const vr=function(t,e){t=t||10;const o=new Array(t),p=new Array(t);let b,n=0,M=0;return e=void 0!==e?e:1e3,function(z){const c=Date.now(),r=p[M];b||(b=c),o[n]=z,p[n]=c;let i=M,a=0;for(;i!==n;)a+=o[i++],i%=t;if(n=(n+1)%t,n===M&&(M=(M+1)%t),c-b{const n=b.loaded,M=b.lengthComputable?b.total:void 0,z=n-o,c=p(z);o=n;const r={loaded:n,total:M,progress:M?n/M:void 0,bytes:z,rate:c||void 0,estimated:c&&M&&n<=M?(M-n)/c:void 0,event:b};r[e?"download":"upload"]=!0,t(r)}}const yr="undefined"!=typeof XMLHttpRequest&&function(t){return new Promise((function(e,o){let p=t.data;const b=Ar.from(t.headers).normalize(),n=t.responseType;let M;function z(){t.cancelToken&&t.cancelToken.unsubscribe(M),t.signal&&t.signal.removeEventListener("abort",M)}Sc.isFormData(p)&&(nr.isStandardBrowserEnv||nr.isStandardBrowserWebWorkerEnv)&&b.setContentType(!1);let c=new XMLHttpRequest;if(t.auth){const e=t.auth.username||"",o=t.auth.password?unescape(encodeURIComponent(t.auth.password)):"";b.set("Authorization","Basic "+btoa(e+":"+o))}const r=mr(t.baseURL,t.url);function i(){if(!c)return;const p=Ar.from("getAllResponseHeaders"in c&&c.getAllResponseHeaders());!function(t,e,o){const p=o.config.validateStatus;o.status&&p&&!p(o.status)?e(new Pc("Request failed with status code "+o.status,[Pc.ERR_BAD_REQUEST,Pc.ERR_BAD_RESPONSE][Math.floor(o.status/100)-4],o.config,o.request,o)):t(o)}((function(t){e(t),z()}),(function(t){o(t),z()}),{data:n&&"text"!==n&&"json"!==n?c.response:c.responseText,status:c.status,statusText:c.statusText,headers:p,config:t,request:c}),c=null}if(c.open(t.method.toUpperCase(),Qc(r,t.params,t.paramsSerializer),!0),c.timeout=t.timeout,"onloadend"in c?c.onloadend=i:c.onreadystatechange=function(){c&&4===c.readyState&&(0!==c.status||c.responseURL&&0===c.responseURL.indexOf("file:"))&&setTimeout(i)},c.onabort=function(){c&&(o(new Pc("Request aborted",Pc.ECONNABORTED,t,c)),c=null)},c.onerror=function(){o(new Pc("Network Error",Pc.ERR_NETWORK,t,c)),c=null},c.ontimeout=function(){let e=t.timeout?"timeout of "+t.timeout+"ms exceeded":"timeout exceeded";const p=t.transitional||tr;t.timeoutErrorMessage&&(e=t.timeoutErrorMessage),o(new Pc(e,p.clarifyTimeoutError?Pc.ETIMEDOUT:Pc.ECONNABORTED,t,c)),c=null},nr.isStandardBrowserEnv){const e=(t.withCredentials||gr(r))&&t.xsrfCookieName&&Wr.read(t.xsrfCookieName);e&&b.set(t.xsrfHeaderName,e)}void 0===p&&b.setContentType(null),"setRequestHeader"in c&&Sc.forEach(b.toJSON(),(function(t,e){c.setRequestHeader(e,t)})),Sc.isUndefined(t.withCredentials)||(c.withCredentials=!!t.withCredentials),n&&"json"!==n&&(c.responseType=t.responseType),"function"==typeof t.onDownloadProgress&&c.addEventListener("progress",Rr(t.onDownloadProgress,!0)),"function"==typeof t.onUploadProgress&&c.upload&&c.upload.addEventListener("progress",Rr(t.onUploadProgress)),(t.cancelToken||t.signal)&&(M=e=>{c&&(o(!e||e.type?new hr(null,t,c):e),c.abort(),c=null)},t.cancelToken&&t.cancelToken.subscribe(M),t.signal&&(t.signal.aborted?M():t.signal.addEventListener("abort",M)));const a=function(t){const e=/^([-+\w]{1,25})(:?\/\/|:)/.exec(t);return e&&e[1]||""}(r);a&&-1===nr.protocols.indexOf(a)?o(new Pc("Unsupported protocol "+a+":",Pc.ERR_BAD_REQUEST,t)):c.send(p||null)}))},Br={http:null,xhr:yr};Sc.forEach(Br,((t,e)=>{if(t){try{Object.defineProperty(t,"name",{value:e})}catch(t){}Object.defineProperty(t,"adapterName",{value:e})}}));const Lr=t=>{t=Sc.isArray(t)?t:[t];const{length:e}=t;let o,p;for(let b=0;bt instanceof Ar?t.toJSON():t;function wr(t,e){e=e||{};const o={};function p(t,e,o){return Sc.isPlainObject(t)&&Sc.isPlainObject(e)?Sc.merge.call({caseless:o},t,e):Sc.isPlainObject(e)?Sc.merge({},e):Sc.isArray(e)?e.slice():e}function b(t,e,o){return Sc.isUndefined(e)?Sc.isUndefined(t)?void 0:p(void 0,t,o):p(t,e,o)}function n(t,e){if(!Sc.isUndefined(e))return p(void 0,e)}function M(t,e){return Sc.isUndefined(e)?Sc.isUndefined(t)?void 0:p(void 0,t):p(void 0,e)}function z(o,b,n){return n in e?p(o,b):n in t?p(void 0,o):void 0}const c={url:n,method:n,data:n,baseURL:M,transformRequest:M,transformResponse:M,paramsSerializer:M,timeout:M,timeoutMessage:M,withCredentials:M,adapter:M,responseType:M,xsrfCookieName:M,xsrfHeaderName:M,onUploadProgress:M,onDownloadProgress:M,decompress:M,maxContentLength:M,maxBodyLength:M,beforeRedirect:M,transport:M,httpAgent:M,httpsAgent:M,cancelToken:M,socketPath:M,responseEncoding:M,validateStatus:z,headers:(t,e)=>b(Nr(t),Nr(e),!0)};return Sc.forEach(Object.keys(t).concat(Object.keys(e)),(function(p){const n=c[p]||b,M=n(t[p],e[p],p);Sc.isUndefined(M)&&n!==z||(o[p]=M)})),o}const xr="1.3.2",Tr={};["object","boolean","number","function","string","symbol"].forEach(((t,e)=>{Tr[t]=function(o){return typeof o===t||"a"+(e<1?"n ":" ")+t}}));const Cr={};Tr.transitional=function(t,e,o){return(p,b,n)=>{if(!1===t)throw new Pc(function(t,e){return"[Axios v1.3.2] Transitional option '"+t+"'"+e+(o?". "+o:"")}(b," has been removed"+(e?" in "+e:"")),Pc.ERR_DEPRECATED);return e&&!Cr[b]&&(Cr[b]=!0),!t||t(p,b,n)}};const Sr={assertOptions:function(t,e,o){if("object"!=typeof t)throw new Pc("options must be an object",Pc.ERR_BAD_OPTION_VALUE);const p=Object.keys(t);let b=p.length;for(;b-- >0;){const n=p[b],M=e[n];if(M){const e=t[n],o=void 0===e||M(e,n,t);if(!0!==o)throw new Pc("option "+n+" must be "+o,Pc.ERR_BAD_OPTION_VALUE)}else if(!0!==o)throw new Pc("Unknown option "+n,Pc.ERR_BAD_OPTION)}},validators:Tr},kr=Sr.validators;class Er{constructor(t){this.defaults=t,this.interceptors={request:new Zc,response:new Zc}}request(t,e){"string"==typeof t?(e=e||{}).url=t:e=t||{},e=wr(this.defaults,e);const{transitional:o,paramsSerializer:p,headers:b}=e;let n;void 0!==o&&Sr.assertOptions(o,{silentJSONParsing:kr.transitional(kr.boolean),forcedJSONParsing:kr.transitional(kr.boolean),clarifyTimeoutError:kr.transitional(kr.boolean)},!1),void 0!==p&&Sr.assertOptions(p,{encode:kr.function,serialize:kr.function},!0),e.method=(e.method||this.defaults.method||"get").toLowerCase(),n=b&&Sc.merge(b.common,b[e.method]),n&&Sc.forEach(["delete","get","head","post","put","patch","common"],(t=>{delete b[t]})),e.headers=Ar.concat(n,b);const M=[];let z=!0;this.interceptors.request.forEach((function(t){"function"==typeof t.runWhen&&!1===t.runWhen(e)||(z=z&&t.synchronous,M.unshift(t.fulfilled,t.rejected))}));const c=[];let r;this.interceptors.response.forEach((function(t){c.push(t.fulfilled,t.rejected)}));let i,a=0;if(!z){const t=[_r.bind(this),void 0];for(t.unshift.apply(t,M),t.push.apply(t,c),i=t.length,r=Promise.resolve(e);a{if(!o._listeners)return;let e=o._listeners.length;for(;e-- >0;)o._listeners[e](t);o._listeners=null})),this.promise.then=t=>{let e;const p=new Promise((t=>{o.subscribe(t),e=t})).then(t);return p.cancel=function(){o.unsubscribe(e)},p},t((function(t,p,b){o.reason||(o.reason=new hr(t,p,b),e(o.reason))}))}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){this.reason?t(this.reason):this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const e=this._listeners.indexOf(t);-1!==e&&this._listeners.splice(e,1)}static source(){let t;return{token:new Pr((function(e){t=e})),cancel:t}}}const jr=Pr;const Ir={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Ir).forEach((([t,e])=>{Ir[e]=t}));const Fr=Ir;const Hr=function t(e){const o=new Dr(e),p=pc(Dr.prototype.request,o);return Sc.extend(p,Dr.prototype,o,{allOwnKeys:!0}),Sc.extend(p,o,null,{allOwnKeys:!0}),p.create=function(o){return t(wr(e,o))},p}(rr);Hr.Axios=Dr,Hr.CanceledError=hr,Hr.CancelToken=jr,Hr.isCancel=fr,Hr.VERSION=xr,Hr.toFormData=Vc,Hr.AxiosError=Pc,Hr.Cancel=Hr.CanceledError,Hr.all=function(t){return Promise.all(t)},Hr.spread=function(t){return function(e){return t.apply(null,e)}},Hr.isAxiosError=function(t){return Sc.isObject(t)&&!0===t.isAxiosError},Hr.mergeConfig=wr,Hr.AxiosHeaders=Ar,Hr.formToJSON=t=>Mr(Sc.isHTMLForm(t)?new FormData(t):t),Hr.HttpStatusCode=Fr,Hr.default=Hr;const Ur=Hr,Vr=[{path:"/",redirect:"/dashboard"},{path:"/dashboard",name:"dashboard",component:o(122).Z},{path:"/monitoring",name:"monitoring",component:o(300).Z},{path:"/monitoring/:tag",component:o(997).Z,children:[{path:"jobs",name:"monitoring-jobs",component:o(790).Z,props:{type:"jobs"}},{path:"failed",name:"monitoring-failed",component:o(790).Z,props:{type:"failed"}}]},{path:"/metrics",redirect:"/metrics/jobs"},{path:"/metrics/",component:o(20).Z,children:[{path:"jobs",name:"metrics-jobs",component:o(253).Z},{path:"queues",name:"metrics-queues",component:o(871).Z}]},{path:"/metrics/:type/:slug",name:"metrics-preview",component:o(295).Z},{path:"/jobs/:type",name:"jobs",component:o(347).Z},{path:"/jobs/pending/:jobId",name:"pending-jobs-preview",component:o(967).Z},{path:"/jobs/completed/:jobId",name:"completed-jobs-preview",component:o(967).Z},{path:"/jobs/silenced/:jobId",name:"silenced-jobs-preview",component:o(967).Z},{path:"/failed",name:"failed-jobs",component:o(404).Z},{path:"/failed/:jobId",name:"failed-jobs-preview",component:o(5).Z},{path:"/batches",name:"batches",component:o(472).Z},{path:"/batches/:batchId",name:"batches-preview",component:o(681).Z}];function $r(t,e){for(var o in e)t[o]=e[o];return t}var Yr=/[!'()*]/g,Gr=function(t){return"%"+t.charCodeAt(0).toString(16)},Jr=/%2C/g,Kr=function(t){return encodeURIComponent(t).replace(Yr,Gr).replace(Jr,",")};function Qr(t){try{return decodeURIComponent(t)}catch(t){0}return t}var Zr=function(t){return null==t||"object"==typeof t?t:String(t)};function ti(t){var e={};return(t=t.trim().replace(/^(\?|#|&)/,""))?(t.split("&").forEach((function(t){var o=t.replace(/\+/g," ").split("="),p=Qr(o.shift()),b=o.length>0?Qr(o.join("=")):null;void 0===e[p]?e[p]=b:Array.isArray(e[p])?e[p].push(b):e[p]=[e[p],b]})),e):e}function ei(t){var e=t?Object.keys(t).map((function(e){var o=t[e];if(void 0===o)return"";if(null===o)return Kr(e);if(Array.isArray(o)){var p=[];return o.forEach((function(t){void 0!==t&&(null===t?p.push(Kr(e)):p.push(Kr(e)+"="+Kr(t)))})),p.join("&")}return Kr(e)+"="+Kr(o)})).filter((function(t){return t.length>0})).join("&"):null;return e?"?"+e:""}var oi=/\/?$/;function pi(t,e,o,p){var b=p&&p.options.stringifyQuery,n=e.query||{};try{n=bi(n)}catch(t){}var M={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:n,params:e.params||{},fullPath:zi(e,b),matched:t?Mi(t):[]};return o&&(M.redirectedFrom=zi(o,b)),Object.freeze(M)}function bi(t){if(Array.isArray(t))return t.map(bi);if(t&&"object"==typeof t){var e={};for(var o in t)e[o]=bi(t[o]);return e}return t}var ni=pi(null,{path:"/"});function Mi(t){for(var e=[];t;)e.unshift(t),t=t.parent;return e}function zi(t,e){var o=t.path,p=t.query;void 0===p&&(p={});var b=t.hash;return void 0===b&&(b=""),(o||"/")+(e||ei)(p)+b}function ci(t,e,o){return e===ni?t===e:!!e&&(t.path&&e.path?t.path.replace(oi,"")===e.path.replace(oi,"")&&(o||t.hash===e.hash&&ri(t.query,e.query)):!(!t.name||!e.name)&&(t.name===e.name&&(o||t.hash===e.hash&&ri(t.query,e.query)&&ri(t.params,e.params))))}function ri(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var o=Object.keys(t).sort(),p=Object.keys(e).sort();return o.length===p.length&&o.every((function(o,b){var n=t[o];if(p[b]!==o)return!1;var M=e[o];return null==n||null==M?n===M:"object"==typeof n&&"object"==typeof M?ri(n,M):String(n)===String(M)}))}function ii(t){for(var e=0;e=0&&(e=t.slice(p),t=t.slice(0,p));var b=t.indexOf("?");return b>=0&&(o=t.slice(b+1),t=t.slice(0,b)),{path:t,query:o,hash:e}}(b.path||""),r=e&&e.path||"/",i=c.path?si(c.path,r,o||b.append):r,a=function(t,e,o){void 0===e&&(e={});var p,b=o||ti;try{p=b(t||"")}catch(t){p={}}for(var n in e){var M=e[n];p[n]=Array.isArray(M)?M.map(Zr):Zr(M)}return p}(c.query,b.query,p&&p.options.parseQuery),O=b.hash||c.hash;return O&&"#"!==O.charAt(0)&&(O="#"+O),{_normalized:!0,path:i,query:a,hash:O}}var Ci,Si=function(){},ki={name:"RouterLink",props:{to:{type:[String,Object],required:!0},tag:{type:String,default:"a"},custom:Boolean,exact:Boolean,exactPath:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:[String,Array],default:"click"}},render:function(t){var e=this,o=this.$router,p=this.$route,b=o.resolve(this.to,p,this.append),n=b.location,M=b.route,z=b.href,c={},r=o.options.linkActiveClass,i=o.options.linkExactActiveClass,a=null==r?"router-link-active":r,O=null==i?"router-link-exact-active":i,s=null==this.activeClass?a:this.activeClass,l=null==this.exactActiveClass?O:this.exactActiveClass,d=M.redirectedFrom?pi(null,Ti(M.redirectedFrom),null,o):M;c[l]=ci(p,d,this.exactPath),c[s]=this.exact||this.exactPath?c[l]:function(t,e){return 0===t.path.replace(oi,"/").indexOf(e.path.replace(oi,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(var o in e)if(!(o in t))return!1;return!0}(t.query,e.query)}(p,d);var A=c[l]?this.ariaCurrentValue:null,u=function(t){Ei(t)&&(e.replace?o.replace(n,Si):o.push(n,Si))},f={click:Ei};Array.isArray(this.event)?this.event.forEach((function(t){f[t]=u})):f[this.event]=u;var q={class:c},h=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:z,route:M,navigate:u,isActive:c[s],isExactActive:c[l]});if(h){if(1===h.length)return h[0];if(h.length>1||!h.length)return 0===h.length?t():t("span",{},h)}if("a"===this.tag)q.on=f,q.attrs={href:z,"aria-current":A};else{var W=Di(this.$slots.default);if(W){W.isStatic=!1;var m=W.data=$r({},W.data);for(var g in m.on=m.on||{},m.on){var v=m.on[g];g in f&&(m.on[g]=Array.isArray(v)?v:[v])}for(var R in f)R in m.on?m.on[R].push(f[R]):m.on[R]=u;var y=W.data.attrs=$r({},W.data.attrs);y.href=z,y["aria-current"]=A}else q.on=f}return t(this.tag,q,this.$slots.default)}};function Ei(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey||t.defaultPrevented||void 0!==t.button&&0!==t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function Di(t){if(t)for(var e,o=0;o-1&&(z.params[O]=o.params[O]);return z.path=xi(i.path,z.params),c(i,z,M)}if(z.path){z.params={};for(var s=0;s-1}function ua(t,e){return Aa(t)&&t._isRouter&&(null==e||t.type===e)}function fa(t,e,o){var p=function(b){b>=t.length?o():t[b]?e(t[b],(function(){p(b+1)})):p(b+1)};p(0)}function qa(t){return function(e,o,p){var b=!1,n=0,M=null;ha(t,(function(t,e,o,z){if("function"==typeof t&&void 0===t.cid){b=!0,n++;var c,r=ga((function(e){var b;((b=e).__esModule||ma&&"Module"===b[Symbol.toStringTag])&&(e=e.default),t.resolved="function"==typeof e?e:Ci.extend(e),o.components[z]=e,--n<=0&&p()})),i=ga((function(t){var e="Failed to resolve async component "+z+": "+t;M||(M=Aa(t)?t:new Error(e),p(M))}));try{c=t(r,i)}catch(t){i(t)}if(c)if("function"==typeof c.then)c.then(r,i);else{var a=c.component;a&&"function"==typeof a.then&&a.then(r,i)}}})),b||p()}}function ha(t,e){return Wa(t.map((function(t){return Object.keys(t.components).map((function(o){return e(t.components[o],t.instances[o],t,o)}))})))}function Wa(t){return Array.prototype.concat.apply([],t)}var ma="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function ga(t){var e=!1;return function(){for(var o=[],p=arguments.length;p--;)o[p]=arguments[p];if(!e)return e=!0,t.apply(this,o)}}var va=function(t,e){this.router=t,this.base=function(t){if(!t)if(Pi){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";"/"!==t.charAt(0)&&(t="/"+t);return t.replace(/\/$/,"")}(e),this.current=ni,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function Ra(t,e,o,p){var b=ha(t,(function(t,p,b,n){var M=function(t,e){"function"!=typeof t&&(t=Ci.extend(t));return t.options[e]}(t,e);if(M)return Array.isArray(M)?M.map((function(t){return o(t,p,b,n)})):o(M,p,b,n)}));return Wa(p?b.reverse():b)}function ya(t,e){if(e)return function(){return t.apply(e,arguments)}}va.prototype.listen=function(t){this.cb=t},va.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},va.prototype.onError=function(t){this.errorCbs.push(t)},va.prototype.transitionTo=function(t,e,o){var p,b=this;try{p=this.router.match(t,this.current)}catch(t){throw this.errorCbs.forEach((function(e){e(t)})),t}var n=this.current;this.confirmTransition(p,(function(){b.updateRoute(p),e&&e(p),b.ensureURL(),b.router.afterHooks.forEach((function(t){t&&t(p,n)})),b.ready||(b.ready=!0,b.readyCbs.forEach((function(t){t(p)})))}),(function(t){o&&o(t),t&&!b.ready&&(ua(t,aa.redirected)&&n===ni||(b.ready=!0,b.readyErrorCbs.forEach((function(e){e(t)}))))}))},va.prototype.confirmTransition=function(t,e,o){var p=this,b=this.current;this.pending=t;var n,M,z=function(t){!ua(t)&&Aa(t)&&p.errorCbs.length&&p.errorCbs.forEach((function(e){e(t)})),o&&o(t)},c=t.matched.length-1,r=b.matched.length-1;if(ci(t,b)&&c===r&&t.matched[c]===b.matched[r])return this.ensureURL(),t.hash&&Zi(this.router,b,t,!1),z(((M=la(n=b,t,aa.duplicated,'Avoided redundant navigation to current location: "'+n.fullPath+'".')).name="NavigationDuplicated",M));var i=function(t,e){var o,p=Math.max(t.length,e.length);for(o=0;o0)){var e=this.router,o=e.options.scrollBehavior,p=ca&&o;p&&this.listeners.push(Qi());var b=function(){var o=t.current,b=La(t.base);t.current===ni&&b===t._startLocation||t.transitionTo(b,(function(t){p&&Zi(e,t,o,!0)}))};window.addEventListener("popstate",b),this.listeners.push((function(){window.removeEventListener("popstate",b)}))}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,o){var p=this,b=this.current;this.transitionTo(t,(function(t){ra(li(p.base+t.fullPath)),Zi(p.router,t,b,!1),e&&e(t)}),o)},e.prototype.replace=function(t,e,o){var p=this,b=this.current;this.transitionTo(t,(function(t){ia(li(p.base+t.fullPath)),Zi(p.router,t,b,!1),e&&e(t)}),o)},e.prototype.ensureURL=function(t){if(La(this.base)!==this.current.fullPath){var e=li(this.base+this.current.fullPath);t?ra(e):ia(e)}},e.prototype.getCurrentLocation=function(){return La(this.base)},e}(va);function La(t){var e=window.location.pathname,o=e.toLowerCase(),p=t.toLowerCase();return!t||o!==p&&0!==o.indexOf(li(p+"/"))||(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}var Xa=function(t){function e(e,o,p){t.call(this,e,o),p&&function(t){var e=La(t);if(!/^\/#/.test(e))return window.location.replace(li(t+"/#"+e)),!0}(this.base)||_a()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router.options.scrollBehavior,o=ca&&e;o&&this.listeners.push(Qi());var p=function(){var e=t.current;_a()&&t.transitionTo(Na(),(function(p){o&&Zi(t.router,p,e,!0),ca||Ta(p.fullPath)}))},b=ca?"popstate":"hashchange";window.addEventListener(b,p),this.listeners.push((function(){window.removeEventListener(b,p)}))}},e.prototype.push=function(t,e,o){var p=this,b=this.current;this.transitionTo(t,(function(t){xa(t.fullPath),Zi(p.router,t,b,!1),e&&e(t)}),o)},e.prototype.replace=function(t,e,o){var p=this,b=this.current;this.transitionTo(t,(function(t){Ta(t.fullPath),Zi(p.router,t,b,!1),e&&e(t)}),o)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;Na()!==e&&(t?xa(e):Ta(e))},e.prototype.getCurrentLocation=function(){return Na()},e}(va);function _a(){var t=Na();return"/"===t.charAt(0)||(Ta("/"+t),!1)}function Na(){var t=window.location.href,e=t.indexOf("#");return e<0?"":t=t.slice(e+1)}function wa(t){var e=window.location.href,o=e.indexOf("#");return(o>=0?e.slice(0,o):e)+"#"+t}function xa(t){ca?ra(wa(t)):window.location.hash=t}function Ta(t){ca?ia(wa(t)):window.location.replace(wa(t))}var Ca=function(t){function e(e,o){t.call(this,e,o),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,o){var p=this;this.transitionTo(t,(function(t){p.stack=p.stack.slice(0,p.index+1).concat(t),p.index++,e&&e(t)}),o)},e.prototype.replace=function(t,e,o){var p=this;this.transitionTo(t,(function(t){p.stack=p.stack.slice(0,p.index).concat(t),e&&e(t)}),o)},e.prototype.go=function(t){var e=this,o=this.index+t;if(!(o<0||o>=this.stack.length)){var p=this.stack[o];this.confirmTransition(p,(function(){var t=e.current;e.index=o,e.updateRoute(p),e.router.afterHooks.forEach((function(e){e&&e(p,t)}))}),(function(t){ua(t,aa.duplicated)&&(e.index=o)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(va),Sa=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Hi(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!ca&&!1!==t.fallback,this.fallback&&(e="hash"),Pi||(e="abstract"),this.mode=e,e){case"history":this.history=new Ba(this,t.base);break;case"hash":this.history=new Xa(this,t.base,this.fallback);break;case"abstract":this.history=new Ca(this,t.base)}},ka={currentRoute:{configurable:!0}};Sa.prototype.match=function(t,e,o){return this.matcher.match(t,e,o)},ka.currentRoute.get=function(){return this.history&&this.history.current},Sa.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once("hook:destroyed",(function(){var o=e.apps.indexOf(t);o>-1&&e.apps.splice(o,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardown()})),!this.app){this.app=t;var o=this.history;if(o instanceof Ba||o instanceof Xa){var p=function(t){o.setupListeners(),function(t){var p=o.current,b=e.options.scrollBehavior;ca&&b&&"fullPath"in t&&Zi(e,t,p,!1)}(t)};o.transitionTo(o.getCurrentLocation(),p,p)}o.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},Sa.prototype.beforeEach=function(t){return Da(this.beforeHooks,t)},Sa.prototype.beforeResolve=function(t){return Da(this.resolveHooks,t)},Sa.prototype.afterEach=function(t){return Da(this.afterHooks,t)},Sa.prototype.onReady=function(t,e){this.history.onReady(t,e)},Sa.prototype.onError=function(t){this.history.onError(t)},Sa.prototype.push=function(t,e,o){var p=this;if(!e&&!o&&"undefined"!=typeof Promise)return new Promise((function(e,o){p.history.push(t,e,o)}));this.history.push(t,e,o)},Sa.prototype.replace=function(t,e,o){var p=this;if(!e&&!o&&"undefined"!=typeof Promise)return new Promise((function(e,o){p.history.replace(t,e,o)}));this.history.replace(t,e,o)},Sa.prototype.go=function(t){this.history.go(t)},Sa.prototype.back=function(){this.go(-1)},Sa.prototype.forward=function(){this.go(1)},Sa.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},Sa.prototype.resolve=function(t,e,o){var p=Ti(t,e=e||this.history.current,o,this),b=this.match(p,e),n=b.redirectedFrom||b.fullPath,M=function(t,e,o){var p="hash"===o?"#"+e:e;return t?li(t+"/"+p):p}(this.history.base,n,this.mode);return{location:p,route:b,href:M,normalizedTo:p,resolved:b}},Sa.prototype.getRoutes=function(){return this.matcher.getRoutes()},Sa.prototype.addRoute=function(t,e){this.matcher.addRoute(t,e),this.history.current!==ni&&this.history.transitionTo(this.history.getCurrentLocation())},Sa.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==ni&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(Sa.prototype,ka);var Ea=Sa;function Da(t,e){return t.push(e),function(){var o=t.indexOf(e);o>-1&&t.splice(o,1)}}Sa.install=function t(e){if(!t.installed||Ci!==e){t.installed=!0,Ci=e;var o=function(t){return void 0!==t},p=function(t,e){var p=t.$options._parentVnode;o(p)&&o(p=p.data)&&o(p=p.registerRouteInstance)&&p(t,e)};e.mixin({beforeCreate:function(){o(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),e.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,p(this,this)},destroyed:function(){p(this)}}),Object.defineProperty(e.prototype,"$router",{get:function(){return this._routerRoot._router}}),Object.defineProperty(e.prototype,"$route",{get:function(){return this._routerRoot._route}}),e.component("RouterView",ai),e.component("RouterLink",ki);var b=e.config.optionMergeStrategies;b.beforeRouteEnter=b.beforeRouteLeave=b.beforeRouteUpdate=b.created}},Sa.version="3.6.5",Sa.isNavigationFailure=ua,Sa.NavigationFailureType=aa,Sa.START_LOCATION=ni,Pi&&window.Vue&&window.Vue.use(Sa);var Pa=o(566),ja=o.n(Pa);window.Popper=o(981).default;try{window.$=window.jQuery=o(755),o(734)}catch(t){}var Ia=document.head.querySelector('meta[name="csrf-token"]');Ur.defaults.headers.common["X-Requested-With"]="XMLHttpRequest",Ia&&(Ur.defaults.headers.common["X-CSRF-TOKEN"]=Ia.content),ep.use(Ea),ep.prototype.$http=Ur.create(),window.Horizon.basePath="/"+window.Horizon.path;var Fa=window.Horizon.basePath+"/";""!==window.Horizon.path&&"/"!==window.Horizon.path||(Fa="/",window.Horizon.basePath="");var Ha=new Ea({routes:Vr,mode:"history",base:Fa});ep.component("vue-json-pretty",ja()),ep.component("alert",o(682).Z),ep.component("scheme-toggler",o(529).Z),ep.mixin(oc),ep.directive("tooltip",(function(t,e){$(t).tooltip({title:e.value,placement:e.arg,trigger:"hover"})})),new ep({el:"#horizon",router:Ha,data:function(){return{alert:{type:null,autoClose:0,message:"",confirmationProceed:null,confirmationCancel:null},autoLoadsNewEntries:"1"===localStorage.autoLoadsNewEntries}}})},742:(t,e)=>{"use strict";e.byteLength=function(t){var e=c(t),o=e[0],p=e[1];return 3*(o+p)/4-p},e.toByteArray=function(t){var e,o,n=c(t),M=n[0],z=n[1],r=new b(function(t,e,o){return 3*(e+o)/4-o}(0,M,z)),i=0,a=z>0?M-4:M;for(o=0;o>16&255,r[i++]=e>>8&255,r[i++]=255&e;2===z&&(e=p[t.charCodeAt(o)]<<2|p[t.charCodeAt(o+1)]>>4,r[i++]=255&e);1===z&&(e=p[t.charCodeAt(o)]<<10|p[t.charCodeAt(o+1)]<<4|p[t.charCodeAt(o+2)]>>2,r[i++]=e>>8&255,r[i++]=255&e);return r},e.fromByteArray=function(t){for(var e,p=t.length,b=p%3,n=[],M=16383,z=0,c=p-b;zc?c:z+M));1===b?(e=t[p-1],n.push(o[e>>2]+o[e<<4&63]+"==")):2===b&&(e=(t[p-2]<<8)+t[p-1],n.push(o[e>>10]+o[e>>4&63]+o[e<<2&63]+"="));return n.join("")};for(var o=[],p=[],b="undefined"!=typeof Uint8Array?Uint8Array:Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",M=0,z=n.length;M0)throw new Error("Invalid string. Length must be a multiple of 4");var o=t.indexOf("=");return-1===o&&(o=e),[o,o===e?0:4-o%4]}function r(t,e,p){for(var b,n,M=[],z=e;z>18&63]+o[n>>12&63]+o[n>>6&63]+o[63&n]);return M.join("")}p["-".charCodeAt(0)]=62,p["_".charCodeAt(0)]=63},734:function(t,e,o){!function(t,e,o){"use strict";function p(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var b=p(e),n=p(o);function M(t,e){for(var o=0;o=M)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};f.jQueryDetection(),u();var q="alert",h="4.6.2",W="bs.alert",m="."+W,g=".data-api",v=b.default.fn[q],R="alert",y="fade",B="show",L="close"+m,X="closed"+m,_="click"+m+g,N='[data-dismiss="alert"]',w=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){b.default.removeData(this._element,W),this._element=null},e._getRootElement=function(t){var e=f.getSelectorFromElement(t),o=!1;return e&&(o=document.querySelector(e)),o||(o=b.default(t).closest("."+R)[0]),o},e._triggerCloseEvent=function(t){var e=b.default.Event(L);return b.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(b.default(t).removeClass(B),b.default(t).hasClass(y)){var o=f.getTransitionDurationFromElement(t);b.default(t).one(f.TRANSITION_END,(function(o){return e._destroyElement(t,o)})).emulateTransitionEnd(o)}else this._destroyElement(t)},e._destroyElement=function(t){b.default(t).detach().trigger(X).remove()},t._jQueryInterface=function(e){return this.each((function(){var o=b.default(this),p=o.data(W);p||(p=new t(this),o.data(W,p)),"close"===e&&p[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},z(t,null,[{key:"VERSION",get:function(){return h}}]),t}();b.default(document).on(_,N,w._handleDismiss(new w)),b.default.fn[q]=w._jQueryInterface,b.default.fn[q].Constructor=w,b.default.fn[q].noConflict=function(){return b.default.fn[q]=v,w._jQueryInterface};var x="button",T="4.6.2",C="bs.button",S="."+C,k=".data-api",E=b.default.fn[x],D="active",P="btn",j="focus",I="click"+S+k,F="focus"+S+k+" blur"+S+k,H="load"+S+k,U='[data-toggle^="button"]',V='[data-toggle="buttons"]',$='[data-toggle="button"]',Y='[data-toggle="buttons"] .btn',G='input:not([type="hidden"])',J=".active",K=".btn",Q=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,o=b.default(this._element).closest(V)[0];if(o){var p=this._element.querySelector(G);if(p){if("radio"===p.type)if(p.checked&&this._element.classList.contains(D))t=!1;else{var n=o.querySelector(J);n&&b.default(n).removeClass(D)}t&&("checkbox"!==p.type&&"radio"!==p.type||(p.checked=!this._element.classList.contains(D)),this.shouldAvoidTriggerChange||b.default(p).trigger("change")),p.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains(D)),t&&b.default(this._element).toggleClass(D))},e.dispose=function(){b.default.removeData(this._element,C),this._element=null},t._jQueryInterface=function(e,o){return this.each((function(){var p=b.default(this),n=p.data(C);n||(n=new t(this),p.data(C,n)),n.shouldAvoidTriggerChange=o,"toggle"===e&&n[e]()}))},z(t,null,[{key:"VERSION",get:function(){return T}}]),t}();b.default(document).on(I,U,(function(t){var e=t.target,o=e;if(b.default(e).hasClass(P)||(e=b.default(e).closest(K)[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var p=e.querySelector(G);if(p&&(p.hasAttribute("disabled")||p.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==o.tagName&&"LABEL"===e.tagName||Q._jQueryInterface.call(b.default(e),"toggle","INPUT"===o.tagName)}})).on(F,U,(function(t){var e=b.default(t.target).closest(K)[0];b.default(e).toggleClass(j,/^focus(in)?$/.test(t.type))})),b.default(window).on(H,(function(){for(var t=[].slice.call(document.querySelectorAll(Y)),e=0,o=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide(ut)},e.nextWhenVisible=function(){var t=b.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide(ft)},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(Et)&&(f.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(Ct);var o=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)b.default(this._element).one(mt,(function(){return e.to(t)}));else{if(o===t)return this.pause(),void this.cycle();var p=t>o?ut:ft;this._slide(p,this._items[t])}},e.dispose=function(){b.default(this._element).off(ot),b.default.removeData(this._element,et),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=c({},It,t),f.typeCheckConfig(Z,t,Ft),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=ct)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&b.default(this._element).on(gt,(function(e){return t._keydown(e)})),"hover"===this._config.pause&&b.default(this._element).on(vt,(function(e){return t.pause(e)})).on(Rt,(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&Ht[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},o=function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX},p=function(e){t._pointerEvent&&Ht[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),zt+t._config.interval))};b.default(this._element.querySelectorAll(kt)).on(Nt,(function(t){return t.preventDefault()})),this._pointerEvent?(b.default(this._element).on(Xt,(function(t){return e(t)})),b.default(this._element).on(_t,(function(t){return p(t)})),this._element.classList.add(At)):(b.default(this._element).on(yt,(function(t){return e(t)})),b.default(this._element).on(Bt,(function(t){return o(t)})),b.default(this._element).on(Lt,(function(t){return p(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case nt:t.preventDefault(),this.prev();break;case Mt:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(St)):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var o=t===ut,p=t===ft,b=this._getItemIndex(e),n=this._items.length-1;if((p&&0===b||o&&b===n)&&!this._config.wrap)return e;var M=(b+(t===ft?-1:1))%this._items.length;return-1===M?this._items[this._items.length-1]:this._items[M]},e._triggerSlideEvent=function(t,e){var o=this._getItemIndex(t),p=this._getItemIndex(this._element.querySelector(Ct)),n=b.default.Event(Wt,{relatedTarget:t,direction:e,from:p,to:o});return b.default(this._element).trigger(n),n},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(Tt));b.default(e).removeClass(it);var o=this._indicatorsElement.children[this._getItemIndex(t)];o&&b.default(o).addClass(it)}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(Ct);if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var o,p,n,M=this,z=this._element.querySelector(Ct),c=this._getItemIndex(z),r=e||z&&this._getItemByDirection(t,z),i=this._getItemIndex(r),a=Boolean(this._interval);if(t===ut?(o=st,p=lt,n=qt):(o=Ot,p=dt,n=ht),r&&b.default(r).hasClass(it))this._isSliding=!1;else if(!this._triggerSlideEvent(r,n).isDefaultPrevented()&&z&&r){this._isSliding=!0,a&&this.pause(),this._setActiveIndicatorElement(r),this._activeElement=r;var O=b.default.Event(mt,{relatedTarget:r,direction:n,from:c,to:i});if(b.default(this._element).hasClass(at)){b.default(r).addClass(p),f.reflow(r),b.default(z).addClass(o),b.default(r).addClass(o);var s=f.getTransitionDurationFromElement(z);b.default(z).one(f.TRANSITION_END,(function(){b.default(r).removeClass(o+" "+p).addClass(it),b.default(z).removeClass(it+" "+p+" "+o),M._isSliding=!1,setTimeout((function(){return b.default(M._element).trigger(O)}),0)})).emulateTransitionEnd(s)}else b.default(z).removeClass(it),b.default(r).addClass(it),this._isSliding=!1,b.default(this._element).trigger(O);a&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var o=b.default(this).data(et),p=c({},It,b.default(this).data());"object"==typeof e&&(p=c({},p,e));var n="string"==typeof e?e:p.slide;if(o||(o=new t(this,p),b.default(this).data(et,o)),"number"==typeof e)o.to(e);else if("string"==typeof n){if(void 0===o[n])throw new TypeError('No method named "'+n+'"');o[n]()}else p.interval&&p.ride&&(o.pause(),o.cycle())}))},t._dataApiClickHandler=function(e){var o=f.getSelectorFromElement(this);if(o){var p=b.default(o)[0];if(p&&b.default(p).hasClass(rt)){var n=c({},b.default(p).data(),b.default(this).data()),M=this.getAttribute("data-slide-to");M&&(n.interval=!1),t._jQueryInterface.call(b.default(p),n),M&&b.default(p).data(et).to(M),e.preventDefault()}}},z(t,null,[{key:"VERSION",get:function(){return tt}},{key:"Default",get:function(){return It}}]),t}();b.default(document).on(xt,Pt,Ut._dataApiClickHandler),b.default(window).on(wt,(function(){for(var t=[].slice.call(document.querySelectorAll(jt)),e=0,o=t.length;e0&&(this._selector=M,this._triggerArray.push(n))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){b.default(this._element).hasClass(Qt)?this.hide():this.show()},e.show=function(){var e,o,p=this;if(!(this._isTransitioning||b.default(this._element).hasClass(Qt)||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(re)).filter((function(t){return"string"==typeof p._config.parent?t.getAttribute("data-parent")===p._config.parent:t.classList.contains(Zt)}))).length&&(e=null),e&&(o=b.default(e).not(this._selector).data(Yt))&&o._isTransitioning))){var n=b.default.Event(be);if(b.default(this._element).trigger(n),!n.isDefaultPrevented()){e&&(t._jQueryInterface.call(b.default(e).not(this._selector),"hide"),o||b.default(e).data(Yt,null));var M=this._getDimension();b.default(this._element).removeClass(Zt).addClass(te),this._element.style[M]=0,this._triggerArray.length&&b.default(this._triggerArray).removeClass(ee).attr("aria-expanded",!0),this.setTransitioning(!0);var z=function(){b.default(p._element).removeClass(te).addClass(Zt+" "+Qt),p._element.style[M]="",p.setTransitioning(!1),b.default(p._element).trigger(ne)},c="scroll"+(M[0].toUpperCase()+M.slice(1)),r=f.getTransitionDurationFromElement(this._element);b.default(this._element).one(f.TRANSITION_END,z).emulateTransitionEnd(r),this._element.style[M]=this._element[c]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&b.default(this._element).hasClass(Qt)){var e=b.default.Event(Me);if(b.default(this._element).trigger(e),!e.isDefaultPrevented()){var o=this._getDimension();this._element.style[o]=this._element.getBoundingClientRect()[o]+"px",f.reflow(this._element),b.default(this._element).addClass(te).removeClass(Zt+" "+Qt);var p=this._triggerArray.length;if(p>0)for(var n=0;n0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=c({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),c({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var o=b.default(this).data(Ae);if(o||(o=new t(this,"object"==typeof e?e:null),b.default(this).data(Ae,o)),"string"==typeof e){if(void 0===o[e])throw new TypeError('No method named "'+e+'"');o[e]()}}))},t._clearMenus=function(e){if(!e||e.which!==Re&&("keyup"!==e.type||e.which===me))for(var o=[].slice.call(document.querySelectorAll(Ie)),p=0,n=o.length;p0&&M--,e.which===ve&&Mdocument.documentElement.clientHeight;o||(this._element.style.overflowY="hidden"),this._element.classList.add(Ao);var p=f.getTransitionDurationFromElement(this._dialog);b.default(this._element).off(f.TRANSITION_END),b.default(this._element).one(f.TRANSITION_END,(function(){t._element.classList.remove(Ao),o||b.default(t._element).one(f.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,p)})).emulateTransitionEnd(p),this._element.focus()}},e._showElement=function(t){var e=this,o=b.default(this._element).hasClass(so),p=this._dialog?this._dialog.querySelector(_o):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),b.default(this._dialog).hasClass(ro)&&p?p.scrollTop=0:this._element.scrollTop=0,o&&f.reflow(this._element),b.default(this._element).addClass(lo),this._config.focus&&this._enforceFocus();var n=b.default.Event(Wo,{relatedTarget:t}),M=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,b.default(e._element).trigger(n)};if(o){var z=f.getTransitionDurationFromElement(this._dialog);b.default(this._dialog).one(f.TRANSITION_END,M).emulateTransitionEnd(z)}else M()},e._enforceFocus=function(){var t=this;b.default(document).off(mo).on(mo,(function(e){document!==e.target&&t._element!==e.target&&0===b.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?b.default(this._element).on(Ro,(function(e){t._config.keyboard&&e.which===co?(e.preventDefault(),t.hide()):t._config.keyboard||e.which!==co||t._triggerBackdropTransition()})):this._isShown||b.default(this._element).off(Ro)},e._setResizeEvent=function(){var t=this;this._isShown?b.default(window).on(go,(function(e){return t.handleUpdate(e)})):b.default(window).off(go)},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){b.default(document.body).removeClass(Oo),t._resetAdjustments(),t._resetScrollbar(),b.default(t._element).trigger(qo)}))},e._removeBackdrop=function(){this._backdrop&&(b.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,o=b.default(this._element).hasClass(so)?so:"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className=ao,o&&this._backdrop.classList.add(o),b.default(this._backdrop).appendTo(document.body),b.default(this._element).on(vo,(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),o&&f.reflow(this._backdrop),b.default(this._backdrop).addClass(lo),!t)return;if(!o)return void t();var p=f.getTransitionDurationFromElement(this._backdrop);b.default(this._backdrop).one(f.TRANSITION_END,t).emulateTransitionEnd(p)}else if(!this._isShown&&this._backdrop){b.default(this._backdrop).removeClass(lo);var n=function(){e._removeBackdrop(),t&&t()};if(b.default(this._element).hasClass(so)){var M=f.getTransitionDurationFromElement(this._backdrop);b.default(this._backdrop).one(f.TRANSITION_END,n).emulateTransitionEnd(M)}else n()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:Do,popperConfig:null},ip={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},ap={HIDE:"hide"+$o,HIDDEN:"hidden"+$o,SHOW:"show"+$o,SHOWN:"shown"+$o,INSERTED:"inserted"+$o,CLICK:"click"+$o,FOCUSIN:"focusin"+$o,FOCUSOUT:"focusout"+$o,MOUSEENTER:"mouseenter"+$o,MOUSELEAVE:"mouseleave"+$o},Op=function(){function t(t,e){if(void 0===n.default)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,o=b.default(t.currentTarget).data(e);o||(o=new this.constructor(t.currentTarget,this._getDelegateConfig()),b.default(t.currentTarget).data(e,o)),o._activeTrigger.click=!o._activeTrigger.click,o._isWithActiveTrigger()?o._enter(null,o):o._leave(null,o)}else{if(b.default(this.getTipElement()).hasClass(Zo))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),b.default.removeData(this.element,this.constructor.DATA_KEY),b.default(this.element).off(this.constructor.EVENT_KEY),b.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&b.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===b.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=b.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){b.default(this.element).trigger(e);var o=f.findShadowRoot(this.element),p=b.default.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!p)return;var M=this.getTipElement(),z=f.getUID(this.constructor.NAME);M.setAttribute("id",z),this.element.setAttribute("aria-describedby",z),this.setContent(),this.config.animation&&b.default(M).addClass(Qo);var c="function"==typeof this.config.placement?this.config.placement.call(this,M,this.element):this.config.placement,r=this._getAttachment(c);this.addAttachmentClass(r);var i=this._getContainer();b.default(M).data(this.constructor.DATA_KEY,this),b.default.contains(this.element.ownerDocument.documentElement,this.tip)||b.default(M).appendTo(i),b.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n.default(this.element,M,this._getPopperConfig(r)),b.default(M).addClass(Zo),b.default(M).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&b.default(document.body).children().on("mouseover",null,b.default.noop);var a=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,b.default(t.element).trigger(t.constructor.Event.SHOWN),e===ep&&t._leave(null,t)};if(b.default(this.tip).hasClass(Qo)){var O=f.getTransitionDurationFromElement(this.tip);b.default(this.tip).one(f.TRANSITION_END,a).emulateTransitionEnd(O)}else a()}},e.hide=function(t){var e=this,o=this.getTipElement(),p=b.default.Event(this.constructor.Event.HIDE),n=function(){e._hoverState!==tp&&o.parentNode&&o.parentNode.removeChild(o),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),b.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(b.default(this.element).trigger(p),!p.isDefaultPrevented()){if(b.default(o).removeClass(Zo),"ontouchstart"in document.documentElement&&b.default(document.body).children().off("mouseover",null,b.default.noop),this._activeTrigger[Mp]=!1,this._activeTrigger[np]=!1,this._activeTrigger[bp]=!1,b.default(this.tip).hasClass(Qo)){var M=f.getTransitionDurationFromElement(o);b.default(o).one(f.TRANSITION_END,n).emulateTransitionEnd(M)}else n();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){b.default(this.getTipElement()).addClass(Go+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||b.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(b.default(t.querySelectorAll(op)),this.getTitle()),b.default(t).removeClass(Qo+" "+Zo)},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Fo(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?b.default(e).parent().is(t)||t.empty().append(e):t.text(b.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return c({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:pp},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=c({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:f.isElement(this.config.container)?b.default(this.config.container):b.default(document).find(this.config.container)},e._getAttachment=function(t){return cp[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)b.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if(e!==zp){var o=e===bp?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,p=e===bp?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;b.default(t.element).on(o,t.config.selector,(function(e){return t._enter(e)})).on(p,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},b.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=c({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var o=this.constructor.DATA_KEY;(e=e||b.default(t.currentTarget).data(o))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),b.default(t.currentTarget).data(o,e)),t&&(e._activeTrigger["focusin"===t.type?np:bp]=!0),b.default(e.getTipElement()).hasClass(Zo)||e._hoverState===tp?e._hoverState=tp:(clearTimeout(e._timeout),e._hoverState=tp,e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){e._hoverState===tp&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var o=this.constructor.DATA_KEY;(e=e||b.default(t.currentTarget).data(o))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),b.default(t.currentTarget).data(o,e)),t&&(e._activeTrigger["focusout"===t.type?np:bp]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=ep,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){e._hoverState===ep&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=b.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Ko.indexOf(t)&&delete e[t]})),"number"==typeof(t=c({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),f.typeCheckConfig(Ho,t,this.constructor.DefaultType),t.sanitize&&(t.template=Fo(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=b.default(this.getTipElement()),e=t.attr("class").match(Jo);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(b.default(t).removeClass(Qo),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var o=b.default(this),p=o.data(Vo),n="object"==typeof e&&e;if((p||!/dispose|hide/.test(e))&&(p||(p=new t(this,n),o.data(Vo,p)),"string"==typeof e)){if(void 0===p[e])throw new TypeError('No method named "'+e+'"');p[e]()}}))},z(t,null,[{key:"VERSION",get:function(){return Uo}},{key:"Default",get:function(){return rp}},{key:"NAME",get:function(){return Ho}},{key:"DATA_KEY",get:function(){return Vo}},{key:"Event",get:function(){return ap}},{key:"EVENT_KEY",get:function(){return $o}},{key:"DefaultType",get:function(){return ip}}]),t}();b.default.fn[Ho]=Op._jQueryInterface,b.default.fn[Ho].Constructor=Op,b.default.fn[Ho].noConflict=function(){return b.default.fn[Ho]=Yo,Op._jQueryInterface};var sp="popover",lp="4.6.2",dp="bs.popover",Ap="."+dp,up=b.default.fn[sp],fp="bs-popover",qp=new RegExp("(^|\\s)"+fp+"\\S+","g"),hp="fade",Wp="show",mp=".popover-header",gp=".popover-body",vp=c({},Op.Default,{placement:"right",trigger:"click",content:"",template:''}),Rp=c({},Op.DefaultType,{content:"(string|element|function)"}),yp={HIDE:"hide"+Ap,HIDDEN:"hidden"+Ap,SHOW:"show"+Ap,SHOWN:"shown"+Ap,INSERTED:"inserted"+Ap,CLICK:"click"+Ap,FOCUSIN:"focusin"+Ap,FOCUSOUT:"focusout"+Ap,MOUSEENTER:"mouseenter"+Ap,MOUSELEAVE:"mouseleave"+Ap},Bp=function(t){function e(){return t.apply(this,arguments)||this}r(e,t);var o=e.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){b.default(this.getTipElement()).addClass(fp+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||b.default(this.config.template)[0],this.tip},o.setContent=function(){var t=b.default(this.getTipElement());this.setElementContent(t.find(mp),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(gp),e),t.removeClass(hp+" "+Wp)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=b.default(this.getTipElement()),e=t.attr("class").match(qp);null!==e&&e.length>0&&t.removeClass(e.join(""))},e._jQueryInterface=function(t){return this.each((function(){var o=b.default(this).data(dp),p="object"==typeof t?t:null;if((o||!/dispose|hide/.test(t))&&(o||(o=new e(this,p),b.default(this).data(dp,o)),"string"==typeof t)){if(void 0===o[t])throw new TypeError('No method named "'+t+'"');o[t]()}}))},z(e,null,[{key:"VERSION",get:function(){return lp}},{key:"Default",get:function(){return vp}},{key:"NAME",get:function(){return sp}},{key:"DATA_KEY",get:function(){return dp}},{key:"Event",get:function(){return yp}},{key:"EVENT_KEY",get:function(){return Ap}},{key:"DefaultType",get:function(){return Rp}}]),e}(Op);b.default.fn[sp]=Bp._jQueryInterface,b.default.fn[sp].Constructor=Bp,b.default.fn[sp].noConflict=function(){return b.default.fn[sp]=up,Bp._jQueryInterface};var Lp="scrollspy",Xp="4.6.2",_p="bs.scrollspy",Np="."+_p,wp=".data-api",xp=b.default.fn[Lp],Tp="dropdown-item",Cp="active",Sp="activate"+Np,kp="scroll"+Np,Ep="load"+Np+wp,Dp="offset",Pp="position",jp='[data-spy="scroll"]',Ip=".nav, .list-group",Fp=".nav-link",Hp=".nav-item",Up=".list-group-item",Vp=".dropdown",$p=".dropdown-item",Yp=".dropdown-toggle",Gp={offset:10,method:"auto",target:""},Jp={offset:"number",method:"string",target:"(string|element)"},Kp=function(){function t(t,e){var o=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" "+Fp+","+this._config.target+" "+Up+","+this._config.target+" "+$p,this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,b.default(this._scrollElement).on(kp,(function(t){return o._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?Dp:Pp,o="auto"===this._config.method?e:this._config.method,p=o===Pp?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,n=f.getSelectorFromElement(t);if(n&&(e=document.querySelector(n)),e){var M=e.getBoundingClientRect();if(M.width||M.height)return[b.default(e)[o]().top+p,n]}return null})).filter(Boolean).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){b.default.removeData(this._element,_p),b.default(this._scrollElement).off(Np),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=c({},Gp,"object"==typeof t&&t?t:{})).target&&f.isElement(t.target)){var e=b.default(t.target).attr("id");e||(e=f.getUID(Lp),b.default(t.target).attr("id",e)),t.target="#"+e}return f.typeCheckConfig(Lp,t,Jp),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),o=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=o){var p=this._targets[this._targets.length-1];this._activeTarget!==p&&this._activate(p)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var b=this._offsets.length;b--;)this._activeTarget!==this._targets[b]&&t>=this._offsets[b]&&(void 0===this._offsets[b+1]||t{"use strict";var p=o(742),b=o(645),n=o(826);function M(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function z(t,e){if(M()=M())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+M().toString(16)+" bytes");return 0|t}function l(t,e){if(c.isBuffer(t))return t.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;"string"!=typeof t&&(t=""+t);var o=t.length;if(0===o)return 0;for(var p=!1;;)switch(e){case"ascii":case"latin1":case"binary":return o;case"utf8":case"utf-8":case void 0:return j(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*o;case"hex":return o>>>1;case"base64":return I(t).length;default:if(p)return j(t).length;e=(""+e).toLowerCase(),p=!0}}function d(t,e,o){var p=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===o||o>this.length)&&(o=this.length),o<=0)return"";if((o>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return _(this,e,o);case"utf8":case"utf-8":return y(this,e,o);case"ascii":return L(this,e,o);case"latin1":case"binary":return X(this,e,o);case"base64":return R(this,e,o);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return N(this,e,o);default:if(p)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),p=!0}}function A(t,e,o){var p=t[e];t[e]=t[o],t[o]=p}function u(t,e,o,p,b){if(0===t.length)return-1;if("string"==typeof o?(p=o,o=0):o>2147483647?o=2147483647:o<-2147483648&&(o=-2147483648),o=+o,isNaN(o)&&(o=b?0:t.length-1),o<0&&(o=t.length+o),o>=t.length){if(b)return-1;o=t.length-1}else if(o<0){if(!b)return-1;o=0}if("string"==typeof e&&(e=c.from(e,p)),c.isBuffer(e))return 0===e.length?-1:f(t,e,o,p,b);if("number"==typeof e)return e&=255,c.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?b?Uint8Array.prototype.indexOf.call(t,e,o):Uint8Array.prototype.lastIndexOf.call(t,e,o):f(t,[e],o,p,b);throw new TypeError("val must be string, number or Buffer")}function f(t,e,o,p,b){var n,M=1,z=t.length,c=e.length;if(void 0!==p&&("ucs2"===(p=String(p).toLowerCase())||"ucs-2"===p||"utf16le"===p||"utf-16le"===p)){if(t.length<2||e.length<2)return-1;M=2,z/=2,c/=2,o/=2}function r(t,e){return 1===M?t[e]:t.readUInt16BE(e*M)}if(b){var i=-1;for(n=o;nz&&(o=z-c),n=o;n>=0;n--){for(var a=!0,O=0;Ob&&(p=b):p=b;var n=e.length;if(n%2!=0)throw new TypeError("Invalid hex string");p>n/2&&(p=n/2);for(var M=0;M>8,b=o%256,n.push(b),n.push(p);return n}(e,t.length-o),t,o,p)}function R(t,e,o){return 0===e&&o===t.length?p.fromByteArray(t):p.fromByteArray(t.slice(e,o))}function y(t,e,o){o=Math.min(t.length,o);for(var p=[],b=e;b239?4:r>223?3:r>191?2:1;if(b+a<=o)switch(a){case 1:r<128&&(i=r);break;case 2:128==(192&(n=t[b+1]))&&(c=(31&r)<<6|63&n)>127&&(i=c);break;case 3:n=t[b+1],M=t[b+2],128==(192&n)&&128==(192&M)&&(c=(15&r)<<12|(63&n)<<6|63&M)>2047&&(c<55296||c>57343)&&(i=c);break;case 4:n=t[b+1],M=t[b+2],z=t[b+3],128==(192&n)&&128==(192&M)&&128==(192&z)&&(c=(15&r)<<18|(63&n)<<12|(63&M)<<6|63&z)>65535&&c<1114112&&(i=c)}null===i?(i=65533,a=1):i>65535&&(i-=65536,p.push(i>>>10&1023|55296),i=56320|1023&i),p.push(i),b+=a}return function(t){var e=t.length;if(e<=B)return String.fromCharCode.apply(String,t);var o="",p=0;for(;p0&&(t=this.toString("hex",0,o).match(/.{2}/g).join(" "),this.length>o&&(t+=" ... ")),""},c.prototype.compare=function(t,e,o,p,b){if(!c.isBuffer(t))throw new TypeError("Argument must be a Buffer");if(void 0===e&&(e=0),void 0===o&&(o=t?t.length:0),void 0===p&&(p=0),void 0===b&&(b=this.length),e<0||o>t.length||p<0||b>this.length)throw new RangeError("out of range index");if(p>=b&&e>=o)return 0;if(p>=b)return-1;if(e>=o)return 1;if(this===t)return 0;for(var n=(b>>>=0)-(p>>>=0),M=(o>>>=0)-(e>>>=0),z=Math.min(n,M),r=this.slice(p,b),i=t.slice(e,o),a=0;ab)&&(o=b),t.length>0&&(o<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");p||(p="utf8");for(var n=!1;;)switch(p){case"hex":return q(this,t,e,o);case"utf8":case"utf-8":return h(this,t,e,o);case"ascii":return W(this,t,e,o);case"latin1":case"binary":return m(this,t,e,o);case"base64":return g(this,t,e,o);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return v(this,t,e,o);default:if(n)throw new TypeError("Unknown encoding: "+p);p=(""+p).toLowerCase(),n=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var B=4096;function L(t,e,o){var p="";o=Math.min(t.length,o);for(var b=e;bp)&&(o=p);for(var b="",n=e;no)throw new RangeError("Trying to access beyond buffer length")}function x(t,e,o,p,b,n){if(!c.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>b||et.length)throw new RangeError("Index out of range")}function T(t,e,o,p){e<0&&(e=65535+e+1);for(var b=0,n=Math.min(t.length-o,2);b>>8*(p?b:1-b)}function C(t,e,o,p){e<0&&(e=4294967295+e+1);for(var b=0,n=Math.min(t.length-o,4);b>>8*(p?b:3-b)&255}function S(t,e,o,p,b,n){if(o+p>t.length)throw new RangeError("Index out of range");if(o<0)throw new RangeError("Index out of range")}function k(t,e,o,p,n){return n||S(t,0,o,4),b.write(t,e,o,p,23,4),o+4}function E(t,e,o,p,n){return n||S(t,0,o,8),b.write(t,e,o,p,52,8),o+8}c.prototype.slice=function(t,e){var o,p=this.length;if((t=~~t)<0?(t+=p)<0&&(t=0):t>p&&(t=p),(e=void 0===e?p:~~e)<0?(e+=p)<0&&(e=0):e>p&&(e=p),e0&&(b*=256);)p+=this[t+--e]*b;return p},c.prototype.readUInt8=function(t,e){return e||w(t,1,this.length),this[t]},c.prototype.readUInt16LE=function(t,e){return e||w(t,2,this.length),this[t]|this[t+1]<<8},c.prototype.readUInt16BE=function(t,e){return e||w(t,2,this.length),this[t]<<8|this[t+1]},c.prototype.readUInt32LE=function(t,e){return e||w(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},c.prototype.readUInt32BE=function(t,e){return e||w(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},c.prototype.readIntLE=function(t,e,o){t|=0,e|=0,o||w(t,e,this.length);for(var p=this[t],b=1,n=0;++n=(b*=128)&&(p-=Math.pow(2,8*e)),p},c.prototype.readIntBE=function(t,e,o){t|=0,e|=0,o||w(t,e,this.length);for(var p=e,b=1,n=this[t+--p];p>0&&(b*=256);)n+=this[t+--p]*b;return n>=(b*=128)&&(n-=Math.pow(2,8*e)),n},c.prototype.readInt8=function(t,e){return e||w(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},c.prototype.readInt16LE=function(t,e){e||w(t,2,this.length);var o=this[t]|this[t+1]<<8;return 32768&o?4294901760|o:o},c.prototype.readInt16BE=function(t,e){e||w(t,2,this.length);var o=this[t+1]|this[t]<<8;return 32768&o?4294901760|o:o},c.prototype.readInt32LE=function(t,e){return e||w(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},c.prototype.readInt32BE=function(t,e){return e||w(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},c.prototype.readFloatLE=function(t,e){return e||w(t,4,this.length),b.read(this,t,!0,23,4)},c.prototype.readFloatBE=function(t,e){return e||w(t,4,this.length),b.read(this,t,!1,23,4)},c.prototype.readDoubleLE=function(t,e){return e||w(t,8,this.length),b.read(this,t,!0,52,8)},c.prototype.readDoubleBE=function(t,e){return e||w(t,8,this.length),b.read(this,t,!1,52,8)},c.prototype.writeUIntLE=function(t,e,o,p){(t=+t,e|=0,o|=0,p)||x(this,t,e,o,Math.pow(2,8*o)-1,0);var b=1,n=0;for(this[e]=255&t;++n=0&&(n*=256);)this[e+b]=t/n&255;return e+o},c.prototype.writeUInt8=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,1,255,0),c.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[e]=255&t,e+1},c.prototype.writeUInt16LE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):T(this,t,e,!0),e+2},c.prototype.writeUInt16BE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):T(this,t,e,!1),e+2},c.prototype.writeUInt32LE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t):C(this,t,e,!0),e+4},c.prototype.writeUInt32BE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):C(this,t,e,!1),e+4},c.prototype.writeIntLE=function(t,e,o,p){if(t=+t,e|=0,!p){var b=Math.pow(2,8*o-1);x(this,t,e,o,b-1,-b)}var n=0,M=1,z=0;for(this[e]=255&t;++n>0)-z&255;return e+o},c.prototype.writeIntBE=function(t,e,o,p){if(t=+t,e|=0,!p){var b=Math.pow(2,8*o-1);x(this,t,e,o,b-1,-b)}var n=o-1,M=1,z=0;for(this[e+n]=255&t;--n>=0&&(M*=256);)t<0&&0===z&&0!==this[e+n+1]&&(z=1),this[e+n]=(t/M>>0)-z&255;return e+o},c.prototype.writeInt8=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,1,127,-128),c.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),t<0&&(t=255+t+1),this[e]=255&t,e+1},c.prototype.writeInt16LE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8):T(this,t,e,!0),e+2},c.prototype.writeInt16BE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[e]=t>>>8,this[e+1]=255&t):T(this,t,e,!1),e+2},c.prototype.writeInt32LE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,4,2147483647,-2147483648),c.TYPED_ARRAY_SUPPORT?(this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24):C(this,t,e,!0),e+4},c.prototype.writeInt32BE=function(t,e,o){return t=+t,e|=0,o||x(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),c.TYPED_ARRAY_SUPPORT?(this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t):C(this,t,e,!1),e+4},c.prototype.writeFloatLE=function(t,e,o){return k(this,t,e,!0,o)},c.prototype.writeFloatBE=function(t,e,o){return k(this,t,e,!1,o)},c.prototype.writeDoubleLE=function(t,e,o){return E(this,t,e,!0,o)},c.prototype.writeDoubleBE=function(t,e,o){return E(this,t,e,!1,o)},c.prototype.copy=function(t,e,o,p){if(o||(o=0),p||0===p||(p=this.length),e>=t.length&&(e=t.length),e||(e=0),p>0&&p=this.length)throw new RangeError("sourceStart out of bounds");if(p<0)throw new RangeError("sourceEnd out of bounds");p>this.length&&(p=this.length),t.length-e=0;--b)t[b+e]=this[b+o];else if(n<1e3||!c.TYPED_ARRAY_SUPPORT)for(b=0;b>>=0,o=void 0===o?this.length:o>>>0,t||(t=0),"number"==typeof t)for(n=e;n55295&&o<57344){if(!b){if(o>56319){(e-=3)>-1&&n.push(239,191,189);continue}if(M+1===p){(e-=3)>-1&&n.push(239,191,189);continue}b=o;continue}if(o<56320){(e-=3)>-1&&n.push(239,191,189),b=o;continue}o=65536+(b-55296<<10|o-56320)}else b&&(e-=3)>-1&&n.push(239,191,189);if(b=null,o<128){if((e-=1)<0)break;n.push(o)}else if(o<2048){if((e-=2)<0)break;n.push(o>>6|192,63&o|128)}else if(o<65536){if((e-=3)<0)break;n.push(o>>12|224,o>>6&63|128,63&o|128)}else{if(!(o<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;n.push(o>>18|240,o>>12&63|128,o>>6&63|128,63&o|128)}}return n}function I(t){return p.toByteArray(function(t){if((t=function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}(t).replace(D,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function F(t,e,o,p){for(var b=0;b=e.length||b>=t.length);++b)e[b+o]=t[b];return b}},757:function(t,e,o){t.exports=function(t){"use strict";function e(t,e){return t(e={exports:{}},e.exports),e.exports}function o(t){return t&&t.default||t}t=t&&t.hasOwnProperty("default")?t.default:t;var p={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},b=e((function(t){var e={};for(var o in p)p.hasOwnProperty(o)&&(e[p[o]]=o);var b=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var n in b)if(b.hasOwnProperty(n)){if(!("channels"in b[n]))throw new Error("missing channels property: "+n);if(!("labels"in b[n]))throw new Error("missing channel labels property: "+n);if(b[n].labels.length!==b[n].channels)throw new Error("channel and label counts mismatch: "+n);var M=b[n].channels,z=b[n].labels;delete b[n].channels,delete b[n].labels,Object.defineProperty(b[n],"channels",{value:M}),Object.defineProperty(b[n],"labels",{value:z})}function c(t,e){return Math.pow(t[0]-e[0],2)+Math.pow(t[1]-e[1],2)+Math.pow(t[2]-e[2],2)}b.rgb.hsl=function(t){var e,o,p=t[0]/255,b=t[1]/255,n=t[2]/255,M=Math.min(p,b,n),z=Math.max(p,b,n),c=z-M;return z===M?e=0:p===z?e=(b-n)/c:b===z?e=2+(n-p)/c:n===z&&(e=4+(p-b)/c),(e=Math.min(60*e,360))<0&&(e+=360),o=(M+z)/2,[e,100*(z===M?0:o<=.5?c/(z+M):c/(2-z-M)),100*o]},b.rgb.hsv=function(t){var e,o,p,b,n,M=t[0]/255,z=t[1]/255,c=t[2]/255,r=Math.max(M,z,c),i=r-Math.min(M,z,c),a=function(t){return(r-t)/6/i+.5};return 0===i?b=n=0:(n=i/r,e=a(M),o=a(z),p=a(c),M===r?b=p-o:z===r?b=1/3+e-p:c===r&&(b=2/3+o-e),b<0?b+=1:b>1&&(b-=1)),[360*b,100*n,100*r]},b.rgb.hwb=function(t){var e=t[0],o=t[1],p=t[2];return[b.rgb.hsl(t)[0],1/255*Math.min(e,Math.min(o,p))*100,100*(p=1-1/255*Math.max(e,Math.max(o,p)))]},b.rgb.cmyk=function(t){var e,o=t[0]/255,p=t[1]/255,b=t[2]/255;return[100*((1-o-(e=Math.min(1-o,1-p,1-b)))/(1-e)||0),100*((1-p-e)/(1-e)||0),100*((1-b-e)/(1-e)||0),100*e]},b.rgb.keyword=function(t){var o=e[t];if(o)return o;var b,n=1/0;for(var M in p)if(p.hasOwnProperty(M)){var z=c(t,p[M]);z.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(o=o>.04045?Math.pow((o+.055)/1.055,2.4):o/12.92)+.1805*(p=p>.04045?Math.pow((p+.055)/1.055,2.4):p/12.92)),100*(.2126*e+.7152*o+.0722*p),100*(.0193*e+.1192*o+.9505*p)]},b.rgb.lab=function(t){var e=b.rgb.xyz(t),o=e[0],p=e[1],n=e[2];return p/=100,n/=108.883,o=(o/=95.047)>.008856?Math.pow(o,1/3):7.787*o+16/116,[116*(p=p>.008856?Math.pow(p,1/3):7.787*p+16/116)-16,500*(o-p),200*(p-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]},b.hsl.rgb=function(t){var e,o,p,b,n,M=t[0]/360,z=t[1]/100,c=t[2]/100;if(0===z)return[n=255*c,n,n];e=2*c-(o=c<.5?c*(1+z):c+z-c*z),b=[0,0,0];for(var r=0;r<3;r++)(p=M+1/3*-(r-1))<0&&p++,p>1&&p--,n=6*p<1?e+6*(o-e)*p:2*p<1?o:3*p<2?e+(o-e)*(2/3-p)*6:e,b[r]=255*n;return b},b.hsl.hsv=function(t){var e=t[0],o=t[1]/100,p=t[2]/100,b=o,n=Math.max(p,.01);return o*=(p*=2)<=1?p:2-p,b*=n<=1?n:2-n,[e,100*(0===p?2*b/(n+b):2*o/(p+o)),(p+o)/2*100]},b.hsv.rgb=function(t){var e=t[0]/60,o=t[1]/100,p=t[2]/100,b=Math.floor(e)%6,n=e-Math.floor(e),M=255*p*(1-o),z=255*p*(1-o*n),c=255*p*(1-o*(1-n));switch(p*=255,b){case 0:return[p,c,M];case 1:return[z,p,M];case 2:return[M,p,c];case 3:return[M,z,p];case 4:return[c,M,p];case 5:return[p,M,z]}},b.hsv.hsl=function(t){var e,o,p,b=t[0],n=t[1]/100,M=t[2]/100,z=Math.max(M,.01);return p=(2-n)*M,o=n*z,[b,100*(o=(o/=(e=(2-n)*z)<=1?e:2-e)||0),100*(p/=2)]},b.hwb.rgb=function(t){var e,o,p,b,n,M,z,c=t[0]/360,r=t[1]/100,i=t[2]/100,a=r+i;switch(a>1&&(r/=a,i/=a),p=6*c-(e=Math.floor(6*c)),0!=(1&e)&&(p=1-p),b=r+p*((o=1-i)-r),e){default:case 6:case 0:n=o,M=b,z=r;break;case 1:n=b,M=o,z=r;break;case 2:n=r,M=o,z=b;break;case 3:n=r,M=b,z=o;break;case 4:n=b,M=r,z=o;break;case 5:n=o,M=r,z=b}return[255*n,255*M,255*z]},b.cmyk.rgb=function(t){var e=t[0]/100,o=t[1]/100,p=t[2]/100,b=t[3]/100;return[255*(1-Math.min(1,e*(1-b)+b)),255*(1-Math.min(1,o*(1-b)+b)),255*(1-Math.min(1,p*(1-b)+b))]},b.xyz.rgb=function(t){var e,o,p,b=t[0]/100,n=t[1]/100,M=t[2]/100;return o=-.9689*b+1.8758*n+.0415*M,p=.0557*b+-.204*n+1.057*M,e=(e=3.2406*b+-1.5372*n+-.4986*M)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,o=o>.0031308?1.055*Math.pow(o,1/2.4)-.055:12.92*o,p=p>.0031308?1.055*Math.pow(p,1/2.4)-.055:12.92*p,[255*(e=Math.min(Math.max(0,e),1)),255*(o=Math.min(Math.max(0,o),1)),255*(p=Math.min(Math.max(0,p),1))]},b.xyz.lab=function(t){var e=t[0],o=t[1],p=t[2];return o/=100,p/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(o=o>.008856?Math.pow(o,1/3):7.787*o+16/116)-16,500*(e-o),200*(o-(p=p>.008856?Math.pow(p,1/3):7.787*p+16/116))]},b.lab.xyz=function(t){var e,o,p,b=t[0];e=t[1]/500+(o=(b+16)/116),p=o-t[2]/200;var n=Math.pow(o,3),M=Math.pow(e,3),z=Math.pow(p,3);return o=n>.008856?n:(o-16/116)/7.787,e=M>.008856?M:(e-16/116)/7.787,p=z>.008856?z:(p-16/116)/7.787,[e*=95.047,o*=100,p*=108.883]},b.lab.lch=function(t){var e,o=t[0],p=t[1],b=t[2];return(e=360*Math.atan2(b,p)/2/Math.PI)<0&&(e+=360),[o,Math.sqrt(p*p+b*b),e]},b.lch.lab=function(t){var e,o=t[0],p=t[1];return e=t[2]/360*2*Math.PI,[o,p*Math.cos(e),p*Math.sin(e)]},b.rgb.ansi16=function(t){var e=t[0],o=t[1],p=t[2],n=1 in arguments?arguments[1]:b.rgb.hsv(t)[2];if(0===(n=Math.round(n/50)))return 30;var M=30+(Math.round(p/255)<<2|Math.round(o/255)<<1|Math.round(e/255));return 2===n&&(M+=60),M},b.hsv.ansi16=function(t){return b.rgb.ansi16(b.hsv.rgb(t),t[2])},b.rgb.ansi256=function(t){var e=t[0],o=t[1],p=t[2];return e===o&&o===p?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(o/255*5)+Math.round(p/255*5)},b.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var o=.5*(1+~~(t>50));return[(1&e)*o*255,(e>>1&1)*o*255,(e>>2&1)*o*255]},b.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var o;return t-=16,[Math.floor(t/36)/5*255,Math.floor((o=t%36)/6)/5*255,o%6/5*255]},b.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},b.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var o=e[0];3===e[0].length&&(o=o.split("").map((function(t){return t+t})).join(""));var p=parseInt(o,16);return[p>>16&255,p>>8&255,255&p]},b.rgb.hcg=function(t){var e,o=t[0]/255,p=t[1]/255,b=t[2]/255,n=Math.max(Math.max(o,p),b),M=Math.min(Math.min(o,p),b),z=n-M;return e=z<=0?0:n===o?(p-b)/z%6:n===p?2+(b-o)/z:4+(o-p)/z+4,e/=6,[360*(e%=1),100*z,100*(z<1?M/(1-z):0)]},b.hsl.hcg=function(t){var e=t[1]/100,o=t[2]/100,p=1,b=0;return(p=o<.5?2*e*o:2*e*(1-o))<1&&(b=(o-.5*p)/(1-p)),[t[0],100*p,100*b]},b.hsv.hcg=function(t){var e=t[1]/100,o=t[2]/100,p=e*o,b=0;return p<1&&(b=(o-p)/(1-p)),[t[0],100*p,100*b]},b.hcg.rgb=function(t){var e=t[0]/360,o=t[1]/100,p=t[2]/100;if(0===o)return[255*p,255*p,255*p];var b=[0,0,0],n=e%1*6,M=n%1,z=1-M,c=0;switch(Math.floor(n)){case 0:b[0]=1,b[1]=M,b[2]=0;break;case 1:b[0]=z,b[1]=1,b[2]=0;break;case 2:b[0]=0,b[1]=1,b[2]=M;break;case 3:b[0]=0,b[1]=z,b[2]=1;break;case 4:b[0]=M,b[1]=0,b[2]=1;break;default:b[0]=1,b[1]=0,b[2]=z}return c=(1-o)*p,[255*(o*b[0]+c),255*(o*b[1]+c),255*(o*b[2]+c)]},b.hcg.hsv=function(t){var e=t[1]/100,o=e+t[2]/100*(1-e),p=0;return o>0&&(p=e/o),[t[0],100*p,100*o]},b.hcg.hsl=function(t){var e=t[1]/100,o=t[2]/100*(1-e)+.5*e,p=0;return o>0&&o<.5?p=e/(2*o):o>=.5&&o<1&&(p=e/(2*(1-o))),[t[0],100*p,100*o]},b.hcg.hwb=function(t){var e=t[1]/100,o=e+t[2]/100*(1-e);return[t[0],100*(o-e),100*(1-o)]},b.hwb.hcg=function(t){var e=t[1]/100,o=1-t[2]/100,p=o-e,b=0;return p<1&&(b=(o-p)/(1-p)),[t[0],100*p,100*b]},b.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},b.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},b.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},b.gray.hsl=b.gray.hsv=function(t){return[0,0,t[0]]},b.gray.hwb=function(t){return[0,100,t[0]]},b.gray.cmyk=function(t){return[0,0,0,t[0]]},b.gray.lab=function(t){return[t[0],0,0]},b.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),o=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(o.length)+o},b.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));function n(){for(var t={},e=Object.keys(b),o=e.length,p=0;p1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}function O(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var o=t(e);if("object"==typeof o)for(var p=o.length,b=0;b=0&&e<1?w(Math.round(255*e)):"")}function g(t,e){return e<1||t[3]&&t[3]<1?v(t,e):"rgb("+t[0]+", "+t[1]+", "+t[2]+")"}function v(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function R(t,e){return e<1||t[3]&&t[3]<1?y(t,e):"rgb("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%)"}function y(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function B(t,e){return e<1||t[3]&&t[3]<1?L(t,e):"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"}function L(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function X(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"}function _(t){return x[t.slice(0,3)]}function N(t,e,o){return Math.min(Math.max(e,t),o)}function w(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var x={};for(var T in l)x[l[T]]=T;var C=function(t){return t instanceof C?t:this instanceof C?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=d.getRgba(t))?this.setValues("rgb",e):(e=d.getHsla(t))?this.setValues("hsl",e):(e=d.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new C(t);var e};C.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return d.hexString(this.values.rgb)},rgbString:function(){return d.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return d.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return d.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return d.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return d.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return d.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return d.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],o=0;oo?(e+.05)/(o+.05):(o+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,o=(e[0]+t)%360;return e[0]=o<0?360+o:o,this.setValues("hsl",e),this},mix:function(t,e){var o=this,p=t,b=void 0===e?.5:e,n=2*b-1,M=o.alpha()-p.alpha(),z=((n*M==-1?n:(n+M)/(1+n*M))+1)/2,c=1-z;return this.rgb(z*o.red()+c*p.red(),z*o.green()+c*p.green(),z*o.blue()+c*p.blue()).alpha(o.alpha()*b+p.alpha()*(1-b))},toJSON:function(){return this.rgb()},clone:function(){var t,e,o=new C,p=this.values,b=o.values;for(var n in p)p.hasOwnProperty(n)&&(t=p[n],"[object Array]"===(e={}.toString.call(t))?b[n]=t.slice(0):"[object Number]"===e&&(b[n]=t));return o}},C.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},C.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},C.prototype.getValues=function(t){for(var e=this.values,o={},p=0;p=0;b--)e.call(o,t[b],b);else for(b=0;b=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,o=0,p=1;return 0===t?0:1===t?1:(o||(o=.3),p<1?(p=1,e=o/4):e=o/(2*Math.PI)*Math.asin(1/p),-p*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/o))},easeOutElastic:function(t){var e=1.70158,o=0,p=1;return 0===t?0:1===t?1:(o||(o=.3),p<1?(p=1,e=o/4):e=o/(2*Math.PI)*Math.asin(1/p),p*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/o)+1)},easeInOutElastic:function(t){var e=1.70158,o=0,p=1;return 0===t?0:2==(t/=.5)?1:(o||(o=.45),p<1?(p=1,e=o/4):e=o/(2*Math.PI)*Math.asin(1/p),t<1?p*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/o)*-.5:p*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/o)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-j.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*j.easeInBounce(2*t):.5*j.easeOutBounce(2*t-1)+.5}},I={effects:j};P.easingEffects=j;var F=Math.PI,H=F/180,U=2*F,V=F/2,$=F/4,Y=2*F/3,G={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,o,p,b,n){if(n){var M=Math.min(n,b/2,p/2),z=e+M,c=o+M,r=e+p-M,i=o+b-M;t.moveTo(e,c),ze.left-o&&t.xe.top-o&&t.y0&&t.requestAnimationFrame()},advance:function(){for(var t,e,o,p,b=this.animations,n=0;n=o?(zt.callback(t.onAnimationComplete,[t],e),e.animating=!1,b.splice(n,1)):++n}},qt=zt.options.resolve,ht=["push","pop","shift","splice","unshift"];function Wt(t,e){t._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),ht.forEach((function(e){var o="onData"+e.charAt(0).toUpperCase()+e.slice(1),p=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),b=p.apply(this,e);return zt.each(t._chartjs.listeners,(function(t){"function"==typeof t[o]&&t[o].apply(t,e)})),b}})})))}function mt(t,e){var o=t._chartjs;if(o){var p=o.listeners,b=p.indexOf(e);-1!==b&&p.splice(b,1),p.length>0||(ht.forEach((function(e){delete t[e]})),delete t._chartjs)}}var gt=function(t,e){this.initialize(t,e)};zt.extend(gt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var o=this;o.chart=t,o.index=e,o.linkScales(),o.addElements(),o._type=o.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),o=t.chart,p=o.scales,b=t.getDataset(),n=o.options.scales;null!==e.xAxisID&&e.xAxisID in p&&!b.xAxisID||(e.xAxisID=b.xAxisID||n.xAxes[0].id),null!==e.yAxisID&&e.yAxisID in p&&!b.yAxisID||(e.yAxisID=b.yAxisID||n.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&mt(this._data,this)},createMetaDataset:function(){var t=this,e=t.datasetElementType;return e&&new e({_chart:t.chart,_datasetIndex:t.index})},createMetaData:function(t){var e=this,o=e.dataElementType;return o&&new o({_chart:e.chart,_datasetIndex:e.index,_index:t})},addElements:function(){var t,e,o=this,p=o.getMeta(),b=o.getDataset().data||[],n=p.data;for(t=0,e=b.length;tp&&t.insertElements(p,b-p)},insertElements:function(t,e){for(var o=0;ob?(n=b/e.innerRadius,t.arc(M,z,e.innerRadius-b,p+n,o-n,!0)):t.arc(M,z,b,p+Math.PI/2,o-Math.PI/2),t.closePath(),t.clip()}function Bt(t,e,o,p){var b,n=o.endAngle;for(p&&(o.endAngle=o.startAngle+Rt,yt(t,o),o.endAngle=n,o.endAngle===o.startAngle&&o.fullCircles&&(o.endAngle+=Rt,o.fullCircles--)),t.beginPath(),t.arc(o.x,o.y,o.innerRadius,o.startAngle+Rt,o.startAngle,!0),b=0;bz;)b-=Rt;for(;b=M&&b<=z,r=n>=o.innerRadius&&n<=o.outerRadius;return c&&r}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,o=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*o,y:t.y+Math.sin(e)*o}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,o=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*o,y:t.y+Math.sin(e)*o}},draw:function(){var t,e=this._chart.ctx,o=this._view,p="inner"===o.borderAlign?.33:0,b={x:o.x,y:o.y,innerRadius:o.innerRadius,outerRadius:Math.max(o.outerRadius-p,0),pixelMargin:p,startAngle:o.startAngle,endAngle:o.endAngle,fullCircles:Math.floor(o.circumference/Rt)};if(e.save(),e.fillStyle=o.backgroundColor,e.strokeStyle=o.borderColor,b.fullCircles){for(b.endAngle=b.startAngle+Rt,e.beginPath(),e.arc(b.x,b.y,b.outerRadius,b.startAngle,b.endAngle),e.arc(b.x,b.y,b.innerRadius,b.endAngle,b.startAngle,!0),e.closePath(),t=0;tt.x&&(e=jt(e,"left","right")):t.baseo?o:p,r:c.right||b<0?0:b>e?e:b,b:c.bottom||n<0?0:n>o?o:n,l:c.left||M<0?0:M>e?e:M}}function Ht(t){var e=Pt(t),o=e.right-e.left,p=e.bottom-e.top,b=Ft(t,o/2,p/2);return{outer:{x:e.left,y:e.top,w:o,h:p},inner:{x:e.left+b.l,y:e.top+b.t,w:o-b.l-b.r,h:p-b.t-b.b}}}function Ut(t,e,o){var p=null===e,b=null===o,n=!(!t||p&&b)&&Pt(t);return n&&(p||e>=n.left&&e<=n.right)&&(b||o>=n.top&&o<=n.bottom)}Q._set("global",{elements:{rectangle:{backgroundColor:Et,borderColor:Et,borderSkipped:"bottom",borderWidth:0}}});var Vt=dt.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,o=Ht(e),p=o.outer,b=o.inner;t.fillStyle=e.backgroundColor,t.fillRect(p.x,p.y,p.w,p.h),p.w===b.w&&p.h===b.h||(t.save(),t.beginPath(),t.rect(p.x,p.y,p.w,p.h),t.clip(),t.fillStyle=e.borderColor,t.rect(b.x,b.y,b.w,b.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return Ut(this._view,t,e)},inLabelRange:function(t,e){var o=this._view;return Dt(o)?Ut(o,t,null):Ut(o,null,e)},inXRange:function(t){return Ut(this._view,t,null)},inYRange:function(t){return Ut(this._view,null,t)},getCenterPoint:function(){var t,e,o=this._view;return Dt(o)?(t=o.x,e=(o.y+o.base)/2):(t=(o.x+o.base)/2,e=o.y),{x:t,y:e}},getArea:function(){var t=this._view;return Dt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),$t={},Yt=Xt,Gt=wt,Jt=kt,Kt=Vt;$t.Arc=Yt,$t.Line=Gt,$t.Point=Jt,$t.Rectangle=Kt;var Qt=zt._deprecated,Zt=zt.valueOrDefault;function te(t,e){var o,p,b,n,M=t._length;for(b=1,n=e.length;b0?Math.min(M,Math.abs(p-o)):M,o=p;return M}function ee(t,e,o){var p,b,n=o.barThickness,M=e.stackCount,z=e.pixels[t],c=zt.isNullOrUndef(n)?te(e.scale,e.pixels):-1;return zt.isNullOrUndef(n)?(p=c*o.categoryPercentage,b=o.barPercentage):(p=n*M,b=1),{chunk:p/M,ratio:b,start:z-p/2}}function oe(t,e,o){var p,b=e.pixels,n=b[t],M=t>0?b[t-1]:null,z=t=0&&A.min>=0?A.min:A.max,W=void 0===A.start?A.end:A.max>=0&&A.min>=0?A.max-A.min:A.min-A.max,m=d.length;if(f||void 0===f&&void 0!==q)for(p=0;p=0&&r.max>=0?r.max:r.min,(A.min<0&&n<0||A.max>=0&&n>0)&&(h+=n));return M=O.getPixelForValue(h),c=(z=O.getPixelForValue(h+W))-M,void 0!==u&&Math.abs(c)=0&&!s||W<0&&s?M-u:M+u),{size:c,base:M,head:z,center:z+c/2}},calculateBarIndexPixels:function(t,e,o,p){var b=this,n="flex"===p.barThickness?oe(e,o,p):ee(e,o,p),M=b.getStackIndex(t,b.getMeta().stack),z=n.start+n.chunk*M+n.chunk/2,c=Math.min(Zt(p.maxBarThickness,1/0),n.chunk*n.ratio);return{base:z-c/2,head:z+c/2,center:z,size:c}},draw:function(){var t=this,e=t.chart,o=t._getValueScale(),p=t.getMeta().data,b=t.getDataset(),n=p.length,M=0;for(zt.canvas.clipArea(e.ctx,e.chartArea);M=ce?-re:f<-ce?re:0)+A,h=Math.cos(f),W=Math.sin(f),m=Math.cos(q),g=Math.sin(q),v=f<=0&&q>=0||q>=re,R=f<=ie&&q>=ie||q>=re+ie,y=f<=-ie&&q>=-ie||q>=ce+ie,B=f===-ce||q>=ce?-1:Math.min(h,h*d,m,m*d),L=y?-1:Math.min(W,W*d,g,g*d),X=v?1:Math.max(h,h*d,m,m*d),_=R?1:Math.max(W,W*d,g,g*d);r=(X-B)/2,i=(_-L)/2,a=-(X+B)/2,O=-(_+L)/2}for(p=0,b=l.length;p0&&!isNaN(t)?re*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,o,p,b,n,M,z,c,r=this,i=0,a=r.chart;if(!t)for(e=0,o=a.data.datasets.length;e(i=z>i?z:i)?c:i);return i},setHoverStyle:function(t){var e=t._model,o=t._options,p=zt.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=ze(o.hoverBackgroundColor,p(o.backgroundColor)),e.borderColor=ze(o.hoverBorderColor,p(o.borderColor)),e.borderWidth=ze(o.hoverBorderWidth,o.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,o=0;o0&&de(r[t-1]._model,c)&&(o.controlPointPreviousX=i(o.controlPointPreviousX,c.left,c.right),o.controlPointPreviousY=i(o.controlPointPreviousY,c.top,c.bottom)),t0&&(n=t.getDatasetMeta(n[0]._datasetIndex).data),n},"x-axis":function(t,e){return Ne(t,e,{intersect:!1})},point:function(t,e){return Le(t,ye(e,t))},nearest:function(t,e,o){var p=ye(e,t);o.axis=o.axis||"xy";var b=_e(o.axis);return Xe(t,p,o.intersect,b)},x:function(t,e,o){var p=ye(e,t),b=[],n=!1;return Be(t,(function(t){t.inXRange(p.x)&&b.push(t),t.inRange(p.x,p.y)&&(n=!0)})),o.intersect&&!n&&(b=[]),b},y:function(t,e,o){var p=ye(e,t),b=[],n=!1;return Be(t,(function(t){t.inYRange(p.y)&&b.push(t),t.inRange(p.x,p.y)&&(n=!0)})),o.intersect&&!n&&(b=[]),b}}},xe=zt.extend;function Te(t,e){return zt.where(t,(function(t){return t.pos===e}))}function Ce(t,e){return t.sort((function(t,o){var p=e?o:t,b=e?t:o;return p.weight===b.weight?p.index-b.index:p.weight-b.weight}))}function Se(t){var e,o,p,b=[];for(e=0,o=(t||[]).length;e div {\r\n\tposition: absolute;\r\n\twidth: 1000000px;\r\n\theight: 1000000px;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n\r\n.chartjs-size-monitor-shrink > div {\r\n\tposition: absolute;\r\n\twidth: 200%;\r\n\theight: 200%;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n",Ye=o(Object.freeze({__proto__:null,default:$e})),Ge="$chartjs",Je="chartjs-",Ke=Je+"size-monitor",Qe=Je+"render-monitor",Ze=Je+"render-animation",to=["animationstart","webkitAnimationStart"],eo={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function oo(t,e){var o=zt.getStyle(t,e),p=o&&o.match(/^(\d+)(\.\d+)?px$/);return p?Number(p[1]):void 0}function po(t,e){var o=t.style,p=t.getAttribute("height"),b=t.getAttribute("width");if(t[Ge]={initial:{height:p,width:b,style:{display:o.display,height:o.height,width:o.width}}},o.display=o.display||"block",null===b||""===b){var n=oo(t,"width");void 0!==n&&(t.width=n)}if(null===p||""===p)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var M=oo(t,"height");void 0!==n&&(t.height=M)}return t}var bo=function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}(),no=!!bo&&{passive:!0};function Mo(t,e,o){t.addEventListener(e,o,no)}function zo(t,e,o){t.removeEventListener(e,o,no)}function co(t,e,o,p,b){return{type:t,chart:e,native:b||null,x:void 0!==o?o:null,y:void 0!==p?p:null}}function ro(t,e){var o=eo[t.type]||t.type,p=zt.getRelativePosition(t,e);return co(o,e,p.x,p.y,t)}function io(t,e){var o=!1,p=[];return function(){p=Array.prototype.slice.call(arguments),e=e||this,o||(o=!0,zt.requestAnimFrame.call(window,(function(){o=!1,t.apply(e,p)})))}}function ao(t){var e=document.createElement("div");return e.className=t||"",e}function Oo(t){var e=1e6,o=ao(Ke),p=ao(Ke+"-expand"),b=ao(Ke+"-shrink");p.appendChild(ao()),b.appendChild(ao()),o.appendChild(p),o.appendChild(b),o._reset=function(){p.scrollLeft=e,p.scrollTop=e,b.scrollLeft=e,b.scrollTop=e};var n=function(){o._reset(),t()};return Mo(p,"scroll",n.bind(p,"expand")),Mo(b,"scroll",n.bind(b,"shrink")),o}function so(t,e){var o=t[Ge]||(t[Ge]={}),p=o.renderProxy=function(t){t.animationName===Ze&&e()};zt.each(to,(function(e){Mo(t,e,p)})),o.reflow=!!t.offsetParent,t.classList.add(Qe)}function lo(t){var e=t[Ge]||{},o=e.renderProxy;o&&(zt.each(to,(function(e){zo(t,e,o)})),delete e.renderProxy),t.classList.remove(Qe)}function Ao(t,e,o){var p=t[Ge]||(t[Ge]={}),b=p.resizer=Oo(io((function(){if(p.resizer){var b=o.options.maintainAspectRatio&&t.parentNode,n=b?b.clientWidth:0;e(co("resize",o)),b&&b.clientWidth0){var n=t[0];n.label?o=n.label:n.xLabel?o=n.xLabel:b>0&&n.index-1?t.split("\n"):t}function Xo(t){var e=t._xScale,o=t._yScale||t._scale,p=t._index,b=t._datasetIndex,n=t._chart.getDatasetMeta(b).controller,M=n._getIndexScale(),z=n._getValueScale();return{xLabel:e?e.getLabelForIndex(p,b):"",yLabel:o?o.getLabelForIndex(p,b):"",label:M?""+M.getLabelForIndex(p,b):"",value:z?""+z.getLabelForIndex(p,b):"",index:p,datasetIndex:b,x:t._model.x,y:t._model.y}}function _o(t){var e=Q.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:vo(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:vo(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:vo(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:vo(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:vo(t.titleFontStyle,e.defaultFontStyle),titleFontSize:vo(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:vo(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:vo(t.footerFontStyle,e.defaultFontStyle),footerFontSize:vo(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function No(t,e){var o=t._chart.ctx,p=2*e.yPadding,b=0,n=e.body,M=n.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);M+=e.beforeBody.length+e.afterBody.length;var z=e.title.length,c=e.footer.length,r=e.titleFontSize,i=e.bodyFontSize,a=e.footerFontSize;p+=z*r,p+=z?(z-1)*e.titleSpacing:0,p+=z?e.titleMarginBottom:0,p+=M*i,p+=M?(M-1)*e.bodySpacing:0,p+=c?e.footerMarginTop:0,p+=c*a,p+=c?(c-1)*e.footerSpacing:0;var O=0,s=function(t){b=Math.max(b,o.measureText(t).width+O)};return o.font=zt.fontString(r,e._titleFontStyle,e._titleFontFamily),zt.each(e.title,s),o.font=zt.fontString(i,e._bodyFontStyle,e._bodyFontFamily),zt.each(e.beforeBody.concat(e.afterBody),s),O=e.displayColors?i+2:0,zt.each(n,(function(t){zt.each(t.before,s),zt.each(t.lines,s),zt.each(t.after,s)})),O=0,o.font=zt.fontString(a,e._footerFontStyle,e._footerFontFamily),zt.each(e.footer,s),{width:b+=2*e.xPadding,height:p}}function wo(t,e){var o,p,b,n,M,z=t._model,c=t._chart,r=t._chart.chartArea,i="center",a="center";z.yc.height-e.height&&(a="bottom");var O=(r.left+r.right)/2,s=(r.top+r.bottom)/2;"center"===a?(o=function(t){return t<=O},p=function(t){return t>O}):(o=function(t){return t<=e.width/2},p=function(t){return t>=c.width-e.width/2}),b=function(t){return t+e.width+z.caretSize+z.caretPadding>c.width},n=function(t){return t-e.width-z.caretSize-z.caretPadding<0},M=function(t){return t<=s?"top":"bottom"},o(z.x)?(i="left",b(z.x)&&(i="center",a=M(z.y))):p(z.x)&&(i="right",n(z.x)&&(i="center",a=M(z.y)));var l=t._options;return{xAlign:l.xAlign?l.xAlign:i,yAlign:l.yAlign?l.yAlign:a}}function xo(t,e,o,p){var b=t.x,n=t.y,M=t.caretSize,z=t.caretPadding,c=t.cornerRadius,r=o.xAlign,i=o.yAlign,a=M+z,O=c+z;return"right"===r?b-=e.width:"center"===r&&((b-=e.width/2)+e.width>p.width&&(b=p.width-e.width),b<0&&(b=0)),"top"===i?n+=a:n-="bottom"===i?e.height+a:e.height/2,"center"===i?"left"===r?b+=a:"right"===r&&(b-=a):"left"===r?b-=O:"right"===r&&(b+=O),{x:b,y:n}}function To(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Co(t){return Bo([],Lo(t))}var So=dt.extend({initialize:function(){this._model=_o(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options.callbacks,o=e.beforeTitle.apply(t,arguments),p=e.title.apply(t,arguments),b=e.afterTitle.apply(t,arguments),n=[];return n=Bo(n,Lo(o)),n=Bo(n,Lo(p)),n=Bo(n,Lo(b))},getBeforeBody:function(){return Co(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var o=this,p=o._options.callbacks,b=[];return zt.each(t,(function(t){var n={before:[],lines:[],after:[]};Bo(n.before,Lo(p.beforeLabel.call(o,t,e))),Bo(n.lines,p.label.call(o,t,e)),Bo(n.after,Lo(p.afterLabel.call(o,t,e))),b.push(n)})),b},getAfterBody:function(){return Co(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,o=e.beforeFooter.apply(t,arguments),p=e.footer.apply(t,arguments),b=e.afterFooter.apply(t,arguments),n=[];return n=Bo(n,Lo(o)),n=Bo(n,Lo(p)),n=Bo(n,Lo(b))},update:function(t){var e,o,p=this,b=p._options,n=p._model,M=p._model=_o(b),z=p._active,c=p._data,r={xAlign:n.xAlign,yAlign:n.yAlign},i={x:n.x,y:n.y},a={width:n.width,height:n.height},O={x:n.caretX,y:n.caretY};if(z.length){M.opacity=1;var s=[],l=[];O=yo[b.position].call(p,z,p._eventPosition);var d=[];for(e=0,o=z.length;e0&&o.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var o={width:e.width,height:e.height},p={x:e.x,y:e.y},b=Math.abs(e.opacity<.001)?0:e.opacity,n=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&n&&(t.save(),t.globalAlpha=b,this.drawBackground(p,e,t,o),p.y+=e.yPadding,zt.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(p,e,t),this.drawBody(p,e,t),this.drawFooter(p,e,t),zt.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e=this,o=e._options,p=!1;return e._lastActive=e._lastActive||[],"mouseout"===t.type?e._active=[]:(e._active=e._chart.getElementsAtEventForMode(t,o.mode,o),o.reverse&&e._active.reverse()),(p=!zt.arrayEquals(e._active,e._lastActive))&&(e._lastActive=e._active,(o.enabled||o.custom)&&(e._eventPosition={x:t.x,y:t.y},e.update(!0),e.pivot())),p}}),ko=yo,Eo=So;Eo.positioners=ko;var Do=zt.valueOrDefault;function Po(){return zt.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,o,p){if("xAxes"===t||"yAxes"===t){var b,n,M,z=o[t].length;for(e[t]||(e[t]=[]),b=0;b=e[t].length&&e[t].push({}),!e[t][b].type||M.type&&M.type!==e[t][b].type?zt.merge(e[t][b],[go.getScaleDefaults(n),M]):zt.merge(e[t][b],M)}else zt._merger(t,e,o,p)}})}function jo(){return zt.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,o,p){var b=e[t]||Object.create(null),n=o[t];"scales"===t?e[t]=Po(b,n):"scale"===t?e[t]=zt.merge(b,[go.getScaleDefaults(n.type),n]):zt._merger(t,e,o,p)}})}function Io(t){var e=(t=t||Object.create(null)).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=jo(Q.global,Q[t.type],t.options||{}),t}function Fo(t){var e=t.options;zt.each(t.scales,(function(e){Ue.removeBox(t,e)})),e=jo(Q.global,Q[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ho(t,e,o){var p,b=function(t){return t.id===p};do{p=e+o++}while(zt.findIndex(t,b)>=0);return p}function Uo(t){return"top"===t||"bottom"===t}function Vo(t,e){return function(o,p){return o[t]===p[t]?o[e]-p[e]:o[t]-p[t]}}Q._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var $o=function(t,e){return this.construct(t,e),this};zt.extend($o.prototype,{construct:function(t,e){var o=this;e=Io(e);var p=Wo.acquireContext(t,e),b=p&&p.canvas,n=b&&b.height,M=b&&b.width;o.id=zt.uid(),o.ctx=p,o.canvas=b,o.config=e,o.width=M,o.height=n,o.aspectRatio=n?M/n:null,o.options=e.options,o._bufferedRender=!1,o._layers=[],o.chart=o,o.controller=o,$o.instances[o.id]=o,Object.defineProperty(o,"data",{get:function(){return o.config.data},set:function(t){o.config.data=t}}),p&&b&&(o.initialize(),o.update())},initialize:function(){var t=this;return mo.notify(t,"beforeInit"),zt.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),mo.notify(t,"afterInit"),t},clear:function(){return zt.canvas.clear(this),this},stop:function(){return ft.cancelAnimation(this),this},resize:function(t){var e=this,o=e.options,p=e.canvas,b=o.maintainAspectRatio&&e.aspectRatio||null,n=Math.max(0,Math.floor(zt.getMaximumWidth(p))),M=Math.max(0,Math.floor(b?n/b:zt.getMaximumHeight(p)));if((e.width!==n||e.height!==M)&&(p.width=e.width=n,p.height=e.height=M,p.style.width=n+"px",p.style.height=M+"px",zt.retinaScale(e,o.devicePixelRatio),!t)){var z={width:n,height:M};mo.notify(e,"resize",[z]),o.onResize&&o.onResize(e,z),e.stop(),e.update({duration:o.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},o=t.scale;zt.each(e.xAxes,(function(t,o){t.id||(t.id=Ho(e.xAxes,"x-axis-",o))})),zt.each(e.yAxes,(function(t,o){t.id||(t.id=Ho(e.yAxes,"y-axis-",o))})),o&&(o.id=o.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,o=t.scales||{},p=[],b=Object.keys(o).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(p=p.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&p.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),zt.each(p,(function(e){var p=e.options,n=p.id,M=Do(p.type,e.dtype);Uo(p.position)!==Uo(e.dposition)&&(p.position=e.dposition),b[n]=!0;var z=null;if(n in o&&o[n].type===M)(z=o[n]).options=p,z.ctx=t.ctx,z.chart=t;else{var c=go.getScaleConstructor(M);if(!c)return;z=new c({id:n,type:M,options:p,ctx:t.ctx,chart:t}),o[z.id]=z}z.mergeTicksOptions(),e.isDefault&&(t.scale=z)})),zt.each(b,(function(t,e){t||delete o[e]})),t.scales=o,go.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,o=this,p=[],b=o.data.datasets;for(t=0,e=b.length;t=0;--o)p.drawDataset(e[o],t);mo.notify(p,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var o=this,p={meta:t,index:t.index,easingValue:e};!1!==mo.notify(o,"beforeDatasetDraw",[p])&&(t.controller.draw(e),mo.notify(o,"afterDatasetDraw",[p]))},_drawTooltip:function(t){var e=this,o=e.tooltip,p={tooltip:o,easingValue:t};!1!==mo.notify(e,"beforeTooltipDraw",[p])&&(o.draw(),mo.notify(e,"afterTooltipDraw",[p]))},getElementAtEvent:function(t){return we.modes.single(this,t)},getElementsAtEvent:function(t){return we.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return we.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,o){var p=we.modes[e];return"function"==typeof p?p(this,t,o):[]},getDatasetAtEvent:function(t){return we.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this,o=e.data.datasets[t];o._meta||(o._meta={});var p=o._meta[e.id];return p||(p=o._meta[e.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:o.order||0,index:t}),p},getVisibleDatasetCount:function(){for(var t=0,e=0,o=this.data.datasets.length;e=0;p--){var b=t[p];if(e(b))return b}},zt.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},zt.almostEquals=function(t,e,o){return Math.abs(t-e)=t},zt.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},zt.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},zt.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},zt.toRadians=function(t){return t*(Math.PI/180)},zt.toDegrees=function(t){return t*(180/Math.PI)},zt._decimalPlaces=function(t){if(zt.isFinite(t)){for(var e=1,o=0;Math.round(t*e)/e!==t;)e*=10,o++;return o}},zt.getAngleFromPoint=function(t,e){var o=e.x-t.x,p=e.y-t.y,b=Math.sqrt(o*o+p*p),n=Math.atan2(p,o);return n<-.5*Math.PI&&(n+=2*Math.PI),{angle:n,distance:b}},zt.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},zt.aliasPixel=function(t){return t%2==0?0:.5},zt._alignPixel=function(t,e,o){var p=t.currentDevicePixelRatio,b=o/2;return Math.round((e-b)*p)/p+b},zt.splineCurve=function(t,e,o,p){var b=t.skip?e:t,n=e,M=o.skip?e:o,z=Math.sqrt(Math.pow(n.x-b.x,2)+Math.pow(n.y-b.y,2)),c=Math.sqrt(Math.pow(M.x-n.x,2)+Math.pow(M.y-n.y,2)),r=z/(z+c),i=c/(z+c),a=p*(r=isNaN(r)?0:r),O=p*(i=isNaN(i)?0:i);return{previous:{x:n.x-a*(M.x-b.x),y:n.y-a*(M.y-b.y)},next:{x:n.x+O*(M.x-b.x),y:n.y+O*(M.y-b.y)}}},zt.EPSILON=Number.EPSILON||1e-14,zt.splineCurveMonotone=function(t){var e,o,p,b,n,M,z,c,r,i=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),a=i.length;for(e=0;e0?i[e-1]:null,(b=e0?i[e-1]:null,b=e=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},zt.previousItem=function(t,e,o){return o?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},zt.niceNum=function(t,e){var o=Math.floor(zt.log10(t)),p=t/Math.pow(10,o);return(e?p<1.5?1:p<3?2:p<7?5:10:p<=1?1:p<=2?2:p<=5?5:10)*Math.pow(10,o)},zt.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},zt.getRelativePosition=function(t,e){var o,p,b=t.originalEvent||t,n=t.target||t.srcElement,M=n.getBoundingClientRect(),z=b.touches;z&&z.length>0?(o=z[0].clientX,p=z[0].clientY):(o=b.clientX,p=b.clientY);var c=parseFloat(zt.getStyle(n,"padding-left")),r=parseFloat(zt.getStyle(n,"padding-top")),i=parseFloat(zt.getStyle(n,"padding-right")),a=parseFloat(zt.getStyle(n,"padding-bottom")),O=M.right-M.left-c-i,s=M.bottom-M.top-r-a;return{x:o=Math.round((o-M.left-c)/O*n.width/e.currentDevicePixelRatio),y:p=Math.round((p-M.top-r)/s*n.height/e.currentDevicePixelRatio)}},zt.getConstraintWidth=function(t){return o(t,"max-width","clientWidth")},zt.getConstraintHeight=function(t){return o(t,"max-height","clientHeight")},zt._calculatePadding=function(t,e,o){return(e=zt.getStyle(t,e)).indexOf("%")>-1?o*parseInt(e,10)/100:parseInt(e,10)},zt._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},zt.getMaximumWidth=function(t){var e=zt._getParentNode(t);if(!e)return t.clientWidth;var o=e.clientWidth,p=o-zt._calculatePadding(e,"padding-left",o)-zt._calculatePadding(e,"padding-right",o),b=zt.getConstraintWidth(t);return isNaN(b)?p:Math.min(p,b)},zt.getMaximumHeight=function(t){var e=zt._getParentNode(t);if(!e)return t.clientHeight;var o=e.clientHeight,p=o-zt._calculatePadding(e,"padding-top",o)-zt._calculatePadding(e,"padding-bottom",o),b=zt.getConstraintHeight(t);return isNaN(b)?p:Math.min(p,b)},zt.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},zt.retinaScale=function(t,e){var o=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==o){var p=t.canvas,b=t.height,n=t.width;p.height=b*o,p.width=n*o,t.ctx.scale(o,o),p.style.height||p.style.width||(p.style.height=b+"px",p.style.width=n+"px")}},zt.fontString=function(t,e,o){return e+" "+t+"px "+o},zt.longestText=function(t,e,o,p){var b=(p=p||{}).data=p.data||{},n=p.garbageCollect=p.garbageCollect||[];p.font!==e&&(b=p.data={},n=p.garbageCollect=[],p.font=e),t.font=e;var M,z,c,r,i,a=0,O=o.length;for(M=0;Mo.length){for(M=0;Mp&&(p=n),p},zt.numberOfLabelLines=function(t){var e=1;return zt.each(t,(function(t){zt.isArray(t)&&t.length>e&&(e=t.length)})),e},zt.color=S?function(t){return t instanceof CanvasGradient&&(t=Q.global.defaultColor),S(t)}:function(t){return t},zt.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:zt.color(t).saturate(.5).darken(.1).rgbString()}};function Jo(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function Ko(t){this.options=t||{}}zt.extend(Ko.prototype,{formats:Jo,parse:Jo,format:Jo,add:Jo,diff:Jo,startOf:Jo,endOf:Jo,_create:function(t){return t}}),Ko.override=function(t){zt.extend(Ko.prototype,t)};var Qo={_date:Ko},Zo={formatters:{values:function(t){return zt.isArray(t)?t:""+t},linear:function(t,e,o){var p=o.length>3?o[2]-o[1]:o[1]-o[0];Math.abs(p)>1&&t!==Math.floor(t)&&(p=t-Math.floor(t));var b=zt.log10(Math.abs(p)),n="";if(0!==t)if(Math.max(Math.abs(o[0]),Math.abs(o[o.length-1]))<1e-4){var M=zt.log10(Math.abs(t)),z=Math.floor(M)-Math.floor(b);z=Math.max(Math.min(z,20),0),n=t.toExponential(z)}else{var c=-1*Math.floor(b);c=Math.max(Math.min(c,20),0),n=t.toFixed(c)}else n="0";return n},logarithmic:function(t,e,o){var p=t/Math.pow(10,Math.floor(zt.log10(t)));return 0===t?"0":1===p||2===p||5===p||0===e||e===o.length-1?t.toExponential():""}}},tp=zt.isArray,ep=zt.isNullOrUndef,op=zt.valueOrDefault,pp=zt.valueAtIndexOrDefault;function bp(t,e){for(var o=[],p=t.length/e,b=0,n=t.length;bc+r)))return M}function Mp(t,e){zt.each(t,(function(t){var o,p=t.gc,b=p.length/2;if(b>e){for(o=0;or)return n;return Math.max(r,1)}function dp(t){var e,o,p=[];for(e=0,o=t.length;e=O||i<=1||!z.isHorizontal()?z.labelRotation=a:(e=(t=z._getLabelSizes()).widest.width,o=t.highest.height-t.highest.offset,p=Math.min(z.maxWidth,z.chart.width-e),e+6>(b=c.offset?z.maxWidth/i:p/(i-1))&&(b=p/(i-(c.offset?.5:1)),n=z.maxHeight-cp(c.gridLines)-r.padding-rp(c.scaleLabel),M=Math.sqrt(e*e+o*o),s=zt.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/b,1)),Math.asin(Math.min(n/M,1))-Math.asin(o/M))),s=Math.max(a,Math.min(O,s))),z.labelRotation=s)},afterCalculateTickRotation:function(){zt.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){zt.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},o=t.chart,p=t.options,b=p.ticks,n=p.scaleLabel,M=p.gridLines,z=t._isVisible(),c="bottom"===p.position,r=t.isHorizontal();if(r?e.width=t.maxWidth:z&&(e.width=cp(M)+rp(n)),r?z&&(e.height=cp(M)+rp(n)):e.height=t.maxHeight,b.display&&z){var i=ap(b),a=t._getLabelSizes(),O=a.first,s=a.last,l=a.widest,d=a.highest,A=.4*i.minor.lineHeight,u=b.padding;if(r){var f=0!==t.labelRotation,q=zt.toRadians(t.labelRotation),h=Math.cos(q),W=Math.sin(q),m=W*l.width+h*(d.height-(f?d.offset:0))+(f?0:A);e.height=Math.min(t.maxHeight,e.height+m+u);var g,v,R=t.getPixelForTick(0)-t.left,y=t.right-t.getPixelForTick(t.getTicks().length-1);f?(g=c?h*O.width+W*O.offset:W*(O.height-O.offset),v=c?W*(s.height-s.offset):h*s.width+W*s.offset):(g=O.width/2,v=s.width/2),t.paddingLeft=Math.max((g-R)*t.width/(t.width-R),0)+3,t.paddingRight=Math.max((v-y)*t.width/(t.width-y),0)+3}else{var B=b.mirror?0:l.width+u+A;e.width=Math.min(t.maxWidth,e.width+B),t.paddingTop=O.height/2,t.paddingBottom=s.height/2}}t.handleMargins(),r?(t.width=t._length=o.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=o.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){zt.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ep(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,o,p,b=this;for(b.ticks=t.map((function(t){return t.value})),b.beforeTickToLabelConversion(),e=b.convertTicksToLabels(t)||b.ticks,b.afterTickToLabelConversion(),o=0,p=t.length;op-1?null:e.getPixelForDecimal(t*b+(o?b/2:0))},getPixelForDecimal:function(t){var e=this;return e._reversePixels&&(t=1-t),e._startPixel+t*e._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this,e=t.min,o=t.max;return t.beginAtZero?0:e<0&&o<0?o:e>0&&o>0?e:0},_autoSkip:function(t){var e,o,p,b,n=this,M=n.options.ticks,z=n._length,c=M.maxTicksLimit||z/n._tickSize()+1,r=M.major.enabled?dp(t):[],i=r.length,a=r[0],O=r[i-1];if(i>c)return Ap(t,r,i/c),Op(t);if(p=lp(r,t,z,c),i>0){for(e=0,o=i-1;e1?(O-a)/(i-1):null,up(t,p,zt.isNullOrUndef(b)?0:a-b,a),up(t,p,O,zt.isNullOrUndef(b)?t.length:O+b),Op(t)}return up(t,p),Op(t)},_tickSize:function(){var t=this,e=t.options.ticks,o=zt.toRadians(t.labelRotation),p=Math.abs(Math.cos(o)),b=Math.abs(Math.sin(o)),n=t._getLabelSizes(),M=e.autoSkipPadding||0,z=n?n.widest.width+M:0,c=n?n.highest.height+M:0;return t.isHorizontal()?c*p>z*b?z/p:c/b:c*b=0&&(M=t),void 0!==n&&(t=o.indexOf(n))>=0&&(z=t),e.minIndex=M,e.maxIndex=z,e.min=o[M],e.max=o[z]},buildTicks:function(){var t=this,e=t._getLabels(),o=t.minIndex,p=t.maxIndex;t.ticks=0===o&&p===e.length-1?e:e.slice(o,p+1)},getLabelForIndex:function(t,e){var o=this,p=o.chart;return p.getDatasetMeta(e).controller._getValueScaleId()===o.id?o.getRightValue(p.data.datasets[e].data[t]):o._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,o=t.ticks;qp.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),o&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(o.length-(e?0:1),1))},getPixelForValue:function(t,e,o){var p,b,n,M=this;return hp(e)||hp(o)||(t=M.chart.data.datasets[o].data[e]),hp(t)||(p=M.isHorizontal()?t.x:t.y),(void 0!==p||void 0!==t&&isNaN(e))&&(b=M._getLabels(),t=zt.valueOrDefault(p,t),e=-1!==(n=b.indexOf(t))?n:e,isNaN(e)&&(e=t)),M.getPixelForDecimal((e-M._startValue)/M._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=this,o=Math.round(e._startValue+e.getDecimalForPixel(t)*e._valueRange);return Math.min(Math.max(o,0),e.ticks.length-1)},getBasePixel:function(){return this.bottom}}),gp=Wp;mp._defaults=gp;var vp=zt.noop,Rp=zt.isNullOrUndef;function yp(t,e){var o,p,b,n,M=[],z=1e-14,c=t.stepSize,r=c||1,i=t.maxTicks-1,a=t.min,O=t.max,s=t.precision,l=e.min,d=e.max,A=zt.niceNum((d-l)/i/r)*r;if(Ai&&(A=zt.niceNum(n*A/i/r)*r),c||Rp(s)?o=Math.pow(10,zt._decimalPlaces(A)):(o=Math.pow(10,s),A=Math.ceil(A*o)/o),p=Math.floor(l/A)*A,b=Math.ceil(d/A)*A,c&&(!Rp(a)&&zt.almostWhole(a/A,A/1e3)&&(p=a),!Rp(O)&&zt.almostWhole(O/A,A/1e3)&&(b=O)),n=(b-p)/A,n=zt.almostEquals(n,Math.round(n),A/1e3)?Math.round(n):Math.ceil(n),p=Math.round(p*o)/o,b=Math.round(b*o)/o,M.push(Rp(a)?p:a);for(var u=1;u0&&p>0&&(t.min=0)}var b=void 0!==e.min||void 0!==e.suggestedMin,n=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),b!==n&&t.min>=t.max&&(b?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this,o=e.options.ticks,p=o.stepSize,b=o.maxTicksLimit;return p?t=Math.ceil(e.max/p)-Math.floor(e.min/p)+1:(t=e._computeTickLimit(),b=b||11),b&&(t=Math.min(b,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:vp,buildTicks:function(){var t=this,e=t.options.ticks,o=t.getTickLimit(),p={maxTicks:o=Math.max(2,o),min:e.min,max:e.max,precision:e.precision,stepSize:zt.valueOrDefault(e.fixedStepSize,e.stepSize)},b=t.ticks=yp(p,t);t.handleDirectionalChanges(),t.max=zt.max(b),t.min=zt.min(b),e.reverse?(b.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),qp.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,o=e.getTicks(),p=e.min,b=e.max;qp.prototype._configure.call(e),e.options.offset&&o.length&&(p-=t=(b-p)/Math.max(o.length-1,1)/2,b+=t),e._startValue=p,e._endValue=b,e._valueRange=b-p}}),Lp={position:"left",ticks:{callback:Zo.formatters.linear}},Xp=0,_p=1;function Np(t,e,o){var p=[o.type,void 0===e&&void 0===o.stack?o.index:"",o.stack].join(".");return void 0===t[p]&&(t[p]={pos:[],neg:[]}),t[p]}function wp(t,e,o,p){var b,n,M=t.options,z=Np(e,M.stacked,o),c=z.pos,r=z.neg,i=p.length;for(b=0;be.length-1?null:this.getPixelForValue(e[t])}}),Cp=Lp;Tp._defaults=Cp;var Sp=zt.valueOrDefault,kp=zt.math.log10;function Ep(t,e){var o,p,b=[],n=Sp(t.min,Math.pow(10,Math.floor(kp(e.min)))),M=Math.floor(kp(e.max)),z=Math.ceil(e.max/Math.pow(10,M));0===n?(o=Math.floor(kp(e.minNotZero)),p=Math.floor(e.minNotZero/Math.pow(10,o)),b.push(n),n=p*Math.pow(10,o)):(o=Math.floor(kp(n)),p=Math.floor(n/Math.pow(10,o)));var c=o<0?Math.pow(10,Math.abs(o)):1;do{b.push(n),10==++p&&(p=1,c=++o>=0?1:c),n=Math.round(p*Math.pow(10,o)*c)/c}while(o=0?t:e}var jp=qp.extend({determineDataLimits:function(){var t,e,o,p,b,n,M=this,z=M.options,c=M.chart,r=c.data.datasets,i=M.isHorizontal();function a(t){return i?t.xAxisID===M.id:t.yAxisID===M.id}M.min=Number.POSITIVE_INFINITY,M.max=Number.NEGATIVE_INFINITY,M.minNotZero=Number.POSITIVE_INFINITY;var O=z.stacked;if(void 0===O)for(t=0;t0){var e=zt.min(t),o=zt.max(t);M.min=Math.min(M.min,e),M.max=Math.max(M.max,o)}}))}else for(t=0;t0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(kp(t.max))):t.minNotZero=o)},buildTicks:function(){var t=this,e=t.options.ticks,o=!t.isHorizontal(),p={min:Pp(e.min),max:Pp(e.max)},b=t.ticks=Ep(p,t);t.max=zt.max(b),t.min=zt.min(b),e.reverse?(o=!o,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),o&&b.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),qp.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(kp(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,o=0;qp.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),o=Sp(t.options.ticks.fontSize,Q.global.defaultFontSize)/t._length),t._startValue=kp(e),t._valueOffset=o,t._valueRange=(kp(t.max)-kp(e))/(1-o)},getPixelForValue:function(t){var e=this,o=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(o=(kp(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(o)},getValueForPixel:function(t){var e=this,o=e.getDecimalForPixel(t);return 0===o&&0===e.min?0:Math.pow(10,e._startValue+(o-e._valueOffset)*e._valueRange)}}),Ip=Dp;jp._defaults=Ip;var Fp=zt.valueOrDefault,Hp=zt.valueAtIndexOrDefault,Up=zt.options.resolve,Vp={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:Zo.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function $p(t){var e=t.ticks;return e.display&&t.display?Fp(e.fontSize,Q.global.defaultFontSize)+2*e.backdropPaddingY:0}function Yp(t,e,o){return zt.isArray(o)?{w:zt.longestText(t,t.font,o),h:o.length*e}:{w:t.measureText(o).width,h:e}}function Gp(t,e,o,p,b){return t===p||t===b?{start:e-o/2,end:e+o/2}:tb?{start:e-o,end:e}:{start:e,end:e+o}}function Jp(t){var e,o,p,b=zt.options._parseFont(t.options.pointLabels),n={l:0,r:t.width,t:0,b:t.height-t.paddingTop},M={};t.ctx.font=b.string,t._pointLabelSizes=[];var z=t.chart.data.labels.length;for(e=0;en.r&&(n.r=i.end,M.r=c),a.startn.b&&(n.b=a.end,M.b=c)}t.setReductions(t.drawingArea,n,M)}function Kp(t){return 0===t||180===t?"center":t<180?"left":"right"}function Qp(t,e,o,p){var b,n,M=o.y+p/2;if(zt.isArray(e))for(b=0,n=e.length;b270||t<90)&&(o.y-=e.h)}function tb(t){var e=t.ctx,o=t.options,p=o.pointLabels,b=$p(o),n=t.getDistanceFromCenterForValue(o.ticks.reverse?t.min:t.max),M=zt.options._parseFont(p);e.save(),e.font=M.string,e.textBaseline="middle";for(var z=t.chart.data.labels.length-1;z>=0;z--){var c=0===z?b/2:0,r=t.getPointPosition(z,n+c+5),i=Hp(p.fontColor,z,Q.global.defaultFontColor);e.fillStyle=i;var a=t.getIndexAngle(z),O=zt.toDegrees(a);e.textAlign=Kp(O),Zp(O,t._pointLabelSizes[z],r),Qp(e,t.pointLabels[z],r,M.lineHeight)}e.restore()}function eb(t,e,o,p){var b,n=t.ctx,M=e.circular,z=t.chart.data.labels.length,c=Hp(e.color,p-1),r=Hp(e.lineWidth,p-1);if((M||z)&&c&&r){if(n.save(),n.strokeStyle=c,n.lineWidth=r,n.setLineDash&&(n.setLineDash(e.borderDash||[]),n.lineDashOffset=e.borderDashOffset||0),n.beginPath(),M)n.arc(t.xCenter,t.yCenter,o,0,2*Math.PI);else{b=t.getPointPosition(0,o),n.moveTo(b.x,b.y);for(var i=1;i0&&p>0?o:0)},_drawGrid:function(){var t,e,o,p=this,b=p.ctx,n=p.options,M=n.gridLines,z=n.angleLines,c=Fp(z.lineWidth,M.lineWidth),r=Fp(z.color,M.color);if(n.pointLabels.display&&tb(p),M.display&&zt.each(p.ticks,(function(t,o){0!==o&&(e=p.getDistanceFromCenterForValue(p.ticksAsNumbers[o]),eb(p,M,e,o))})),z.display&&c&&r){for(b.save(),b.lineWidth=c,b.strokeStyle=r,b.setLineDash&&(b.setLineDash(Up([z.borderDash,M.borderDash,[]])),b.lineDashOffset=Up([z.borderDashOffset,M.borderDashOffset,0])),t=p.chart.data.labels.length-1;t>=0;t--)e=p.getDistanceFromCenterForValue(n.ticks.reverse?p.min:p.max),o=p.getPointPosition(t,e),b.beginPath(),b.moveTo(p.xCenter,p.yCenter),b.lineTo(o.x,o.y),b.stroke();b.restore()}},_drawLabels:function(){var t=this,e=t.ctx,o=t.options.ticks;if(o.display){var p,b,n=t.getIndexAngle(0),M=zt.options._parseFont(o),z=Fp(o.fontColor,Q.global.defaultFontColor);e.save(),e.font=M.string,e.translate(t.xCenter,t.yCenter),e.rotate(n),e.textAlign="center",e.textBaseline="middle",zt.each(t.ticks,(function(n,c){(0!==c||o.reverse)&&(p=t.getDistanceFromCenterForValue(t.ticksAsNumbers[c]),o.showLabelBackdrop&&(b=e.measureText(n).width,e.fillStyle=o.backdropColor,e.fillRect(-b/2-o.backdropPaddingX,-p-M.size/2-o.backdropPaddingY,b+2*o.backdropPaddingX,M.size+2*o.backdropPaddingY)),e.fillStyle=z,e.fillText(n,0,-p))})),e.restore()}},_drawTitle:zt.noop}),bb=Vp;pb._defaults=bb;var nb=zt._deprecated,Mb=zt.options.resolve,zb=zt.valueOrDefault,cb=Number.MIN_SAFE_INTEGER||-9007199254740991,rb=Number.MAX_SAFE_INTEGER||9007199254740991,ib={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ab=Object.keys(ib);function Ob(t,e){return t-e}function sb(t){var e,o,p,b={},n=[];for(e=0,o=t.length;ee&&z=0&&M<=z;){if(b=t[(p=M+z>>1)-1]||null,n=t[p],!b)return{lo:null,hi:n};if(n[e]o))return{lo:b,hi:n};z=p-1}}return{lo:n,hi:null}}function fb(t,e,o,p){var b=ub(t,e,o),n=b.lo?b.hi?b.lo:t[t.length-2]:t[0],M=b.lo?b.hi?b.hi:t[t.length-1]:t[1],z=M[e]-n[e],c=z?(o-n[e])/z:0,r=(M[p]-n[p])*c;return n[p]+r}function qb(t,e){var o=t._adapter,p=t.options.time,b=p.parser,n=b||p.format,M=e;return"function"==typeof b&&(M=b(M)),zt.isFinite(M)||(M="string"==typeof n?o.parse(M,n):o.parse(M)),null!==M?+M:(b||"function"!=typeof n||(M=n(e),zt.isFinite(M)||(M=o.parse(M))),M)}function hb(t,e){if(zt.isNullOrUndef(e))return null;var o=t.options.time,p=qb(t,t.getRightValue(e));return null===p||o.round&&(p=+t._adapter.startOf(p,o.round)),p}function Wb(t,e,o,p){var b,n,M,z=ab.length;for(b=ab.indexOf(t);b=ab.indexOf(o);n--)if(M=ab[n],ib[M].common&&t._adapter.diff(b,p,M)>=e-1)return M;return ab[o?ab.indexOf(o):0]}function gb(t){for(var e=ab.indexOf(t)+1,o=ab.length;e1e5*r)throw e+" and "+o+" are too far apart with stepSize of "+r+" "+c;for(b=a;b=0&&(e[n].major=!0);return e}function Bb(t,e,o){var p,b,n=[],M={},z=e.length;for(p=0;p1?sb(l).sort(Ob):l.sort(Ob),O=Math.min(O,l[0]),s=Math.max(s,l[l.length-1])),O=hb(z,lb(i))||O,s=hb(z,db(i))||s,O=O===rb?+r.startOf(Date.now(),a):O,s=s===cb?+r.endOf(Date.now(),a)+1:s,z.min=Math.min(O,s),z.max=Math.max(O+1,s),z._table=[],z._timestamps={data:l,datasets:d,labels:A}},buildTicks:function(){var t,e,o,p=this,b=p.min,n=p.max,M=p.options,z=M.ticks,c=M.time,r=p._timestamps,i=[],a=p.getLabelCapacity(b),O=z.source,s=M.distribution;for(r="data"===O||"auto"===O&&"series"===s?r.data:"labels"===O?r.labels:vb(p,b,n,a),"ticks"===M.bounds&&r.length&&(b=r[0],n=r[r.length-1]),b=hb(p,lb(M))||b,n=hb(p,db(M))||n,t=0,e=r.length;t=b&&o<=n&&i.push(o);return p.min=b,p.max=n,p._unit=c.unit||(z.autoSkip?Wb(c.minUnit,p.min,p.max,a):mb(p,i.length,c.minUnit,p.min,p.max)),p._majorUnit=z.major.enabled&&"year"!==p._unit?gb(p._unit):void 0,p._table=Ab(p._timestamps.data,b,n,s),p._offsets=Rb(p._table,i,b,n,M),z.reverse&&i.reverse(),Bb(p,i,p._majorUnit)},getLabelForIndex:function(t,e){var o=this,p=o._adapter,b=o.chart.data,n=o.options.time,M=b.labels&&t=0&&t0?z:1}}),_b=Lb;Xb._defaults=_b;var Nb={category:mp,linear:Tp,logarithmic:jp,radialLinear:pb,time:Xb},wb={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};Qo._date.override("function"==typeof t?{_id:"moment",formats:function(){return wb},parse:function(e,o){return"string"==typeof e&&"string"==typeof o?e=t(e,o):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,o){return t(e).format(o)},add:function(e,o,p){return t(e).add(o,p).valueOf()},diff:function(e,o,p){return t(e).diff(t(o),p)},startOf:function(e,o,p){return e=t(e),"isoWeek"===o?e.isoWeekday(p).valueOf():e.startOf(o).valueOf()},endOf:function(e,o){return t(e).endOf(o).valueOf()},_create:function(e){return t(e)}}:{}),Q._set("global",{plugins:{filler:{propagate:!0}}});var xb={dataset:function(t){var e=t.fill,o=t.chart,p=o.getDatasetMeta(e),b=p&&o.isDatasetVisible(e)&&p.dataset._children||[],n=b.length||0;return n?function(t,e){return e=o)&&p;switch(n){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return n;default:return!1}}function Cb(t){var e,o=t.el._model||{},p=t.el._scale||{},b=t.fill,n=null;if(isFinite(b))return null;if("start"===b?n=void 0===o.scaleBottom?p.bottom:o.scaleBottom:"end"===b?n=void 0===o.scaleTop?p.top:o.scaleTop:void 0!==o.scaleZero?n=o.scaleZero:p.getBasePixel&&(n=p.getBasePixel()),null!=n){if(void 0!==n.x&&void 0!==n.y)return n;if(zt.isFinite(n))return{x:(e=p.isHorizontal())?n:null,y:e?null:n}}return null}function Sb(t){var e,o,p,b,n,M=t.el._scale,z=M.options,c=M.chart.data.labels.length,r=t.fill,i=[];if(!c)return null;for(e=z.ticks.reverse?M.max:M.min,o=z.ticks.reverse?M.min:M.max,p=M.getPointPositionForValue(0,e),b=0;b0;--n)zt.canvas.lineTo(t,o[n],o[n-1],!0);else for(M=o[0].cx,z=o[0].cy,c=Math.sqrt(Math.pow(o[0].x-M,2)+Math.pow(o[0].y-z,2)),n=b-1;n>0;--n)t.arc(M,z,c,o[n].angle,o[n-1].angle,!0)}}function Ib(t,e,o,p,b,n){var M,z,c,r,i,a,O,s,l=e.length,d=p.spanGaps,A=[],u=[],f=0,q=0;for(t.beginPath(),M=0,z=l;M=0;--o)(e=c[o].$filler)&&e.visible&&(b=(p=e.el)._view,n=p._children||[],M=e.mapper,z=b.backgroundColor||Q.global.defaultColor,M&&z&&n.length&&(zt.canvas.clipArea(r,t.chartArea),Ib(r,n,M,b,z,p._loop),zt.canvas.unclipArea(r)))}},Hb=zt.rtl.getRtlAdapter,Ub=zt.noop,Vb=zt.valueOrDefault;function $b(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}Q._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var o=e.datasetIndex,p=this.chart,b=p.getDatasetMeta(o);b.hidden=null===b.hidden?!p.data.datasets[o].hidden:null,p.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,o=t.options.legend||{},p=o.labels&&o.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(o){var b=o.controller.getStyle(p?0:void 0);return{text:e[o.index].label,fillStyle:b.backgroundColor,hidden:!t.isDatasetVisible(o.index),lineCap:b.borderCapStyle,lineDash:b.borderDash,lineDashOffset:b.borderDashOffset,lineJoin:b.borderJoinStyle,lineWidth:b.borderWidth,strokeStyle:b.borderColor,pointStyle:b.pointStyle,rotation:b.rotation,datasetIndex:o.index}}),this)}}},legendCallback:function(t){var e,o,p,b=document.createElement("ul"),n=t.data.datasets;for(b.setAttribute("class",t.id+"-legend"),e=0,o=n.length;ec.width)&&(a+=M+o.padding,i[i.length-(e>0?0:1)]=0),z[e]={left:0,top:0,width:p,height:M},i[i.length-1]+=p+o.padding})),c.height+=a}else{var O=o.padding,s=t.columnWidths=[],l=t.columnHeights=[],d=o.padding,A=0,u=0;zt.each(t.legendItems,(function(t,e){var p=$b(o,M)+M/2+b.measureText(t.text).width;e>0&&u+M+2*O>c.height&&(d+=A+o.padding,s.push(A),l.push(u),A=0,u=0),A=Math.max(A,p),u+=M+O,z[e]={left:0,top:0,width:p,height:M}})),d+=A,s.push(A),l.push(u),c.width+=d}t.width=c.width,t.height=c.height}else t.width=c.width=t.height=c.height=0},afterFit:Ub,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,o=e.labels,p=Q.global,b=p.defaultColor,n=p.elements.line,M=t.height,z=t.columnHeights,c=t.width,r=t.lineWidths;if(e.display){var i,a=Hb(e.rtl,t.left,t.minSize.width),O=t.ctx,s=Vb(o.fontColor,p.defaultFontColor),l=zt.options._parseFont(o),d=l.size;O.textAlign=a.textAlign("left"),O.textBaseline="middle",O.lineWidth=.5,O.strokeStyle=s,O.fillStyle=s,O.font=l.string;var A=$b(o,d),u=t.legendHitBoxes,f=function(t,e,p){if(!(isNaN(A)||A<=0)){O.save();var M=Vb(p.lineWidth,n.borderWidth);if(O.fillStyle=Vb(p.fillStyle,b),O.lineCap=Vb(p.lineCap,n.borderCapStyle),O.lineDashOffset=Vb(p.lineDashOffset,n.borderDashOffset),O.lineJoin=Vb(p.lineJoin,n.borderJoinStyle),O.lineWidth=M,O.strokeStyle=Vb(p.strokeStyle,b),O.setLineDash&&O.setLineDash(Vb(p.lineDash,n.borderDash)),o&&o.usePointStyle){var z=A*Math.SQRT2/2,c=a.xPlus(t,A/2),r=e+d/2;zt.canvas.drawPoint(O,p.pointStyle,z,c,r,p.rotation)}else O.fillRect(a.leftForLtr(t,A),e,A,d),0!==M&&O.strokeRect(a.leftForLtr(t,A),e,A,d);O.restore()}},q=function(t,e,o,p){var b=d/2,n=a.xPlus(t,A+b),M=e+b;O.fillText(o.text,n,M),o.hidden&&(O.beginPath(),O.lineWidth=2,O.moveTo(n,M),O.lineTo(a.xPlus(n,p),M),O.stroke())},h=function(t,p){switch(e.align){case"start":return o.padding;case"end":return t-p;default:return(t-p+o.padding)/2}},W=t.isHorizontal();i=W?{x:t.left+h(c,r[0]),y:t.top+o.padding,line:0}:{x:t.left+o.padding,y:t.top+h(M,z[0]),line:0},zt.rtl.overrideTextDirection(t.ctx,e.textDirection);var m=d+o.padding;zt.each(t.legendItems,(function(e,p){var b=O.measureText(e.text).width,n=A+d/2+b,s=i.x,l=i.y;a.setWidth(t.minSize.width),W?p>0&&s+n+o.padding>t.left+t.minSize.width&&(l=i.y+=m,i.line++,s=i.x=t.left+h(c,r[i.line])):p>0&&l+m>t.top+t.minSize.height&&(s=i.x=s+t.columnWidths[i.line]+o.padding,i.line++,l=i.y=t.top+h(M,z[i.line]));var g=a.x(s);f(g,l,e),u[p].left=a.leftForLtr(g,u[p].width),u[p].top=l,q(g,l,e,b),W?i.x+=n+o.padding:i.y+=m})),zt.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var o,p,b,n=this;if(t>=n.left&&t<=n.right&&e>=n.top&&e<=n.bottom)for(b=n.legendHitBoxes,o=0;o=(p=b[o]).left&&t<=p.left+p.width&&e>=p.top&&e<=p.top+p.height)return n.legendItems[o];return null},handleEvent:function(t){var e,o=this,p=o.options,b="mouseup"===t.type?"click":t.type;if("mousemove"===b){if(!p.onHover&&!p.onLeave)return}else{if("click"!==b)return;if(!p.onClick)return}e=o._getLegendItemAt(t.x,t.y),"click"===b?e&&p.onClick&&p.onClick.call(o,t.native,e):(p.onLeave&&e!==o._hoveredItem&&(o._hoveredItem&&p.onLeave.call(o,t.native,o._hoveredItem),o._hoveredItem=e),p.onHover&&e&&p.onHover.call(o,t.native,e))}});function Gb(t,e){var o=new Yb({ctx:t.ctx,options:e,chart:t});Ue.configure(t,o,e),Ue.addBox(t,o),t.legend=o}var Jb={id:"legend",_element:Yb,beforeInit:function(t){var e=t.options.legend;e&&Gb(t,e)},beforeUpdate:function(t){var e=t.options.legend,o=t.legend;e?(zt.mergeIf(e,Q.global.legend),o?(Ue.configure(t,o,e),o.options=e):Gb(t,e)):o&&(Ue.removeBox(t,o),delete t.legend)},afterEvent:function(t,e){var o=t.legend;o&&o.handleEvent(e)}},Kb=zt.noop;Q._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Qb=dt.extend({initialize:function(t){var e=this;zt.extend(e,t),e.legendHitBoxes=[]},beforeUpdate:Kb,update:function(t,e,o){var p=this;return p.beforeUpdate(),p.maxWidth=t,p.maxHeight=e,p.margins=o,p.beforeSetDimensions(),p.setDimensions(),p.afterSetDimensions(),p.beforeBuildLabels(),p.buildLabels(),p.afterBuildLabels(),p.beforeFit(),p.fit(),p.afterFit(),p.afterUpdate(),p.minSize},afterUpdate:Kb,beforeSetDimensions:Kb,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Kb,beforeBuildLabels:Kb,buildLabels:Kb,afterBuildLabels:Kb,beforeFit:Kb,fit:function(){var t,e=this,o=e.options,p=e.minSize={},b=e.isHorizontal();o.display?(t=(zt.isArray(o.text)?o.text.length:1)*zt.options._parseFont(o).lineHeight+2*o.padding,e.width=p.width=b?e.maxWidth:t,e.height=p.height=b?t:e.maxHeight):e.width=p.width=e.height=p.height=0},afterFit:Kb,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,o=t.options;if(o.display){var p,b,n,M=zt.options._parseFont(o),z=M.lineHeight,c=z/2+o.padding,r=0,i=t.top,a=t.left,O=t.bottom,s=t.right;e.fillStyle=zt.valueOrDefault(o.fontColor,Q.global.defaultFontColor),e.font=M.string,t.isHorizontal()?(b=a+(s-a)/2,n=i+c,p=s-a):(b="left"===o.position?a+c:s-c,n=i+(O-i)/2,p=O-i,r=Math.PI*("left"===o.position?-.5:.5)),e.save(),e.translate(b,n),e.rotate(r),e.textAlign="center",e.textBaseline="middle";var l=o.text;if(zt.isArray(l))for(var d=0,A=0;A{e.read=function(t,e,o,p,b){var n,M,z=8*b-p-1,c=(1<>1,i=-7,a=o?b-1:0,O=o?-1:1,s=t[e+a];for(a+=O,n=s&(1<<-i)-1,s>>=-i,i+=z;i>0;n=256*n+t[e+a],a+=O,i-=8);for(M=n&(1<<-i)-1,n>>=-i,i+=p;i>0;M=256*M+t[e+a],a+=O,i-=8);if(0===n)n=1-r;else{if(n===c)return M?NaN:1/0*(s?-1:1);M+=Math.pow(2,p),n-=r}return(s?-1:1)*M*Math.pow(2,n-p)},e.write=function(t,e,o,p,b,n){var M,z,c,r=8*n-b-1,i=(1<>1,O=23===b?Math.pow(2,-24)-Math.pow(2,-77):0,s=p?0:n-1,l=p?1:-1,d=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(z=isNaN(e)?1:0,M=i):(M=Math.floor(Math.log(e)/Math.LN2),e*(c=Math.pow(2,-M))<1&&(M--,c*=2),(e+=M+a>=1?O/c:O*Math.pow(2,1-a))*c>=2&&(M++,c/=2),M+a>=i?(z=0,M=i):M+a>=1?(z=(e*c-1)*Math.pow(2,b),M+=a):(z=e*Math.pow(2,a-1)*Math.pow(2,b),M=0));b>=8;t[o+s]=255&z,s+=l,z/=256,b-=8);for(M=M<0;t[o+s]=255&M,s+=l,M/=256,r-=8);t[o+s-l]|=128*d}},826:t=>{var e={}.toString;t.exports=Array.isArray||function(t){return"[object Array]"==e.call(t)}},755:function(t,e){var o;!function(e,o){"use strict";"object"==typeof t.exports?t.exports=e.document?o(e,!0):function(t){if(!t.document)throw new Error("jQuery requires a window with a document");return o(t)}:o(e)}("undefined"!=typeof window?window:this,(function(p,b){"use strict";var n=[],M=Object.getPrototypeOf,z=n.slice,c=n.flat?function(t){return n.flat.call(t)}:function(t){return n.concat.apply([],t)},r=n.push,i=n.indexOf,a={},O=a.toString,s=a.hasOwnProperty,l=s.toString,d=l.call(Object),A={},u=function(t){return"function"==typeof t&&"number"!=typeof t.nodeType&&"function"!=typeof t.item},f=function(t){return null!=t&&t===t.window},q=p.document,h={type:!0,src:!0,nonce:!0,noModule:!0};function W(t,e,o){var p,b,n=(o=o||q).createElement("script");if(n.text=t,e)for(p in h)(b=e[p]||e.getAttribute&&e.getAttribute(p))&&n.setAttribute(p,b);o.head.appendChild(n).parentNode.removeChild(n)}function m(t){return null==t?t+"":"object"==typeof t||"function"==typeof t?a[O.call(t)]||"object":typeof t}var g="3.6.1",v=function(t,e){return new v.fn.init(t,e)};function R(t){var e=!!t&&"length"in t&&t.length,o=m(t);return!u(t)&&!f(t)&&("array"===o||0===e||"number"==typeof e&&e>0&&e-1 in t)}v.fn=v.prototype={jquery:g,constructor:v,length:0,toArray:function(){return z.call(this)},get:function(t){return null==t?z.call(this):t<0?this[t+this.length]:this[t]},pushStack:function(t){var e=v.merge(this.constructor(),t);return e.prevObject=this,e},each:function(t){return v.each(this,t)},map:function(t){return this.pushStack(v.map(this,(function(e,o){return t.call(e,o,e)})))},slice:function(){return this.pushStack(z.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(v.grep(this,(function(t,e){return(e+1)%2})))},odd:function(){return this.pushStack(v.grep(this,(function(t,e){return e%2})))},eq:function(t){var e=this.length,o=+t+(t<0?e:0);return this.pushStack(o>=0&&o+~]|[\\x20\\t\\r\\n\\f])[\\x20\\t\\r\\n\\f]*"),U=new RegExp(k+"|>"),V=new RegExp(P),$=new RegExp("^"+E+"$"),Y={ID:new RegExp("^#("+E+")"),CLASS:new RegExp("^\\.("+E+")"),TAG:new RegExp("^("+E+"|[*])"),ATTR:new RegExp("^"+D),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\([\\x20\\t\\r\\n\\f]*(even|odd|(([+-]|)(\\d*)n|)[\\x20\\t\\r\\n\\f]*(?:([+-]|)[\\x20\\t\\r\\n\\f]*(\\d+)|))[\\x20\\t\\r\\n\\f]*\\)|)","i"),bool:new RegExp("^(?:"+S+")$","i"),needsContext:new RegExp("^[\\x20\\t\\r\\n\\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\([\\x20\\t\\r\\n\\f]*((?:-\\d)?\\d*)[\\x20\\t\\r\\n\\f]*\\)|)(?=[^-]|$)","i")},G=/HTML$/i,J=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,tt=/[+~]/,et=new RegExp("\\\\[\\da-fA-F]{1,6}[\\x20\\t\\r\\n\\f]?|\\\\([^\\r\\n\\f])","g"),ot=function(t,e){var o="0x"+t.slice(1)-65536;return e||(o<0?String.fromCharCode(o+65536):String.fromCharCode(o>>10|55296,1023&o|56320))},pt=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,bt=function(t,e){return e?"\0"===t?"�":t.slice(0,-1)+"\\"+t.charCodeAt(t.length-1).toString(16)+" ":"\\"+t},nt=function(){O()},Mt=ht((function(t){return!0===t.disabled&&"fieldset"===t.nodeName.toLowerCase()}),{dir:"parentNode",next:"legend"});try{x.apply(_=T.call(W.childNodes),W.childNodes),_[W.childNodes.length].nodeType}catch(t){x={apply:_.length?function(t,e){w.apply(t,T.call(e))}:function(t,e){for(var o=t.length,p=0;t[o++]=e[p++];);t.length=o-1}}}function zt(t,e,p,b){var n,z,r,i,a,l,u,f=e&&e.ownerDocument,W=e?e.nodeType:9;if(p=p||[],"string"!=typeof t||!t||1!==W&&9!==W&&11!==W)return p;if(!b&&(O(e),e=e||s,d)){if(11!==W&&(a=Z.exec(t)))if(n=a[1]){if(9===W){if(!(r=e.getElementById(n)))return p;if(r.id===n)return p.push(r),p}else if(f&&(r=f.getElementById(n))&&q(e,r)&&r.id===n)return p.push(r),p}else{if(a[2])return x.apply(p,e.getElementsByTagName(t)),p;if((n=a[3])&&o.getElementsByClassName&&e.getElementsByClassName)return x.apply(p,e.getElementsByClassName(n)),p}if(o.qsa&&!B[t+" "]&&(!A||!A.test(t))&&(1!==W||"object"!==e.nodeName.toLowerCase())){if(u=t,f=e,1===W&&(U.test(t)||H.test(t))){for((f=tt.test(t)&&ut(e.parentNode)||e)===e&&o.scope||((i=e.getAttribute("id"))?i=i.replace(pt,bt):e.setAttribute("id",i=h)),z=(l=M(t)).length;z--;)l[z]=(i?"#"+i:":scope")+" "+qt(l[z]);u=l.join(",")}try{return x.apply(p,f.querySelectorAll(u)),p}catch(e){B(t,!0)}finally{i===h&&e.removeAttribute("id")}}}return c(t.replace(I,"$1"),e,p,b)}function ct(){var t=[];return function e(o,b){return t.push(o+" ")>p.cacheLength&&delete e[t.shift()],e[o+" "]=b}}function rt(t){return t[h]=!0,t}function it(t){var e=s.createElement("fieldset");try{return!!t(e)}catch(t){return!1}finally{e.parentNode&&e.parentNode.removeChild(e),e=null}}function at(t,e){for(var o=t.split("|"),b=o.length;b--;)p.attrHandle[o[b]]=e}function Ot(t,e){var o=e&&t,p=o&&1===t.nodeType&&1===e.nodeType&&t.sourceIndex-e.sourceIndex;if(p)return p;if(o)for(;o=o.nextSibling;)if(o===e)return-1;return t?1:-1}function st(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function lt(t){return function(e){var o=e.nodeName.toLowerCase();return("input"===o||"button"===o)&&e.type===t}}function dt(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&Mt(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function At(t){return rt((function(e){return e=+e,rt((function(o,p){for(var b,n=t([],o.length,e),M=n.length;M--;)o[b=n[M]]&&(o[b]=!(p[b]=o[b]))}))}))}function ut(t){return t&&void 0!==t.getElementsByTagName&&t}for(e in o=zt.support={},n=zt.isXML=function(t){var e=t&&t.namespaceURI,o=t&&(t.ownerDocument||t).documentElement;return!G.test(e||o&&o.nodeName||"HTML")},O=zt.setDocument=function(t){var e,b,M=t?t.ownerDocument||t:W;return M!=s&&9===M.nodeType&&M.documentElement?(l=(s=M).documentElement,d=!n(s),W!=s&&(b=s.defaultView)&&b.top!==b&&(b.addEventListener?b.addEventListener("unload",nt,!1):b.attachEvent&&b.attachEvent("onunload",nt)),o.scope=it((function(t){return l.appendChild(t).appendChild(s.createElement("div")),void 0!==t.querySelectorAll&&!t.querySelectorAll(":scope fieldset div").length})),o.attributes=it((function(t){return t.className="i",!t.getAttribute("className")})),o.getElementsByTagName=it((function(t){return t.appendChild(s.createComment("")),!t.getElementsByTagName("*").length})),o.getElementsByClassName=Q.test(s.getElementsByClassName),o.getById=it((function(t){return l.appendChild(t).id=h,!s.getElementsByName||!s.getElementsByName(h).length})),o.getById?(p.filter.ID=function(t){var e=t.replace(et,ot);return function(t){return t.getAttribute("id")===e}},p.find.ID=function(t,e){if(void 0!==e.getElementById&&d){var o=e.getElementById(t);return o?[o]:[]}}):(p.filter.ID=function(t){var e=t.replace(et,ot);return function(t){var o=void 0!==t.getAttributeNode&&t.getAttributeNode("id");return o&&o.value===e}},p.find.ID=function(t,e){if(void 0!==e.getElementById&&d){var o,p,b,n=e.getElementById(t);if(n){if((o=n.getAttributeNode("id"))&&o.value===t)return[n];for(b=e.getElementsByName(t),p=0;n=b[p++];)if((o=n.getAttributeNode("id"))&&o.value===t)return[n]}return[]}}),p.find.TAG=o.getElementsByTagName?function(t,e){return void 0!==e.getElementsByTagName?e.getElementsByTagName(t):o.qsa?e.querySelectorAll(t):void 0}:function(t,e){var o,p=[],b=0,n=e.getElementsByTagName(t);if("*"===t){for(;o=n[b++];)1===o.nodeType&&p.push(o);return p}return n},p.find.CLASS=o.getElementsByClassName&&function(t,e){if(void 0!==e.getElementsByClassName&&d)return e.getElementsByClassName(t)},u=[],A=[],(o.qsa=Q.test(s.querySelectorAll))&&(it((function(t){var e;l.appendChild(t).innerHTML="",t.querySelectorAll("[msallowcapture^='']").length&&A.push("[*^$]=[\\x20\\t\\r\\n\\f]*(?:''|\"\")"),t.querySelectorAll("[selected]").length||A.push("\\[[\\x20\\t\\r\\n\\f]*(?:value|"+S+")"),t.querySelectorAll("[id~="+h+"-]").length||A.push("~="),(e=s.createElement("input")).setAttribute("name",""),t.appendChild(e),t.querySelectorAll("[name='']").length||A.push("\\[[\\x20\\t\\r\\n\\f]*name[\\x20\\t\\r\\n\\f]*=[\\x20\\t\\r\\n\\f]*(?:''|\"\")"),t.querySelectorAll(":checked").length||A.push(":checked"),t.querySelectorAll("a#"+h+"+*").length||A.push(".#.+[+~]"),t.querySelectorAll("\\\f"),A.push("[\\r\\n\\f]")})),it((function(t){t.innerHTML="";var e=s.createElement("input");e.setAttribute("type","hidden"),t.appendChild(e).setAttribute("name","D"),t.querySelectorAll("[name=d]").length&&A.push("name[\\x20\\t\\r\\n\\f]*[*^$|!~]?="),2!==t.querySelectorAll(":enabled").length&&A.push(":enabled",":disabled"),l.appendChild(t).disabled=!0,2!==t.querySelectorAll(":disabled").length&&A.push(":enabled",":disabled"),t.querySelectorAll("*,:x"),A.push(",.*:")}))),(o.matchesSelector=Q.test(f=l.matches||l.webkitMatchesSelector||l.mozMatchesSelector||l.oMatchesSelector||l.msMatchesSelector))&&it((function(t){o.disconnectedMatch=f.call(t,"*"),f.call(t,"[s!='']:x"),u.push("!=",P)})),A=A.length&&new RegExp(A.join("|")),u=u.length&&new RegExp(u.join("|")),e=Q.test(l.compareDocumentPosition),q=e||Q.test(l.contains)?function(t,e){var o=9===t.nodeType?t.documentElement:t,p=e&&e.parentNode;return t===p||!(!p||1!==p.nodeType||!(o.contains?o.contains(p):t.compareDocumentPosition&&16&t.compareDocumentPosition(p)))}:function(t,e){if(e)for(;e=e.parentNode;)if(e===t)return!0;return!1},L=e?function(t,e){if(t===e)return a=!0,0;var p=!t.compareDocumentPosition-!e.compareDocumentPosition;return p||(1&(p=(t.ownerDocument||t)==(e.ownerDocument||e)?t.compareDocumentPosition(e):1)||!o.sortDetached&&e.compareDocumentPosition(t)===p?t==s||t.ownerDocument==W&&q(W,t)?-1:e==s||e.ownerDocument==W&&q(W,e)?1:i?C(i,t)-C(i,e):0:4&p?-1:1)}:function(t,e){if(t===e)return a=!0,0;var o,p=0,b=t.parentNode,n=e.parentNode,M=[t],z=[e];if(!b||!n)return t==s?-1:e==s?1:b?-1:n?1:i?C(i,t)-C(i,e):0;if(b===n)return Ot(t,e);for(o=t;o=o.parentNode;)M.unshift(o);for(o=e;o=o.parentNode;)z.unshift(o);for(;M[p]===z[p];)p++;return p?Ot(M[p],z[p]):M[p]==W?-1:z[p]==W?1:0},s):s},zt.matches=function(t,e){return zt(t,null,null,e)},zt.matchesSelector=function(t,e){if(O(t),o.matchesSelector&&d&&!B[e+" "]&&(!u||!u.test(e))&&(!A||!A.test(e)))try{var p=f.call(t,e);if(p||o.disconnectedMatch||t.document&&11!==t.document.nodeType)return p}catch(t){B(e,!0)}return zt(e,s,null,[t]).length>0},zt.contains=function(t,e){return(t.ownerDocument||t)!=s&&O(t),q(t,e)},zt.attr=function(t,e){(t.ownerDocument||t)!=s&&O(t);var b=p.attrHandle[e.toLowerCase()],n=b&&X.call(p.attrHandle,e.toLowerCase())?b(t,e,!d):void 0;return void 0!==n?n:o.attributes||!d?t.getAttribute(e):(n=t.getAttributeNode(e))&&n.specified?n.value:null},zt.escape=function(t){return(t+"").replace(pt,bt)},zt.error=function(t){throw new Error("Syntax error, unrecognized expression: "+t)},zt.uniqueSort=function(t){var e,p=[],b=0,n=0;if(a=!o.detectDuplicates,i=!o.sortStable&&t.slice(0),t.sort(L),a){for(;e=t[n++];)e===t[n]&&(b=p.push(n));for(;b--;)t.splice(p[b],1)}return i=null,t},b=zt.getText=function(t){var e,o="",p=0,n=t.nodeType;if(n){if(1===n||9===n||11===n){if("string"==typeof t.textContent)return t.textContent;for(t=t.firstChild;t;t=t.nextSibling)o+=b(t)}else if(3===n||4===n)return t.nodeValue}else for(;e=t[p++];)o+=b(e);return o},p=zt.selectors={cacheLength:50,createPseudo:rt,match:Y,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(t){return t[1]=t[1].replace(et,ot),t[3]=(t[3]||t[4]||t[5]||"").replace(et,ot),"~="===t[2]&&(t[3]=" "+t[3]+" "),t.slice(0,4)},CHILD:function(t){return t[1]=t[1].toLowerCase(),"nth"===t[1].slice(0,3)?(t[3]||zt.error(t[0]),t[4]=+(t[4]?t[5]+(t[6]||1):2*("even"===t[3]||"odd"===t[3])),t[5]=+(t[7]+t[8]||"odd"===t[3])):t[3]&&zt.error(t[0]),t},PSEUDO:function(t){var e,o=!t[6]&&t[2];return Y.CHILD.test(t[0])?null:(t[3]?t[2]=t[4]||t[5]||"":o&&V.test(o)&&(e=M(o,!0))&&(e=o.indexOf(")",o.length-e)-o.length)&&(t[0]=t[0].slice(0,e),t[2]=o.slice(0,e)),t.slice(0,3))}},filter:{TAG:function(t){var e=t.replace(et,ot).toLowerCase();return"*"===t?function(){return!0}:function(t){return t.nodeName&&t.nodeName.toLowerCase()===e}},CLASS:function(t){var e=v[t+" "];return e||(e=new RegExp("(^|[\\x20\\t\\r\\n\\f])"+t+"("+k+"|$)"))&&v(t,(function(t){return e.test("string"==typeof t.className&&t.className||void 0!==t.getAttribute&&t.getAttribute("class")||"")}))},ATTR:function(t,e,o){return function(p){var b=zt.attr(p,t);return null==b?"!="===e:!e||(b+="","="===e?b===o:"!="===e?b!==o:"^="===e?o&&0===b.indexOf(o):"*="===e?o&&b.indexOf(o)>-1:"$="===e?o&&b.slice(-o.length)===o:"~="===e?(" "+b.replace(j," ")+" ").indexOf(o)>-1:"|="===e&&(b===o||b.slice(0,o.length+1)===o+"-"))}},CHILD:function(t,e,o,p,b){var n="nth"!==t.slice(0,3),M="last"!==t.slice(-4),z="of-type"===e;return 1===p&&0===b?function(t){return!!t.parentNode}:function(e,o,c){var r,i,a,O,s,l,d=n!==M?"nextSibling":"previousSibling",A=e.parentNode,u=z&&e.nodeName.toLowerCase(),f=!c&&!z,q=!1;if(A){if(n){for(;d;){for(O=e;O=O[d];)if(z?O.nodeName.toLowerCase()===u:1===O.nodeType)return!1;l=d="only"===t&&!l&&"nextSibling"}return!0}if(l=[M?A.firstChild:A.lastChild],M&&f){for(q=(s=(r=(i=(a=(O=A)[h]||(O[h]={}))[O.uniqueID]||(a[O.uniqueID]={}))[t]||[])[0]===m&&r[1])&&r[2],O=s&&A.childNodes[s];O=++s&&O&&O[d]||(q=s=0)||l.pop();)if(1===O.nodeType&&++q&&O===e){i[t]=[m,s,q];break}}else if(f&&(q=s=(r=(i=(a=(O=e)[h]||(O[h]={}))[O.uniqueID]||(a[O.uniqueID]={}))[t]||[])[0]===m&&r[1]),!1===q)for(;(O=++s&&O&&O[d]||(q=s=0)||l.pop())&&((z?O.nodeName.toLowerCase()!==u:1!==O.nodeType)||!++q||(f&&((i=(a=O[h]||(O[h]={}))[O.uniqueID]||(a[O.uniqueID]={}))[t]=[m,q]),O!==e)););return(q-=b)===p||q%p==0&&q/p>=0}}},PSEUDO:function(t,e){var o,b=p.pseudos[t]||p.setFilters[t.toLowerCase()]||zt.error("unsupported pseudo: "+t);return b[h]?b(e):b.length>1?(o=[t,t,"",e],p.setFilters.hasOwnProperty(t.toLowerCase())?rt((function(t,o){for(var p,n=b(t,e),M=n.length;M--;)t[p=C(t,n[M])]=!(o[p]=n[M])})):function(t){return b(t,0,o)}):b}},pseudos:{not:rt((function(t){var e=[],o=[],p=z(t.replace(I,"$1"));return p[h]?rt((function(t,e,o,b){for(var n,M=p(t,null,b,[]),z=t.length;z--;)(n=M[z])&&(t[z]=!(e[z]=n))})):function(t,b,n){return e[0]=t,p(e,null,n,o),e[0]=null,!o.pop()}})),has:rt((function(t){return function(e){return zt(t,e).length>0}})),contains:rt((function(t){return t=t.replace(et,ot),function(e){return(e.textContent||b(e)).indexOf(t)>-1}})),lang:rt((function(t){return $.test(t||"")||zt.error("unsupported lang: "+t),t=t.replace(et,ot).toLowerCase(),function(e){var o;do{if(o=d?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(o=o.toLowerCase())===t||0===o.indexOf(t+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}})),target:function(e){var o=t.location&&t.location.hash;return o&&o.slice(1)===e.id},root:function(t){return t===l},focus:function(t){return t===s.activeElement&&(!s.hasFocus||s.hasFocus())&&!!(t.type||t.href||~t.tabIndex)},enabled:dt(!1),disabled:dt(!0),checked:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&!!t.checked||"option"===e&&!!t.selected},selected:function(t){return t.parentNode&&t.parentNode.selectedIndex,!0===t.selected},empty:function(t){for(t=t.firstChild;t;t=t.nextSibling)if(t.nodeType<6)return!1;return!0},parent:function(t){return!p.pseudos.empty(t)},header:function(t){return K.test(t.nodeName)},input:function(t){return J.test(t.nodeName)},button:function(t){var e=t.nodeName.toLowerCase();return"input"===e&&"button"===t.type||"button"===e},text:function(t){var e;return"input"===t.nodeName.toLowerCase()&&"text"===t.type&&(null==(e=t.getAttribute("type"))||"text"===e.toLowerCase())},first:At((function(){return[0]})),last:At((function(t,e){return[e-1]})),eq:At((function(t,e,o){return[o<0?o+e:o]})),even:At((function(t,e){for(var o=0;oe?e:o;--p>=0;)t.push(p);return t})),gt:At((function(t,e,o){for(var p=o<0?o+e:o;++p1?function(e,o,p){for(var b=t.length;b--;)if(!t[b](e,o,p))return!1;return!0}:t[0]}function mt(t,e,o,p,b){for(var n,M=[],z=0,c=t.length,r=null!=e;z-1&&(n[r]=!(M[r]=a))}}else u=mt(u===M?u.splice(l,u.length):u),b?b(null,M,u,c):x.apply(M,u)}))}function vt(t){for(var e,o,b,n=t.length,M=p.relative[t[0].type],z=M||p.relative[" "],c=M?1:0,i=ht((function(t){return t===e}),z,!0),a=ht((function(t){return C(e,t)>-1}),z,!0),O=[function(t,o,p){var b=!M&&(p||o!==r)||((e=o).nodeType?i(t,o,p):a(t,o,p));return e=null,b}];c1&&Wt(O),c>1&&qt(t.slice(0,c-1).concat({value:" "===t[c-2].type?"*":""})).replace(I,"$1"),o,c0,b=t.length>0,n=function(n,M,z,c,i){var a,l,A,u=0,f="0",q=n&&[],h=[],W=r,g=n||b&&p.find.TAG("*",i),v=m+=null==W?1:Math.random()||.1,R=g.length;for(i&&(r=M==s||M||i);f!==R&&null!=(a=g[f]);f++){if(b&&a){for(l=0,M||a.ownerDocument==s||(O(a),z=!d);A=t[l++];)if(A(a,M||s,z)){c.push(a);break}i&&(m=v)}o&&((a=!A&&a)&&u--,n&&q.push(a))}if(u+=f,o&&f!==u){for(l=0;A=e[l++];)A(q,h,M,z);if(n){if(u>0)for(;f--;)q[f]||h[f]||(h[f]=N.call(c));h=mt(h)}x.apply(c,h),i&&!n&&h.length>0&&u+e.length>1&&zt.uniqueSort(c)}return i&&(m=v,r=W),q};return o?rt(n):n}(n,b)),z.selector=t}return z},c=zt.select=function(t,e,o,b){var n,c,r,i,a,O="function"==typeof t&&t,s=!b&&M(t=O.selector||t);if(o=o||[],1===s.length){if((c=s[0]=s[0].slice(0)).length>2&&"ID"===(r=c[0]).type&&9===e.nodeType&&d&&p.relative[c[1].type]){if(!(e=(p.find.ID(r.matches[0].replace(et,ot),e)||[])[0]))return o;O&&(e=e.parentNode),t=t.slice(c.shift().value.length)}for(n=Y.needsContext.test(t)?0:c.length;n--&&(r=c[n],!p.relative[i=r.type]);)if((a=p.find[i])&&(b=a(r.matches[0].replace(et,ot),tt.test(c[0].type)&&ut(e.parentNode)||e))){if(c.splice(n,1),!(t=b.length&&qt(c)))return x.apply(o,b),o;break}}return(O||z(t,s))(b,e,!d,o,!e||tt.test(t)&&ut(e.parentNode)||e),o},o.sortStable=h.split("").sort(L).join("")===h,o.detectDuplicates=!!a,O(),o.sortDetached=it((function(t){return 1&t.compareDocumentPosition(s.createElement("fieldset"))})),it((function(t){return t.innerHTML="","#"===t.firstChild.getAttribute("href")}))||at("type|href|height|width",(function(t,e,o){if(!o)return t.getAttribute(e,"type"===e.toLowerCase()?1:2)})),o.attributes&&it((function(t){return t.innerHTML="",t.firstChild.setAttribute("value",""),""===t.firstChild.getAttribute("value")}))||at("value",(function(t,e,o){if(!o&&"input"===t.nodeName.toLowerCase())return t.defaultValue})),it((function(t){return null==t.getAttribute("disabled")}))||at(S,(function(t,e,o){var p;if(!o)return!0===t[e]?e.toLowerCase():(p=t.getAttributeNode(e))&&p.specified?p.value:null})),zt}(p);v.find=y,v.expr=y.selectors,v.expr[":"]=v.expr.pseudos,v.uniqueSort=v.unique=y.uniqueSort,v.text=y.getText,v.isXMLDoc=y.isXML,v.contains=y.contains,v.escapeSelector=y.escape;var B=function(t,e,o){for(var p=[],b=void 0!==o;(t=t[e])&&9!==t.nodeType;)if(1===t.nodeType){if(b&&v(t).is(o))break;p.push(t)}return p},L=function(t,e){for(var o=[];t;t=t.nextSibling)1===t.nodeType&&t!==e&&o.push(t);return o},X=v.expr.match.needsContext;function _(t,e){return t.nodeName&&t.nodeName.toLowerCase()===e.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function w(t,e,o){return u(e)?v.grep(t,(function(t,p){return!!e.call(t,p,t)!==o})):e.nodeType?v.grep(t,(function(t){return t===e!==o})):"string"!=typeof e?v.grep(t,(function(t){return i.call(e,t)>-1!==o})):v.filter(e,t,o)}v.filter=function(t,e,o){var p=e[0];return o&&(t=":not("+t+")"),1===e.length&&1===p.nodeType?v.find.matchesSelector(p,t)?[p]:[]:v.find.matches(t,v.grep(e,(function(t){return 1===t.nodeType})))},v.fn.extend({find:function(t){var e,o,p=this.length,b=this;if("string"!=typeof t)return this.pushStack(v(t).filter((function(){for(e=0;e1?v.uniqueSort(o):o},filter:function(t){return this.pushStack(w(this,t||[],!1))},not:function(t){return this.pushStack(w(this,t||[],!0))},is:function(t){return!!w(this,"string"==typeof t&&X.test(t)?v(t):t||[],!1).length}});var x,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(v.fn.init=function(t,e,o){var p,b;if(!t)return this;if(o=o||x,"string"==typeof t){if(!(p="<"===t[0]&&">"===t[t.length-1]&&t.length>=3?[null,t,null]:T.exec(t))||!p[1]&&e)return!e||e.jquery?(e||o).find(t):this.constructor(e).find(t);if(p[1]){if(e=e instanceof v?e[0]:e,v.merge(this,v.parseHTML(p[1],e&&e.nodeType?e.ownerDocument||e:q,!0)),N.test(p[1])&&v.isPlainObject(e))for(p in e)u(this[p])?this[p](e[p]):this.attr(p,e[p]);return this}return(b=q.getElementById(p[2]))&&(this[0]=b,this.length=1),this}return t.nodeType?(this[0]=t,this.length=1,this):u(t)?void 0!==o.ready?o.ready(t):t(v):v.makeArray(t,this)}).prototype=v.fn,x=v(q);var C=/^(?:parents|prev(?:Until|All))/,S={children:!0,contents:!0,next:!0,prev:!0};function k(t,e){for(;(t=t[e])&&1!==t.nodeType;);return t}v.fn.extend({has:function(t){var e=v(t,this),o=e.length;return this.filter((function(){for(var t=0;t-1:1===o.nodeType&&v.find.matchesSelector(o,t))){n.push(o);break}return this.pushStack(n.length>1?v.uniqueSort(n):n)},index:function(t){return t?"string"==typeof t?i.call(v(t),this[0]):i.call(this,t.jquery?t[0]:t):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(t,e){return this.pushStack(v.uniqueSort(v.merge(this.get(),v(t,e))))},addBack:function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}}),v.each({parent:function(t){var e=t.parentNode;return e&&11!==e.nodeType?e:null},parents:function(t){return B(t,"parentNode")},parentsUntil:function(t,e,o){return B(t,"parentNode",o)},next:function(t){return k(t,"nextSibling")},prev:function(t){return k(t,"previousSibling")},nextAll:function(t){return B(t,"nextSibling")},prevAll:function(t){return B(t,"previousSibling")},nextUntil:function(t,e,o){return B(t,"nextSibling",o)},prevUntil:function(t,e,o){return B(t,"previousSibling",o)},siblings:function(t){return L((t.parentNode||{}).firstChild,t)},children:function(t){return L(t.firstChild)},contents:function(t){return null!=t.contentDocument&&M(t.contentDocument)?t.contentDocument:(_(t,"template")&&(t=t.content||t),v.merge([],t.childNodes))}},(function(t,e){v.fn[t]=function(o,p){var b=v.map(this,e,o);return"Until"!==t.slice(-5)&&(p=o),p&&"string"==typeof p&&(b=v.filter(p,b)),this.length>1&&(S[t]||v.uniqueSort(b),C.test(t)&&b.reverse()),this.pushStack(b)}}));var E=/[^\x20\t\r\n\f]+/g;function D(t){return t}function P(t){throw t}function j(t,e,o,p){var b;try{t&&u(b=t.promise)?b.call(t).done(e).fail(o):t&&u(b=t.then)?b.call(t,e,o):e.apply(void 0,[t].slice(p))}catch(t){o.apply(void 0,[t])}}v.Callbacks=function(t){t="string"==typeof t?function(t){var e={};return v.each(t.match(E)||[],(function(t,o){e[o]=!0})),e}(t):v.extend({},t);var e,o,p,b,n=[],M=[],z=-1,c=function(){for(b=b||t.once,p=e=!0;M.length;z=-1)for(o=M.shift();++z-1;)n.splice(o,1),o<=z&&z--})),this},has:function(t){return t?v.inArray(t,n)>-1:n.length>0},empty:function(){return n&&(n=[]),this},disable:function(){return b=M=[],n=o="",this},disabled:function(){return!n},lock:function(){return b=M=[],o||e||(n=o=""),this},locked:function(){return!!b},fireWith:function(t,o){return b||(o=[t,(o=o||[]).slice?o.slice():o],M.push(o),e||c()),this},fire:function(){return r.fireWith(this,arguments),this},fired:function(){return!!p}};return r},v.extend({Deferred:function(t){var e=[["notify","progress",v.Callbacks("memory"),v.Callbacks("memory"),2],["resolve","done",v.Callbacks("once memory"),v.Callbacks("once memory"),0,"resolved"],["reject","fail",v.Callbacks("once memory"),v.Callbacks("once memory"),1,"rejected"]],o="pending",b={state:function(){return o},always:function(){return n.done(arguments).fail(arguments),this},catch:function(t){return b.then(null,t)},pipe:function(){var t=arguments;return v.Deferred((function(o){v.each(e,(function(e,p){var b=u(t[p[4]])&&t[p[4]];n[p[1]]((function(){var t=b&&b.apply(this,arguments);t&&u(t.promise)?t.promise().progress(o.notify).done(o.resolve).fail(o.reject):o[p[0]+"With"](this,b?[t]:arguments)}))})),t=null})).promise()},then:function(t,o,b){var n=0;function M(t,e,o,b){return function(){var z=this,c=arguments,r=function(){var p,r;if(!(t=n&&(o!==P&&(z=void 0,c=[p]),e.rejectWith(z,c))}};t?i():(v.Deferred.getStackHook&&(i.stackTrace=v.Deferred.getStackHook()),p.setTimeout(i))}}return v.Deferred((function(p){e[0][3].add(M(0,p,u(b)?b:D,p.notifyWith)),e[1][3].add(M(0,p,u(t)?t:D)),e[2][3].add(M(0,p,u(o)?o:P))})).promise()},promise:function(t){return null!=t?v.extend(t,b):b}},n={};return v.each(e,(function(t,p){var M=p[2],z=p[5];b[p[1]]=M.add,z&&M.add((function(){o=z}),e[3-t][2].disable,e[3-t][3].disable,e[0][2].lock,e[0][3].lock),M.add(p[3].fire),n[p[0]]=function(){return n[p[0]+"With"](this===n?void 0:this,arguments),this},n[p[0]+"With"]=M.fireWith})),b.promise(n),t&&t.call(n,n),n},when:function(t){var e=arguments.length,o=e,p=Array(o),b=z.call(arguments),n=v.Deferred(),M=function(t){return function(o){p[t]=this,b[t]=arguments.length>1?z.call(arguments):o,--e||n.resolveWith(p,b)}};if(e<=1&&(j(t,n.done(M(o)).resolve,n.reject,!e),"pending"===n.state()||u(b[o]&&b[o].then)))return n.then();for(;o--;)j(b[o],M(o),n.reject);return n.promise()}});var I=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;v.Deferred.exceptionHook=function(t,e){p.console&&p.console.warn&&t&&I.test(t.name)&&p.console.warn("jQuery.Deferred exception: "+t.message,t.stack,e)},v.readyException=function(t){p.setTimeout((function(){throw t}))};var F=v.Deferred();function H(){q.removeEventListener("DOMContentLoaded",H),p.removeEventListener("load",H),v.ready()}v.fn.ready=function(t){return F.then(t).catch((function(t){v.readyException(t)})),this},v.extend({isReady:!1,readyWait:1,ready:function(t){(!0===t?--v.readyWait:v.isReady)||(v.isReady=!0,!0!==t&&--v.readyWait>0||F.resolveWith(q,[v]))}}),v.ready.then=F.then,"complete"===q.readyState||"loading"!==q.readyState&&!q.documentElement.doScroll?p.setTimeout(v.ready):(q.addEventListener("DOMContentLoaded",H),p.addEventListener("load",H));var U=function(t,e,o,p,b,n,M){var z=0,c=t.length,r=null==o;if("object"===m(o))for(z in b=!0,o)U(t,e,z,o[z],!0,n,M);else if(void 0!==p&&(b=!0,u(p)||(M=!0),r&&(M?(e.call(t,p),e=null):(r=e,e=function(t,e,o){return r.call(v(t),o)})),e))for(;z1,null,!0)},removeData:function(t){return this.each((function(){Z.remove(this,t)}))}}),v.extend({queue:function(t,e,o){var p;if(t)return e=(e||"fx")+"queue",p=Q.get(t,e),o&&(!p||Array.isArray(o)?p=Q.access(t,e,v.makeArray(o)):p.push(o)),p||[]},dequeue:function(t,e){e=e||"fx";var o=v.queue(t,e),p=o.length,b=o.shift(),n=v._queueHooks(t,e);"inprogress"===b&&(b=o.shift(),p--),b&&("fx"===e&&o.unshift("inprogress"),delete n.stop,b.call(t,(function(){v.dequeue(t,e)}),n)),!p&&n&&n.empty.fire()},_queueHooks:function(t,e){var o=e+"queueHooks";return Q.get(t,o)||Q.access(t,o,{empty:v.Callbacks("once memory").add((function(){Q.remove(t,[e+"queue",o])}))})}}),v.fn.extend({queue:function(t,e){var o=2;return"string"!=typeof t&&(e=t,t="fx",o--),arguments.length\x20\t\r\n\f]*)/i,ft=/^$|^module$|\/(?:java|ecma)script/i;lt=q.createDocumentFragment().appendChild(q.createElement("div")),(dt=q.createElement("input")).setAttribute("type","radio"),dt.setAttribute("checked","checked"),dt.setAttribute("name","t"),lt.appendChild(dt),A.checkClone=lt.cloneNode(!0).cloneNode(!0).lastChild.checked,lt.innerHTML="",A.noCloneChecked=!!lt.cloneNode(!0).lastChild.defaultValue,lt.innerHTML="",A.option=!!lt.lastChild;var qt={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ht(t,e){var o;return o=void 0!==t.getElementsByTagName?t.getElementsByTagName(e||"*"):void 0!==t.querySelectorAll?t.querySelectorAll(e||"*"):[],void 0===e||e&&_(t,e)?v.merge([t],o):o}function Wt(t,e){for(var o=0,p=t.length;o",""]);var mt=/<|&#?\w+;/;function gt(t,e,o,p,b){for(var n,M,z,c,r,i,a=e.createDocumentFragment(),O=[],s=0,l=t.length;s-1)b&&b.push(n);else if(r=zt(n),M=ht(a.appendChild(n),"script"),r&&Wt(M),o)for(i=0;n=M[i++];)ft.test(n.type||"")&&o.push(n);return a}var vt=/^([^.]*)(?:\.(.+)|)/;function Rt(){return!0}function yt(){return!1}function Bt(t,e){return t===function(){try{return q.activeElement}catch(t){}}()==("focus"===e)}function Lt(t,e,o,p,b,n){var M,z;if("object"==typeof e){for(z in"string"!=typeof o&&(p=p||o,o=void 0),e)Lt(t,z,o,p,e[z],n);return t}if(null==p&&null==b?(b=o,p=o=void 0):null==b&&("string"==typeof o?(b=p,p=void 0):(b=p,p=o,o=void 0)),!1===b)b=yt;else if(!b)return t;return 1===n&&(M=b,b=function(t){return v().off(t),M.apply(this,arguments)},b.guid=M.guid||(M.guid=v.guid++)),t.each((function(){v.event.add(this,e,b,p,o)}))}function Xt(t,e,o){o?(Q.set(t,e,!1),v.event.add(t,e,{namespace:!1,handler:function(t){var p,b,n=Q.get(this,e);if(1&t.isTrigger&&this[e]){if(n.length)(v.event.special[e]||{}).delegateType&&t.stopPropagation();else if(n=z.call(arguments),Q.set(this,e,n),p=o(this,e),this[e](),n!==(b=Q.get(this,e))||p?Q.set(this,e,!1):b={},n!==b)return t.stopImmediatePropagation(),t.preventDefault(),b&&b.value}else n.length&&(Q.set(this,e,{value:v.event.trigger(v.extend(n[0],v.Event.prototype),n.slice(1),this)}),t.stopImmediatePropagation())}})):void 0===Q.get(t,e)&&v.event.add(t,e,Rt)}v.event={global:{},add:function(t,e,o,p,b){var n,M,z,c,r,i,a,O,s,l,d,A=Q.get(t);if(J(t))for(o.handler&&(o=(n=o).handler,b=n.selector),b&&v.find.matchesSelector(Mt,b),o.guid||(o.guid=v.guid++),(c=A.events)||(c=A.events=Object.create(null)),(M=A.handle)||(M=A.handle=function(e){return void 0!==v&&v.event.triggered!==e.type?v.event.dispatch.apply(t,arguments):void 0}),r=(e=(e||"").match(E)||[""]).length;r--;)s=d=(z=vt.exec(e[r])||[])[1],l=(z[2]||"").split(".").sort(),s&&(a=v.event.special[s]||{},s=(b?a.delegateType:a.bindType)||s,a=v.event.special[s]||{},i=v.extend({type:s,origType:d,data:p,handler:o,guid:o.guid,selector:b,needsContext:b&&v.expr.match.needsContext.test(b),namespace:l.join(".")},n),(O=c[s])||((O=c[s]=[]).delegateCount=0,a.setup&&!1!==a.setup.call(t,p,l,M)||t.addEventListener&&t.addEventListener(s,M)),a.add&&(a.add.call(t,i),i.handler.guid||(i.handler.guid=o.guid)),b?O.splice(O.delegateCount++,0,i):O.push(i),v.event.global[s]=!0)},remove:function(t,e,o,p,b){var n,M,z,c,r,i,a,O,s,l,d,A=Q.hasData(t)&&Q.get(t);if(A&&(c=A.events)){for(r=(e=(e||"").match(E)||[""]).length;r--;)if(s=d=(z=vt.exec(e[r])||[])[1],l=(z[2]||"").split(".").sort(),s){for(a=v.event.special[s]||{},O=c[s=(p?a.delegateType:a.bindType)||s]||[],z=z[2]&&new RegExp("(^|\\.)"+l.join("\\.(?:.*\\.|)")+"(\\.|$)"),M=n=O.length;n--;)i=O[n],!b&&d!==i.origType||o&&o.guid!==i.guid||z&&!z.test(i.namespace)||p&&p!==i.selector&&("**"!==p||!i.selector)||(O.splice(n,1),i.selector&&O.delegateCount--,a.remove&&a.remove.call(t,i));M&&!O.length&&(a.teardown&&!1!==a.teardown.call(t,l,A.handle)||v.removeEvent(t,s,A.handle),delete c[s])}else for(s in c)v.event.remove(t,s+e[r],o,p,!0);v.isEmptyObject(c)&&Q.remove(t,"handle events")}},dispatch:function(t){var e,o,p,b,n,M,z=new Array(arguments.length),c=v.event.fix(t),r=(Q.get(this,"events")||Object.create(null))[c.type]||[],i=v.event.special[c.type]||{};for(z[0]=c,e=1;e=1))for(;r!==this;r=r.parentNode||this)if(1===r.nodeType&&("click"!==t.type||!0!==r.disabled)){for(n=[],M={},o=0;o-1:v.find(b,this,null,[r]).length),M[b]&&n.push(p);n.length&&z.push({elem:r,handlers:n})}return r=this,c\s*$/g;function xt(t,e){return _(t,"table")&&_(11!==e.nodeType?e:e.firstChild,"tr")&&v(t).children("tbody")[0]||t}function Tt(t){return t.type=(null!==t.getAttribute("type"))+"/"+t.type,t}function Ct(t){return"true/"===(t.type||"").slice(0,5)?t.type=t.type.slice(5):t.removeAttribute("type"),t}function St(t,e){var o,p,b,n,M,z;if(1===e.nodeType){if(Q.hasData(t)&&(z=Q.get(t).events))for(b in Q.remove(e,"handle events"),z)for(o=0,p=z[b].length;o1&&"string"==typeof l&&!A.checkClone&&Nt.test(l))return t.each((function(b){var n=t.eq(b);d&&(e[0]=l.call(this,b,n.html())),Et(n,e,o,p)}));if(O&&(n=(b=gt(e,t[0].ownerDocument,!1,t,p)).firstChild,1===b.childNodes.length&&(b=n),n||p)){for(z=(M=v.map(ht(b,"script"),Tt)).length;a0&&Wt(M,!c&&ht(t,"script")),z},cleanData:function(t){for(var e,o,p,b=v.event.special,n=0;void 0!==(o=t[n]);n++)if(J(o)){if(e=o[Q.expando]){if(e.events)for(p in e.events)b[p]?v.event.remove(o,p):v.removeEvent(o,p,e.handle);o[Q.expando]=void 0}o[Z.expando]&&(o[Z.expando]=void 0)}}}),v.fn.extend({detach:function(t){return Dt(this,t,!0)},remove:function(t){return Dt(this,t)},text:function(t){return U(this,(function(t){return void 0===t?v.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=t)}))}),null,t,arguments.length)},append:function(){return Et(this,arguments,(function(t){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||xt(this,t).appendChild(t)}))},prepend:function(){return Et(this,arguments,(function(t){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var e=xt(this,t);e.insertBefore(t,e.firstChild)}}))},before:function(){return Et(this,arguments,(function(t){this.parentNode&&this.parentNode.insertBefore(t,this)}))},after:function(){return Et(this,arguments,(function(t){this.parentNode&&this.parentNode.insertBefore(t,this.nextSibling)}))},empty:function(){for(var t,e=0;null!=(t=this[e]);e++)1===t.nodeType&&(v.cleanData(ht(t,!1)),t.textContent="");return this},clone:function(t,e){return t=null!=t&&t,e=null==e?t:e,this.map((function(){return v.clone(this,t,e)}))},html:function(t){return U(this,(function(t){var e=this[0]||{},o=0,p=this.length;if(void 0===t&&1===e.nodeType)return e.innerHTML;if("string"==typeof t&&!_t.test(t)&&!qt[(ut.exec(t)||["",""])[1].toLowerCase()]){t=v.htmlPrefilter(t);try{for(;o=0&&(c+=Math.max(0,Math.ceil(t["offset"+e[0].toUpperCase()+e.slice(1)]-n-c-z-.5))||0),c}function pe(t,e,o){var p=It(t),b=(!A.boxSizingReliable()||o)&&"border-box"===v.css(t,"boxSizing",!1,p),n=b,M=Vt(t,e,p),z="offset"+e[0].toUpperCase()+e.slice(1);if(Pt.test(M)){if(!o)return M;M="auto"}return(!A.boxSizingReliable()&&b||!A.reliableTrDimensions()&&_(t,"tr")||"auto"===M||!parseFloat(M)&&"inline"===v.css(t,"display",!1,p))&&t.getClientRects().length&&(b="border-box"===v.css(t,"boxSizing",!1,p),(n=z in t)&&(M=t[z])),(M=parseFloat(M)||0)+oe(t,e,o||(b?"border":"content"),n,p,M)+"px"}function be(t,e,o,p,b){return new be.prototype.init(t,e,o,p,b)}v.extend({cssHooks:{opacity:{get:function(t,e){if(e){var o=Vt(t,"opacity");return""===o?"1":o}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(t,e,o,p){if(t&&3!==t.nodeType&&8!==t.nodeType&&t.style){var b,n,M,z=G(e),c=jt.test(e),r=t.style;if(c||(e=Kt(z)),M=v.cssHooks[e]||v.cssHooks[z],void 0===o)return M&&"get"in M&&void 0!==(b=M.get(t,!1,p))?b:r[e];"string"===(n=typeof o)&&(b=bt.exec(o))&&b[1]&&(o=it(t,e,b),n="number"),null!=o&&o==o&&("number"!==n||c||(o+=b&&b[3]||(v.cssNumber[z]?"":"px")),A.clearCloneStyle||""!==o||0!==e.indexOf("background")||(r[e]="inherit"),M&&"set"in M&&void 0===(o=M.set(t,o,p))||(c?r.setProperty(e,o):r[e]=o))}},css:function(t,e,o,p){var b,n,M,z=G(e);return jt.test(e)||(e=Kt(z)),(M=v.cssHooks[e]||v.cssHooks[z])&&"get"in M&&(b=M.get(t,!0,o)),void 0===b&&(b=Vt(t,e,p)),"normal"===b&&e in te&&(b=te[e]),""===o||o?(n=parseFloat(b),!0===o||isFinite(n)?n||0:b):b}}),v.each(["height","width"],(function(t,e){v.cssHooks[e]={get:function(t,o,p){if(o)return!Qt.test(v.css(t,"display"))||t.getClientRects().length&&t.getBoundingClientRect().width?pe(t,e,p):Ft(t,Zt,(function(){return pe(t,e,p)}))},set:function(t,o,p){var b,n=It(t),M=!A.scrollboxSize()&&"absolute"===n.position,z=(M||p)&&"border-box"===v.css(t,"boxSizing",!1,n),c=p?oe(t,e,p,z,n):0;return z&&M&&(c-=Math.ceil(t["offset"+e[0].toUpperCase()+e.slice(1)]-parseFloat(n[e])-oe(t,e,"border",!1,n)-.5)),c&&(b=bt.exec(o))&&"px"!==(b[3]||"px")&&(t.style[e]=o,o=v.css(t,e)),ee(0,o,c)}}})),v.cssHooks.marginLeft=$t(A.reliableMarginLeft,(function(t,e){if(e)return(parseFloat(Vt(t,"marginLeft"))||t.getBoundingClientRect().left-Ft(t,{marginLeft:0},(function(){return t.getBoundingClientRect().left})))+"px"})),v.each({margin:"",padding:"",border:"Width"},(function(t,e){v.cssHooks[t+e]={expand:function(o){for(var p=0,b={},n="string"==typeof o?o.split(" "):[o];p<4;p++)b[t+nt[p]+e]=n[p]||n[p-2]||n[0];return b}},"margin"!==t&&(v.cssHooks[t+e].set=ee)})),v.fn.extend({css:function(t,e){return U(this,(function(t,e,o){var p,b,n={},M=0;if(Array.isArray(e)){for(p=It(t),b=e.length;M1)}}),v.Tween=be,be.prototype={constructor:be,init:function(t,e,o,p,b,n){this.elem=t,this.prop=o,this.easing=b||v.easing._default,this.options=e,this.start=this.now=this.cur(),this.end=p,this.unit=n||(v.cssNumber[o]?"":"px")},cur:function(){var t=be.propHooks[this.prop];return t&&t.get?t.get(this):be.propHooks._default.get(this)},run:function(t){var e,o=be.propHooks[this.prop];return this.options.duration?this.pos=e=v.easing[this.easing](t,this.options.duration*t,0,1,this.options.duration):this.pos=e=t,this.now=(this.end-this.start)*e+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),o&&o.set?o.set(this):be.propHooks._default.set(this),this}},be.prototype.init.prototype=be.prototype,be.propHooks={_default:{get:function(t){var e;return 1!==t.elem.nodeType||null!=t.elem[t.prop]&&null==t.elem.style[t.prop]?t.elem[t.prop]:(e=v.css(t.elem,t.prop,""))&&"auto"!==e?e:0},set:function(t){v.fx.step[t.prop]?v.fx.step[t.prop](t):1!==t.elem.nodeType||!v.cssHooks[t.prop]&&null==t.elem.style[Kt(t.prop)]?t.elem[t.prop]=t.now:v.style(t.elem,t.prop,t.now+t.unit)}}},be.propHooks.scrollTop=be.propHooks.scrollLeft={set:function(t){t.elem.nodeType&&t.elem.parentNode&&(t.elem[t.prop]=t.now)}},v.easing={linear:function(t){return t},swing:function(t){return.5-Math.cos(t*Math.PI)/2},_default:"swing"},v.fx=be.prototype.init,v.fx.step={};var ne,Me,ze=/^(?:toggle|show|hide)$/,ce=/queueHooks$/;function re(){Me&&(!1===q.hidden&&p.requestAnimationFrame?p.requestAnimationFrame(re):p.setTimeout(re,v.fx.interval),v.fx.tick())}function ie(){return p.setTimeout((function(){ne=void 0})),ne=Date.now()}function ae(t,e){var o,p=0,b={height:t};for(e=e?1:0;p<4;p+=2-e)b["margin"+(o=nt[p])]=b["padding"+o]=t;return e&&(b.opacity=b.width=t),b}function Oe(t,e,o){for(var p,b=(se.tweeners[e]||[]).concat(se.tweeners["*"]),n=0,M=b.length;n1)},removeAttr:function(t){return this.each((function(){v.removeAttr(this,t)}))}}),v.extend({attr:function(t,e,o){var p,b,n=t.nodeType;if(3!==n&&8!==n&&2!==n)return void 0===t.getAttribute?v.prop(t,e,o):(1===n&&v.isXMLDoc(t)||(b=v.attrHooks[e.toLowerCase()]||(v.expr.match.bool.test(e)?le:void 0)),void 0!==o?null===o?void v.removeAttr(t,e):b&&"set"in b&&void 0!==(p=b.set(t,o,e))?p:(t.setAttribute(e,o+""),o):b&&"get"in b&&null!==(p=b.get(t,e))?p:null==(p=v.find.attr(t,e))?void 0:p)},attrHooks:{type:{set:function(t,e){if(!A.radioValue&&"radio"===e&&_(t,"input")){var o=t.value;return t.setAttribute("type",e),o&&(t.value=o),e}}}},removeAttr:function(t,e){var o,p=0,b=e&&e.match(E);if(b&&1===t.nodeType)for(;o=b[p++];)t.removeAttribute(o)}}),le={set:function(t,e,o){return!1===e?v.removeAttr(t,o):t.setAttribute(o,o),o}},v.each(v.expr.match.bool.source.match(/\w+/g),(function(t,e){var o=de[e]||v.find.attr;de[e]=function(t,e,p){var b,n,M=e.toLowerCase();return p||(n=de[M],de[M]=b,b=null!=o(t,e,p)?M:null,de[M]=n),b}}));var Ae=/^(?:input|select|textarea|button)$/i,ue=/^(?:a|area)$/i;function fe(t){return(t.match(E)||[]).join(" ")}function qe(t){return t.getAttribute&&t.getAttribute("class")||""}function he(t){return Array.isArray(t)?t:"string"==typeof t&&t.match(E)||[]}v.fn.extend({prop:function(t,e){return U(this,v.prop,t,e,arguments.length>1)},removeProp:function(t){return this.each((function(){delete this[v.propFix[t]||t]}))}}),v.extend({prop:function(t,e,o){var p,b,n=t.nodeType;if(3!==n&&8!==n&&2!==n)return 1===n&&v.isXMLDoc(t)||(e=v.propFix[e]||e,b=v.propHooks[e]),void 0!==o?b&&"set"in b&&void 0!==(p=b.set(t,o,e))?p:t[e]=o:b&&"get"in b&&null!==(p=b.get(t,e))?p:t[e]},propHooks:{tabIndex:{get:function(t){var e=v.find.attr(t,"tabindex");return e?parseInt(e,10):Ae.test(t.nodeName)||ue.test(t.nodeName)&&t.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),A.optSelected||(v.propHooks.selected={get:function(t){var e=t.parentNode;return e&&e.parentNode&&e.parentNode.selectedIndex,null},set:function(t){var e=t.parentNode;e&&(e.selectedIndex,e.parentNode&&e.parentNode.selectedIndex)}}),v.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){v.propFix[this.toLowerCase()]=this})),v.fn.extend({addClass:function(t){var e,o,p,b,n,M;return u(t)?this.each((function(e){v(this).addClass(t.call(this,e,qe(this)))})):(e=he(t)).length?this.each((function(){if(p=qe(this),o=1===this.nodeType&&" "+fe(p)+" "){for(n=0;n-1;)o=o.replace(" "+b+" "," ");M=fe(o),p!==M&&this.setAttribute("class",M)}})):this:this.attr("class","")},toggleClass:function(t,e){var o,p,b,n,M=typeof t,z="string"===M||Array.isArray(t);return u(t)?this.each((function(o){v(this).toggleClass(t.call(this,o,qe(this),e),e)})):"boolean"==typeof e&&z?e?this.addClass(t):this.removeClass(t):(o=he(t),this.each((function(){if(z)for(n=v(this),b=0;b-1)return!0;return!1}});var We=/\r/g;v.fn.extend({val:function(t){var e,o,p,b=this[0];return arguments.length?(p=u(t),this.each((function(o){var b;1===this.nodeType&&(null==(b=p?t.call(this,o,v(this).val()):t)?b="":"number"==typeof b?b+="":Array.isArray(b)&&(b=v.map(b,(function(t){return null==t?"":t+""}))),(e=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()])&&"set"in e&&void 0!==e.set(this,b,"value")||(this.value=b))}))):b?(e=v.valHooks[b.type]||v.valHooks[b.nodeName.toLowerCase()])&&"get"in e&&void 0!==(o=e.get(b,"value"))?o:"string"==typeof(o=b.value)?o.replace(We,""):null==o?"":o:void 0}}),v.extend({valHooks:{option:{get:function(t){var e=v.find.attr(t,"value");return null!=e?e:fe(v.text(t))}},select:{get:function(t){var e,o,p,b=t.options,n=t.selectedIndex,M="select-one"===t.type,z=M?null:[],c=M?n+1:b.length;for(p=n<0?c:M?n:0;p-1)&&(o=!0);return o||(t.selectedIndex=-1),n}}}}),v.each(["radio","checkbox"],(function(){v.valHooks[this]={set:function(t,e){if(Array.isArray(e))return t.checked=v.inArray(v(t).val(),e)>-1}},A.checkOn||(v.valHooks[this].get=function(t){return null===t.getAttribute("value")?"on":t.value})})),A.focusin="onfocusin"in p;var me=/^(?:focusinfocus|focusoutblur)$/,ge=function(t){t.stopPropagation()};v.extend(v.event,{trigger:function(t,e,o,b){var n,M,z,c,r,i,a,O,l=[o||q],d=s.call(t,"type")?t.type:t,A=s.call(t,"namespace")?t.namespace.split("."):[];if(M=O=z=o=o||q,3!==o.nodeType&&8!==o.nodeType&&!me.test(d+v.event.triggered)&&(d.indexOf(".")>-1&&(A=d.split("."),d=A.shift(),A.sort()),r=d.indexOf(":")<0&&"on"+d,(t=t[v.expando]?t:new v.Event(d,"object"==typeof t&&t)).isTrigger=b?2:3,t.namespace=A.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+A.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=o),e=null==e?[t]:v.makeArray(e,[t]),a=v.event.special[d]||{},b||!a.trigger||!1!==a.trigger.apply(o,e))){if(!b&&!a.noBubble&&!f(o)){for(c=a.delegateType||d,me.test(c+d)||(M=M.parentNode);M;M=M.parentNode)l.push(M),z=M;z===(o.ownerDocument||q)&&l.push(z.defaultView||z.parentWindow||p)}for(n=0;(M=l[n++])&&!t.isPropagationStopped();)O=M,t.type=n>1?c:a.bindType||d,(i=(Q.get(M,"events")||Object.create(null))[t.type]&&Q.get(M,"handle"))&&i.apply(M,e),(i=r&&M[r])&&i.apply&&J(M)&&(t.result=i.apply(M,e),!1===t.result&&t.preventDefault());return t.type=d,b||t.isDefaultPrevented()||a._default&&!1!==a._default.apply(l.pop(),e)||!J(o)||r&&u(o[d])&&!f(o)&&((z=o[r])&&(o[r]=null),v.event.triggered=d,t.isPropagationStopped()&&O.addEventListener(d,ge),o[d](),t.isPropagationStopped()&&O.removeEventListener(d,ge),v.event.triggered=void 0,z&&(o[r]=z)),t.result}},simulate:function(t,e,o){var p=v.extend(new v.Event,o,{type:t,isSimulated:!0});v.event.trigger(p,null,e)}}),v.fn.extend({trigger:function(t,e){return this.each((function(){v.event.trigger(t,e,this)}))},triggerHandler:function(t,e){var o=this[0];if(o)return v.event.trigger(t,e,o,!0)}}),A.focusin||v.each({focus:"focusin",blur:"focusout"},(function(t,e){var o=function(t){v.event.simulate(e,t.target,v.event.fix(t))};v.event.special[e]={setup:function(){var p=this.ownerDocument||this.document||this,b=Q.access(p,e);b||p.addEventListener(t,o,!0),Q.access(p,e,(b||0)+1)},teardown:function(){var p=this.ownerDocument||this.document||this,b=Q.access(p,e)-1;b?Q.access(p,e,b):(p.removeEventListener(t,o,!0),Q.remove(p,e))}}}));var ve=p.location,Re={guid:Date.now()},ye=/\?/;v.parseXML=function(t){var e,o;if(!t||"string"!=typeof t)return null;try{e=(new p.DOMParser).parseFromString(t,"text/xml")}catch(t){}return o=e&&e.getElementsByTagName("parsererror")[0],e&&!o||v.error("Invalid XML: "+(o?v.map(o.childNodes,(function(t){return t.textContent})).join("\n"):t)),e};var Be=/\[\]$/,Le=/\r?\n/g,Xe=/^(?:submit|button|image|reset|file)$/i,_e=/^(?:input|select|textarea|keygen)/i;function Ne(t,e,o,p){var b;if(Array.isArray(e))v.each(e,(function(e,b){o||Be.test(t)?p(t,b):Ne(t+"["+("object"==typeof b&&null!=b?e:"")+"]",b,o,p)}));else if(o||"object"!==m(e))p(t,e);else for(b in e)Ne(t+"["+b+"]",e[b],o,p)}v.param=function(t,e){var o,p=[],b=function(t,e){var o=u(e)?e():e;p[p.length]=encodeURIComponent(t)+"="+encodeURIComponent(null==o?"":o)};if(null==t)return"";if(Array.isArray(t)||t.jquery&&!v.isPlainObject(t))v.each(t,(function(){b(this.name,this.value)}));else for(o in t)Ne(o,t[o],e,b);return p.join("&")},v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var t=v.prop(this,"elements");return t?v.makeArray(t):this})).filter((function(){var t=this.type;return this.name&&!v(this).is(":disabled")&&_e.test(this.nodeName)&&!Xe.test(t)&&(this.checked||!At.test(t))})).map((function(t,e){var o=v(this).val();return null==o?null:Array.isArray(o)?v.map(o,(function(t){return{name:e.name,value:t.replace(Le,"\r\n")}})):{name:e.name,value:o.replace(Le,"\r\n")}})).get()}});var we=/%20/g,xe=/#.*$/,Te=/([?&])_=[^&]*/,Ce=/^(.*?):[ \t]*([^\r\n]*)$/gm,Se=/^(?:GET|HEAD)$/,ke=/^\/\//,Ee={},De={},Pe="*/".concat("*"),je=q.createElement("a");function Ie(t){return function(e,o){"string"!=typeof e&&(o=e,e="*");var p,b=0,n=e.toLowerCase().match(E)||[];if(u(o))for(;p=n[b++];)"+"===p[0]?(p=p.slice(1)||"*",(t[p]=t[p]||[]).unshift(o)):(t[p]=t[p]||[]).push(o)}}function Fe(t,e,o,p){var b={},n=t===De;function M(z){var c;return b[z]=!0,v.each(t[z]||[],(function(t,z){var r=z(e,o,p);return"string"!=typeof r||n||b[r]?n?!(c=r):void 0:(e.dataTypes.unshift(r),M(r),!1)})),c}return M(e.dataTypes[0])||!b["*"]&&M("*")}function He(t,e){var o,p,b=v.ajaxSettings.flatOptions||{};for(o in e)void 0!==e[o]&&((b[o]?t:p||(p={}))[o]=e[o]);return p&&v.extend(!0,t,p),t}je.href=ve.href,v.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ve.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(ve.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Pe,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":v.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(t,e){return e?He(He(t,v.ajaxSettings),e):He(v.ajaxSettings,t)},ajaxPrefilter:Ie(Ee),ajaxTransport:Ie(De),ajax:function(t,e){"object"==typeof t&&(e=t,t=void 0),e=e||{};var o,b,n,M,z,c,r,i,a,O,s=v.ajaxSetup({},e),l=s.context||s,d=s.context&&(l.nodeType||l.jquery)?v(l):v.event,A=v.Deferred(),u=v.Callbacks("once memory"),f=s.statusCode||{},h={},W={},m="canceled",g={readyState:0,getResponseHeader:function(t){var e;if(r){if(!M)for(M={};e=Ce.exec(n);)M[e[1].toLowerCase()+" "]=(M[e[1].toLowerCase()+" "]||[]).concat(e[2]);e=M[t.toLowerCase()+" "]}return null==e?null:e.join(", ")},getAllResponseHeaders:function(){return r?n:null},setRequestHeader:function(t,e){return null==r&&(t=W[t.toLowerCase()]=W[t.toLowerCase()]||t,h[t]=e),this},overrideMimeType:function(t){return null==r&&(s.mimeType=t),this},statusCode:function(t){var e;if(t)if(r)g.always(t[g.status]);else for(e in t)f[e]=[f[e],t[e]];return this},abort:function(t){var e=t||m;return o&&o.abort(e),R(0,e),this}};if(A.promise(g),s.url=((t||s.url||ve.href)+"").replace(ke,ve.protocol+"//"),s.type=e.method||e.type||s.method||s.type,s.dataTypes=(s.dataType||"*").toLowerCase().match(E)||[""],null==s.crossDomain){c=q.createElement("a");try{c.href=s.url,c.href=c.href,s.crossDomain=je.protocol+"//"+je.host!=c.protocol+"//"+c.host}catch(t){s.crossDomain=!0}}if(s.data&&s.processData&&"string"!=typeof s.data&&(s.data=v.param(s.data,s.traditional)),Fe(Ee,s,e,g),r)return g;for(a in(i=v.event&&s.global)&&0==v.active++&&v.event.trigger("ajaxStart"),s.type=s.type.toUpperCase(),s.hasContent=!Se.test(s.type),b=s.url.replace(xe,""),s.hasContent?s.data&&s.processData&&0===(s.contentType||"").indexOf("application/x-www-form-urlencoded")&&(s.data=s.data.replace(we,"+")):(O=s.url.slice(b.length),s.data&&(s.processData||"string"==typeof s.data)&&(b+=(ye.test(b)?"&":"?")+s.data,delete s.data),!1===s.cache&&(b=b.replace(Te,"$1"),O=(ye.test(b)?"&":"?")+"_="+Re.guid+++O),s.url=b+O),s.ifModified&&(v.lastModified[b]&&g.setRequestHeader("If-Modified-Since",v.lastModified[b]),v.etag[b]&&g.setRequestHeader("If-None-Match",v.etag[b])),(s.data&&s.hasContent&&!1!==s.contentType||e.contentType)&&g.setRequestHeader("Content-Type",s.contentType),g.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+("*"!==s.dataTypes[0]?", "+Pe+"; q=0.01":""):s.accepts["*"]),s.headers)g.setRequestHeader(a,s.headers[a]);if(s.beforeSend&&(!1===s.beforeSend.call(l,g,s)||r))return g.abort();if(m="abort",u.add(s.complete),g.done(s.success),g.fail(s.error),o=Fe(De,s,e,g)){if(g.readyState=1,i&&d.trigger("ajaxSend",[g,s]),r)return g;s.async&&s.timeout>0&&(z=p.setTimeout((function(){g.abort("timeout")}),s.timeout));try{r=!1,o.send(h,R)}catch(t){if(r)throw t;R(-1,t)}}else R(-1,"No Transport");function R(t,e,M,c){var a,O,q,h,W,m=e;r||(r=!0,z&&p.clearTimeout(z),o=void 0,n=c||"",g.readyState=t>0?4:0,a=t>=200&&t<300||304===t,M&&(h=function(t,e,o){for(var p,b,n,M,z=t.contents,c=t.dataTypes;"*"===c[0];)c.shift(),void 0===p&&(p=t.mimeType||e.getResponseHeader("Content-Type"));if(p)for(b in z)if(z[b]&&z[b].test(p)){c.unshift(b);break}if(c[0]in o)n=c[0];else{for(b in o){if(!c[0]||t.converters[b+" "+c[0]]){n=b;break}M||(M=b)}n=n||M}if(n)return n!==c[0]&&c.unshift(n),o[n]}(s,g,M)),!a&&v.inArray("script",s.dataTypes)>-1&&v.inArray("json",s.dataTypes)<0&&(s.converters["text script"]=function(){}),h=function(t,e,o,p){var b,n,M,z,c,r={},i=t.dataTypes.slice();if(i[1])for(M in t.converters)r[M.toLowerCase()]=t.converters[M];for(n=i.shift();n;)if(t.responseFields[n]&&(o[t.responseFields[n]]=e),!c&&p&&t.dataFilter&&(e=t.dataFilter(e,t.dataType)),c=n,n=i.shift())if("*"===n)n=c;else if("*"!==c&&c!==n){if(!(M=r[c+" "+n]||r["* "+n]))for(b in r)if((z=b.split(" "))[1]===n&&(M=r[c+" "+z[0]]||r["* "+z[0]])){!0===M?M=r[b]:!0!==r[b]&&(n=z[0],i.unshift(z[1]));break}if(!0!==M)if(M&&t.throws)e=M(e);else try{e=M(e)}catch(t){return{state:"parsererror",error:M?t:"No conversion from "+c+" to "+n}}}return{state:"success",data:e}}(s,h,g,a),a?(s.ifModified&&((W=g.getResponseHeader("Last-Modified"))&&(v.lastModified[b]=W),(W=g.getResponseHeader("etag"))&&(v.etag[b]=W)),204===t||"HEAD"===s.type?m="nocontent":304===t?m="notmodified":(m=h.state,O=h.data,a=!(q=h.error))):(q=m,!t&&m||(m="error",t<0&&(t=0))),g.status=t,g.statusText=(e||m)+"",a?A.resolveWith(l,[O,m,g]):A.rejectWith(l,[g,m,q]),g.statusCode(f),f=void 0,i&&d.trigger(a?"ajaxSuccess":"ajaxError",[g,s,a?O:q]),u.fireWith(l,[g,m]),i&&(d.trigger("ajaxComplete",[g,s]),--v.active||v.event.trigger("ajaxStop")))}return g},getJSON:function(t,e,o){return v.get(t,e,o,"json")},getScript:function(t,e){return v.get(t,void 0,e,"script")}}),v.each(["get","post"],(function(t,e){v[e]=function(t,o,p,b){return u(o)&&(b=b||p,p=o,o=void 0),v.ajax(v.extend({url:t,type:e,dataType:b,data:o,success:p},v.isPlainObject(t)&&t))}})),v.ajaxPrefilter((function(t){var e;for(e in t.headers)"content-type"===e.toLowerCase()&&(t.contentType=t.headers[e]||"")})),v._evalUrl=function(t,e,o){return v.ajax({url:t,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(t){v.globalEval(t,e,o)}})},v.fn.extend({wrapAll:function(t){var e;return this[0]&&(u(t)&&(t=t.call(this[0])),e=v(t,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&e.insertBefore(this[0]),e.map((function(){for(var t=this;t.firstElementChild;)t=t.firstElementChild;return t})).append(this)),this},wrapInner:function(t){return u(t)?this.each((function(e){v(this).wrapInner(t.call(this,e))})):this.each((function(){var e=v(this),o=e.contents();o.length?o.wrapAll(t):e.append(t)}))},wrap:function(t){var e=u(t);return this.each((function(o){v(this).wrapAll(e?t.call(this,o):t)}))},unwrap:function(t){return this.parent(t).not("body").each((function(){v(this).replaceWith(this.childNodes)})),this}}),v.expr.pseudos.hidden=function(t){return!v.expr.pseudos.visible(t)},v.expr.pseudos.visible=function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)},v.ajaxSettings.xhr=function(){try{return new p.XMLHttpRequest}catch(t){}};var Ue={0:200,1223:204},Ve=v.ajaxSettings.xhr();A.cors=!!Ve&&"withCredentials"in Ve,A.ajax=Ve=!!Ve,v.ajaxTransport((function(t){var e,o;if(A.cors||Ve&&!t.crossDomain)return{send:function(b,n){var M,z=t.xhr();if(z.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(M in t.xhrFields)z[M]=t.xhrFields[M];for(M in t.mimeType&&z.overrideMimeType&&z.overrideMimeType(t.mimeType),t.crossDomain||b["X-Requested-With"]||(b["X-Requested-With"]="XMLHttpRequest"),b)z.setRequestHeader(M,b[M]);e=function(t){return function(){e&&(e=o=z.onload=z.onerror=z.onabort=z.ontimeout=z.onreadystatechange=null,"abort"===t?z.abort():"error"===t?"number"!=typeof z.status?n(0,"error"):n(z.status,z.statusText):n(Ue[z.status]||z.status,z.statusText,"text"!==(z.responseType||"text")||"string"!=typeof z.responseText?{binary:z.response}:{text:z.responseText},z.getAllResponseHeaders()))}},z.onload=e(),o=z.onerror=z.ontimeout=e("error"),void 0!==z.onabort?z.onabort=o:z.onreadystatechange=function(){4===z.readyState&&p.setTimeout((function(){e&&o()}))},e=e("abort");try{z.send(t.hasContent&&t.data||null)}catch(t){if(e)throw t}},abort:function(){e&&e()}}})),v.ajaxPrefilter((function(t){t.crossDomain&&(t.contents.script=!1)})),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(t){return v.globalEval(t),t}}}),v.ajaxPrefilter("script",(function(t){void 0===t.cache&&(t.cache=!1),t.crossDomain&&(t.type="GET")})),v.ajaxTransport("script",(function(t){var e,o;if(t.crossDomain||t.scriptAttrs)return{send:function(p,b){e=v(" diff --git a/resources/js/Builder/Components/BuilderText.vue b/resources/js/Builder/Components/BuilderText.vue new file mode 100644 index 0000000..26fb6a2 --- /dev/null +++ b/resources/js/Builder/Components/BuilderText.vue @@ -0,0 +1,63 @@ + + + + diff --git a/resources/js/Builder/Components/Cards/TitleAndFooterTextCard.vue b/resources/js/Builder/Components/Cards/TitleAndFooterTextCard.vue new file mode 100644 index 0000000..6e26a49 --- /dev/null +++ b/resources/js/Builder/Components/Cards/TitleAndFooterTextCard.vue @@ -0,0 +1,45 @@ + + + diff --git a/resources/js/Builder/Components/Form/FieldInput.vue b/resources/js/Builder/Components/Form/FieldInput.vue new file mode 100644 index 0000000..5838c8a --- /dev/null +++ b/resources/js/Builder/Components/Form/FieldInput.vue @@ -0,0 +1,59 @@ + + + + diff --git a/resources/js/Builder/Components/Form/Form.vue b/resources/js/Builder/Components/Form/Form.vue new file mode 100644 index 0000000..159d012 --- /dev/null +++ b/resources/js/Builder/Components/Form/Form.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/resources/js/Builder/Components/Heros/Classic.vue b/resources/js/Builder/Components/Heros/Classic.vue new file mode 100644 index 0000000..09bb8ed --- /dev/null +++ b/resources/js/Builder/Components/Heros/Classic.vue @@ -0,0 +1,58 @@ + + + diff --git a/resources/js/Components/ActionMessage.vue b/resources/js/Components/ActionMessage.vue index 0f7fd5d..d0cf5e9 100644 --- a/resources/js/Components/ActionMessage.vue +++ b/resources/js/Components/ActionMessage.vue @@ -7,7 +7,7 @@ defineProps({